----------------------------------------------------------------------- -- | -- Module : Lang.Crucible.LLVM.Translation.Monad -- Description : Translation monad for LLVM and support operations -- Copyright : (c) Galois, Inc 2018 -- License : BSD3 -- Maintainer : Rob Dockins -- Stability : provisional ----------------------------------------------------------------------- {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} module Lang.Crucible.LLVM.Translation.Monad ( -- * Generator monad LLVMGenerator , LLVMGenerator' , LLVMState(..) , identMap , blockInfoMap , translationWarnings , functionSymbol , addWarning , LLVMTranslationWarning(..) , IdentMap , LLVMBlockInfo(..) , LLVMBlockInfoMap , buildBlockInfoMap , initialState , getMemVar -- * Malformed modules , MalformedLLVMModule(..) , malformedLLVMModule , renderMalformedLLVMModule -- * LLVMContext , LLVMContext(..) , llvmTypeCtx , mkLLVMContext , useTypedVal ) where import Control.Lens hiding (op, (:>), to, from ) import Control.Monad (unless) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.State.Strict (MonadState(..)) import Data.IORef (IORef, modifyIORef) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Data.Set (Set) import Data.Text (Text) import qualified Text.LLVM.AST as L import qualified Data.Parameterized.Context as Ctx import Data.Parameterized.NatRepr as NatRepr import Data.Parameterized.Some import Lang.Crucible.CFG.Generator import Lang.Crucible.Panic ( panic ) import Lang.Crucible.LLVM.DataLayout import Lang.Crucible.LLVM.Extension import Lang.Crucible.LLVM.MalformedLLVMModule import Lang.Crucible.LLVM.MemModel import Lang.Crucible.LLVM.MemType import Lang.Crucible.LLVM.Translation.BlockInfo import Lang.Crucible.LLVM.Translation.Types import Lang.Crucible.LLVM.TypeContext import Lang.Crucible.Types ------------------------------------------------------------------------ -- ** LLVMContext -- | Information about the LLVM module. data LLVMContext arch = LLVMContext { -- | Map LLVM symbols to their associated state. llvmArch :: ArchRepr arch , llvmPtrWidth :: forall a. (16 <= (ArchWidth arch) => NatRepr (ArchWidth arch) -> a) -> a , llvmMemVar :: GlobalVar Mem , _llvmTypeCtx :: TypeContext -- | For each global variable symbol, compute the set of -- aliases to that symbol , llvmGlobalAliases :: Map L.Symbol (Set L.GlobalAlias) -- | For each function symbol, compute the set of -- aliases to that symbol , llvmFunctionAliases :: Map L.Symbol (Set L.GlobalAlias) } llvmTypeCtx :: Simple Lens (LLVMContext arch) TypeContext llvmTypeCtx = lens _llvmTypeCtx (\s v -> s{ _llvmTypeCtx = v }) mkLLVMContext :: GlobalVar Mem -> L.Module -> IO (Some LLVMContext) mkLLVMContext mvar m = do let (errs, typeCtx) = typeContextFromModule m unless (null errs) $ malformedLLVMModule "Failed to construct LLVM type context" errs let dl = llvmDataLayout typeCtx case mkNatRepr (ptrBitwidth dl) of Some (wptr :: NatRepr wptr) | Just LeqProof <- testLeq (knownNat @16) wptr -> withPtrWidth wptr $ do let archRepr = X86Repr wptr -- FIXME! we should select the architecture based on -- the target triple, but llvm-pretty doesn't capture this -- currently. let ctx :: LLVMContext (X86 wptr) ctx = LLVMContext { llvmArch = archRepr , llvmMemVar = mvar , llvmPtrWidth = \x -> x wptr , _llvmTypeCtx = typeCtx , llvmGlobalAliases = mempty -- these are computed later , llvmFunctionAliases = mempty -- these are computed later } return (Some ctx) _ -> fail ("Cannot load LLVM bitcode file with illegal pointer width: " ++ show (dl^.ptrSize)) -- | A monad providing state and continuations for translating LLVM expressions -- to CFGs. type LLVMGenerator s arch ret a = (?lc :: TypeContext, HasPtrWidth (ArchWidth arch)) => Generator LLVM s (LLVMState arch) ret IO a -- | @LLVMGenerator@ without the constraint, can be nested further inside monads. type LLVMGenerator' s arch ret = Generator LLVM s (LLVMState arch) ret IO -- LLVMState getMemVar :: LLVMGenerator s arch reg (GlobalVar Mem) getMemVar = llvmMemVar . llvmContext <$> get -- | Maps identifiers to an associated register or defined expression. type IdentMap s = Map L.Ident (Either (Some (Reg s)) (Some (Atom s))) -- | A warning generated during translation data LLVMTranslationWarning = LLVMTranslationWarning L.Symbol -- ^ Function name in which the warning was generated Position -- ^ Source position where the warning was generated Text -- ^ Description of the warning data LLVMState arch s = LLVMState { -- | Map from identifiers to associated register shape _identMap :: !(IdentMap s) , _blockInfoMap :: !(LLVMBlockInfoMap s) , llvmContext :: LLVMContext arch , _translationWarnings :: IORef [LLVMTranslationWarning] , _functionSymbol :: L.Symbol } identMap :: Lens' (LLVMState arch s) (IdentMap s) identMap = lens _identMap (\s v -> s { _identMap = v }) blockInfoMap :: Lens' (LLVMState arch s) (LLVMBlockInfoMap s) blockInfoMap = lens _blockInfoMap (\s v -> s { _blockInfoMap = v }) translationWarnings :: Lens' (LLVMState arch s) (IORef [LLVMTranslationWarning]) translationWarnings = lens _translationWarnings (\s v -> s { _translationWarnings = v }) functionSymbol :: Lens' (LLVMState arch s) L.Symbol functionSymbol = lens _functionSymbol (\s v -> s{ _functionSymbol = v }) addWarning :: Text -> LLVMGenerator s arch ret () addWarning warn = do r <- use translationWarnings s <- use functionSymbol p <- getPosition liftIO (modifyIORef r ((LLVMTranslationWarning s p warn):)) -- | Given a list of LLVM formal parameters and a corresponding crucible -- register assignment, build an IdentMap mapping LLVM identifiers to -- corresponding crucible registers. buildIdentMap :: (?lc :: TypeContext, HasPtrWidth wptr) => [L.Typed L.Ident] -> Bool -- ^ varargs -> CtxRepr ctx -> Ctx.Assignment (Atom s) ctx -> IdentMap s -> IdentMap s buildIdentMap ts True ctx asgn m = -- Vararg functions are translated as taking a vector of extra arguments packBase (VectorRepr AnyRepr) ctx asgn $ \_x ctx' asgn' -> buildIdentMap ts False ctx' asgn' m buildIdentMap [] _ ctx _ m | Ctx.null ctx = m | otherwise = panic "crucible-llvm:Translation.buildIdentMap" [ "buildIdentMap: passed arguments do not match LLVM input signature" ] buildIdentMap (ti:ts) _ ctx asgn m = do let ty = case liftMemType (L.typedType ti) of Right t -> t Left err -> panic "crucible-llvm:Translation.buildIdentMap" [ "Error attempting to lift type " <> show ti , show err ] packType ty ctx asgn $ \x ctx' asgn' -> buildIdentMap ts False ctx' asgn' (Map.insert (L.typedValue ti) (Right x) m) -- | Build the initial LLVM generator state upon entry to to the entry point of a function. initialState :: (?lc :: TypeContext, HasPtrWidth wptr) => L.Define -> LLVMContext arch -> CtxRepr args -> Ctx.Assignment (Atom s) args -> IORef [LLVMTranslationWarning] -> LLVMState arch s initialState d llvmctx args asgn warnRef = let m = buildIdentMap (reverse (L.defArgs d)) (L.defVarArgs d) args asgn Map.empty in LLVMState { _identMap = m , _blockInfoMap = Map.empty , llvmContext = llvmctx , _translationWarnings = warnRef , _functionSymbol = L.defName d } -- | Given an LLVM type and a type context and a register assignment, -- peel off the rightmost register from the assignment, which is -- expected to match the given LLVM type. Pass the register and -- the remaining type and register context to the given continuation. -- -- This procedure is used to set up the initial state of the -- registers at the entry point of a function. packType :: HasPtrWidth wptr => MemType -> CtxRepr ctx -> Ctx.Assignment (Atom s) ctx -> (forall ctx'. Some (Atom s) -> CtxRepr ctx' -> Ctx.Assignment (Atom s) ctx' -> a) -> a packType tp ctx asgn k = llvmTypeAsRepr tp $ \repr -> packBase repr ctx asgn k packBase :: TypeRepr tp -> CtxRepr ctx -> Ctx.Assignment (Atom s) ctx -> (forall ctx'. Some (Atom s) -> CtxRepr ctx' -> Ctx.Assignment (Atom s) ctx' -> a) -> a packBase ctp ctx0 asgn k = case ctx0 of ctx' Ctx.:> ctp' -> case testEquality ctp ctp' of Nothing -> error $ unwords ["crucible type mismatch",show ctp,show ctp'] Just Refl -> let asgn' = Ctx.init asgn idx = Ctx.nextIndex (Ctx.size asgn') in k (Some (asgn Ctx.! idx)) ctx' asgn' _ -> error "packType: ran out of actual arguments!"