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    fromByte,
  8    fromReg,
  9    fromSExpr,
 10    toSExpr,
 11    symbolic,
 12    toCond,
 13  )
 14where
 15
 16import Control.Exception (assert)
 17import Data.Word (Word8)
 18import Language.QBE.Simulator.Default.Expression qualified as D
 19import Language.QBE.Simulator.Expression qualified as E
 20import Language.QBE.Simulator.Memory qualified as MEM
 21import Language.QBE.Types qualified as QBE
 22import SimpleSMT qualified as SMT
 23
 24-- This types supports more than just the 'QBE.BaseType' to ease the
 25-- implementation of the 'MEM.Storable' type class. Essentially, this
 26-- data type just provides a generic BitVector abstraction on top of
 27-- 'SMT.SExpr'.
 28data BitVector
 29  = BitVector
 30  { sexpr :: SMT.SExpr,
 31    -- TODO: For BitVectors, we can extract the type from the SExpr.
 32    -- Technically, we don't need the 'ExtType' here and can just
 33    -- turn this into a newtype.
 34    qtype :: QBE.ExtType
 35  }
 36  deriving (Show, Eq)
 37
 38fromByte :: Word8 -> BitVector
 39fromByte byte = BitVector (SMT.bvBin 8 $ fromIntegral byte) QBE.Byte
 40
 41fromReg :: D.RegVal -> BitVector
 42fromReg (D.VWord v) = BitVector (SMT.bvBin 32 $ fromIntegral v) (QBE.Base QBE.Word)
 43fromReg (D.VLong v) = BitVector (SMT.bvBin 64 $ fromIntegral v) (QBE.Base QBE.Long)
 44fromReg (D.VSingle _) = error "symbolic floats not supported"
 45fromReg (D.VDouble _) = error "symbolic doubles not supported"
 46
 47fromSExpr :: QBE.BaseType -> SMT.SExpr -> BitVector
 48fromSExpr ty sexpr = BitVector sexpr (QBE.Base ty)
 49
 50toSExpr :: BitVector -> SMT.SExpr
 51toSExpr = sexpr
 52
 53symbolic :: SMT.Solver -> String -> QBE.BaseType -> IO BitVector
 54symbolic solver name ty = do
 55  let bits = SMT.tBits $ fromIntegral (QBE.baseTypeBitSize ty)
 56  sym <- SMT.declare solver name bits
 57  return $ BitVector sym (QBE.Base ty)
 58
 59-- In the QBE a condition (see `jnz`) is true if the Word value is not zero.
 60toCond :: Bool -> BitVector -> SMT.SExpr
 61toCond isTrue BitVector {sexpr = s, qtype = ty} =
 62  -- Equality is only defined for Words.
 63  assert (ty == QBE.Base QBE.Word) $
 64    let zeroSExpr = sexpr (fromReg $ E.fromLit QBE.Word 0)
 65     in toCond' s zeroSExpr
 66  where
 67    toCond' lhs rhs
 68      | isTrue = SMT.not (SMT.eq lhs rhs) -- /= 0
 69      | otherwise = SMT.eq lhs rhs -- == 0
 70
 71------------------------------------------------------------------------
 72
 73instance MEM.Storable BitVector BitVector where
 74  toBytes BitVector {sexpr = s, qtype = ty} =
 75    assert (size `mod` 8 == 0) $
 76      map (\n -> BitVector (nthByte s n) QBE.Byte) [1 .. size `div` 8]
 77    where
 78      size :: Integer
 79      size = fromIntegral $ QBE.extTypeBitSize ty
 80
 81      nthByte :: SMT.SExpr -> Integer -> SMT.SExpr
 82      nthByte expr n = SMT.extract expr ((n * 8) - 1) ((n - 1) * 8)
 83
 84  fromBytes _ [] = Nothing
 85  fromBytes ty bytes@(BitVector {sexpr = s} : xs) =
 86    if length bytes /= fromIntegral (QBE.loadByteSize ty)
 87      then Nothing
 88      else case (ty, bytes) of
 89        (QBE.LSubWord QBE.UnsignedByte, [_]) ->
 90          Just (BitVector (SMT.zeroExtend 24 concated) (QBE.Base QBE.Word))
 91        (QBE.LSubWord QBE.SignedByte, [_]) ->
 92          Just (BitVector (SMT.signExtend 24 concated) (QBE.Base QBE.Word))
 93        (QBE.LSubWord QBE.SignedHalf, [_, _]) ->
 94          Just (BitVector (SMT.signExtend 16 concated) (QBE.Base QBE.Word))
 95        (QBE.LSubWord QBE.UnsignedHalf, [_, _]) ->
 96          Just (BitVector (SMT.zeroExtend 16 concated) (QBE.Base QBE.Word))
 97        (QBE.LBase QBE.Word, [_, _, _, _]) ->
 98          Just (BitVector concated (QBE.Base QBE.Word))
 99        (QBE.LBase QBE.Long, [_, _, _, _, _, _, _, _]) ->
