packages feed

crucible-llvm-0.6: src/Lang/Crucible/LLVM/Ctors.hs

 ------------------------------------------------------------------------
-- |
-- Module           : Lang.Crucible.LLVM.Ctors
-- Description      : Extract and manipulate the @llvm.global_ctors@ variable
-- Copyright        : (c) Galois, Inc 2018
-- License          : BSD3
-- Maintainer       : Rob Dockins <rdockins@galois.com>
-- Stability        : provisional
------------------------------------------------------------------------
{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE DeriveDataTypeable    #-}
{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE ImplicitParams        #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeOperators         #-}

module Lang.Crucible.LLVM.Ctors
  ( Ctor(..)
  , globalCtors
  , callCtors
  , callAllCtors
  , callCtorsCFG
  ) where

import           Data.Data (Data)
import           Data.IORef (newIORef)
import           Data.String(fromString)
import           Data.Typeable (Typeable)
import qualified Data.Text as Text
import           GHC.Generics (Generic)
import           Data.Parameterized.Nonce

import           Control.Monad (forM, forM_)
import           Control.Monad.Except (MonadError(..))
import           Data.List (find, sortBy)
import           Data.Ord (comparing, Down(..))
import           Data.Maybe (fromMaybe)

import qualified Text.LLVM.AST as L

import           Lang.Crucible.LLVM.Translation.Instruction (callOrdinaryFunction)
import           Lang.Crucible.LLVM.Translation.Monad (LLVMGenerator, LLVMState(..))

-- Generating CFGs

import           Data.Map.Strict (empty)
import           Data.Text (Text)
import           GHC.TypeNats

import qualified Data.Parameterized.Context.Unsafe as Ctx

import           What4.FunctionName (functionNameFromText)
import           What4.ProgramLoc (Position(InternalPos))

import qualified Lang.Crucible.CFG.Core as Core
import           Lang.Crucible.CFG.Expr (App(EmptyApp))
import           Lang.Crucible.CFG.Generator (FunctionDef, defineFunction)
import           Lang.Crucible.CFG.Reg (Expr(App))
import qualified Lang.Crucible.CFG.Reg as Reg
import           Lang.Crucible.CFG.SSAConversion (toSSA)
import           Lang.Crucible.FunctionHandle (HandleAllocator, mkHandle')
import           Lang.Crucible.Types (UnitType, TypeRepr(UnitRepr))
import           Lang.Crucible.LLVM.Extension (LLVM, ArchWidth)
import           Lang.Crucible.LLVM.Translation.Monad (LLVMContext, _llvmTypeCtx, malformedLLVMModule)
import           Lang.Crucible.LLVM.Types (HasPtrWidth)

{- Example:

@llvm.global_ctors = appending global [3 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_HkdfTest.cpp, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_gtest_all.cc, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_iostream.cpp, i8* null }]

-}

-- | A representation of well-typed inhabitants of the @llvm.global_ctors@ array
--
-- See https://llvm.org/docs/LangRef.html#the-llvm-global-ctors-global-variable
data Ctor = Ctor
  { ctorPriority :: Integer
  , ctorFunction :: L.Symbol
  , ctorData     :: Maybe L.Symbol
  } deriving (Data, Eq, Generic, Ord, Show, Typeable)

-- | Get the global variable representing @llvm.global_ctors@.
getGlobalCtorsGlobal :: L.Module -> Maybe L.Global
getGlobalCtorsGlobal mod_ =
  let symb = L.Symbol "llvm.global_ctors"
  in find (\x -> L.globalSym x == symb) (L.modGlobals mod_)

-- | Unpack a @ctors@ value of type @{ i32, void ()*, i8* }@ from the AST
extractCtors :: L.Value -> Maybe Ctor
extractCtors val =
  case val of
    -- This is permissive about the integer widths... No reason to get caught up.
    L.ValStruct [ L.Typed (L.PrimType (L.Integer _w0)) (L.ValInteger priority)
                , L.Typed (L.PtrTo (L.FunTy (L.PrimType L.Void) [] _bool)) (L.ValSymbol symb)
                , L.Typed (L.PtrTo (L.PrimType (L.Integer _w1))) data0_
                ] -> Just . Ctor priority symb $
                       case data0_ of
                         L.ValSymbol data_ -> Just data_
                         _                 -> Nothing
    _ -> Nothing

-- | Unpack and sort the values in @llvm.global_ctors@ by priority
globalCtors :: (MonadError String m)
            => L.Module
            -> m [Ctor]
globalCtors mod_ =
  case getGlobalCtorsGlobal mod_ >>= L.globalValue of -- in the Maybe monad
    Just (L.ValArray _ty vs) -> do

      -- Assert that each value is of the expected type.
      vs' <- forM vs $ \v ->
        fromMaybe
          (throwError $ unlines $ [ "Ill-typed value in llvm.global_ctors: "
                                  , show v
                                  ])
          (pure <$> extractCtors v)

      -- Sort the values by priority, highest to lowest.
      pure (sortBy (comparing (Down . ctorPriority)) vs')

    -- @llvm.ctors value not found, assume there are no global_ctors to run
    Nothing -> return []

    Just v  -> throwError $ unlines $
      [ "llvm.global_ctors wasn't an array"
      , "Value: " ++ show v
      ]

----------------------------------------------------------------------
-- ** callCtors

-- | Call some or all of the functions in @llvm.global_ctors@
callCtors :: (Ctor -> Bool) -- ^ Filter function
          -> L.Module
          -> LLVMGenerator s arch UnitType (Expr LLVM s UnitType)
callCtors select mod_ = do
  let err msg = malformedLLVMModule "Error loading @llvm.global_ctors" [fromString msg]
  let ty = L.FunTy (L.PrimType L.Void) [] False

  ctors <- either err (pure . filter select) (globalCtors mod_)
  forM_ ctors $ \ctor ->
    callOrdinaryFunction Nothing False ty (L.ValSymbol (ctorFunction ctor)) [] (\_ -> pure ())
  return (App EmptyApp)

-- | Call each function in @llvm.global_ctors@ in order of decreasing priority
callAllCtors :: L.Module -> LLVMGenerator s arch UnitType (Expr LLVM s UnitType)
callAllCtors = callCtors (const True)

----------------------------------------------------------------------
-- ** callCtorsCFG

-- | Make a 'LLVMGenerator' into a CFG by making it a function with no arguments
-- that returns unit.
generatorToCFG :: forall arch wptr ret. (HasPtrWidth wptr, wptr ~ ArchWidth arch, 16 <= wptr)
               => Text
               -> HandleAllocator
               -> LLVMContext arch
               -> (forall s. LLVMGenerator s arch ret (Expr LLVM s ret))
               -> TypeRepr ret
               -> IO (Core.SomeCFG LLVM Core.EmptyCtx ret)
generatorToCFG name halloc llvmctx gen ret = do
  ref <- newIORef []
  let ?lc = _llvmTypeCtx llvmctx
  let def :: forall args. FunctionDef LLVM (LLVMState arch) args ret IO
      def _inputs = (state, gen)
        where state = LLVMState { _identMap     = empty
                                , _blockInfoMap = empty
                                , llvmContext   = llvmctx
                                , _translationWarnings = ref
                                , _functionSymbol = L.Symbol (Text.unpack name)
                                }

  hand <- mkHandle' halloc (functionNameFromText name) Ctx.empty ret
  sng <- newIONonceGenerator
  (Reg.SomeCFG g, []) <- defineFunction InternalPos sng hand def
  return $! toSSA g

-- | Create a CFG that calls some of the functions in @llvm.global_ctors@.
callCtorsCFG :: forall arch wptr. (HasPtrWidth wptr, wptr ~ ArchWidth arch, 16 <= wptr)
             => (Ctor -> Bool) -- ^ Filter function
             -> L.Module
             -> HandleAllocator
             -> LLVMContext arch
             -> IO (Core.SomeCFG LLVM Core.EmptyCtx UnitType)
callCtorsCFG select mod_ halloc llvmctx = do
  generatorToCFG "llvm_global_ctors" halloc llvmctx (callCtors select mod_) UnitRepr