quebex

A software analysis framework built around the QBE intermediate language

git clone https://git.8pit.net/quebex.git

  1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>
  2--
  3-- SPDX-License-Identifier: GPL-3.0-only
  4
  5module Language.QBE.Simulator.Symbolic.Expression
  6  ( BitVector,
  7    fromReg,
  8    fromSExpr,
  9    toSExpr,
 10    symbolic,
 11    toCond,
 12  )
 13where
 14
 15import Control.Exception (assert)
 16import Language.QBE.Simulator.Default.Expression qualified as D
 17import Language.QBE.Simulator.Expression qualified as E
 18import Language.QBE.Types qualified as QBE
 19import SimpleSMT qualified as SMT
 20
 21data BitVector
 22  = BitVector
 23  { sexpr :: SMT.SExpr,
 24    qtype :: QBE.ExtType
 25  }
 26  deriving (Show, Eq)
 27
 28fromReg :: D.RegVal -> BitVector
 29fromReg (D.VByte v) = BitVector (SMT.bvBin 8 $ fromIntegral v) QBE.Byte
 30fromReg (D.VHalf v) = BitVector (SMT.bvBin 16 $ fromIntegral v) QBE.HalfWord
 31fromReg (D.VWord v) = BitVector (SMT.bvBin 32 $ fromIntegral v) (QBE.Base QBE.Word)
 32fromReg (D.VLong v) = BitVector (SMT.bvBin 64 $ fromIntegral v) (QBE.Base QBE.Long)
 33fromReg (D.VSingle _) = error "symbolic floats not supported"
 34fromReg (D.VDouble _) = error "symbolic doubles not supported"
 35
 36fromSExpr :: QBE.BaseType -> SMT.SExpr -> BitVector
 37fromSExpr ty sexpr = BitVector sexpr (QBE.Base ty)
 38
 39toSExpr :: BitVector -> SMT.SExpr
 40toSExpr = sexpr
 41
 42symbolic :: SMT.Solver -> String -> QBE.BaseType -> IO BitVector
 43symbolic solver name ty = do
 44  let bits = SMT.tBits $ fromIntegral (QBE.baseTypeBitSize ty)
 45  sym <- SMT.declare solver name bits
 46  return $ BitVector sym (QBE.Base ty)
 47
 48-- In the QBE a condition (see `jnz`) is true if the Word value is not zero.
 49toCond :: Bool -> BitVector -> SMT.SExpr
 50toCond isTrue BitVector {sexpr = s, qtype = ty} =
 51  -- Equality is only defined for Words.
 52  assert (ty == QBE.Base QBE.Word) $
 53    let zeroSExpr = sexpr (fromReg $ E.fromLit QBE.Word 0)
 54     in toCond' s zeroSExpr
 55  where
 56    toCond' lhs rhs
 57      | isTrue = SMT.not (SMT.eq lhs rhs) -- /= 0
 58      | otherwise = SMT.eq lhs rhs -- == 0
 59
 60------------------------------------------------------------------------
 61
 62instance E.Storable BitVector where
 63  toBytes BitVector {sexpr = s, qtype = ty} =
 64    assert (size `mod` 8 == 0) $
 65      map (\n -> BitVector (nthByte s n) QBE.Byte) [1 .. size `div` 8]
 66    where
 67      size :: Integer
 68      size = fromIntegral $ QBE.extTypeBitSize ty
 69
 70      nthByte :: SMT.SExpr -> Integer -> SMT.SExpr
 71      nthByte expr n = SMT.extract expr ((n * 8) - 1) ((n - 1) * 8)
 72
 73  fromBytes _ [] = Nothing
 74  fromBytes ty bytes@(BitVector {sexpr = s} : xs) =
 75    if length bytes /= QBE.extTypeByteSize ty
 76      then Nothing
 77      else Just $ BitVector (foldl concatBV s xs) ty
 78    where
 79      concatBV :: SMT.SExpr -> BitVector -> SMT.SExpr
 80      concatBV acc byte =
 81        assert (qtype byte == QBE.Byte) $
 82          SMT.concat (sexpr byte) acc
 83
 84------------------------------------------------------------------------
 85
 86binaryOp :: (SMT.SExpr -> SMT.SExpr -> SMT.SExpr) -> BitVector -> BitVector -> Maybe BitVector
 87binaryOp op lhs@(BitVector {sexpr = slhs}) rhs@(BitVector {sexpr = srhs})
 88  | qtype lhs == qtype rhs = Just $ lhs {sexpr = slhs `op` srhs}
 89  | otherwise = Nothing
 90
 91-- TODO: If we Change E.ValueRepr to operate in 'Exec' then we can do IO stuff here.
 92instance E.ValueRepr BitVector where
 93  fromLit ty n =
 94    let exty = QBE.Base ty
 95        size = fromIntegral $ QBE.extTypeBitSize exty
 96     in BitVector (SMT.bvBin size $ fromIntegral n) exty
 97
 98  fromFloat = error "symbolic floats currently unsupported"
 99  fromDouble = error "symbolic doubles currently unsupported"
100
101  -- XXX: This only works for constants values, but this is fine since we implement
102  -- concolic execution and can obtain the address from the concrete value part.
103  toAddress addr =
104    case SMT.sexprToVal (sexpr addr) of
105      -- TODO: Don't hardcode address type
106      SMT.Bits 64 n -> Just $ fromIntegral n
107      _ -> Nothing
108  fromAddress addr = fromReg (E.fromLit QBE.Long addr)
109
110  -- TODO: Don't hardcode bitsizes here
111  -- TODO: refactor (just need to select sign/zero extend)
112  extend QBE.SignedByte (BitVector {sexpr = s, qtype = QBE.Byte}) =
113    Just $ BitVector (SMT.signExtend 56 s) (QBE.Base QBE.Long)
114  extend QBE.UnsignedByte (BitVector {sexpr = s, qtype = QBE.Byte}) =
115    Just $ BitVector (SMT.zeroExtend 56 s) (QBE.Base QBE.Long)
116  extend QBE.SignedHalf (BitVector {sexpr = s, qtype = QBE.HalfWord}) =
117    Just $ BitVector (SMT.signExtend 48 s) (QBE.Base QBE.Long)
118  extend QBE.UnsignedHalf (BitVector {sexpr = s, qtype = QBE.HalfWord}) =
119    Just $ BitVector (SMT.zeroExtend 48 s) (QBE.Base QBE.Long)
120  extend _ _ = Nothing
121
122  subType QBE.Word v@(BitVector {qtype = QBE.Base QBE.Word}) = Just v
123  subType QBE.Word (BitVector {qtype = QBE.Base QBE.Long, sexpr = s}) =
124    Just $ BitVector (SMT.extract s 31 0) (QBE.Base QBE.Word)
125  subType QBE.Long v@(BitVector {qtype = QBE.Base QBE.Long}) = Just v
126  subType QBE.Single v@(BitVector {qtype = QBE.Base QBE.Single}) = Just v
127  subType QBE.Double v@(BitVector {qtype = QBE.Base QBE.Double}) = Just v
128  subType _ _ = Nothing
129
130  -- XXX: This only works for constants values, but this is fine (see above).
131  isZero bv =
132    case SMT.sexprToVal (sexpr bv) of
133      SMT.Bits _ v -> v == 0
134      _ -> error "unreachable"
135
136  add = binaryOp SMT.bvAdd
137  sub = binaryOp SMT.bvSub