100          Just (BitVector concated (QBE.Base QBE.Long))
101        (QBE.LBase QBE.Single, [_, _, _, _]) ->
102          error "float loading not implemented"
103        (QBE.LBase QBE.Double, [_, _, _, _, _, _, _, _]) ->
104          error "double loading not implemented"
105        _ -> Nothing
106    where
107      concated :: SMT.SExpr
108      concated = foldl concatBV s xs
109
110      concatBV :: SMT.SExpr -> BitVector -> SMT.SExpr
111      concatBV acc byte =
112        assert (qtype byte == QBE.Byte) $
113          SMT.concat (sexpr byte) acc
114
115------------------------------------------------------------------------
116
117binaryOp :: (SMT.SExpr -> SMT.SExpr -> SMT.SExpr) -> BitVector -> BitVector -> Maybe BitVector
118binaryOp op lhs@(BitVector {sexpr = slhs}) rhs@(BitVector {sexpr = srhs})
119  | qtype lhs == qtype rhs = Just $ lhs {sexpr = slhs `op` srhs}
120  | otherwise = Nothing
121
122binaryBoolOp :: (SMT.SExpr -> SMT.SExpr -> SMT.SExpr) -> BitVector -> BitVector -> Maybe BitVector
123binaryBoolOp op lhs rhs = do
124  bv <- binaryOp op lhs rhs
125  -- TODO: Can we get rid of the ITE somehow?
126  return $ fromSExpr QBE.Long (SMT.ite (toSExpr bv) trueValue falseValue)
127  where
128    -- TODO: Declare these as constants.
129    trueValue :: SMT.SExpr
130    trueValue = toSExpr $ E.fromLit QBE.Long 1
131
132    falseValue :: SMT.SExpr
133    falseValue = toSExpr $ E.fromLit QBE.Long 0
134
135-- TODO: If we Change E.ValueRepr to operate in 'Exec' then we can do IO stuff here.
136instance E.ValueRepr BitVector where
137  fromLit ty n =
138    let exty = QBE.Base ty
139        size = fromIntegral $ QBE.extTypeBitSize exty
140     in BitVector (SMT.bvBin size $ fromIntegral n) exty
141
142  fromFloat = error "symbolic floats currently unsupported"
143  fromDouble = error "symbolic doubles currently unsupported"
144
145  -- XXX: This only works for constants values, but this is fine since we implement
146  -- concolic execution and can obtain the address from the concrete value part.
147  toAddress addr =
148    case SMT.sexprToVal (sexpr addr) of
149      -- TODO: Don't hardcode address type
150      SMT.Bits 64 n -> Just $ fromIntegral n
151      _ -> Nothing
152  fromAddress addr = fromReg (E.fromLit QBE.Long addr)
153
154  wordToLong (QBE.SLSubWord QBE.SignedByte) (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
155    Just $ BitVector (SMT.signExtend 56 (SMT.extract s 7 0)) (QBE.Base QBE.Long)
156  wordToLong (QBE.SLSubWord QBE.UnsignedByte) (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
157    Just $ BitVector (SMT.zeroExtend 56 (SMT.extract s 7 0)) (QBE.Base QBE.Long)
158  wordToLong (QBE.SLSubWord QBE.SignedHalf) (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
159    Just $ BitVector (SMT.signExtend 48 (SMT.extract s 15 0)) (QBE.Base QBE.Long)
160  wordToLong (QBE.SLSubWord QBE.UnsignedHalf) (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
161    Just $ BitVector (SMT.zeroExtend 48 (SMT.extract s 15 0)) (QBE.Base QBE.Long)
162  wordToLong QBE.SLSignedWord (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
163    Just $ BitVector (SMT.signExtend 32 s) (QBE.Base QBE.Long)
164  wordToLong QBE.SLUnsignedWord (BitVector {sexpr = s, qtype = QBE.Base QBE.Word}) =
165    Just $ BitVector (SMT.zeroExtend 32 s) (QBE.Base QBE.Long)
166  wordToLong _ _ = Nothing
167
168  subType QBE.Word v@(BitVector {qtype = QBE.Base QBE.Word}) = Just v
169  subType QBE.Word (BitVector {qtype = QBE.Base QBE.Long, sexpr = s}) =
170    Just $ BitVector (SMT.extract s 31 0) (QBE.Base QBE.Word)
171  subType QBE.Long v@(BitVector {qtype = QBE.Base QBE.Long}) = Just v
172  subType QBE.Single v@(BitVector {qtype = QBE.Base QBE.Single}) = Just v
173  subType QBE.Double v@(BitVector {qtype = QBE.Base QBE.Double}) = Just v
174  subType _ _ = Nothing
175
176  -- XXX: This only works for constants values, but this is fine (see above).
177  isZero bv =
178    case SMT.sexprToVal (sexpr bv) of
179      SMT.Bits _ v -> v == 0
180      _ -> error "unreachable"
181
182  add = binaryOp SMT.bvAdd
183  sub = binaryOp SMT.bvSub
184  mul = binaryOp SMT.bvMul
185  or = binaryOp SMT.bvOr
186  xor = binaryOp SMT.bvXOr
187  and = binaryOp SMT.bvAnd
188  urem = binaryOp SMT.bvURem
189  srem = binaryOp SMT.bvSRem
190  udiv = binaryOp SMT.bvUDiv
191
192  eq = binaryBoolOp SMT.eq
193  ne = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.eq lhs rhs)
194  sle = binaryBoolOp SMT.bvSLeq
195  slt = binaryBoolOp SMT.bvSLt
196  sge = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.bvSLt lhs rhs)
197  sgt = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.bvSLeq lhs rhs)
198  ule = binaryBoolOp SMT.bvULeq
199  ult = binaryBoolOp SMT.bvULt
200  uge = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.bvULt lhs rhs)
201  ugt = binaryBoolOp (\lhs rhs -> SMT.not $ SMT.bvULeq lhs rhs)