Skip to content

Commit

Permalink
Data.Macaw.PPC: add classifier to handle pc-relative loads for reloca…
Browse files Browse the repository at this point in the history
…table code

To do PC-relative reads for relocatable code, PPC binaries will
use the `bl` instruction to jump to the next address in order to read
the PC into the link register. This is incorrectly classified as
a function call by the default classifier.

This adds an additional classifier to PPC that specifically checks
for this case (i.e. a `bl` to the subsequent address) and instead
treats it like a normal jump, where the LR is concretely known to be
the PC at the jump target.

It is unlikely that binaries would have this pattern but intend it
to be treated like a normal function call. Nevertheless, this would be a
breaking change for such a binary if it existed and would require
refining this classifier further in order to distinguish this case.
  • Loading branch information
danmatichuk committed Dec 15, 2023
1 parent c2c2a3d commit 9b1b120
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 2 deletions.
1 change: 1 addition & 0 deletions base/src/Data/Macaw/Discovery.hs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ module Data.Macaw.Discovery
, jumpTableClassifier
-- * Simplification
, eliminateDeadStmts
, identifyCallTargets
-- * Re-exports
, ArchAddrWidth
) where
Expand Down
1 change: 1 addition & 0 deletions base/src/Data/Macaw/Discovery/Classifier.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module Data.Macaw.Discovery.Classifier (
-- * Reusable helpers
, branchBlockState
, classifierEndBlock
, identifyCallTargets
) where

import Control.Applicative ( Alternative(empty) )
Expand Down
44 changes: 42 additions & 2 deletions macaw-ppc/src/Data/Macaw/PPC.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Data.Macaw.PPC (
-- * Macaw configurations
ppc32_linux_info,
ppc64_linux_info,
ppcReadPCClassifier,
-- * Type-level tags
AnyPPC,
Variant,
Expand All @@ -29,6 +30,7 @@ module Data.Macaw.PPC (
import Control.Lens ( (^.) )
import Data.Maybe
import Data.Proxy ( Proxy(..) )
import Control.Applicative ( (<|>) )

import qualified Data.Macaw.Architecture.Info as MI
import qualified Data.Macaw.CFG as MC
Expand Down Expand Up @@ -64,6 +66,7 @@ import Data.Macaw.PPC.Identify ( identifyCall
import qualified Data.Macaw.PPC.PPCReg as R
import qualified Data.Macaw.PPC.Semantics.PPC32 as PPC32
import qualified Data.Macaw.PPC.Semantics.PPC64 as PPC64
import qualified Control.Monad.Reader as CMR

-- | The constructor for type tags for PowerPC
type AnyPPC = PPC.AnyPPC
Expand Down Expand Up @@ -111,7 +114,7 @@ ppc64_linux_info binData =
, MI.initialBlockRegs = PPC.Eval.ppcInitialBlockRegs
, MI.archCallParams = PPC.Eval.ppcCallParams (preserveRegAcrossSyscall proxy)
, MI.extractBlockPrecond = PPC.Eval.ppcExtractBlockPrecond
, MI.archClassifier = MD.defaultClassifier
, MI.archClassifier = ppcReadPCClassifier <|> MD.defaultClassifier
}
where
proxy = Proxy @PPC.V64
Expand All @@ -137,7 +140,44 @@ ppc32_linux_info =
, MI.initialBlockRegs = PPC.Eval.ppcInitialBlockRegs
, MI.archCallParams = PPC.Eval.ppcCallParams (preserveRegAcrossSyscall proxy)
, MI.extractBlockPrecond = PPC.Eval.ppcExtractBlockPrecond
, MI.archClassifier = MD.defaultClassifier
, MI.archClassifier = ppcReadPCClassifier <|> MD.defaultClassifier
}
where
proxy = Proxy @PPC.V32


-- | This classifier handles a ppc-specific pattern used to read the pc
-- in order to do pc-relative loads for relocatable code.
--
-- To do this, the pc is loaded into the link register by "calling" a
-- function at the immediate next address, causing the LR and
-- the pc to be equal. The original LR value
-- is then restored before the function return.
--
-- e.g.
-- 0x000018f8 mflr r0
-- 0x000018fc bdnzl 0x1900
-- 0x00001900 mflr r30
-- 0x00001904 lwz r9, -0x20(r30)
-- ...
-- 0x00001bd4 mtlr r0
-- 0x00001bd8 blr
--
-- This stashes the original LR in r0, and then the pc (i.e. 0x1900) in the
-- LR, which is then used to do a pc-relative load into r9.
-- Before the function returns, the original LR is restored from r0.

ppcReadPCClassifier :: PPCArchConstraints v => MD.BlockClassifier (AnyPPC v) ids
ppcReadPCClassifier = MI.classifierName "PPCReadPC" $ do
bcc <- CMR.ask
let ctx = MI.classifierParseContext bcc
let finalRegs = MI.classifierFinalRegState bcc
let ainfo = MI.pctxArchInfo ctx
let mem = MI.pctxMemory ctx
ret <- case MI.identifyCall ainfo mem (MI.classifierStmts bcc) finalRegs of
Just (_prev_stmts, ret) -> pure ret
Nothing -> fail "no call identified"
let targets = MD.identifyCallTargets mem (MI.classifierAbsState bcc) finalRegs
case targets of
[target] | target == ret -> MD.directJumpClassifier
_ -> fail $ ("call is not a pc read: " ++ show targets)

0 comments on commit 9b1b120

Please sign in to comment.