Skip to content

Commit db2b19a

Browse files
Add support for x86 64 bit integer stack arguments. (#2007)
* Add support for x86 64 bit integer stack arguments. * Address comments. * Address comments.
1 parent 3aa9f31 commit db2b19a

File tree

7 files changed

+151
-39
lines changed

7 files changed

+151
-39
lines changed

intTests/test_llvm_x86_08/Makefile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CC = clang
2+
CFLAGS = -g -frecord-command-line -O0
3+
4+
all: test.bc test
5+
6+
test.bc: test.c
7+
$(CC) $(CFLAGS) -c -emit-llvm $< -o $@
8+
9+
test: test.c
10+
$(CC) $< -o $@
11+
12+
.PHONY: clean
13+
clean:
14+
rm -f test.bc test
15+

intTests/test_llvm_x86_08/test

1.09 KB
Binary file not shown.

intTests/test_llvm_x86_08/test.bc

3.18 KB
Binary file not shown.

intTests/test_llvm_x86_08/test.c

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
unsigned long test(unsigned long a, unsigned long b, unsigned long c, unsigned long d,unsigned long e, unsigned long f, unsigned long g, unsigned long h) {
2+
return g - h;
3+
}
4+
5+
int main() {
6+
return 0;
7+
}
8+

intTests/test_llvm_x86_08/test.saw

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
enable_experimental;
2+
3+
m <- llvm_load_module "test.bc";
4+
5+
// Test a function with more than 6 arguments to ensure that the remaining
6+
// arguments are spilled to the stack on x86-64.
7+
let test_spec = do {
8+
x <- llvm_fresh_var "x" (llvm_int 64);
9+
llvm_execute_func
10+
[ llvm_term {{ 0 : [64] }}
11+
, llvm_term {{ 1 : [64] }}
12+
, llvm_term {{ 2 : [64] }}
13+
, llvm_term {{ 3 : [64] }}
14+
, llvm_term {{ 4 : [64] }}
15+
, llvm_term {{ 5 : [64] }}
16+
, llvm_term x
17+
, llvm_term {{ 1 : [64] }}
18+
];
19+
llvm_return (llvm_term {{ x - 1 }});
20+
};
21+
22+
llvm_verify_x86 m "./test" "test" [] true test_spec w4;
23+

intTests/test_llvm_x86_08/test.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/sh
2+
set -e
3+
4+
clang -c -emit-llvm -g -frecord-command-line test.c
5+
# clang -c -target x86_64 test.c
6+
# ld.lld -o test test.o
7+
clang -o test test.c
8+
$SAW test.saw
9+

src/SAWScript/Crucible/LLVM/X86.hs

+96-39
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import Verifier.SAW.SCTypeCheck (scTypeCheck)
7878

7979
import Verifier.SAW.Simulator.What4.ReturnTrip
8080

81+
import SAWScript.Panic (panic)
8182
import SAWScript.Proof
8283
import SAWScript.Prover.SolverStats
8384
import SAWScript.TopLevel
@@ -960,11 +961,14 @@ setupMemory ::
960961
setupMemory globsyms balign = do
961962
setupGlobals globsyms
962963

963-
-- Allocate a reasonable amount of stack (4 KiB + 0b10000 for least valid alignment + 1 qword for IP)
964-
allocateStack (4096 + 16) balign
965-
966964
ms <- use x86MethodSpec
967965

966+
-- Allocate a reasonable amount of stack (4 KiB + 1 qword for the previous
967+
-- %rbp value + 1 qword for the return address + 1 qword for each stack
968+
-- argument)
969+
let argsStackSize = fromIntegral $ 8 * (length $ Prelude.drop (length argRegs) $ Map.elems $ ms ^. MS.csArgBindings)
970+
allocateStack (4096 + 16 + argsStackSize) balign
971+
968972
let
969973
tyenv = ms ^. MS.csPreState . MS.csAllocs
970974
nameEnv = MS.csTypeNames ms
@@ -975,6 +979,8 @@ setupMemory globsyms balign = do
975979

976980
setArgs env tyenv nameEnv . fmap snd . Map.elems $ ms ^. MS.csArgBindings
977981

982+
pushFreshReturnAddress
983+
978984
pure env
979985

980986
-- | Given an alist of symbol names and sizes (in bytes), allocate space and copy
@@ -1008,8 +1014,7 @@ setupGlobals globsyms = do
10081014
mem' <- liftIO $ foldlM writeGlobal mem globs
10091015
x86Mem .= mem'
10101016

1011-
-- | Allocate memory for the stack, and pushes a fresh pointer as the return
1012-
-- address.
1017+
-- | Allocate memory for the stack.
10131018
allocateStack ::
10141019
X86Constraints =>
10151020
Integer {- ^ Stack size in bytes -} ->
@@ -1020,17 +1025,44 @@ allocateStack szInt balign = do
10201025
sym <- use x86Sym
10211026
mem <- use x86Mem
10221027
regs <- use x86Regs
1023-
sz <- liftIO $ W4.bvLit sym knownNat $ BV.mkBV knownNat $ szInt + 8
1024-
(base, mem') <- liftIO $ C.LLVM.doMalloc bak C.LLVM.HeapAlloc C.LLVM.Mutable "stack_alloc" mem sz balign
1028+
sz <- liftIO $ W4.bvLit sym knownNat $ BV.mkBV knownNat $ szInt
1029+
(base, finalMem) <- liftIO $ C.LLVM.doMalloc bak C.LLVM.HeapAlloc C.LLVM.Mutable "stack_alloc" mem sz balign
1030+
ptr <- liftIO $ C.LLVM.doPtrAddOffset bak finalMem base sz
1031+
x86Mem .= finalMem
1032+
finalRegs <- setReg Macaw.RSP ptr regs
1033+
x86Regs .= finalRegs
1034+
1035+
-- | Push a fresh pointer as the return address.
1036+
pushFreshReturnAddress ::
1037+
X86Constraints =>
1038+
X86Sim ()
1039+
pushFreshReturnAddress = do
1040+
SomeOnlineBackend bak <- use x86Backend
1041+
sym <- use x86Sym
1042+
mem <- use x86Mem
1043+
regs <- use x86Regs
10251044
sn <- case W4.userSymbol "stack" of
10261045
Left err -> throwX86 $ "Invalid symbol for stack: " <> show err
10271046
Right sn -> pure sn
10281047
fresh <- liftIO $ C.LLVM.LLVMPointer
10291048
<$> W4.natLit sym 0
10301049
<*> W4.freshConstant sym sn (W4.BaseBVRepr $ knownNat @64)
1031-
ptr <- liftIO $ C.LLVM.doPtrAddOffset bak mem' base =<< W4.bvLit sym knownNat (BV.mkBV knownNat szInt)
1032-
writeAlign <- integerToAlignment defaultStackBaseAlign
1033-
finalMem <- liftIO $ C.LLVM.doStore bak mem' ptr
1050+
rsp <- getReg Macaw.RSP regs
1051+
1052+
-- x86-64 System V ABI specifies that: "In other words, the stack needs to be
1053+
-- 16 (32 or 64) byte aligned immediately before the call instruction is
1054+
-- executed. Once control has been transferred to the function entry point,
1055+
-- i.e. immediately after the return address has been pushed, %rsp points to
1056+
-- the return address, and the value of (%rsp + 8) is a multiple of 16 (32 or
1057+
-- 64)."
1058+
stackAlign <- integerToAlignment defaultStackBaseAlign
1059+
is_aligned <- liftIO $ C.LLVM.isAligned sym (knownNat @64) rsp stackAlign
1060+
when (W4.asConstantPred is_aligned /= Just True) $
1061+
panic "SAWScript.Crucible.LLVM.X86.pushFreshReturnAddress" ["%rsp is not 16 byte aligned before the call instruction is executed."]
1062+
1063+
ptr <- liftIO $ C.LLVM.doPtrAddOffset bak mem rsp =<< W4.bvLit sym knownNat (BV.mkBV knownNat (-8))
1064+
let writeAlign = C.LLVM.noAlignment
1065+
finalMem <- liftIO $ C.LLVM.doStore bak mem ptr
10341066
(C.LLVM.LLVMPointerRepr $ knownNat @64)
10351067
(C.LLVM.bitvectorType 8) writeAlign fresh
10361068
x86Mem .= finalMem
@@ -1152,37 +1184,62 @@ setArgs ::
11521184
Map MS.AllocIndex C.LLVM.Ident {- ^ Associates each AllocIndex with its name -} ->
11531185
[MS.SetupValue LLVM] {- ^ Arguments passed to llvm_execute_func -} ->
11541186
X86Sim ()
1155-
setArgs env tyenv nameEnv args
1156-
| length args > length argRegs = throwX86 "More arguments than would fit into general-purpose registers"
1157-
| otherwise = do
1158-
sym <- use x86Sym
1159-
cc <- use x86CrucibleContext
1160-
mem <- use x86Mem
1161-
let
1162-
setRegSetupValue rs (reg, sval) =
1163-
exceptToFail (typeOfSetupValue cc tyenv nameEnv sval) >>= \case
1164-
ty | C.LLVM.isPointerMemType ty -> do
1165-
val <- C.LLVM.unpackMemValue sym (C.LLVM.LLVMPointerRepr $ knownNat @64)
1166-
=<< resolveSetupVal cc mem env tyenv nameEnv sval
1167-
setReg reg val rs
1168-
C.LLVM.IntType 64 -> do
1187+
setArgs env tyenv nameEnv args = do
1188+
SomeOnlineBackend bak <- use x86Backend
1189+
sym <- use x86Sym
1190+
cc <- use x86CrucibleContext
1191+
mem <- use x86Mem
1192+
let
1193+
setRegSetupValue rs (reg, sval) =
1194+
exceptToFail (typeOfSetupValue cc tyenv nameEnv sval) >>= \case
1195+
ty | C.LLVM.isPointerMemType ty -> do
1196+
val <- C.LLVM.unpackMemValue sym (C.LLVM.LLVMPointerRepr $ knownNat @64)
1197+
=<< resolveSetupVal cc mem env tyenv nameEnv sval
1198+
setReg reg val rs
1199+
C.LLVM.IntType 64 -> do
1200+
val <- C.LLVM.unpackMemValue sym (C.LLVM.LLVMPointerRepr $ knownNat @64)
1201+
=<< resolveSetupVal cc mem env tyenv nameEnv sval
1202+
setReg reg val rs
1203+
C.LLVM.IntType _ -> do
1204+
C.LLVM.LLVMValInt base off <- resolveSetupVal cc mem env tyenv nameEnv sval
1205+
case testLeq (incNat $ W4.bvWidth off) (knownNat @64) of
1206+
Nothing -> fail "Argument bitvector does not fit in a single general-purpose register"
1207+
Just LeqProof -> do
1208+
off' <- W4.bvZext sym (knownNat @64) off
11691209
val <- C.LLVM.unpackMemValue sym (C.LLVM.LLVMPointerRepr $ knownNat @64)
1170-
=<< resolveSetupVal cc mem env tyenv nameEnv sval
1210+
$ C.LLVM.LLVMValInt base off'
11711211
setReg reg val rs
1172-
C.LLVM.IntType _ -> do
1173-
C.LLVM.LLVMValInt base off <- resolveSetupVal cc mem env tyenv nameEnv sval
1174-
case testLeq (incNat $ W4.bvWidth off) (knownNat @64) of
1175-
Nothing -> fail "Argument bitvector does not fit in a single general-purpose register"
1176-
Just LeqProof -> do
1177-
off' <- W4.bvZext sym (knownNat @64) off
1178-
val <- C.LLVM.unpackMemValue sym (C.LLVM.LLVMPointerRepr $ knownNat @64)
1179-
$ C.LLVM.LLVMValInt base off'
1180-
setReg reg val rs
1181-
_ -> fail "Argument does not fit into a single general-purpose register"
1182-
regs <- use x86Regs
1183-
newRegs <- liftIO . foldlM setRegSetupValue regs $ zip argRegs args
1184-
x86Regs .= newRegs
1185-
where argRegs = [Macaw.RDI, Macaw.RSI, Macaw.RDX, Macaw.RCX, Macaw.R8, Macaw.R9]
1212+
_ -> fail "Argument does not fit into a single general-purpose register"
1213+
regs <- use x86Regs
1214+
newRegs <- liftIO . foldlM setRegSetupValue regs $ zip argRegs args
1215+
x86Regs .= newRegs
1216+
1217+
-- x86-64 System V ABI specifies that: "Once registers are assigned, the
1218+
-- arguments passed in memory are pushed on the stack in reversed
1219+
-- (right-to-left21) order."
1220+
let stackArgs = reverse $ Prelude.drop (length argRegs) args
1221+
forM_ stackArgs $ \sval -> do
1222+
liftIO $ exceptToFail (typeOfSetupValue cc tyenv nameEnv sval) >>= \case
1223+
C.LLVM.PtrType _ -> pure ()
1224+
C.LLVM.IntType 64 -> pure ()
1225+
_ -> fail "Stack argument is not a 64 bit integer."
1226+
1227+
regs' <- use x86Regs
1228+
rsp <- getReg Macaw.RSP regs'
1229+
rsp' <- liftIO $ C.LLVM.doPtrAddOffset bak mem rsp =<< W4.bvLit sym knownNat (BV.mkBV knownNat (-8))
1230+
newRegs' <- setReg Macaw.RSP rsp' regs'
1231+
x86Regs .= newRegs'
1232+
1233+
val <- liftIO $ C.LLVM.unpackMemValue sym (C.LLVM.LLVMPointerRepr $ knownNat @64)
1234+
=<< resolveSetupVal cc mem env tyenv nameEnv sval
1235+
1236+
mem' <- use x86Mem
1237+
mem'' <- liftIO $
1238+
C.LLVM.doStore bak mem' rsp' (C.LLVM.LLVMPointerRepr $ knownNat @64) (C.LLVM.bitvectorType 8) C.LLVM.noAlignment val
1239+
x86Mem .= mem''
1240+
1241+
argRegs :: [Macaw.X86Reg (Macaw.BVType 64)]
1242+
argRegs = [Macaw.RDI, Macaw.RSI, Macaw.RDX, Macaw.RCX, Macaw.R8, Macaw.R9]
11861243

11871244
--------------------------------------------------------------------------------
11881245
-- ** Postcondition

0 commit comments

Comments
 (0)