Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new llvm_union command that uses DWARF debug information #1576

Merged
merged 7 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
dealing with C `union` types, as the type information provided by
LLVM is imprecise in these cases.

* A new `llvm_union` function has been added that uses debug
information to allow users to select fields from `union` types by
name. This automates the process of manually applying
`llvm_cast_pointer` with the type of the selected union field. Just
as with `llvm_field`, debug symbols are required for `llvm_union` to
work correctly.

# Version 0.9

## New Features
Expand Down
1 change: 1 addition & 0 deletions crux-mir-comp/src/Mir/Compositional/Builder.hs
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ substMethodSpec sc sm ms = do
MS.SetupElem b sv idx -> MS.SetupElem b <$> goSetupValue sv <*> pure idx
MS.SetupField b sv name -> MS.SetupField b <$> goSetupValue sv <*> pure name
MS.SetupCast v _ _ -> case v of {}
MS.SetupUnion v _ _ -> case v of {}
MS.SetupGlobal _ _ -> return sv
MS.SetupGlobalInitializer _ _ -> return sv

Expand Down
1 change: 1 addition & 0 deletions crux-mir-comp/src/Mir/Compositional/MethodSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type instance MS.HasSetupArray MIR = 'True
type instance MS.HasSetupElem MIR = 'True
type instance MS.HasSetupField MIR = 'True
type instance MS.HasSetupCast MIR = 'False
type instance MS.HasSetupUnion MIR = 'False
type instance MS.HasSetupGlobalInitializer MIR = 'False

type instance MS.HasGhostState MIR = 'False
Expand Down
2 changes: 1 addition & 1 deletion deps/llvm-pretty
12 changes: 12 additions & 0 deletions doc/manual/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -2256,6 +2256,18 @@ flows into. This is especially useful for dealing with C `union`
types, as the type information provided by LLVM is imprecise in these
cases.

We can automate the process of applying pointer casts if we have debug
information avaliable:

* `llvm_union : SetupValue -> String -> SetupValue`

Given a pointer setup value, this attempts to select the named union
branch and cast the type of the pointer. For this to work, debug
symbols must be included; moreover, the process of correlating LLVM
type information with information contained in debug symbols is a bit
heuristic. If `llvm_union` cannot figure out how to cast a pointer,
one can fall back on the more manual `llvm_cast_pointer` instead.


In the experimental Java verification implementation, the following
functions can be used to state the equivalent of a combination of
Expand Down
Binary file modified doc/manual/manual.pdf
Binary file not shown.
2 changes: 2 additions & 0 deletions intTests/test_llvm_union2/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test.bc : test.c
clang -O0 -c -g -emit-llvm -o test.bc test.c
4 changes: 4 additions & 0 deletions intTests/test_llvm_union2/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This example is derived from an older union example
from `examples/llvm/union`. It is intended to demonstrate
the use of the `llvm_union` operation for selecting
the branches of unions.
Binary file added intTests/test_llvm_union2/test.bc
Binary file not shown.
35 changes: 35 additions & 0 deletions intTests/test_llvm_union2/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <stdint.h>

typedef enum { INC_1 , INC_2 } alg;

typedef struct {
uint32_t x;
} inc_1_st;

typedef struct {
uint32_t x;
uint32_t y;
} inc_2_st;

typedef struct {
alg alg;
union {
inc_1_st inc_1_st;
inc_2_st inc_2_st;
} inc_st;
} st;

uint32_t inc(st *st) {
switch (st->alg) {
case INC_1:
st->inc_st.inc_1_st.x += 1;
break;
case INC_2:
st->inc_st.inc_2_st.x += 1;
st->inc_st.inc_2_st.y += 1;
break;
default:
return 1/0;
}
return 0;
}
52 changes: 52 additions & 0 deletions intTests/test_llvm_union2/test.saw
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
m <- llvm_load_module "test.bc";

let {{
INC_1 = 0 : [32]
INC_2 = 1 : [32]
}};


