Change memory addresses.

This changes the way memory addresses are represented to fix bugs
that could arrise if one can jump between two segments.  It replaces
the SegmentAddr type with two types:

* `MemAddr w` represents an address that is not necessarily valid.  It
  could either be an absolute address or an address relative to some
  non-fixed segment.
* `MemSegmentOff w` represents a legal offset into a memory segment.
  It must point to at least one valid byte of memory.
This commit is contained in:
Joe Hendrix 2017-07-17 15:37:05 -07:00
parent 0e66a3dfea
commit 2eaa823372
No known key found for this signature in database
GPG Key ID: 8DFA5FF784098C4F
15 changed files with 637 additions and 574 deletions

View File

@ -173,7 +173,7 @@ liftST :: ST s r -> CrucGen arch ids s r
liftST m = CrucGen $ \s cont -> m >>= cont s
getPos :: CrucGen arch ids s C.Position
getPos = undefined
getPos = C.BinaryPos <$> gets binaryPath <*> gets codeAddr
addStmt :: C.Stmt s -> CrucGen arch ids s ()
addStmt stmt = seq stmt $ do

View File

@ -39,6 +39,7 @@ module Data.Macaw.AbsDomain.AbsState
, codePointerSet
, AbsDomain(..)
, AbsProcessorState
, absMem
, curAbsStack
, absInitialRegs
, indexBounds
@ -68,7 +69,7 @@ import Data.Int
import Data.Map (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe
import Data.Parameterized.Classes (EqF(..), OrdF(..), ShowF(..))
import Data.Parameterized.Classes (EqF(..), ShowF(..))
import Data.Parameterized.Map (MapF)
import qualified Data.Parameterized.Map as MapF
import Data.Parameterized.NatRepr
@ -137,13 +138,15 @@ data AbsValue w (tp :: Type)
-- ^ A Boolean constant
| forall n . (tp ~ BVType n) => FinSet !(Set Integer)
-- ^ Denotes that this value can take any one of the fixed set.
| (tp ~ BVType w) => CodePointers !(Set (SegmentedAddr w)) !Bool
-- ^ A possibly empty set of values that either point to a code segment.
-- This Boolean indicates whether this set contains the address 0.
| (tp ~ BVType w) => StackOffset !(SegmentedAddr w) !(Set Int64)
| (tp ~ BVType w) => CodePointers !(Set (MemSegmentOff w)) !Bool
-- ^ Represents that all values point to a bounded set of
-- addresses in an executable segment or the constant zero. The
-- set contains the possible addresses, and the Boolean indicates
-- whether this set contains the address 0.
| (tp ~ BVType w) => StackOffset !(MemAddr w) !(Set Int64)
-- ^ Offset of stack from the beginning of the block at the given address.
-- First argument is address of block.
| (tp ~ BVType w) => SomeStackOffset !(SegmentedAddr w)
| (tp ~ BVType w) => SomeStackOffset !(MemAddr w)
-- ^ An offset to the stack at some offset.
| forall n . (tp ~ BVType n) => StridedInterval !(SI.StridedInterval n)
-- ^ A strided interval
@ -172,16 +175,15 @@ data SomeFinSet tp where
-- | Given a segmented addr and flag indicating if zero is contained return the underlying
-- integer set and the set of addresses that had no base.
partitionAbsoluteAddrs :: MemWidth w
=> Set (SegmentedAddr w)
=> Set (MemSegmentOff w)
-> Bool
-> (Set Integer, Set (SegmentedAddr w))
-> (Set Integer, Set (MemSegmentOff w))
partitionAbsoluteAddrs addrSet b = foldl' f (s0, Set.empty) addrSet
where s0 = if b then Set.singleton 0 else Set.empty
f (intSet,badSet) addr =
case segmentBase (addrSegment addr) of
Just base -> seq intSet' $ (intSet', badSet)
where intSet' = Set.insert w intSet
w = toInteger base + toInteger (addr^.addrOffset)
case msegAddr addr of
Just addrVal -> seq intSet' $ (intSet', badSet)
where intSet' = Set.insert (toInteger addrVal) intSet
Nothing -> seq badSet' $ (intSet, badSet')
where badSet' = Set.insert addr badSet
@ -197,13 +199,11 @@ asFinSet _ (CodePointers s True)
| Set.null s = IsFin (Set.singleton 0)
asFinSet nm (CodePointers addrSet b) = go (Set.toList addrSet) $! s0
where s0 = if b then Set.singleton 0 else Set.empty
go :: [SegmentedAddr w] -> Set Integer -> SomeFinSet (BVType w)
go :: [MemSegmentOff w] -> Set Integer -> SomeFinSet (BVType w)
go [] s = debug DAbsInt ("dropping Codeptr " ++ nm) $ IsFin s
go (a:r) s =
case segmentBase (addrSegment a) of
Just base -> go r $! s'
where v = toInteger base + toInteger (a^.addrOffset)
s' = Set.insert v s
go (seg_off: r) s =
case msegAddr seg_off of
Just addr -> go r $! Set.insert (toInteger addr) s
Nothing -> NotFin
asFinSet _ _ = NotFin
@ -214,7 +214,7 @@ asFinSet _ _ = NotFin
-- | otherwise = debug DAbsInt ("dropping Codeptr " ++ nm) $ Just s
-- asFinSet64 _ _ = Nothing
codePointerSet :: AbsValue w tp -> Set (SegmentedAddr w)
codePointerSet :: AbsValue w tp -> Set (MemSegmentOff w)
codePointerSet (CodePointers s _) = s
codePointerSet _ = Set.empty
@ -240,10 +240,10 @@ instance Eq (AbsValue w tp) where
instance EqF (AbsValue w) where
eqF = (==)
instance Show (AbsValue w tp) where
instance MemWidth w => Show (AbsValue w tp) where
show = show . pretty
instance Pretty (AbsValue w tp) where
instance MemWidth w => Pretty (AbsValue w tp) where
pretty (BoolConst b) = text (show b)
pretty (FinSet s) = text "finset" <+> ppIntegerSet s
pretty (CodePointers s b) = text "code" <+> ppSet (s0 ++ sd)
@ -277,9 +277,9 @@ ppIntegerSet s = ppSet (ppv <$> Set.toList s)
concretize :: MemWidth w => AbsValue w tp -> Maybe (Set Integer)
concretize (FinSet s) = Just s
concretize (CodePointers s b) = Just $ Set.fromList $
[ toInteger base + toInteger (addr^.addrOffset)
| addr <- Set.toList s
, base <- maybeToList (segmentBase (addrSegment addr))
[ toInteger addr
| mseg <- Set.toList s
, addr <- maybeToList (msegAddr mseg)
]
++ (if b then [0] else [])
concretize (SubValue _ _) = Nothing -- we know nothing about _all_ values
@ -331,9 +331,6 @@ isEmpty _ = False
-------------------------------------------------------------------------------
-- Joining abstract values
ppSegAddrSet :: Set (SegmentedAddr w) -> Doc
ppSegAddrSet s = ppSet (text . show <$> Set.toList s)
-- | Join the old and new states and return the updated state iff
-- the result is larger than the old state.
-- This also returns any addresses that are discarded during joining.
@ -345,9 +342,9 @@ joinAbsValue x y
| Set.null s = r
| otherwise = debug DAbsInt ("dropping " ++ show dropped ++ "\n" ++ show x ++ "\n" ++ show y ++ "\n") r
where (r,s) = runState (joinAbsValue' x y) Set.empty
dropped = ppSegAddrSet s
dropped = ppSet (text . show <$> Set.toList s)
addWords :: Set (SegmentedAddr w) -> State (Set (SegmentedAddr w)) ()
addWords :: Set (MemSegmentOff w) -> State (Set (MemSegmentOff w)) ()
addWords s = modify $ Set.union s
-- | Join the old and new states and return the updated state iff
@ -356,7 +353,7 @@ addWords s = modify $ Set.union s
joinAbsValue' :: MemWidth w
=> AbsValue w tp
-> AbsValue w tp
-> State (Set (SegmentedAddr w)) (Maybe (AbsValue w tp))
-> State (Set (MemSegmentOff w)) (Maybe (AbsValue w tp))
joinAbsValue' TopV x = do
addWords (codePointerSet x)
return $! Nothing
@ -630,7 +627,7 @@ bvsub :: forall w u
-> AbsValue w (BVType u)
-> AbsValue w (BVType u)
-> AbsValue w (BVType u)
bvsub _mem w (CodePointers s b) (FinSet t)
bvsub mem w (CodePointers s b) (FinSet t)
-- If we just have zero.
| Set.null s && b = FinSet (Set.map (toUnsigned w . negate) t)
| all isJust vals && (not b || Set.singleton 0 == t) =
@ -639,15 +636,16 @@ bvsub _mem w (CodePointers s b) (FinSet t)
-- TODO: Fix this.
-- debug DAbsInt ("drooping " ++ show (ppIntegerSet s) ++ " " ++ show (ppIntegerSet t)) $
-- setL (stridedInterval . SI.fromFoldable w) FinSet (toInteger <$> vals)
where vals :: [Maybe (SegmentedAddr w)]
where vals :: [Maybe (MemSegmentOff w)]
vals = do
x <- Set.toList s
y <- Set.toList t
if toInteger (x^.addrOffset) >= y then
return $ Just $ x & addrOffset -~ fromInteger y
else
return $ Nothing
let z = relativeSegmentAddr x & incAddr (negate y)
case asSegmentOff mem z of
Just z_mseg | segmentFlags (msegSegment z_mseg) `Perm.hasPerm` Perm.execute ->
pure (Just z_mseg)
_ ->
pure Nothing
bvsub _ _ xv@(CodePointers xs xb) (CodePointers ys yb)
-- If we just have zero.
| Set.null ys && yb = xv
@ -665,10 +663,7 @@ bvsub _ _ xv@(CodePointers xs xb) (CodePointers ys yb)
vals = do
x <- Set.toList xs
y <- Set.toList ys
if segmentIndex (addrSegment x) == segmentIndex (addrSegment y) then
pure $ Just $ toInteger (x^.addrOffset) - toInteger (y^.addrOffset)
else do
pure $ Nothing
pure (relativeSegmentAddr x `diffAddr` relativeSegmentAddr y)
bvsub _ w (FinSet s) (asFinSet "bvsub3" -> IsFin t) =
setL (stridedInterval . SI.fromFoldable w) FinSet $ do
x <- Set.toList s
@ -719,10 +714,12 @@ bvmul _ _ _ = TopV
bvand :: MemWidth w
=> NatRepr u
-> AbsValue w (BVType u)
-> AbsValue w (BVType u)
-> AbsValue w (BVType u)
bvand _w (asFinSet "bvand" -> IsFin s) (asConcreteSingleton -> Just v) = FinSet (Set.map (flip (.&.) v) s)
bvand _w (asConcreteSingleton -> Just v) (asFinSet "bvand" -> IsFin s) = FinSet (Set.map ((.&.) v) s)
-> AbsValue w (BVType u)
bvand _w (asFinSet "bvand" -> IsFin s) (asConcreteSingleton -> Just v) =
FinSet (Set.map (flip (.&.) v) s)
bvand _w (asConcreteSingleton -> Just v) (asFinSet "bvand" -> IsFin s) =
FinSet (Set.map ((.&.) v) s)
bvand _ _ _ = TopV
-- FIXME: generalise
@ -738,12 +735,12 @@ bitop doOp _w (asConcreteSingleton -> Just v) (asFinSet "bvand" -> IsFin s)
= FinSet (Set.map (doOp v) s)
bitop _ _ _ _ = TopV
ppAbsValue :: AbsValue w tp -> Maybe Doc
ppAbsValue :: MemWidth w => AbsValue w tp -> Maybe Doc
ppAbsValue TopV = Nothing
ppAbsValue v = Just (pretty v)
-- | Print a list of Docs vertically separated.
instance ShowF r => PrettyRegValue r (AbsValue w) where
instance (MemWidth w, ShowF r) => PrettyRegValue r (AbsValue w) where
ppValueEq _ TopV = Nothing
ppValueEq r v = Just (text (showF r) <+> text "=" <+> pretty v)
@ -757,23 +754,21 @@ absFalse = BoolConst False
-- | This returns the smallest abstract value that contains the
-- given unsigned integer.
abstractSingleton :: MemWidth w
=> NatRepr w
=> Memory w
-- ^ Width of code pointer
-> (MemWord w -> Maybe (SegmentedAddr w))
-- ^ Predicate that recognizes if the given value is a code
-- pointer.
-> NatRepr n
-> Integer
-> AbsValue w (BVType n)
abstractSingleton code_w is_code w i
| Just Refl <- testEquality w code_w
abstractSingleton mem w i
| Just Refl <- testEquality w (memWidth mem)
, 0 <= i && i <= maxUnsigned w
, Just sa <- is_code (fromInteger i) =
, Just sa <- resolveAbsoluteAddr mem (fromInteger i)
, segmentFlags (msegSegment sa) `Perm.hasPerm` Perm.execute =
CodePointers (Set.singleton sa) False
| 0 <= i && i <= maxUnsigned w = FinSet (Set.singleton i)
| otherwise = error $ "abstractSingleton given bad value: " ++ show i ++ " " ++ show w
concreteStackOffset :: SegmentedAddr w -> Integer -> AbsValue w (BVType w)
concreteStackOffset :: MemAddr w -> Integer -> AbsValue w (BVType w)
concreteStackOffset a o = StackOffset a (Set.singleton (fromInteger o))
------------------------------------------------------------------------
@ -869,7 +864,7 @@ type AbsBlockStack w = Map Int64 (StackEntry w)
absStackJoinD :: MemWidth w
=> AbsBlockStack w
-> AbsBlockStack w
-> State (Bool,Set (SegmentedAddr w)) (AbsBlockStack w)
-> State (Bool,Set (MemSegmentOff w)) (AbsBlockStack w)
absStackJoinD y x = do
-- This attempts to merge information from the new state into the old state.
let entryLeq (o, StackEntry y_tp y_v) =
@ -912,7 +907,7 @@ showSignedHex :: Integer -> ShowS
showSignedHex x | x < 0 = showString "-0x" . showHex (negate x)
| otherwise = showString "0x" . showHex x
ppAbsStack :: AbsBlockStack w -> Doc
ppAbsStack :: MemWidth w => AbsBlockStack w -> Doc
ppAbsStack m = vcat (pp <$> Map.toDescList m)
where pp (o,StackEntry _ v) = text (showSignedHex (toInteger o) " :=") <+> pretty v
@ -978,6 +973,7 @@ instance ( RegisterInfo r
}
instance ( ShowF r
, MemWidth (RegAddrWidth r)
) => Pretty (AbsBlockState r) where
pretty s =
text "registers:" <$$>
@ -990,12 +986,12 @@ instance ( ShowF r
indent 2 (ppAbsStack stack)
jmp_bnds = pretty (s^.initIndexBounds)
instance ShowF r => Show (AbsBlockState r) where
instance (ShowF r, MemWidth (RegAddrWidth r)) => Show (AbsBlockState r) where
show = show . pretty
-- | Update the block state to point to a specific IP address.
setAbsIP :: RegisterInfo r
=> SegmentedAddr (RegAddrWidth r)
=> MemSegmentOff (RegAddrWidth r)
-- ^ The address to set.
-> AbsBlockState r
-> AbsBlockState r
@ -1031,10 +1027,6 @@ data AbsProcessorState r ids
, _indexBounds :: !(Jmp.IndexBounds r ids)
}
-- | The width of an address
absCodeWidth :: AbsProcessorState r ids -> NatRepr (RegAddrWidth r)
absCodeWidth = memWidth . absMem
absInitialRegs :: Simple Lens (AbsProcessorState r ids)
(RegState r (AbsValue (RegAddrWidth r)))
absInitialRegs = lens _absInitialRegs (\s v -> s { _absInitialRegs = v })
@ -1050,12 +1042,8 @@ curAbsStack = lens _curAbsStack (\s v -> s { _curAbsStack = v })
indexBounds :: Simple Lens (AbsProcessorState r ids) (Jmp.IndexBounds r ids)
indexBounds = lens _indexBounds (\s v -> s { _indexBounds = v })
instance ShowF r
=> Show (AbsProcessorState r ids) where
show = show . pretty
-- FIXME
instance (ShowF r)
instance (ShowF r, MemWidth (RegAddrWidth r))
=> Pretty (AbsProcessorState r ids) where
pretty s =
text "registers:" <$$>
@ -1066,6 +1054,10 @@ instance (ShowF r)
| otherwise = text "stack:" <$$>
indent 2 (ppAbsStack stack)
instance (ShowF r, MemWidth (RegAddrWidth r))
=> Show (AbsProcessorState r ids) where
show = show . pretty
initAbsProcessorState :: Memory (RegAddrWidth r)
-- ^ Current state of memory in the processor.
--
@ -1111,31 +1103,30 @@ pruneStack = Map.filter f
-- Transfer Value
-- | Compute abstract value from value and current registers.
transferValue :: ( OrdF (ArchReg a)
, ShowF (ArchReg a)
, MemWidth (ArchAddrWidth a)
transferValue :: forall a ids tp
. ( RegisterInfo (ArchReg a)
, HasCallStack
)
=> AbsProcessorState (ArchReg a) ids
-> Value a ids tp
-> ArchAbsValue a tp
transferValue c v = do
let code_width = absCodeWidth c
is_code addr = do
sa <- absoluteAddrSegment (absMem c) addr
if segmentFlags (addrSegment sa) `Perm.hasPerm` Perm.execute then
Just $! sa
else
Nothing
amap = c^.absAssignments
let amap = c^.absAssignments
aregs = c^.absInitialRegs
case v of
BoolValue b -> BoolConst b
BVValue w i
| 0 <= i && i <= maxUnsigned w -> abstractSingleton code_width is_code w i
| 0 <= i && i <= maxUnsigned w -> abstractSingleton (absMem c) w i
| otherwise -> error $ "transferValue given illegal value " ++ show (pretty v)
-- TODO: Ensure a relocatable value is in code.
RelocatableValue _w i -> CodePointers (Set.singleton i) False
RelocatableValue _w i
| Just addr <- asSegmentOff (absMem c) i
, segmentFlags (msegSegment addr) `Perm.hasPerm` Perm.execute ->
CodePointers (Set.singleton addr) False
| Just addr <- asAbsoluteAddr i ->
FinSet $ Set.singleton $ toInteger addr
| otherwise ->
TopV
-- Invariant: v is in m
AssignedValue a ->
fromMaybe (error $ "Missing assignment for " ++ show (assignId a))
@ -1212,9 +1203,7 @@ finalAbsBlockState c s =
------------------------------------------------------------------------
-- Transfer functions
transferApp :: ( OrdF (ArchReg a)
, ShowF (ArchReg a)
, MemWidth (ArchAddrWidth a)
transferApp :: ( RegisterInfo (ArchReg a)
, HasCallStack
)
=> AbsProcessorState (ArchReg a) ids
@ -1248,7 +1237,7 @@ absEvalCall :: forall r
-- ^ Configuration
-> AbsBlockState r
-- ^ State before call
-> SegmentedAddr (RegAddrWidth r)
-> MemSegmentOff (RegAddrWidth r)
-- ^ Address we are jumping to
-> AbsBlockState r
absEvalCall params ab0 addr =

View File

@ -24,15 +24,14 @@ import Data.Parameterized.NatRepr
import Data.Macaw.AbsDomain.AbsState
import Data.Macaw.CFG
import Data.Macaw.Memory (MemWidth)
import Data.Macaw.Types
-- | Constraints needed for refinement on abstract states.
type RefineConstraints arch
= ( OrdF (ArchReg arch)
, ShowF (ArchReg arch)
, HasRepr (ArchReg arch) TypeRepr
, MemWidth (ArchAddrWidth arch)
= ( RegisterInfo (ArchReg arch)
-- , ShowF (ArchReg arch)
-- , HasRepr (ArchReg arch) TypeRepr
-- , MemWidth (ArchAddrWidth arch)
)
-- FIXME: if val \notin av then we should return bottom

View File

@ -36,10 +36,11 @@ import Data.Macaw.Memory
-- block.
type DisassembleFn arch
= forall ids
. NonceGenerator (ST ids) ids
-> SegmentedAddr (ArchAddrWidth arch)
-- ^ Segment that we are disassembling from
-> MemWord (ArchAddrWidth arch)
. Memory (ArchAddrWidth arch)
-> NonceGenerator (ST ids) ids
-> ArchSegmentOff arch
-- ^ The offset to start reading from.
-> ArchAddrWord arch
-- ^ Maximum offset for this to read from.
-> AbsBlockState (ArchReg arch)
-- ^ Abstract state associated with address that we are disassembling
@ -66,10 +67,12 @@ data ArchitectureInfo arch
-- ^ Return true if architecture register should be preserved across a system call.
, mkInitialAbsState :: !(Memory (RegAddrWidth (ArchReg arch))
-> SegmentedAddr (RegAddrWidth (ArchReg arch))
-> ArchSegmentOff arch
-> AbsBlockState (ArchReg arch))
-- ^ Creates an abstract block state for representing the beginning of a
-- function.
--
-- The address is the entry point of the function.
, absEvalArchFn :: !(forall ids tp
. AbsProcessorState (ArchReg arch) ids
-> ArchFn arch ids tp
@ -81,7 +84,7 @@ data ArchitectureInfo arch
-> AbsProcessorState (ArchReg arch) ids)
-- ^ Evaluates an architecture-specific statement
, postCallAbsState :: AbsBlockState (ArchReg arch)
-> SegmentedAddr (RegAddrWidth (ArchReg arch))
-> ArchSegmentOff arch
-> AbsBlockState (ArchReg arch)
-- ^ Update the abstract state after a function call returns
, identifyReturn :: forall ids
@ -106,7 +109,7 @@ data ArchitectureInfo arch
archPostSyscallAbsState :: ArchitectureInfo arch
-- ^ Architecture information
-> AbsBlockState (ArchReg arch)
-> SegmentedAddr (RegAddrWidth (ArchReg arch))
-> ArchSegmentOff arch
-> AbsBlockState (ArchReg arch)
archPostSyscallAbsState info = withArchConstraints info $ AbsState.absEvalCall params
where params = CallParams { postCallStackDelta = 0

View File

@ -7,7 +7,7 @@ This exports the main CFG modules
module Data.Macaw.CFG
( module Data.Macaw.CFG.Core
, module Data.Macaw.CFG.App
, Data.Macaw.Memory.SegmentedAddr
, Data.Macaw.Memory.MemAddr
) where
import Data.Macaw.CFG.App

View File

@ -8,10 +8,10 @@ types.
{-# LANGUAGE FlexibleContexts #-}
module Data.Macaw.CFG.Block
( Block(..)
, ppBlock
, TermStmt(..)
) where
import Data.Parameterized.Classes
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Word
@ -44,8 +44,7 @@ data TermStmt arch ids
-- occured and the error message recorded.
| TranslateError !(RegState (ArchReg arch) (Value arch ids)) !Text
instance ( OrdF (ArchReg arch)
, ShowF (ArchReg arch)
instance ( RegisterInfo(ArchReg arch)
)
=> Pretty (TermStmt arch ids) where
pretty (FetchAndExecute s) =
@ -75,7 +74,7 @@ data Block arch ids
-- ^ The last statement in the block.
}
instance ArchConstraints arch => Pretty (Block arch ids) where
pretty b = do
text (show (blockLabel b)) PP.<> text ":" <$$>
indent 2 (vcat (pretty <$> blockStmts b) <$$> pretty (blockTerm b))
ppBlock :: ArchConstraints arch => Block arch ids -> Doc
ppBlock b =
text (show (blockLabel b)) PP.<> text ":" <$$>
indent 2 (vcat (ppStmt (text . show) <$> blockStmts b) <$$> pretty (blockTerm b))

View File

@ -54,16 +54,15 @@ module Data.Macaw.CFG.Core
, ppAssignId
, ppLit
, ppValue
, ppStmt
, PrettyF(..)
, ArchConstraints(..)
, PrettyRegValue(..)
-- * Architecture type families
, ArchAddr
, ArchSegmentedAddr
, ArchFn
, ArchReg
, ArchStmt
, RegAddr
, RegAddrWord
, RegAddrWidth
-- * RegisterInfo
, RegisterInfo(..)
@ -77,6 +76,9 @@ module Data.Macaw.CFG.Core
-- ** Synonyms
, ArchAddrWidth
, ArchAddrValue
, ArchAddrWord
, ArchMemAddr
, ArchSegmentOff
) where
import Control.Exception (assert)
@ -103,7 +105,7 @@ import Numeric (showHex)
import Text.PrettyPrint.ANSI.Leijen as PP hiding ((<$>))
import Data.Macaw.CFG.App
import Data.Macaw.Memory (MemWord, MemWidth, SegmentedAddr(..), Endianness(..))
import Data.Macaw.Memory (MemWord, MemWidth, MemAddr, MemSegmentOff, Endianness(..))
import Data.Macaw.Types
-- Note:
@ -172,8 +174,8 @@ instance Show (AssignId ids tp) where
-- | Width of register used to store addresses.
type family RegAddrWidth (r :: Type -> *) :: Nat
-- | The value used to store
type RegAddr r = MemWord (RegAddrWidth r)
-- | A word for the given architecture register type.
type RegAddrWord r = MemWord (RegAddrWidth r)
-- | Type family for defining what a "register" is for this architecture.
--
@ -194,18 +196,19 @@ type family ArchFn (arch :: *) :: * -> Type -> *
--
-- The second type parameter is the ids phantom type used to provide
-- uniqueness of Nonce values that identify assignments.
--
type family ArchStmt (arch :: *) :: * -> *
-- | The type to use for addresses on the architecutre.
type ArchAddr arch = RegAddr (ArchReg arch)
-- | Number of bits in addreses for architecture.
-- | Number of bits in addreses for architecture.
type ArchAddrWidth arch = RegAddrWidth (ArchReg arch)
-- | A segmented addr for a given architecture.
type ArchSegmentedAddr arch = SegmentedAddr (ArchAddrWidth arch)
-- | A word for the given architecture bitwidth.
type ArchAddrWord arch = RegAddrWord (ArchReg arch)
-- | A segmented addr for a given architecture.
type ArchMemAddr arch = MemAddr (ArchAddrWidth arch)
-- | A pair containing a segment and valid offset within the segment.
type ArchSegmentOff arch = MemSegmentOff (ArchAddrWidth arch)
------------------------------------------------------------------------
-- Value, Assignment, AssignRhs declarations.
@ -222,8 +225,8 @@ data Value arch ids tp
| ( tp ~ BVType (ArchAddrWidth arch)
, 1 <= ArchAddrWidth arch
)
=> RelocatableValue !(NatRepr (ArchAddrWidth arch)) !(ArchSegmentedAddr arch)
-- ^ A given memory address.
=> RelocatableValue !(NatRepr (ArchAddrWidth arch)) !(ArchMemAddr arch)
-- ^ A legal memory address
| AssignedValue !(Assignment arch ids tp)
-- ^ Value from an assignment statement.
| Initial !(ArchReg arch tp)
@ -493,7 +496,7 @@ mkRegStateM f = RegState . MapF.fromList <$> traverse g archRegs
where g (Some r) = MapF.Pair r <$> f r
-- Create a pure register state
mkRegState :: RegisterInfo r -- AbsRegState r
mkRegState :: RegisterInfo r
=> (forall tp . r tp -> f tp)
-> RegState r f
mkRegState f = runIdentity (mkRegStateM (return . f))
@ -530,20 +533,20 @@ ppLit w i
| otherwise = error "ppLit given negative value"
-- | Pretty print a value.
ppValue :: ShowF (ArchReg arch) => Prec -> Value arch ids tp -> Doc
ppValue :: RegisterInfo (ArchReg arch) => Prec -> Value arch ids tp -> Doc
ppValue _ (BoolValue b) = text $ if b then "true" else "false"
ppValue p (BVValue w i) = assert (i >= 0) $ parenIf (p > colonPrec) $ ppLit w i
ppValue p (RelocatableValue _ a) = parenIf (p > plusPrec) $ text (show a)
ppValue _ (AssignedValue a) = ppAssignId (assignId a)
ppValue _ (Initial r) = text (showF r) PP.<> text "_0"
instance ShowF (ArchReg arch) => PrettyPrec (Value arch ids tp) where
instance RegisterInfo (ArchReg arch) => PrettyPrec (Value arch ids tp) where
prettyPrec = ppValue
instance ShowF (ArchReg arch) => Pretty (Value arch ids tp) where
instance RegisterInfo (ArchReg arch) => Pretty (Value arch ids tp) where
pretty = ppValue 0
instance ShowF (ArchReg arch) => Show (Value arch ids tp) where
instance RegisterInfo (ArchReg arch) => Show (Value arch ids tp) where
show = show . pretty
class ( RegisterInfo (ArchReg arch)
@ -648,8 +651,7 @@ instance ( PrettyRegValue r f
=> Show (RegState r f) where
show s = show (pretty s)
instance ( OrdF r
, ShowF r
instance ( RegisterInfo r
, r ~ ArchReg arch
)
=> PrettyRegValue r (Value arch ids) where
@ -668,22 +670,39 @@ data Stmt arch ids
-- ^ This denotes a write to memory, and consists of an address to write to, a `MemRepr` defining
-- how the value should be stored in memory, and the value to be written.
| PlaceHolderStmt !([Some (Value arch ids)]) !String
-- ^ A placeholder to indicate something the
-- architecture-specific backend does not support.
--
-- Note that we plan to remove this eventually
| InstructionStart !(ArchAddrWord arch) !Text
-- ^ The start of an instruction
--
-- The information includes the offset relative to the start of the block and the
-- disassembler output if available (or empty string if unavailable)
| Comment !Text
-- ^ A user-level comment
| ExecArchStmt !(ArchStmt arch ids)
-- ^ Execute an architecture specific statement
instance ArchConstraints arch => Pretty (Stmt arch ids) where
pretty (AssignStmt a) = pretty a
pretty (WriteMem a _ rhs) = text "*" PP.<> prettyPrec 11 a <+> text ":=" <+> ppValue 0 rhs
pretty (PlaceHolderStmt vals name) = text ("PLACEHOLDER: " ++ name)
<+> parens (hcat $ punctuate comma
$ map (viewSome (ppValue 0)) vals)
pretty (Comment s) = text $ "# " ++ Text.unpack s
pretty (ExecArchStmt s) = prettyF s
ppStmt :: ArchConstraints arch
=> (ArchAddrWord arch -> Doc)
-- ^ Function for pretty printing an offset
-> Stmt arch ids
-> Doc
ppStmt ppOff stmt =
case stmt of
AssignStmt a -> pretty a
WriteMem a _ rhs -> text "*" PP.<> prettyPrec 11 a <+> text ":=" <+> ppValue 0 rhs
PlaceHolderStmt vals name ->
text ("PLACEHOLDER: " ++ name)
<+> parens (hcat $ punctuate comma $ viewSome (ppValue 0) <$> vals)
InstructionStart off mnem -> text "#" <+> ppOff off <+> text (Text.unpack mnem)
Comment s -> text $ "# " ++ Text.unpack s
ExecArchStmt s -> prettyF s
instance ArchConstraints arch => Show (Stmt arch ids) where
show = show . pretty
show = show . ppStmt (\w -> text (show w))
------------------------------------------------------------------------
-- References

View File

@ -110,6 +110,8 @@ addStmtDemands s =
addValueDemands val
PlaceHolderStmt l _ ->
mapM_ (\(Some v) -> addValueDemands v) l
InstructionStart{} ->
pure ()
Comment _ ->
pure ()
ExecArchStmt astmt -> do
@ -126,5 +128,6 @@ stmtNeeded demandSet stmt =
AssignStmt a -> Set.member (Some (assignId a)) demandSet
WriteMem{} -> True
PlaceHolderStmt{} -> True
InstructionStart{} -> True
Comment{} -> True
ExecArchStmt{} -> True

View File

@ -325,11 +325,14 @@ rewriteStmt s =
WriteMem addr repr val -> do
tgtAddr <- rewriteValue addr
tgtVal <- rewriteValue val
appendRewrittenStmt (WriteMem tgtAddr repr tgtVal)
appendRewrittenStmt $ WriteMem tgtAddr repr tgtVal
PlaceHolderStmt args nm -> do
args' <- traverse (traverseSome rewriteValue) args
appendRewrittenStmt (PlaceHolderStmt args' nm)
Comment cmt -> appendRewrittenStmt (Comment cmt)
appendRewrittenStmt $ PlaceHolderStmt args' nm
Comment cmt ->
appendRewrittenStmt $ Comment cmt
InstructionStart off mnem ->
appendRewrittenStmt $ InstructionStart off mnem
ExecArchStmt astmt -> do
f <- Rewriter $ gets $ rwctxArchStmt . rwContext
f astmt

View File

@ -48,7 +48,6 @@ module Data.Macaw.Discovery
, State.symbolAddrs
) where
import Control.Exception
import Control.Lens
import Control.Monad.ST
import Control.Monad.State.Strict
@ -94,18 +93,17 @@ import Data.Macaw.Types
concretizeAbsCodePointers :: MemWidth w
=> Memory w
-> AbsValue w (BVType w)
-> [SegmentedAddr w]
-> [MemSegmentOff w]
concretizeAbsCodePointers mem (FinSet s) =
[ sa
| a <- Set.toList s
, Just sa <- [absoluteAddrSegment mem (fromInteger a)]
, Perm.isExecutable (segmentFlags (addrSegment sa))
, sa <- maybeToList (resolveAbsoluteAddr mem (fromInteger a))
, segmentFlags (msegSegment sa) `Perm.hasPerm` Perm.execute
]
concretizeAbsCodePointers mem (CodePointers s _) =
concretizeAbsCodePointers _ (CodePointers s _) =
[ sa
| a <- Set.toList s
, Just sa <- [absoluteAddrSegment mem (_addrOffset a)]
, Perm.isExecutable (segmentFlags (addrSegment sa))
| sa <- Set.toList s
, segmentFlags (msegSegment sa) `Perm.hasPerm` Perm.execute
]
-- FIXME: this is dangerous !!
concretizeAbsCodePointers _mem StridedInterval{} = [] -- FIXME: this case doesn't make sense
@ -114,8 +112,8 @@ concretizeAbsCodePointers _mem StridedInterval{} = [] -- FIXME: this case doesn'
concretizeAbsCodePointers _mem _ = []
{-
printAddrBacktrace :: Map (ArchSegmentedAddr arch) (FoundAddr arch)
-> ArchSegmentedAddr arch
printAddrBacktrace :: Map (ArchMemAddr arch) (FoundAddr arch)
-> ArchMemAddr arch
-> CodeAddrReason (ArchAddrWidth arch)
-> [String]
printAddrBacktrace found_map addr rsn = do
@ -131,14 +129,13 @@ printAddrBacktrace found_map addr rsn = do
InitAddr -> [pp "Initial entry point."]
CodePointerInMem src -> [pp ("Memory address " ++ show src ++ " contained code.")]
SplitAt src -> pp ("Split from read of " ++ show src ++ ".") : prev src
InterProcedureJump src -> pp ("Reference from external address " ++ show src ++ ".") : prev src
-- | Return true if this address was added because of the contents of a global address
-- in memory initially.
--
-- This heuristic is not very accurate, so we avoid printing errors when it leads to
-- issues.
cameFromUnsoundReason :: Map (ArchSegmentedAddr arch) (FoundAddr arch)
cameFromUnsoundReason :: Map (ArchMemAddr arch) (FoundAddr arch)
-> CodeAddrReason (ArchAddrWidth arch)
-> Bool
cameFromUnsoundReason found_map rsn = do
@ -153,7 +150,6 @@ cameFromUnsoundReason found_map rsn = do
SplitAt src -> prev src
InitAddr -> False
CodePointerInMem{} -> True
InterProcedureJump src -> prev src
-}
------------------------------------------------------------------------
@ -221,14 +217,15 @@ elimDeadBlockStmts demandSet b =
-- Memory utilities
-- | Return true if range is entirely contained within a single read only segment.Q
rangeInReadonlySegment :: MemWidth w
=> SegmentedAddr w -- ^ Start of range
rangeInReadonlySegment :: Memory w
-> MemAddr w -- ^ Start of range
-> MemWord w -- ^ The size of the range
-> Bool
rangeInReadonlySegment base size
= base^.addrOffset + size <= segmentSize seg
&& Perm.isReadonly (segmentFlags seg)
where seg = addrSegment base
rangeInReadonlySegment mem base size = addrWidthClass (memAddrWidth mem) $
case asSegmentOff mem base of
Just mseg -> size <= segmentSize (msegSegment mseg) - msegOffset mseg
&& Perm.isReadonly (segmentFlags (msegSegment mseg))
Nothing -> False
------------------------------------------------------------------------
-- DiscoveryState utilities
@ -238,7 +235,7 @@ markAddrAsFunction :: CodeAddrReason (ArchAddrWidth arch)
-- ^ Information about why the code address was discovered
--
-- Used for debugging
-> ArchSegmentedAddr arch
-> ArchSegmentOff arch
-> DiscoveryState arch
-> DiscoveryState arch
markAddrAsFunction rsn addr s
@ -247,7 +244,7 @@ markAddrAsFunction rsn addr s
-- | Mark a list of addresses as function entries with the same reason.
markAddrsAsFunction :: CodeAddrReason (ArchAddrWidth arch)
-> [ArchSegmentedAddr arch]
-> [ArchSegmentOff arch]
-> DiscoveryState arch
-> DiscoveryState arch
markAddrsAsFunction rsn addrs s0 = foldl' (\s a -> markAddrAsFunction rsn a s) s0 addrs
@ -270,17 +267,17 @@ data FoundAddr arch
-- | The state for the function explroation monad
data FunState arch ids
= FunState { funNonceGen :: !(NonceGenerator (ST ids) ids)
, curFunAddr :: !(ArchSegmentedAddr arch)
, curFunAddr :: !(ArchSegmentOff arch)
, _curFunCtx :: !(DiscoveryState arch)
-- ^ Discovery state without this function
, _curFunBlocks :: !(Map (ArchSegmentedAddr arch) (ParsedBlock arch ids))
, _curFunBlocks :: !(Map (ArchSegmentOff arch) (ParsedBlock arch ids))
-- ^ Maps an address to the blocks associated with that address.
, _foundAddrs :: !(Map (ArchSegmentedAddr arch) (FoundAddr arch))
, _foundAddrs :: !(Map (ArchSegmentOff arch) (FoundAddr arch))
-- ^ Maps found address to the pre-state for that block.
, _reverseEdges :: !(ReverseEdgeMap arch)
-- ^ Maps each code address to the list of predecessors that
-- affected its abstract state.
, _frontier :: !(Set (ArchSegmentedAddr arch))
, _frontier :: !(Set (ArchSegmentOff arch))
-- ^ Addresses to explore next.
}
@ -289,26 +286,24 @@ curFunCtx :: Simple Lens (FunState arch ids) (DiscoveryState arch)
curFunCtx = lens _curFunCtx (\s v -> s { _curFunCtx = v })
-- | Information about current function we are working on
curFunBlocks :: Simple Lens (FunState arch ids) (Map (ArchSegmentedAddr arch) (ParsedBlock arch ids))
curFunBlocks :: Simple Lens (FunState arch ids) (Map (ArchSegmentOff arch) (ParsedBlock arch ids))
curFunBlocks = lens _curFunBlocks (\s v -> s { _curFunBlocks = v })
foundAddrs :: Simple Lens (FunState arch ids) (Map (ArchSegmentedAddr arch) (FoundAddr arch))
foundAddrs :: Simple Lens (FunState arch ids) (Map (ArchSegmentOff arch) (FoundAddr arch))
foundAddrs = lens _foundAddrs (\s v -> s { _foundAddrs = v })
type ReverseEdgeMap arch = Map (ArchSegmentedAddr arch) (Set (ArchSegmentedAddr arch))
type ReverseEdgeMap arch = Map (ArchSegmentOff arch) (Set (ArchSegmentOff arch))
-- | Maps each code address to the list of predecessors that
-- affected its abstract state.
reverseEdges :: Simple Lens (FunState arch ids) (ReverseEdgeMap arch)
reverseEdges = lens _reverseEdges (\s v -> s { _reverseEdges = v })
-- | Set of addresses to explore next.
--
-- This is a map so that we can associate a reason why a code address
-- was added to the frontier.
frontier :: Simple Lens (FunState arch ids) (Set (ArchSegmentedAddr arch))
frontier :: Simple Lens (FunState arch ids) (Set (ArchSegmentOff arch))
frontier = lens _frontier (\s v -> s { _frontier = v })
------------------------------------------------------------------------
@ -330,11 +325,11 @@ liftST = FunM . lift
-- | Joins in the new abstract state and returns the locations for
-- which the new state is changed.
mergeIntraJump :: ArchSegmentedAddr arch
mergeIntraJump :: ArchSegmentOff arch
-- ^ Source label that we are jumping from.
-> AbsBlockState (ArchReg arch)
-- ^ Block state after executing instructions.
-> ArchSegmentedAddr arch
-> ArchSegmentOff arch
-- ^ Address we are trying to reach.
-> FunM arch ids ()
mergeIntraJump src ab tgt = do
@ -373,15 +368,16 @@ mergeIntraJump src ab tgt = do
matchJumpTable :: MemWidth (ArchAddrWidth arch)
=> Memory (ArchAddrWidth arch)
-> BVValue arch ids (ArchAddrWidth arch) -- ^ Memory address that IP is read from.
-> Maybe (ArchSegmentedAddr arch, BVValue arch ids (ArchAddrWidth arch))
-> Maybe (ArchMemAddr arch, BVValue arch ids (ArchAddrWidth arch))
matchJumpTable mem read_addr
-- Turn the read address into base + offset.
| Just (BVAdd _ offset base_val) <- valueAsApp read_addr
, Just base <- asLiteralAddr mem base_val
, Just base <- asLiteralAddr base_val
-- Turn the offset into a multiple by an index.
, Just (BVMul _ (BVValue _ mul) jump_index) <- valueAsApp offset
, mul == toInteger (addrSize (memAddrWidth mem))
, Perm.isReadonly (segmentFlags (addrSegment base)) = do
, Just mseg <- asSegmentOff mem base
, Perm.isReadonly (segmentFlags (msegSegment mseg)) = do
Just (base, jump_index)
matchJumpTable _ _ =
Nothing
@ -409,15 +405,16 @@ showJumpTableBoundsError err =
-- table.
getJumpTableBounds :: ArchitectureInfo a
-> AbsProcessorState (ArchReg a) ids -- ^ Current processor registers.
-> ArchSegmentedAddr a -- ^ Base
-> ArchMemAddr a -- ^ Base
-> BVValue a ids (ArchAddrWidth a) -- ^ Index in jump table
-> Either (JumpTableBoundsError a ids) (ArchAddr a)
-> Either (JumpTableBoundsError a ids) (ArchAddrWord a)
-- ^ One past last index in jump table or nothing
getJumpTableBounds info regs base jump_index = withArchConstraints info $
case transferValue regs jump_index of
StridedInterval (SI.StridedInterval _ index_base index_range index_stride) -> do
let mem = absMem regs
let index_end = index_base + (index_range + 1) * index_stride
if rangeInReadonlySegment base (jumpTableEntrySize info * fromInteger index_end) then
if rangeInReadonlySegment mem base (jumpTableEntrySize info * fromInteger index_end) then
case Jmp.unsignedUpperBound (regs^.indexBounds) jump_index of
Right (Jmp.IntegerUpperBound bnd) | bnd == index_range -> Right $! fromInteger index_end
Right bnd -> Left (UpperBoundMismatch bnd index_range)
@ -430,18 +427,6 @@ getJumpTableBounds info regs base jump_index = withArchConstraints info $
------------------------------------------------------------------------
--
tryLookupBlock :: String
-> ArchSegmentedAddr arch
-> Map Word64 (Block arch ids)
-> Word64
-> Block arch ids
tryLookupBlock ctx base block_map idx =
case Map.lookup idx block_map of
Nothing ->
error $ "internal error: tryLookupBlock " ++ ctx ++ " " ++ show base
++ " given invalid index " ++ show idx
Just b -> b
refineProcStateBounds :: ( OrdF (ArchReg arch)
, HasRepr (ArchReg arch) TypeRepr
)
@ -458,20 +443,20 @@ refineProcStateBounds v isTrue ps =
-- ParseState
data ParseState arch ids =
ParseState { _writtenCodeAddrs :: ![ArchSegmentedAddr arch]
ParseState { _writtenCodeAddrs :: ![ArchSegmentOff arch]
-- ^ Addresses marked executable that were written to memory.
, _intraJumpTargets ::
![(ArchSegmentedAddr arch, AbsBlockState (ArchReg arch))]
, _newFunctionAddrs :: ![ArchSegmentedAddr arch]
![(ArchSegmentOff arch, AbsBlockState (ArchReg arch))]
, _newFunctionAddrs :: ![ArchSegmentOff arch]
}
writtenCodeAddrs :: Simple Lens (ParseState arch ids) [ArchSegmentedAddr arch]
writtenCodeAddrs :: Simple Lens (ParseState arch ids) [ArchSegmentOff arch]
writtenCodeAddrs = lens _writtenCodeAddrs (\s v -> s { _writtenCodeAddrs = v })
intraJumpTargets :: Simple Lens (ParseState arch ids) [(ArchSegmentedAddr arch, AbsBlockState (ArchReg arch))]
intraJumpTargets :: Simple Lens (ParseState arch ids) [(ArchSegmentOff arch, AbsBlockState (ArchReg arch))]
intraJumpTargets = lens _intraJumpTargets (\s v -> s { _intraJumpTargets = v })
newFunctionAddrs :: Simple Lens (ParseState arch ids) [ArchSegmentedAddr arch]
newFunctionAddrs :: Simple Lens (ParseState arch ids) [ArchSegmentOff arch]
newFunctionAddrs = lens _newFunctionAddrs (\s v -> s { _newFunctionAddrs = v })
@ -500,7 +485,7 @@ identifyCall :: ( RegConstraint (ArchReg a)
=> Memory (ArchAddrWidth a)
-> [Stmt a ids]
-> RegState (ArchReg a) (Value a ids)
-> Maybe (Seq (Stmt a ids), ArchSegmentedAddr a)
-> Maybe (Seq (Stmt a ids), ArchSegmentOff a)
identifyCall mem stmts0 s = go (Seq.fromList stmts0) Seq.empty
where -- Get value of stack pointer
next_sp = s^.boundValue sp_reg
@ -516,10 +501,11 @@ identifyCall mem stmts0 s = go (Seq.fromList stmts0) Seq.empty
-- Check this is the right length.
, Just Refl <- testEquality (typeRepr next_sp) (typeRepr val)
-- Check if value is a valid literal address
, Just val_a <- asLiteralAddr mem val
, Just val_a <- asLiteralAddr val
-- Check if segment of address is marked as executable.
, Perm.isExecutable (segmentFlags (addrSegment val_a)) ->
Just (prev Seq.>< after, val_a)
, Just ret_addr <- asSegmentOff mem val_a
, segmentFlags (msegSegment ret_addr) `Perm.hasPerm` Perm.execute ->
Just (prev Seq.>< after, ret_addr)
-- Stop if we hit any architecture specific instructions prior to
-- identifying return address since they may have side effects.
| ExecArchStmt _ <- stmt -> Nothing
@ -531,9 +517,9 @@ identifyCall mem stmts0 s = go (Seq.fromList stmts0) Seq.empty
data ParseContext arch ids = ParseContext { pctxMemory :: !(Memory (ArchAddrWidth arch))
, pctxArchInfo :: !(ArchitectureInfo arch)
, pctxFunAddr :: !(ArchSegmentedAddr arch)
, pctxFunAddr :: !(ArchSegmentOff arch)
-- ^ Address of function this block is being parsed as
, pctxAddr :: !(ArchSegmentedAddr arch)
, pctxAddr :: !(ArchSegmentOff arch)
-- ^ Address of the current block
, pctxBlockMap :: !(Map Word64 (Block arch ids))
}
@ -567,7 +553,7 @@ parseFetchAndExecute ctx lbl_idx stmts regs s' = do
-- The last statement was a call.
-- Note that in some cases the call is known not to return, and thus
-- this code will never jump to the return value.
_ | Just (prev_stmts, ret) <- identifyCall mem stmts s' -> do
_ | Just (prev_stmts, ret) <- identifyCall mem stmts s' -> do
mapM_ (recordWriteStmt arch_info mem absProcState') prev_stmts
let abst = finalAbsBlockState absProcState' s'
seq abst $ do
@ -596,17 +582,18 @@ parseFetchAndExecute ctx lbl_idx stmts regs s' = do
--
-- Note, we disallow jumps back to function entry point thus forcing them to be treated
-- as tail calls or unclassified if the stack has changed size.
| Just tgt_addr <- asLiteralAddr mem (s'^.boundValue ip_reg)
, tgt_addr /= pctxFunAddr ctx -> do
assert (segmentFlags (addrSegment tgt_addr) `Perm.hasPerm` Perm.execute) $ do
| Just tgt_addr <- asLiteralAddr (s'^.boundValue ip_reg)
, Just tgt_mseg <- asSegmentOff mem tgt_addr
, segmentFlags (msegSegment tgt_mseg) `Perm.hasPerm` Perm.execute
, tgt_mseg /= pctxFunAddr ctx -> do
mapM_ (recordWriteStmt arch_info mem absProcState') stmts
-- Merge block state and add intra jump target.
let abst = finalAbsBlockState absProcState' s'
let abst' = abst & setAbsIP tgt_addr
intraJumpTargets %= ((tgt_addr, abst'):)
let abst' = abst & setAbsIP tgt_mseg
intraJumpTargets %= ((tgt_mseg, abst'):)
pure StatementList { stmtsIdent = lbl_idx
, stmtsNonterm = stmts
, stmtsTerm = ParsedJump s' tgt_addr
, stmtsTerm = ParsedJump s' tgt_mseg
, stmtsAbsState = absProcState'
}
-- Block ends with what looks like a jump table.
@ -635,24 +622,25 @@ parseFetchAndExecute ctx lbl_idx stmts regs s' = do
-- If the current index can be interpreted as a intra-procedural jump,
-- then it will add that to the current procedure.
-- This returns the last address read.
let resolveJump :: [ArchSegmentedAddr arch]
let resolveJump :: [ArchSegmentOff arch]
-- /\ Addresses in jump table in reverse order
-> ArchAddr arch
-> ArchAddrWord arch
-- /\ Current index
-> State (ParseState arch ids) [ArchSegmentedAddr arch]
-> State (ParseState arch ids) [ArchSegmentOff arch]
resolveJump prev idx | idx == read_end = do
-- Stop jump table when we have reached computed bounds.
return (reverse prev)
resolveJump prev idx = do
let read_addr = base & addrOffset +~ 8 * idx
let read_addr = base & incAddr (toInteger (8 * idx))
case readAddr mem (archEndianness arch_info) read_addr of
Right tgt_addr
| Perm.isReadonly (segmentFlags (addrSegment read_addr)) -> do
let flags = segmentFlags (addrSegment tgt_addr)
assert (flags `Perm.hasPerm` Perm.execute) $ do
let abst' = abst & setAbsIP tgt_addr
intraJumpTargets %= ((tgt_addr, abst'):)
resolveJump (tgt_addr:prev) (idx+1)
| Just read_mseg <- asSegmentOff mem read_addr
, Perm.isReadonly (segmentFlags (msegSegment read_mseg))
, Just tgt_mseg <- asSegmentOff mem tgt_addr
, Perm.isExecutable (segmentFlags (msegSegment tgt_mseg)) -> do
let abst' = abst & setAbsIP tgt_mseg
intraJumpTargets %= ((tgt_mseg, abst'):)
resolveJump (tgt_mseg:prev) (idx+1)
_ -> do
debug DCFG ("Stop jump table: " ++ show idx ++ " " ++ show read_end) $ do
return (reverse prev)
@ -707,7 +695,6 @@ parseBlock ctx b regs = do
let mem = pctxMemory ctx
let arch_info = pctxArchInfo ctx
withArchConstraints arch_info $ do
let src = pctxAddr ctx
let idx = blockLabel b
let block_map = pctxBlockMap ctx
-- FIXME: we should propagate c back to the initial block, not just b
@ -716,9 +703,9 @@ parseBlock ctx b regs = do
Branch c lb rb -> do
mapM_ (recordWriteStmt arch_info mem absProcState') (blockStmts b)
let l = tryLookupBlock "left branch" src block_map lb
let Just l = Map.lookup lb block_map
let l_regs = refineProcStateBounds c True $ refineProcState c absTrue absProcState'
let r = tryLookupBlock "right branch" src block_map rb
let Just r = Map.lookup rb block_map
let r_regs = refineProcStateBounds c False $ refineProcState c absFalse absProcState'
let l_regs' = absEvalStmts arch_info l_regs (blockStmts b)
@ -764,11 +751,11 @@ parseBlock ctx b regs = do
-- | This evalutes the statements in a block to expand the information known
-- about control flow targets of this block.
transferBlocks :: ArchSegmentedAddr arch
transferBlocks :: ArchSegmentOff arch
-- ^ Address of theze blocks
-> FoundAddr arch
-- ^ State leading to explore block
-> ArchAddr arch
-> ArchAddrWord arch
-- ^ Size of the region these blocks cover.
-> Map Word64 (Block arch ids)
-- ^ Map from labelIndex to associated block
@ -805,24 +792,27 @@ transferBlocks src finfo sz block_map =
mapM_ (\(addr, abs_state) -> mergeIntraJump src abs_state addr) (ps^.intraJumpTargets)
transfer :: ArchSegmentedAddr arch
transfer :: ArchSegmentOff arch
-> FunM arch ids ()
transfer addr = do
mfinfo <- use $ foundAddrs . at addr
let finfo = fromMaybe (error $ "getBlock called on unfound address " ++ show addr ++ ".") $
mfinfo
ainfo <- uses curFunCtx archInfo
withArchConstraints ainfo $ do
mfinfo <- use $ foundAddrs . at addr
let finfo = fromMaybe (error $ "transfer called on unfound address " ++ show addr ++ ".") $
mfinfo
mem <- uses curFunCtx memory
nonceGen <- gets funNonceGen
prev_block_map <- use $ curFunBlocks
-- Get maximum number of bytes to disassemble
let seg = msegSegment addr
off = msegOffset addr
let max_size =
case Map.lookupGT addr prev_block_map of
Just (next,_) | addrSegment next == addrSegment addr -> next^.addrOffset - addr^.addrOffset
_ -> segmentSize (addrSegment addr) - addr^.addrOffset
Just (next,_) | Just o <- diffSegmentOff next addr -> fromInteger o
_ -> segmentSize seg - off
let ab = foundAbstractState finfo
(bs0, sz, maybeError) <-
liftST $ disassembleFn ainfo nonceGen addr max_size ab
liftST $ disassembleFn ainfo mem nonceGen addr max_size ab
let ctx = RewriteContext { rwctxNonceGen = nonceGen
, rwctxArchFn = rewriteArchFn ainfo
@ -842,7 +832,7 @@ transfer addr = do
let -- TODO: Fix this to work with segmented memory
w = addrWidthNatRepr (archAddrWidth ainfo)
errState = mkRegState Initial
& boundValue ip_reg .~ RelocatableValue w addr
& boundValue ip_reg .~ RelocatableValue w (relativeSegmentAddr addr)
errMsg = Text.pack $ fromMaybe "Unknown error" maybeError
errBlock = Block { blockLabel = 0
, blockStmts = []
@ -873,7 +863,7 @@ analyzeBlocks = do
--
-- This returns the updated state and the discovered control flow
-- graph for this function.
analyzeFunction :: ArchSegmentedAddr arch
analyzeFunction :: ArchSegmentOff arch
-- ^ The address to explore
-> CodeAddrReason (ArchAddrWidth arch)
-- ^ Reason to provide for why we are analyzing this function
@ -883,12 +873,13 @@ analyzeFunction :: ArchSegmentedAddr arch
-> DiscoveryState arch
-- ^ The current binary information.
-> (DiscoveryState arch, Some (DiscoveryFunInfo arch))
analyzeFunction addr rsn s =
analyzeFunction addr rsn s = do
case Map.lookup addr (s^.funInfo) of
Just finfo -> (s, finfo)
Nothing -> do
withGlobalSTNonceGenerator $ \gen -> do
let info = archInfo s
withArchConstraints info $ do
withGlobalSTNonceGenerator $ \gen -> do
let mem = memory s
let faddr = FoundAddr { foundReason = rsn
@ -926,27 +917,26 @@ analyzeDiscoveredFunctions info =
analyzeDiscoveredFunctions $! fst (analyzeFunction addr rsn info)
-- | This returns true if the address is writable and value is executable.
isDataCodePointer :: SegmentedAddr w -> SegmentedAddr w -> Bool
isDataCodePointer :: MemSegmentOff w -> MemSegmentOff w -> Bool
isDataCodePointer a v
= segmentFlags (addrSegment a) `Perm.hasPerm` Perm.write
&& segmentFlags (addrSegment v) `Perm.hasPerm` Perm.execute
= segmentFlags (msegSegment a) `Perm.hasPerm` Perm.write
&& segmentFlags (msegSegment v) `Perm.hasPerm` Perm.execute
addMemCodePointer :: (ArchSegmentedAddr arch, ArchSegmentedAddr arch)
addMemCodePointer :: (ArchSegmentOff arch, ArchSegmentOff arch)
-> DiscoveryState arch
-> DiscoveryState arch
addMemCodePointer (src,val) = markAddrAsFunction (CodePointerInMem src) val
exploreMemPointers :: [(ArchSegmentedAddr arch, ArchSegmentedAddr arch)]
exploreMemPointers :: [(ArchSegmentOff arch, ArchSegmentOff arch)]
-- ^ List of addresses and value pairs to use for
-- considering possible addresses.
-> DiscoveryState arch
-> DiscoveryState arch
exploreMemPointers mem_words info =
flip execState info $ do
let mem_addrs =
filter (uncurry isDataCodePointer) $
mem_words
let mem_addrs
= filter (\(a,v) -> isDataCodePointer a v)
$ mem_words
mapM_ (modify . addMemCodePointer) mem_addrs
-- | Construct a discovery info by starting with exploring from a given set of
@ -958,9 +948,9 @@ cfgFromAddrs :: forall arch
-- ^ Memory to use when decoding instructions.
-> SymbolAddrMap (ArchAddrWidth arch)
-- ^ Map from addresses to the associated symbol name.
-> [ArchSegmentedAddr arch]
-> [ArchSegmentOff arch]
-- ^ Initial function entry points.
-> [(ArchSegmentedAddr arch, ArchSegmentedAddr arch)]
-> [(ArchSegmentOff arch, ArchSegmentOff arch)]
-- ^ Function entry points in memory to be explored
-- after exploring function entry points.
--

View File

@ -23,15 +23,13 @@ import Data.Macaw.AbsDomain.AbsState
import Data.Macaw.Architecture.Info
import Data.Macaw.CFG
import Data.Macaw.Memory
-- | Get the absolute value associated with an address.
absEvalReadMem :: (OrdF (ArchReg a), ShowF (ArchReg a), MemWidth (RegAddrWidth (ArchReg a)))
=> AbsProcessorState (ArchReg a) ids
-> ArchAddrValue a ids
-> MemRepr tp
-- ^ Information about the memory layout for the value.
-> ArchAbsValue a tp
absEvalReadMem :: RegisterInfo (ArchReg a)
=> AbsProcessorState (ArchReg a) ids
-> ArchAddrValue a ids
-> MemRepr tp
-- ^ Information about the memory layout for the value.
-> ArchAbsValue a tp
absEvalReadMem r a tp
| StackOffset _ s <- transferValue r a
, [o] <- Set.toList s
@ -74,6 +72,8 @@ absEvalStmt info stmt = withArchConstraints info $
modify $ addMemWrite addr memRepr v
PlaceHolderStmt{} ->
pure ()
InstructionStart _ _ ->
pure ()
Comment{} ->
pure ()
ExecArchStmt astmt ->

View File

@ -75,20 +75,18 @@ import Data.Macaw.Types
-- | This describes the source of an address that was marked as containing code.
data CodeAddrReason w
= InWrite !(SegmentedAddr w)
= InWrite !(MemSegmentOff w)
-- ^ Exploring because the given block writes it to memory.
| NextIP !(SegmentedAddr w)
| NextIP !(MemSegmentOff w)
-- ^ Exploring because the given block jumps here.
| CallTarget !(SegmentedAddr w)
| CallTarget !(MemSegmentOff w)
-- ^ Exploring because address terminates with a call that jumps here.
| InitAddr
-- ^ Identified as an entry point from initial information
| CodePointerInMem !(SegmentedAddr w)
| CodePointerInMem !(MemSegmentOff w)
-- ^ A code pointer that was stored at the given address.
| SplitAt !(SegmentedAddr w)
| SplitAt !(MemAddr w)
-- ^ Added because the address split this block after it had been disassembled.
| InterProcedureJump !(SegmentedAddr w)
-- ^ A jump from an address in another function.
| UserRequest
-- ^ The user requested that we analyze this address as a function.
deriving (Show)
@ -97,18 +95,18 @@ data CodeAddrReason w
-- SymbolAddrMap
-- | Map from addresses to the associated symbol name.
newtype SymbolAddrMap w = SymbolAddrMap { symbolAddrsAsMap :: Map (SegmentedAddr w) BSC.ByteString }
newtype SymbolAddrMap w = SymbolAddrMap { symbolAddrsAsMap :: Map (MemSegmentOff w) BSC.ByteString }
-- | Return an empty symbol addr map
emptySymbolAddrMap :: SymbolAddrMap w
emptySymbolAddrMap = SymbolAddrMap Map.empty
-- | Return addresses in symbol name map
symbolAddrs :: SymbolAddrMap w -> [SegmentedAddr w]
symbolAddrs :: SymbolAddrMap w -> [MemSegmentOff w]
symbolAddrs = Map.keys . symbolAddrsAsMap
-- | Return the symbol at the given map.
symbolAtAddr :: SegmentedAddr w -> SymbolAddrMap w -> Maybe BSC.ByteString
symbolAtAddr :: MemSegmentOff w -> SymbolAddrMap w -> Maybe BSC.ByteString
symbolAtAddr a m = Map.lookup a (symbolAddrsAsMap m)
-- | Check that a symbol name is well formed, returning an error message if not.
@ -125,26 +123,8 @@ checkSymbolName sym_nm =
--
-- It returns either an error message or the map.
symbolAddrMap :: forall w
. Map (SegmentedAddr w) BSC.ByteString
. Map (MemSegmentOff w) BSC.ByteString
-> Either String (SymbolAddrMap w)
{-
symbolAddrMap symbols
| Map.size symbol_names /= Map.size symbols = do
let l = filter isMulti (Map.toList symbol_names)
in Left $ "Duplicate symbol names in symbol name map:\n" ++ show l
where symbol_names :: Map BSC.ByteString [SegmentedAddr w]
symbol_names = foldl insPair Map.empty (Map.toList symbols)
isMulti :: (BSC.ByteString, [SegmentedAddr w])
-> Bool
isMulti (_,[_]) = False
isMulti (_,_) = True
insPair :: Map BSC.ByteString [SegmentedAddr w]
-> (SegmentedAddr w, BSC.ByteString)
-> Map BSC.ByteString [SegmentedAddr w]
insPair m (a,nm) = Map.insertWith (++) nm [a] m
-}
symbolAddrMap symbols = do
mapM_ checkSymbolName (Map.elems symbols)
pure $! SymbolAddrMap symbols
@ -173,13 +153,13 @@ instance (Integral w, Show w) => Show (GlobalDataInfo w) where
-- interpreted.
data ParsedTermStmt arch ids
= ParsedCall !(RegState (ArchReg arch) (Value arch ids))
!(Maybe (ArchSegmentedAddr arch))
!(Maybe (ArchSegmentOff arch))
-- ^ A call with the current register values and location to return to or 'Nothing' if this is a tail call.
| ParsedJump !(RegState (ArchReg arch) (Value arch ids)) !(ArchSegmentedAddr arch)
| ParsedJump !(RegState (ArchReg arch) (Value arch ids)) !(ArchSegmentOff arch)
-- ^ A jump to an explicit address within a function.
| ParsedLookupTable !(RegState (ArchReg arch) (Value arch ids))
!(BVValue arch ids (ArchAddrWidth arch))
!(V.Vector (ArchSegmentedAddr arch))
!(V.Vector (ArchSegmentOff arch))
-- ^ A lookup table that branches to one of a vector of addresses.
--
-- The registers store the registers, the value contains the index to jump
@ -189,7 +169,7 @@ data ParsedTermStmt arch ids
| ParsedIte !(Value arch ids BoolType) !(StatementList arch ids) !(StatementList arch ids)
-- ^ An if-then-else
| ParsedSyscall !(RegState (ArchReg arch) (Value arch ids))
!(ArchSegmentedAddr arch)
!(ArchSegmentOff arch)
-- ^ A system call with the registers prior to call and given return address.
| ParsedTranslateError !Text
-- ^ An error occured in translating the block
@ -199,41 +179,49 @@ data ParsedTermStmt arch ids
deriving instance ArchConstraints arch => Show (ParsedTermStmt arch ids)
-- | Pretty print the block contents indented inside brackets.
ppStatementList :: ArchConstraints arch => StatementList arch ids -> Doc
ppStatementList b =
ppStatementList :: ArchConstraints arch => (ArchAddrWord arch -> Doc) -> StatementList arch ids -> Doc
ppStatementList ppOff b =
text "{" <$$>
indent 2 (vcat (pretty <$> stmtsNonterm b) <$$> pretty (stmtsTerm b)) <$$>
indent 2 (vcat (ppStmt ppOff <$> stmtsNonterm b) <$$>
ppTermStmt ppOff (stmtsTerm b)) <$$>
text "}"
instance ArchConstraints arch => Pretty (ParsedTermStmt arch ids) where
pretty (ParsedCall s Nothing) =
text "tail call" <$$>
indent 2 (pretty s)
pretty (ParsedCall s (Just next)) =
text "call and return to" <+> text (show next) <$$>
indent 2 (pretty s)
pretty (ParsedJump s addr) =
text "jump" <+> text (show addr) <$$>
indent 2 (pretty s)
pretty (ParsedLookupTable s idx entries) =
text "ijump" <+> pretty idx <$$>
indent 2 (vcat (imap (\i v -> int i <+> text ":->" <+> text (show v)) (V.toList entries))) <$$>
indent 2 (pretty s)
pretty (ParsedReturn s) =
text "return" <$$>
indent 2 (pretty s)
pretty (ParsedIte c t f) =
text "ite" <+> pretty c <$$>
ppStatementList t <$$>
ppStatementList f
pretty (ParsedSyscall s addr) =
text "syscall, return to" <+> text (show addr) <$$>
indent 2 (pretty s)
pretty (ParsedTranslateError msg) =
text "translation error" <+> text (Text.unpack msg)
pretty (ClassifyFailure s) =
text "unknown transfer" <$$>
indent 2 (pretty s)
ppTermStmt :: ArchConstraints arch
=> (ArchAddrWord arch -> Doc)
-- ^ Given an address offset, this prints the value
-> ParsedTermStmt arch ids
-> Doc
ppTermStmt ppOff tstmt =
case tstmt of
ParsedCall s Nothing ->
text "tail call" <$$>
indent 2 (pretty s)
ParsedCall s (Just next) ->
text "call and return to" <+> text (show next) <$$>
indent 2 (pretty s)
ParsedJump s addr ->
text "jump" <+> text (show addr) <$$>
indent 2 (pretty s)
ParsedLookupTable s idx entries ->
text "ijump" <+> pretty idx <$$>
indent 2 (vcat (imap (\i v -> int i <+> text ":->" <+> text (show v))
(V.toList entries))) <$$>
indent 2 (pretty s)
ParsedReturn s ->
text "return" <$$>
indent 2 (pretty s)
ParsedIte c t f ->
text "ite" <+> pretty c <$$>
ppStatementList ppOff t <$$>
ppStatementList ppOff f
ParsedSyscall s addr ->
text "syscall, return to" <+> text (show addr) <$$>
indent 2 (pretty s)
ParsedTranslateError msg ->
text "translation error" <+> text (Text.unpack msg)
ClassifyFailure s ->
text "unknown transfer" <$$>
indent 2 (pretty s)
------------------------------------------------------------------------
-- StatementList
@ -307,9 +295,9 @@ rewriteStatementList b = do
-- | A contiguous region of instructions in memory.
data ParsedBlock arch ids
= ParsedBlock { blockAddr :: !(ArchSegmentedAddr arch)
= ParsedBlock { blockAddr :: !(ArchSegmentOff arch)
-- ^ Address of region
, blockSize :: !(ArchAddr arch)
, blockSize :: !(ArchAddrWord arch)
-- ^ The size of the region of memory covered by this.
, blockReason :: !(CodeAddrReason (ArchAddrWidth arch))
-- ^ Reason that we marked this address as
@ -326,25 +314,26 @@ deriving instance ArchConstraints arch
instance ArchConstraints arch
=> Pretty (ParsedBlock arch ids) where
pretty r =
let b = blockStatementList r
in text (show (blockAddr r)) PP.<> text ":" <$$>
indent 2 (vcat (pretty <$> stmtsNonterm b) <$$> pretty (stmtsTerm b))
pretty b =
let sl = blockStatementList b
ppOff o = text (show (incAddr (toInteger o) (relativeSegmentAddr (blockAddr b))))
in text (show (blockAddr b)) PP.<> text ":" <$$>
indent 2 (vcat (ppStmt ppOff <$> stmtsNonterm sl) <$$> ppTermStmt ppOff (stmtsTerm sl))
------------------------------------------------------------------------
-- DiscoveryFunInfo
-- | Information discovered about a particular function
data DiscoveryFunInfo arch ids
= DiscoveryFunInfo { discoveredFunAddr :: !(ArchSegmentedAddr arch)
= DiscoveryFunInfo { discoveredFunAddr :: !(ArchSegmentOff arch)
-- ^ Address of function entry block.
, discoveredFunName :: !BSC.ByteString
-- ^ Name of function should be unique for program
, _parsedBlocks :: !(Map (ArchSegmentedAddr arch) (ParsedBlock arch ids))
, _parsedBlocks :: !(Map (ArchSegmentOff arch) (ParsedBlock arch ids))
-- ^ Maps an address to the blocks associated with that address.
}
parsedBlocks :: Simple Lens (DiscoveryFunInfo arch ids) (Map (ArchSegmentedAddr arch) (ParsedBlock arch ids))
parsedBlocks :: Simple Lens (DiscoveryFunInfo arch ids) (Map (ArchSegmentOff arch) (ParsedBlock arch ids))
parsedBlocks = lens _parsedBlocks (\s v -> s { _parsedBlocks = v })
instance ArchConstraints arch => Pretty (DiscoveryFunInfo arch ids) where
@ -363,13 +352,13 @@ data DiscoveryState arch
-- ^ Map addresses to known symbol names
, archInfo :: !(ArchitectureInfo arch)
-- ^ Architecture-specific information needed for discovery.
, _globalDataMap :: !(Map (ArchSegmentedAddr arch)
(GlobalDataInfo (ArchSegmentedAddr arch)))
, _globalDataMap :: !(Map (ArchMemAddr arch)
(GlobalDataInfo (ArchMemAddr arch)))
-- ^ Maps each address that appears to be global data to information
-- inferred about it.
, _funInfo :: !(Map (ArchSegmentedAddr arch) (Some (DiscoveryFunInfo arch)))
, _funInfo :: !(Map (ArchSegmentOff arch) (Some (DiscoveryFunInfo arch)))
-- ^ Map from function addresses to discovered information about function
, _unexploredFunctions :: !(Map (ArchSegmentedAddr arch) (CodeAddrReason (ArchAddrWidth arch)))
, _unexploredFunctions :: !(Map (ArchSegmentOff arch) (CodeAddrReason (ArchAddrWidth arch)))
-- ^ This maps addresses that have been marked as
-- functions, but not yet analyzed to the reason
-- they are analyzed.
@ -413,17 +402,16 @@ emptyDiscoveryState mem symbols info =
-- | Map each jump table start to the address just after the end.
globalDataMap :: Simple Lens (DiscoveryState arch)
(Map (ArchSegmentedAddr arch)
(GlobalDataInfo (ArchSegmentedAddr arch)))
(Map (ArchMemAddr arch) (GlobalDataInfo (ArchMemAddr arch)))
globalDataMap = lens _globalDataMap (\s v -> s { _globalDataMap = v })
-- | List of functions to explore next.
unexploredFunctions :: Simple Lens (DiscoveryState arch)
(Map (ArchSegmentedAddr arch) (CodeAddrReason (ArchAddrWidth arch)))
(Map (ArchSegmentOff arch) (CodeAddrReason (ArchAddrWidth arch)))
unexploredFunctions = lens _unexploredFunctions (\s v -> s { _unexploredFunctions = v })
-- | Get information for specific functions
funInfo :: Simple Lens (DiscoveryState arch) (Map (ArchSegmentedAddr arch) (Some (DiscoveryFunInfo arch)))
funInfo :: Simple Lens (DiscoveryState arch) (Map (ArchSegmentOff arch) (Some (DiscoveryFunInfo arch)))
funInfo = lens _funInfo (\s v -> s { _funInfo = v })
------------------------------------------------------------------------
@ -435,10 +423,8 @@ type RegConstraint r = (OrdF r, HasRepr r TypeRepr, RegisterInfo r, ShowF r)
-- | This returns a segmented address if the value can be interpreted as a literal memory
-- address, and returns nothing otherwise.
asLiteralAddr :: MemWidth (ArchAddrWidth arch)
=> Memory (ArchAddrWidth arch)
-> BVValue arch ids (ArchAddrWidth arch)
-> Maybe (ArchSegmentedAddr arch)
asLiteralAddr mem (BVValue _ val) =
absoluteAddrSegment mem (fromInteger val)
asLiteralAddr _ (RelocatableValue _ i) = Just i
asLiteralAddr _ _ = Nothing
=> BVValue arch ids (ArchAddrWidth arch)
-> Maybe (ArchMemAddr arch)
asLiteralAddr (BVValue _ val) = Just $ absoluteAddr (fromInteger val)
asLiteralAddr (RelocatableValue _ i) = Just i
asLiteralAddr _ = Nothing

View File

@ -59,7 +59,7 @@ foldValueCached :: forall m arch ids tp
. (Monoid m, CanFoldValues arch)
=> (forall n. NatRepr n -> Integer -> m)
-- ^ Function for literals
-> (forall n. NatRepr n -> ArchSegmentedAddr arch -> m)
-> (ArchMemAddr arch -> m)
-- ^ Function for memwords
-> (forall utp . ArchReg arch utp -> m)
-- ^ Function for input registers
@ -76,7 +76,7 @@ foldValueCached litf rwf initf assignf = getStateMonadMonoid . go
case v of
BoolValue b -> return (litf (knownNat :: NatRepr 1) (if b then 1 else 0))
BVValue sz i -> return $ litf sz i
RelocatableValue w a -> pure $ rwf w a
RelocatableValue _ a -> pure $ rwf a
Initial r -> return $ initf r
AssignedValue (Assignment a_id rhs) -> do
m <- get

View File

@ -13,7 +13,6 @@ n-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Macaw.Memory
@ -28,13 +27,6 @@ module Data.Macaw.Memory
, memSegments
, executableSegments
, readonlySegments
, readAddr
, segmentOfRange
, addrPermissions
, isCodeAddr
, isCodeAddrOrNull
, absoluteAddrSegment
, memAsAddrPairs
-- * AddrWidthRepr
, AddrWidthRepr(..)
, addrWidthNatRepr
@ -52,26 +44,38 @@ module Data.Macaw.Memory
, ppMemSegment
, segmentSize
, SegmentRange(..)
-- * MemWord
, MemWord
, MemWidth(..)
, memWord
-- * Segment offsets
, MemSegmentOff
, resolveAbsoluteAddr
, resolveSegmentOff
, msegSegment
, msegOffset
, msegAddr
, incSegmentOff
, diffSegmentOff
, memAsAddrPairs
-- * Symbols
, SymbolRef(..)
, SymbolVersion(..)
-- * Address and offset.
, MemWidth(..)
, MemWord
, memWord
-- * Segmented Addresses
, SegmentedAddr(..)
, addrOffset
-- * General purposes addrs
, MemAddr
, absoluteAddr
, relativeAddr
, relativeSegmentAddr
, asAbsoluteAddr
, asSegmentOff
, diffAddr
, incAddr
, clearAddrLeastBit
-- * Reading
, MemoryError(..)
, addrContentsAfter
, addrBase
, addrValue
, bsWord8
, bsWord16be
, bsWord16le
, bsWord32be
, bsWord32le
, bsWord64be
, bsWord64le
, readByteString
, readAddr
, readWord8
, readWord16be
, readWord16le
@ -79,18 +83,22 @@ module Data.Macaw.Memory
, readWord32le
, readWord64be
, readWord64le
-- * Memory addrs
, MemoryError(..)
-- * Utilities
, bsWord8
, bsWord16be
, bsWord16le
, bsWord32be
, bsWord32le
, bsWord64be
, bsWord64le
) where
import Control.Exception (assert)
import Control.Lens
import Data.Bits
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.Foldable as Fold
import qualified Data.Map.Strict as Map
import Data.Maybe
import Data.Proxy
import Data.Word
import GHC.TypeLits
@ -178,12 +186,16 @@ bsWord64le bs
------------------------------------------------------------------------
-- MemBase
newtype MemWord (n :: Nat) = MemWord { memWordValue :: Word64 }
-- ^ A value in memory.
-- | This represents a particular numeric address in memory.
--
newtype MemWord (w :: Nat) = MemWord { _memWordValue :: Word64 }
instance Show (MemWord w) where
showsPrec _ (MemWord w) = showString "0x" . showHex w
instance Pretty (MemWord w) where
pretty = text . show
instance Eq (MemWord w) where
MemWord x == MemWord y = x == y
@ -280,8 +292,6 @@ addrWidthClass :: AddrWidthRepr w -> (MemWidth w => a) -> a
addrWidthClass Addr32 x = x
addrWidthClass Addr64 x = x
$(pure [])
-- | A unique identifier for a segment.
type SegmentIndex = Int
@ -369,7 +379,10 @@ data MemSegment w
= MemSegment { segmentIndex :: !SegmentIndex
-- ^ Unique index for this segment
, segmentBase :: !(Maybe (MemWord w))
-- ^ Base for this segment
-- ^ Base for this segment
--
-- Note that the current code assumes that segments are
-- always on even addresses even if the base is omitted.
, segmentFlags :: !Perm.Flags
-- ^ Permisison flags
, segmentContents :: !(SegmentContents w)
@ -377,6 +390,11 @@ data MemSegment w
-- the segment.
}
-- | Check base plus size of segment does not overflow
checkBaseAndSize :: MemWidth w => Maybe (MemWord w) -> MemWord w -> Bool
checkBaseAndSize Nothing _ = True
checkBaseAndSize (Just b) sz = b + sz >= b
-- | Create a memory segment with the given values.
memSegment :: MemWidth w
=> SegmentIndex
@ -388,12 +406,18 @@ memSegment :: MemWidth w
-> [SegmentRange w]
-- ^ Range of vlaues.
-> MemSegment w
memSegment idx base flags contents =
MemSegment { segmentIndex = idx
, segmentBase = base
, segmentFlags = flags
, segmentContents = contentsFromList contents
}
memSegment idx mbase flags contentsl
| checkBaseAndSize mbase (contentsSize contents) =
MemSegment { segmentIndex = idx
, segmentBase = mbase
, segmentFlags = flags
, segmentContents = contents
}
| otherwise =
error "Contents two large for base."
where contents = contentsFromList contentsl
instance Eq (MemSegment w) where
x == y = segmentIndex x == segmentIndex y
@ -417,60 +441,6 @@ ppMemSegment s =
instance MemWidth w => Show (MemSegment w) where
show = show . ppMemSegment
------------------------------------------------------------------------
-- SegmentedAddr
-- | A memory address is a reference to memory that uses an explicit segment plus
-- offset representation.
data SegmentedAddr w = SegmentedAddr { addrSegment :: !(MemSegment w)
, _addrOffset :: !(MemWord w)
}
deriving (Eq, Ord)
addrOffset :: Simple Lens (SegmentedAddr w) (MemWord w)
addrOffset = lens _addrOffset (\s v -> s { _addrOffset = v })
-- | Return the base value of an address or 0 if undefined.
addrBase :: MemWidth w => SegmentedAddr w -> MemWord w
addrBase addr = fromMaybe 0 (segmentBase (addrSegment addr))
-- | Return the offset of the address after adding the base segment value if defined.
addrValue :: MemWidth w => SegmentedAddr w -> MemWord w
addrValue addr = addrBase addr + addr^.addrOffset
instance Show (SegmentedAddr w) where
showsPrec p a =
case segmentBase (addrSegment a) of
Just b ->
showString "0x" . showHex (memWordValue b + memWordValue (a^.addrOffset))
Nothing ->
showParen (p > 6)
$ showString "segment"
. shows (segmentIndex (addrSegment a))
. showString "+"
. shows (a^.addrOffset)
-- | Given a segemnted addr this returns the offset and range at that offset.
nextRegion :: MemWidth w
=> SegmentedAddr w
-> Maybe (MemWord w, SegmentRange w)
nextRegion addr = do
let i = addr^.addrOffset
let SegmentContents m = segmentContents (addrSegment addr)
(k,r) <- Map.lookupLE (addr^.addrOffset) m
Just (i-k,r)
-- | Return contents starting from location or throw a memory error if there
-- is an unaligned relocation.
addrContentsAfter :: MemWidth w
=> SegmentedAddr w
-> Either (MemoryError w) [SegmentRange w]
addrContentsAfter addr =
case contentsAfter (addr^.addrOffset) (segmentContents (addrSegment addr)) of
Nothing -> Left (UnexpectedRelocation addr)
Just l -> Right l
------------------------------------------------------------------------
-- Memory
@ -503,70 +473,17 @@ emptyMemory w = Memory { memAddrWidth = w
memSegments :: Memory w -> [MemSegment w]
memSegments m = Map.elems (memAllSegments m)
-- | Return segment with given index in memory.
lookupSegment :: Memory w -> SegmentIndex -> Maybe (MemSegment w)
lookupSegment m i = Map.lookup i (memAllSegments m)
-- | Return list of segmented address values in memory.
--
-- Each address includes the value and the base.
memAsAddrPairs :: Memory w
-> Endianness
-> [(SegmentedAddr w, SegmentedAddr w)]
memAsAddrPairs mem end = addrWidthClass (memAddrWidth mem) $ do
seg <- memSegments mem
(contents_offset,r) <- contentsList (segmentContents seg)
let addr = SegmentedAddr seg contents_offset
let sz = addrSize mem
case r of
ByteRegion bs -> assert (BS.length bs `rem` fromIntegral sz == 0) $ do
w <- regularChunks (fromIntegral sz) bs
let Just val = addrRead end w
case Map.lookupLE val (memAbsoluteSegments mem) of
Just (base, value_seg) | val <= base + segmentSize value_seg -> do
let seg_val = SegmentedAddr value_seg (val - base)
in [(addr,seg_val)]
_ -> []
SymbolicRef{} -> []
-- | Get executable segments.
executableSegments :: Memory w -> [MemSegment w]
executableSegments = filter (Perm.isExecutable . segmentFlags) . memSegments
-- | Get readonly segments
readonlySegments :: Memory w -> [MemSegment w]
readonlySegments = filter (Perm.isReadonly . segmentFlags) . memSegments
-- | Given an absolute address, this returns a segment and offset into the segment.
absoluteAddrSegment :: Memory w -> MemWord w -> Maybe (SegmentedAddr w)
absoluteAddrSegment mem addr = addrWidthClass (memAddrWidth mem) $
case Map.lookupLE addr (memAbsoluteSegments mem) of
Just (base, seg) | addr < base + segmentSize seg ->
Just $! SegmentedAddr { addrSegment = seg
, _addrOffset = addr - base
}
_ -> Nothing
-- | Read an address from the value in the segment or report a memory error.
readAddr :: Memory w
-> Endianness
-> SegmentedAddr w
-> Either (MemoryError w) (SegmentedAddr w)
readAddr mem end addr = addrWidthClass (memAddrWidth mem) $ do
let sz = fromIntegral (addrSize addr)
case nextRegion addr of
Just (MemWord offset, ByteRegion bs)
| offset + sz >= offset -- Check for no overfow
, offset + sz <= fromIntegral (BS.length bs) -> do -- Check length
let Just val = addrRead end (BS.take (fromIntegral sz) (BS.drop (fromIntegral offset) bs))
case Map.lookupLE val (memAbsoluteSegments mem) of
Just (base, seg) | val <= base + segmentSize seg -> Right $
SegmentedAddr { addrSegment = seg
, _addrOffset = val - base
}
_ -> Left (InvalidAddr addr)
_ | otherwise ->
Left (AccessViolation addr)
-- | Return segment with given index in memory.
lookupSegment :: Memory w -> SegmentIndex -> Maybe (MemSegment w)
lookupSegment m i = Map.lookup i (memAllSegments m)
data InsertError w
= OverlapSegment (MemWord w) (MemSegment w)
@ -616,53 +533,188 @@ insertMemSegment seg mem = addrWidthClass (memAddrWidth mem) $ do
, memAllSegments = allMap
}
-- | Return segment if range is entirely contained within a single segment
-- and 'Nothing' otherwise.
segmentOfRange :: MemWord w -- ^ Start of range
-> MemWord w -- ^ One past last index in range.
-> Memory w
-> Maybe (MemSegment w)
segmentOfRange base end mem = addrWidthClass (memAddrWidth mem) $ do
case Map.lookupLE base (memAbsoluteSegments mem) of
Just (seg_base, seg) | end <= seg_base + segmentSize seg -> Just seg
------------------------------------------------------------------------
-- MemSegmentOff
-- | A pair containing a segment and offset.
--
-- Constructrs enforce that the offset is valid
data MemSegmentOff w = MemSegmentOff { msegSegment :: !(MemSegment w)
, msegOffset :: !(MemWord w)
}
deriving (Eq, Ord)
-- | Return the segment associated with the given address if well-defined.
resolveAbsoluteAddr :: Memory w -> MemWord w -> Maybe (MemSegmentOff w)
resolveAbsoluteAddr mem addr = addrWidthClass (memAddrWidth mem) $
case Map.lookupLE addr (memAbsoluteSegments mem) of
Just (base, seg) | addr - base < segmentSize seg ->
Just $! MemSegmentOff seg (addr - base)
_ -> Nothing
-- | Return true if address satisfies permissions check.
addrPermissions :: MemWord w -> Memory w -> Perm.Flags
addrPermissions addr mem = addrWidthClass (memAddrWidth mem) $
case Map.lookupLE addr (memAbsoluteSegments mem) of
Just (base, seg) | addr < base + segmentSize seg -> segmentFlags seg
_ -> Perm.none
-- | Indicates if address is a code pointer.
isCodeAddr :: Memory w -> MemWord w -> Bool
isCodeAddr mem val =
addrPermissions val mem `Perm.hasPerm` Perm.execute
-- | Make a segment offset pair after ensuring the offset is valid
resolveSegmentOff :: MemWidth w => MemSegment w -> MemWord w -> Maybe (MemSegmentOff w)
resolveSegmentOff seg off
| off < segmentSize seg = Just (MemSegmentOff seg off)
| otherwise = Nothing
-- | Indicates if address is an address in code segment or null.
isCodeAddrOrNull :: Memory w -> MemWord w -> Bool
isCodeAddrOrNull _ (MemWord 0) = True
isCodeAddrOrNull mem a = isCodeAddr mem a
-- | Return the absolute address associated with the segment offset pair (if any)
msegAddr :: MemWidth w => MemSegmentOff w -> Maybe (MemWord w)
msegAddr (MemSegmentOff seg off) = (+ off) <$> segmentBase seg
-- | Increment a segment offset by a given amount.
--
-- Returns 'Nothing' if the result would be out of range.
incSegmentOff :: MemWidth w => MemSegmentOff w -> Integer -> Maybe (MemSegmentOff w)
incSegmentOff (MemSegmentOff seg off) inc
| 0 <= next && next <= toInteger (segmentSize seg) = Just $ MemSegmentOff seg (fromInteger next)
| otherwise = Nothing
where next = toInteger off + inc
-- | Return the difference between two segment offsets pairs or `Nothing` if undefined.
diffSegmentOff :: MemWidth w => MemSegmentOff w -> MemSegmentOff w -> Maybe Integer
diffSegmentOff (MemSegmentOff xseg xoff) (MemSegmentOff yseg yoff)
| xseg == yseg = Just $ toInteger xoff - toInteger yoff
| Just xb <- segmentBase xseg
, Just yb <- segmentBase yseg =
Just ((toInteger xb + toInteger xoff) - (toInteger yb + toInteger yoff))
| otherwise = Nothing
instance MemWidth w => Show (MemSegmentOff w) where
showsPrec p (MemSegmentOff seg off) =
case segmentBase seg of
Just base -> showString "0x" . showHex (base+off)
Nothing ->
showParen (p > 6)
$ showString "segment"
. shows (segmentIndex seg)
. showString "+"
. shows off
instance MemWidth w => Pretty (MemSegmentOff w) where
pretty = text . show
-- | Return list of segmented address values in memory.
--
-- Each address includes the value and the base.
memAsAddrPairs :: Memory w
-> Endianness
-> [(MemSegmentOff w, MemSegmentOff w)]
memAsAddrPairs mem end = addrWidthClass (memAddrWidth mem) $ do
seg <- memSegments mem
(contents_offset,r) <- contentsList (segmentContents seg)
let sz = addrSize mem
case r of
ByteRegion bs -> assert (BS.length bs `rem` fromIntegral sz == 0) $ do
(off,w) <-
zip [contents_offset..]
(regularChunks (fromIntegral sz) bs)
let Just val = addrRead end w
case resolveAbsoluteAddr mem val of
Just val_ref -> do
pure (MemSegmentOff seg off, val_ref)
_ -> []
SymbolicRef{} -> []
------------------------------------------------------------------------
-- MemAddr
-- | A memory address is either an absolute value in memory or an offset of segment that
-- could be relocated.
--
-- This representation does not require that the address is mapped to
-- actual memory (see `MemSegmentOff` for an address representation
-- that ensures the reference points to allocated memory).
data MemAddr w
= AbsoluteAddr (MemWord w)
-- ^ An address formed from a specific value.
| RelativeAddr !(MemSegment w) !(MemWord w)
-- ^ An address that is relative to some specific segment.
--
-- Note that the segment base value of this segment should be nothing.
deriving (Eq, Ord)
-- | Given an absolute address, this returns a segment and offset into the segment.
absoluteAddr :: MemWord w -> MemAddr w
absoluteAddr = AbsoluteAddr
-- | Return an address relative to a known memory segment
-- if the memory is unmapped.
relativeAddr :: MemWidth w => MemSegment w -> MemWord w -> MemAddr w
relativeAddr seg off =
case segmentBase seg of
Just base -> AbsoluteAddr (base + off)
Nothing -> RelativeAddr seg off
-- | Return a segmented addr using the offset of an existing segment, or 'Nothing'
-- if the memory is unmapped.
relativeSegmentAddr :: MemWidth w => MemSegmentOff w -> MemAddr w
relativeSegmentAddr (MemSegmentOff seg off) = relativeAddr seg off
-- | Return the offset of the address after adding the base segment value if defined.
asAbsoluteAddr :: MemWidth w => MemAddr w -> Maybe (MemWord w)
asAbsoluteAddr (AbsoluteAddr w) = Just w
asAbsoluteAddr RelativeAddr{} = Nothing
-- | Return the resolved segment offset reference from an address.
asSegmentOff :: Memory w -> MemAddr w -> Maybe (MemSegmentOff w)
asSegmentOff mem (AbsoluteAddr addr) = resolveAbsoluteAddr mem addr
asSegmentOff mem (RelativeAddr seg off) =
addrWidthClass (memAddrWidth mem) $
resolveSegmentOff seg off
-- | Clear the least significant bit of an address.
clearAddrLeastBit :: MemWidth w => MemAddr w -> MemAddr w
clearAddrLeastBit sa =
case sa of
AbsoluteAddr a -> AbsoluteAddr (a .&. complement 1)
RelativeAddr seg off -> RelativeAddr seg (off .&. complement 1)
-- | Increment an address by a fixed amount.
incAddr :: MemWidth w => Integer -> MemAddr w -> MemAddr w
incAddr o (AbsoluteAddr a) = AbsoluteAddr (a + fromInteger o)
incAddr o (RelativeAddr seg off) = RelativeAddr seg (off + fromInteger o)
-- | Returns the number of bytes between two addresses if they are comparable
-- or 'Nothing' if they are not.
diffAddr :: MemWidth w => MemAddr w -> MemAddr w -> Maybe Integer
diffAddr (AbsoluteAddr x) (AbsoluteAddr y) =
Just $ toInteger x - toInteger y
diffAddr (RelativeAddr xseg xoff) (RelativeAddr yseg yoff) | xseg == yseg =
Just $ toInteger xoff - toInteger yoff
diffAddr _ _ = Nothing
instance MemWidth w => Show (MemAddr w) where
showsPrec _ (AbsoluteAddr a) = showString "0x" . showHex a
showsPrec p (RelativeAddr seg off) =
showParen (p > 6)
$ showString "segment"
. shows (segmentIndex seg)
. showString "+"
. shows off
instance MemWidth w => Pretty (MemAddr w) where
pretty = text . show
------------------------------------------------------------------------
-- MemoryError
-- | Type of errors that may occur when reading memory.
data MemoryError w
= UserMemoryError (SegmentedAddr w) !String
= UserMemoryError (MemAddr w) !String
-- ^ the memory reader threw an unspecified error at the given location.
| InvalidInstruction (SegmentedAddr w) ![SegmentRange w]
| InvalidInstruction (MemAddr w) ![SegmentRange w]
-- ^ The memory reader could not parse the value starting at the given address.
| AccessViolation (SegmentedAddr w)
| AccessViolation (MemAddr w)
-- ^ Memory could not be read, because it was not defined.
| PermissionsError (SegmentedAddr w)
| PermissionsError (MemAddr w)
-- ^ Memory could not be read due to insufficient permissions.
| UnexpectedRelocation (SegmentedAddr w)
| UnexpectedRelocation (MemAddr w)
-- ^ Read from location that partially overlaps a relocated entry
| InvalidAddr (SegmentedAddr w)
| InvalidAddr (MemAddr w)
-- ^ The data at the given address did not refer to a valid memory location.
instance Show (MemoryError w) where
instance MemWidth w => Show (MemoryError w) where
show (UserMemoryError _ msg) = msg
show (InvalidInstruction start contents) =
"Invalid instruction at " ++ show start ++ ": " ++ showList contents ""
@ -676,45 +728,72 @@ instance Show (MemoryError w) where
"Attempt to interpret an invalid address: " ++ show a ++ "."
------------------------------------------------------------------------
-- Meory reading utilities
-- Memory reading utilities
-- | Return contents starting from location or throw a memory error if there
-- is an unaligned relocation.
addrContentsAfter :: Memory w
-> MemAddr w
-> Either (MemoryError w) [SegmentRange w]
addrContentsAfter mem addr = addrWidthClass (memAddrWidth mem) $ do
MemSegmentOff seg off <-
case asSegmentOff mem addr of
Just p -> pure p
Nothing -> Left (InvalidAddr addr)
case contentsAfter off (segmentContents seg) of
Just l -> Right l
Nothing -> Left (UnexpectedRelocation addr)
-- | Attemtp to read a bytestring of the given length
readByteString :: MemWidth w => SegmentedAddr w -> Word64 -> Either (MemoryError w) BS.ByteString
readByteString addr sz =
case nextRegion addr of
Nothing -> Left (InvalidAddr addr)
Just (MemWord offset, ByteRegion bs)
| offset + sz >= offset -- Check for no overfow
, offset + sz <= fromIntegral (BS.length bs) -> do -- Check length
Right $ (BS.take (fromIntegral sz) (BS.drop (fromIntegral offset) bs))
| otherwise -> Left (InvalidAddr addr)
Just (_, SymbolicRef{}) ->
readByteString :: Memory w -> MemAddr w -> Word64 -> Either (MemoryError w) BS.ByteString
readByteString mem addr sz = do
l <- addrContentsAfter mem addr
case l of
ByteRegion bs:_
| sz <= fromIntegral (BS.length bs) -> do -- Check length
Right (BS.take (fromIntegral sz) bs)
| otherwise ->
Left (InvalidAddr addr)
SymbolicRef{}:_ ->
Left (UnexpectedRelocation addr)
[] ->
Left (InvalidAddr addr)
-- | Read an address from the value in the segment or report a memory error.
readAddr :: Memory w
-> Endianness
-> MemAddr w
-> Either (MemoryError w) (MemAddr w)
readAddr mem end addr = addrWidthClass (memAddrWidth mem) $ do
let sz = fromIntegral (addrSize addr)
bs <- readByteString mem addr sz
let Just val = addrRead end bs
Right $ AbsoluteAddr val
-- | Read a big endian word16
readWord8 :: MemWidth w => SegmentedAddr w -> Either (MemoryError w) Word8
readWord8 addr = bsWord8 <$> readByteString addr 8
readWord8 :: Memory w -> MemAddr w -> Either (MemoryError w) Word8
readWord8 mem addr = bsWord8 <$> readByteString mem addr 1
-- | Read a big endian word16
readWord16be :: MemWidth w => SegmentedAddr w -> Either (MemoryError w) Word16
readWord16be addr = bsWord16be <$> readByteString addr 8
readWord16be :: Memory w -> MemAddr w -> Either (MemoryError w) Word16
readWord16be mem addr = bsWord16be <$> readByteString mem addr 2
-- | Read a little endian word16
readWord16le :: MemWidth w => SegmentedAddr w -> Either (MemoryError w) Word16
readWord16le addr = bsWord16le <$> readByteString addr 8
readWord16le :: Memory w -> MemAddr w -> Either (MemoryError w) Word16
readWord16le mem addr = bsWord16le <$> readByteString mem addr 2
-- | Read a big endian word32
readWord32be :: MemWidth w => SegmentedAddr w -> Either (MemoryError w) Word32
readWord32be addr = bsWord32be <$> readByteString addr 8
readWord32be :: Memory w -> MemAddr w -> Either (MemoryError w) Word32
readWord32be mem addr = bsWord32be <$> readByteString mem addr 4
-- | Read a little endian word32
readWord32le :: MemWidth w => SegmentedAddr w -> Either (MemoryError w) Word32
readWord32le addr = bsWord32le <$> readByteString addr 8
readWord32le :: Memory w -> MemAddr w -> Either (MemoryError w) Word32
readWord32le mem addr = bsWord32le <$> readByteString mem addr 4
-- | Read a big endian word64
readWord64be :: MemWidth w => SegmentedAddr w -> Either (MemoryError w) Word64
readWord64be addr = bsWord64be <$> readByteString addr 8
readWord64be :: Memory w -> MemAddr w -> Either (MemoryError w) Word64
readWord64be mem addr = bsWord64be <$> readByteString mem addr 8
-- | Read a little endian word64
readWord64le :: MemWidth w => SegmentedAddr w -> Either (MemoryError w) Word64
readWord64le addr = bsWord64le <$> readByteString addr 8
readWord64le :: Memory w -> MemAddr w -> Either (MemoryError w) Word64
readWord64le mem addr = bsWord64le <$> readByteString mem addr 8

View File

@ -11,6 +11,7 @@ Operations for creating a view of memory from an elf file.
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
module Data.Macaw.Memory.ElfLoader
( SectionIndexMap
@ -32,7 +33,6 @@ import Data.Bits
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as L
import Data.Either (partitionEithers)
import Data.ElfEdit
import Data.Foldable
import Data.IntervalMap.Strict (Interval(..), IntervalMap)
@ -60,7 +60,7 @@ sliceL (i,c) = L.take (fromIntegral c) . L.drop (fromIntegral i)
-- address and section contents.
--
-- The base address is expressed in terms of the underlying memory segment.
type SectionIndexMap w = Map ElfSectionIndex (SegmentedAddr w, ElfSection (ElfWordType w))
type SectionIndexMap w = Map ElfSectionIndex (MemSegmentOff w, ElfSection (ElfWordType w))
------------------------------------------------------------------------
-- Flag conversion
@ -333,8 +333,8 @@ insertElfSegment opt shdrMap contents relocMap phdr = do
when (phdr_offset > shdr_start) $ do
fail $ "Found section header that overlaps with program header."
let sec_offset = fromIntegral $ shdr_start - phdr_offset
let pair = (SegmentedAddr seg sec_offset, sec)
mlsIndexMap %= Map.insert elfIdx pair
let Just addr = resolveSegmentOff seg sec_offset
mlsIndexMap %= Map.insert elfIdx (addr, sec)
_ -> fail "Unexpected shdr interval"
@ -398,8 +398,8 @@ insertElfSection sec = do
let seg = memSegmentForElfSection idx sec
loadMemSegment ("Section " ++ BSC.unpack (elfSectionName sec)) seg
let elfIdx = ElfSectionIndex (elfSectionIndex sec)
let pair = (SegmentedAddr seg 0, sec)
mlsIndexMap %= Map.insert elfIdx pair
let Just addr = resolveSegmentOff seg 0
mlsIndexMap %= Map.insert elfIdx (addr, sec)
-- | Load allocated Elf sections into memory.
--
@ -462,29 +462,22 @@ loadExecutable opt path = do
------------------------------------------------------------------------
-- Elf symbol utilities
-- | The takes the elf symbol table map and attempts to identify segmented addresses for each one.
--
-- It returns a two maps, the first contains entries that could not be resolved; the second
-- contains those that could.
-- | The takes the elf symbol table map, creates a map from function
-- symbol addresses to the associated symbol name.
resolvedSegmentedElfFuncSymbols :: forall w
. Memory w
-> [ElfSymbolTableEntry (ElfWordType w)]
-> (Map (MemWord w) [BS.ByteString], Map (SegmentedAddr w) [BS.ByteString])
-> Map (MemSegmentOff w) [BS.ByteString]
resolvedSegmentedElfFuncSymbols mem entries = reprConstraints (memAddrWidth mem) $
let -- Filter out just function entries
isCodeFuncSymbol ste = steType ste == STT_FUNC
&& isCodeAddr mem (fromIntegral (steValue ste))
func_entries = filter isCodeFuncSymbol entries
-- Build absolute address map
absAddrMap :: Map (MemWord w) [BS.ByteString]
absAddrMap = Map.fromListWith (++) $ [ (fromIntegral (steValue ste), [steName ste]) | ste <- func_entries ]
-- Resolve addresses
resolve (v,nms) =
case absoluteAddrSegment mem v of
Nothing -> Left (v, nms)
Just sv -> Right (sv, nms)
(u,r) = partitionEithers $ resolve <$> Map.toList absAddrMap
in (Map.fromList u, Map.fromList r)
func_entries =
[ (addr, [steName ste])
| ste <- entries
, steType ste == STT_FUNC
, addr <- maybeToList $ resolveAbsoluteAddr mem (fromIntegral (steValue ste))
, segmentFlags (msegSegment addr) `Perm.hasPerm` Perm.execute
]
in Map.fromListWith (++) func_entries
ppElfUnresolvedSymbols :: forall w
. MemWidth w