// The argument 'INC' specifies which 'alg' enum to test.
let inc_spec INC = do {

stp <- llvm_alloc (llvm_alias "struct.st");
llvm_points_to (llvm_field stp "alg") (llvm_term {{ INC }});

if eval_bool {{ INC == INC_1 }} then
do {
let p = llvm_union (llvm_field stp "inc_st") "inc_1_st";

x0 <- llvm_fresh_var "x0" (llvm_int 32);
llvm_points_to (llvm_field p "x") (llvm_term x0);

llvm_execute_func [stp];

llvm_points_to (llvm_field p "x") (llvm_term {{ x0 + 1 }});
}
else if eval_bool {{ INC == INC_2 }} then
do {
let p = llvm_union (llvm_field stp "inc_st") "inc_2_st";

x0 <- llvm_fresh_var "x0" (llvm_int 32);
y0 <- llvm_fresh_var "y0" (llvm_int 32);

llvm_points_to (llvm_field p "x") (llvm_term x0);
llvm_points_to (llvm_field p "y") (llvm_term y0);

llvm_execute_func [stp];

llvm_points_to (llvm_field p "x") (llvm_term {{ x0 + 1 }});
llvm_points_to (llvm_field p "y") (llvm_term {{ y0 + 1 }});
}
else return (); // Unknown INC value

llvm_return (llvm_term {{ 0 : [32] }});
};

print "Verifying 'inc_1' using 'llvm_verify':";
llvm_verify m "inc" [] true (inc_spec {{ INC_1 }}) abc;
print "";

print "Verifying 'inc_2' using 'llvm_verify':";
llvm_verify m "inc" [] true (inc_spec {{ INC_2 }}) abc;
print "";
1 change: 1 addition & 0 deletions intTests/test_llvm_union2/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$SAW test.saw
1 change: 1 addition & 0 deletions saw-remote-api/src/SAWServer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ data CrucibleSetupVal ty e
-- | RecordValue [(String, CrucibleSetupVal e)]
| FieldLValue (CrucibleSetupVal ty e) String
| CastLValue (CrucibleSetupVal ty e) ty
| UnionLValue (CrucibleSetupVal ty e) String
| ElementLValue (CrucibleSetupVal ty e) Int
| GlobalInitializer String
| GlobalLValue String
Expand Down
3 changes: 3 additions & 0 deletions saw-remote-api/src/SAWServer/Data/SetupValue.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data SetupValTag
| TagTupleValue
| TagFieldLValue
| TagCastLValue
| TagUnionLValue
| TagElemLValue
| TagGlobalInit
| TagGlobalLValue
Expand All @@ -31,6 +32,7 @@ instance FromJSON SetupValTag where
"tuple" -> pure TagTupleValue
"field" -> pure TagFieldLValue
"cast" -> pure TagCastLValue
"union" -> pure TagUnionLValue
"element lvalue" -> pure TagElemLValue
"global initializer" -> pure TagGlobalInit
"global lvalue" -> pure TagGlobalLValue
Expand All @@ -49,6 +51,7 @@ instance (FromJSON ty, FromJSON cryptolExpr) => FromJSON (CrucibleSetupVal ty cr
TagTupleValue -> TupleValue <$> o .: "elements"
TagFieldLValue -> FieldLValue <$> o .: "base" <*> o .: "field"
TagCastLValue -> CastLValue <$> o .: "base" <*> o .: "type"
TagUnionLValue -> UnionLValue <$> o .: "base" <*> o .: "field"
TagElemLValue -> ElementLValue <$> o .: "base" <*> o .: "index"
TagGlobalInit -> GlobalInitializer <$> o .: "name"
TagGlobalLValue -> GlobalLValue <$> o .: "name"
2 changes: 2 additions & 0 deletions saw-remote-api/src/SAWServer/JVMCrucibleSetup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ compileJVMContract fileReader bic cenv0 c =
JVMSetupM $ fail "Field l-values unsupported in JVM API."
getSetupVal _ (CastLValue _ _) =
JVMSetupM $ fail "Cast l-values unsupported in JVM API."
getSetupVal _ (UnionLValue _ _) =
JVMSetupM $ fail "Union l-values unsupported in JVM API."
getSetupVal _ (ElementLValue _ _) =
JVMSetupM $ fail "Element l-values unsupported in JVM API."
getSetupVal _ (GlobalInitializer _) =
Expand Down
3 changes: 3 additions & 0 deletions saw-remote-api/src/SAWServer/LLVMCrucibleSetup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ compileLLVMContract fileReader bic ghostEnv cenv0 c =
getSetupVal env (CastLValue base ty) =
do base' <- getSetupVal env base
LLVMCrucibleSetupM $ return $ CMS.anySetupCast base' (llvmType ty)
getSetupVal env (UnionLValue base fld) =
do base' <- getSetupVal env base
LLVMCrucibleSetupM $ return $ CMS.anySetupUnion base' fld
getSetupVal env (ElementLValue base idx) =
do base' <- getSetupVal env base
LLVMCrucibleSetupM $ return $ CMS.anySetupElem base' idx
Expand Down
1 change: 1 addition & 0 deletions saw-script.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ library
, haskeline
, heapster-saw
, hobbits >= 1.3.1
, galois-dwarf >= 0.2.2
, IfElse
, jvm-parser
, lens
Expand Down
4 changes: 4 additions & 0 deletions src/SAWScript/Crucible/Common/MethodSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ type family HasSetupElem ext :: Bool
type family HasSetupField ext :: Bool
type family HasSetupGlobal ext :: Bool
type family HasSetupCast ext :: Bool
type family HasSetupUnion ext :: Bool
type family HasSetupGlobalInitializer ext :: Bool

-- | From the manual: \"The SetupValue type corresponds to values that can occur
Expand All @@ -127,6 +128,7 @@ data SetupValue ext where
SetupElem :: B (HasSetupElem ext) -> SetupValue ext -> Int -> SetupValue ext
SetupField :: B (HasSetupField ext) -> SetupValue ext -> String -> SetupValue ext
SetupCast :: B (HasSetupCast ext) -> SetupValue ext -> CastType ext -> SetupValue ext
SetupUnion :: B (HasSetupUnion ext) -> SetupValue ext -> String -> SetupValue ext

-- | A pointer to a global variable
SetupGlobal :: B (HasSetupGlobal ext) -> String -> SetupValue ext
Expand All @@ -144,6 +146,7 @@ type SetupValueHas (c :: Type -> Constraint) ext =
, c (B (HasSetupElem ext))
, c (B (HasSetupField ext))
, c (B (HasSetupCast ext))
, c (B (HasSetupUnion ext))
, c (B (HasSetupGlobal ext))
, c (B (HasSetupGlobalInitializer ext))
, c (CastType ext)
Expand All @@ -170,6 +173,7 @@ ppSetupValue setupval = case setupval of
SetupArray _ vs -> PP.brackets (commaList (map ppSetupValue vs))
SetupElem _ v i -> PP.parens (ppSetupValue v) PP.<> PP.pretty ("." ++ show i)
SetupField _ v f -> PP.parens (ppSetupValue v) PP.<> PP.pretty ("." ++ f)
SetupUnion _ v u -> PP.parens (ppSetupValue v) PP.<> PP.pretty ("." ++ u)
SetupCast _ v tp -> PP.parens (ppSetupValue v) PP.<> PP.pretty (" AS " ++ show tp)
SetupGlobal _ nm -> PP.pretty ("global(" ++ nm ++ ")")
SetupGlobalInitializer _ nm -> PP.pretty ("global_initializer(" ++ nm ++ ")")
Expand Down
1 change: 1 addition & 0 deletions src/SAWScript/Crucible/JVM/MethodSpecIR.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type instance MS.HasSetupArray CJ.JVM = 'False
type instance MS.HasSetupElem CJ.JVM = 'False
type instance MS.HasSetupField CJ.JVM = 'False
type instance MS.HasSetupCast CJ.JVM = 'False
type instance MS.HasSetupUnion CJ.JVM = 'False
type instance MS.HasSetupGlobalInitializer CJ.JVM = 'False

type instance MS.HasGhostState CJ.JVM = 'False
Expand Down
1 change: 1 addition & 0 deletions src/SAWScript/Crucible/JVM/Override.hs
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ instantiateSetupValue sc s v =
MS.SetupElem empty _ _ -> absurd empty
MS.SetupField empty _ _ -> absurd empty
MS.SetupCast empty _ _ -> absurd empty
MS.SetupUnion empty _ _ -> absurd empty
MS.SetupGlobalInitializer empty _ -> absurd empty
where
doTerm (TypedTerm schema t) = TypedTerm schema <$> scInstantiateExt sc s t
Expand Down
2 changes: 2 additions & 0 deletions src/SAWScript/Crucible/JVM/ResolveSetupValue.hs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ typeOfSetupValue _cc env _nameEnv val =
MS.SetupElem empty _ _ -> absurd empty
MS.SetupField empty _ _ -> absurd empty
MS.SetupCast empty _ _ -> absurd empty
MS.SetupUnion empty _ _ -> absurd empty
MS.SetupGlobalInitializer empty _ -> absurd empty

lookupAllocIndex :: Map AllocIndex a -> AllocIndex -> a
Expand Down Expand Up @@ -175,6 +176,7 @@ resolveSetupVal cc env _tyenv _nameEnv val =
MS.SetupElem empty _ _ -> absurd empty
MS.SetupField empty _ _ -> absurd empty
MS.SetupCast empty _ _ -> absurd empty
MS.SetupUnion empty _ _ -> absurd empty
MS.SetupGlobalInitializer empty _ -> absurd empty
where
sym = cc^.jccSym
Expand Down
18 changes: 9 additions & 9 deletions src/SAWScript/Crucible/LLVM/Builtins.hs
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ checkSpecArgumentTypes cc mspec = mapM_ resolveArg [0..(nArgs-1)]
resolveArg i =
case Map.lookup i (mspec ^. MS.csArgBindings) of
Just (mt, sv) -> do
mt' <- typeOfSetupValue cc tyenv nameEnv sv
mt' <- exceptToFail (typeOfSetupValue cc tyenv nameEnv sv)
checkArgTy i mt mt'
Nothing -> throwMethodSpec mspec $ unwords
["Argument", show i, "unspecified when verifying", show nm]
Expand All @@ -721,7 +721,7 @@ checkSpecReturnType cc mspec =
" has void return type"
]
(Just sv, Just retTy) ->
do retTy' <-
do retTy' <- exceptToFail $
typeOfSetupValue cc
(MS.csAllocations mspec) -- map allocation indices to allocations
(mspec ^. MS.csPreState . MS.csVarTypeNames) -- map alloc indices to var names
Expand Down Expand Up @@ -2206,7 +2206,7 @@ llvm_points_to_internal mbCheckType cond (getAllLLVM -> ptr) (getAllLLVM -> val)
let path = []
lhsTy <- llvm_points_to_check_lhs_validity ptr loc path

valTy <- typeOfSetupValue cc env nameEnv val
valTy <- exceptToFail $ typeOfSetupValue cc env nameEnv val
case mbCheckType of
Nothing -> pure ()
Just CheckAgainstPointerType -> checkMemTypeCompatibility loc lhsTy valTy
Expand Down Expand Up @@ -2243,9 +2243,9 @@ llvm_points_to_bitfield (getAllLLVM -> ptr) fieldName (getAllLLVM -> val) =
let path = [ResolvedField fieldName]
_ <- llvm_points_to_check_lhs_validity ptr loc path

bfIndex <- resolveSetupBitfieldIndexOrFail cc env nameEnv ptr fieldName
bfIndex <- exceptToFail $ resolveSetupBitfield cc env nameEnv ptr fieldName
let lhsFieldTy = Crucible.IntType $ fromIntegral $ biFieldSize bfIndex
valTy <- typeOfSetupValue cc env nameEnv val
valTy <- exceptToFail $ typeOfSetupValue cc env nameEnv val
-- Currently, we require the type of the RHS value to precisely match
-- the type of the field within the bitfield. One could imagine
-- having finer-grained control over this (e.g.,
Expand Down Expand Up @@ -2279,7 +2279,7 @@ llvm_points_to_check_lhs_validity ptr loc path =
else Setup.csResolvedState %= markResolved ptr path
let env = MS.csAllocations (st ^. Setup.csMethodSpec)
nameEnv = MS.csTypeNames (st ^. Setup.csMethodSpec)
ptrTy <- typeOfSetupValue cc env nameEnv ptr
ptrTy <- exceptToFail $ typeOfSetupValue cc env nameEnv ptr
case ptrTy of
Crucible.PtrType symTy ->
case Crucible.asMemType symTy of
Expand Down Expand Up @@ -2326,7 +2326,7 @@ llvm_points_to_array_prefix (getAllLLVM -> ptr) arr sz =
else Setup.csResolvedState %= markResolved ptr []
let env = MS.csAllocations (st ^. Setup.csMethodSpec)
nameEnv = MS.csTypeNames (st ^. Setup.csMethodSpec)
ptrTy <- typeOfSetupValue cc env nameEnv ptr
ptrTy <- exceptToFail $ typeOfSetupValue cc env nameEnv ptr
_ <- case ptrTy of
Crucible.PtrType symTy ->
case Crucible.asMemType symTy of
Expand All @@ -2351,8 +2351,8 @@ llvm_equal (getAllLLVM -> val1) (getAllLLVM -> val2) =
st <- get
let env = MS.csAllocations (st ^. Setup.csMethodSpec)
nameEnv = MS.csTypeNames (st ^. Setup.csMethodSpec)
ty1 <- typeOfSetupValue cc env nameEnv val1
ty2 <- typeOfSetupValue cc env nameEnv val2
ty1 <- exceptToFail $ typeOfSetupValue cc env nameEnv val1
ty2 <- exceptToFail $ typeOfSetupValue cc env nameEnv val2

b <- liftIO $ checkRegisterCompatibility ty1 ty2
unless b $ throwCrucibleSetup loc $ unlines
Expand Down
5 changes: 5 additions & 0 deletions src/SAWScript/Crucible/LLVM/MethodSpecIR.hs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ module SAWScript.Crucible.LLVM.MethodSpecIR
, anySetupStruct
, anySetupElem
, anySetupField
, anySetupUnion
, anySetupNull
, anySetupGlobal
, anySetupGlobalInitializer
Expand Down Expand Up @@ -173,6 +174,7 @@ type instance MS.HasSetupArray (LLVM _) = 'True
type instance MS.HasSetupElem (LLVM _) = 'True
type instance MS.HasSetupField (LLVM _) = 'True
type instance MS.HasSetupCast (LLVM _) = 'True
type instance MS.HasSetupUnion (LLVM _) = 'True
type instance MS.HasSetupGlobal (LLVM _) = 'True
type instance MS.HasSetupGlobalInitializer (LLVM _) = 'True

Expand Down Expand Up @@ -582,6 +584,9 @@ anySetupCast val ty = mkAllLLVM (MS.SetupCast () (getAllLLVM val) ty)
anySetupField :: AllLLVM MS.SetupValue -> String -> AllLLVM MS.SetupValue
anySetupField val field = mkAllLLVM (MS.SetupField () (getAllLLVM val) field)

anySetupUnion :: AllLLVM MS.SetupValue -> String -> AllLLVM MS.SetupValue
anySetupUnion val uname = mkAllLLVM (MS.SetupUnion () (getAllLLVM val) uname)

anySetupNull :: AllLLVM MS.SetupValue
anySetupNull = mkAllLLVM (MS.SetupNull ())

Expand Down
Loading