-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathLLVMCrucibleSetup.hs
228 lines (209 loc) · 8.68 KB
/
LLVMCrucibleSetup.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE PartialTypeSignatures #-}
module SAWServer.LLVMCrucibleSetup
( startLLVMCrucibleSetup
, llvmLoadModule
, llvmLoadModuleDescr
, Contract(..)
, ContractVar(..)
, Allocated(..)
, PointsTo(..)
, compileLLVMContract
) where
import Control.Lens
import Control.Monad.IO.Class
import Control.Monad.State
import Data.Aeson (FromJSON(..), withObject, (.:))
import Data.ByteString (ByteString)
import Data.Foldable
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import qualified Data.Text as T
import qualified Cryptol.Parser.AST as P
import Cryptol.Utils.Ident (mkIdent)
import qualified Text.LLVM.AST as LLVM
import qualified Data.LLVM.BitCode as LLVM
import qualified SAWScript.Crucible.Common.MethodSpec as MS (SetupValue(..))
import SAWScript.Crucible.LLVM.Builtins
( llvm_alloc
, llvm_alloc_aligned
, llvm_alloc_readonly
, llvm_alloc_readonly_aligned
, llvm_execute_func
, llvm_fresh_var
, llvm_points_to_internal
, llvm_return
, llvm_precond
, llvm_postcond )
import qualified SAWScript.Crucible.LLVM.MethodSpecIR as CMS
import SAWScript.Value (BuiltinContext, LLVMCrucibleSetupM(..), biSharedContext)
import qualified Verifier.SAW.CryptolEnv as CEnv
import Verifier.SAW.CryptolEnv (CryptolEnv)
import Verifier.SAW.TypedTerm (TypedTerm)
import Argo
import qualified Argo.Doc as Doc
import SAWServer as Server
import SAWServer.Data.Contract
import SAWServer.Data.LLVMType (JSONLLVMType, llvmType)
import SAWServer.Data.SetupValue ()
import SAWServer.CryptolExpression (getTypedTermOfCExp)
import SAWServer.Exceptions
import SAWServer.OK
import SAWServer.TrackFile
startLLVMCrucibleSetup :: StartLLVMCrucibleSetupParams -> Method SAWState OK
startLLVMCrucibleSetup (StartLLVMCrucibleSetupParams n) =
do pushTask (LLVMCrucibleSetup n [])
ok
data StartLLVMCrucibleSetupParams
= StartLLVMCrucibleSetupParams ServerName
instance FromJSON StartLLVMCrucibleSetupParams where
parseJSON =
withObject "params for \"SAW/Crucible setup\"" $ \o ->
StartLLVMCrucibleSetupParams <$> o .: "name"
data ServerSetupVal = Val (CMS.AllLLVM MS.SetupValue)
-- TODO: this is an extra layer of indirection that could be collapsed, but is easy to implement for now.
compileLLVMContract ::
(FilePath -> IO ByteString) ->
BuiltinContext ->
CryptolEnv ->
Contract JSONLLVMType (P.Expr P.PName) ->
LLVMCrucibleSetupM ()
compileLLVMContract fileReader bic cenv c =
interpretLLVMSetup fileReader bic cenv steps
where
setupFresh (ContractVar n dn ty) = SetupFresh n dn (llvmType ty)
setupAlloc (Allocated n ty mut align) = SetupAlloc n (llvmType ty) mut align
steps =
map setupFresh (preVars c) ++
map SetupPrecond (preConds c) ++
map setupAlloc (preAllocated c) ++
map (\(PointsTo p v chkV cond) -> SetupPointsTo p v (fmap (fmap llvmType) chkV) cond) (prePointsTos c) ++
[ SetupExecuteFunction (argumentVals c) ] ++
map setupFresh (postVars c) ++
map SetupPostcond (postConds c) ++
map setupAlloc (postAllocated c) ++
map (\(PointsTo p v chkV cond) -> SetupPointsTo p v (fmap (fmap llvmType) chkV) cond) (postPointsTos c) ++
[ SetupReturn v | v <- maybeToList (returnVal c) ]
interpretLLVMSetup ::
(FilePath -> IO ByteString) ->
BuiltinContext ->
CryptolEnv ->
[SetupStep LLVM.Type] ->
LLVMCrucibleSetupM ()
interpretLLVMSetup fileReader bic cenv0 ss =
runStateT (traverse_ go ss) (mempty, cenv0) *> pure ()
where
go (SetupReturn v) = get >>= \env -> lift $ getSetupVal env v >>= llvm_return
-- TODO: do we really want two names here?
go (SetupFresh name@(ServerName n) debugName ty) =
do t <- lift $ llvm_fresh_var (T.unpack debugName) ty
(env, cenv) <- get
put (env, CEnv.bindTypedTerm (mkIdent n, t) cenv)
save name (Val (CMS.anySetupTerm t))
go (SetupAlloc name ty True Nothing) =
lift (llvm_alloc ty) >>= save name . Val
go (SetupAlloc name ty False Nothing) =
lift (llvm_alloc_readonly ty) >>= save name . Val
go (SetupAlloc name ty True (Just align)) =
lift (llvm_alloc_aligned align ty) >>= save name . Val
go (SetupAlloc name ty False (Just align)) =
lift (llvm_alloc_readonly_aligned align ty) >>= save name . Val
go (SetupPointsTo src tgt chkTgt cond) = get >>= \env -> lift $
do ptr <- getSetupVal env src
tgt' <- getSetupVal env tgt
cond' <- traverse (getTypedTerm env) cond
llvm_points_to_internal chkTgt cond' ptr tgt'
go (SetupExecuteFunction args) =
get >>= \env ->
lift $ traverse (getSetupVal env) args >>= llvm_execute_func
go (SetupPrecond p) = get >>= \env -> lift $
getTypedTerm env p >>= llvm_precond
go (SetupPostcond p) = get >>= \env -> lift $
getTypedTerm env p >>= llvm_postcond
save name val = modify' (\(env, cenv) -> (Map.insert name val env, cenv))
getSetupVal ::
(Map ServerName ServerSetupVal, CryptolEnv) ->
CrucibleSetupVal (P.Expr P.PName) ->
LLVMCrucibleSetupM (CMS.AllLLVM MS.SetupValue)
getSetupVal _ NullValue = LLVMCrucibleSetupM $ return CMS.anySetupNull
getSetupVal env (ArrayValue elts) =
do elts' <- mapM (getSetupVal env) elts
LLVMCrucibleSetupM $ return $ CMS.anySetupArray elts'
getSetupVal env (StructValue elts) =
do elts' <- mapM (getSetupVal env) elts
LLVMCrucibleSetupM $ return $ CMS.anySetupStruct False elts'
getSetupVal env (FieldLValue base fld) =
do base' <- getSetupVal env base
LLVMCrucibleSetupM $ return $ CMS.anySetupField base' fld
getSetupVal env (ElementLValue base idx) =
do base' <- getSetupVal env base
LLVMCrucibleSetupM $ return $ CMS.anySetupElem base' idx
getSetupVal _ (GlobalInitializer name) =
LLVMCrucibleSetupM $ return $ CMS.anySetupGlobalInitializer name
getSetupVal _ (GlobalLValue name) =
LLVMCrucibleSetupM $ return $ CMS.anySetupGlobal name
getSetupVal (env, _) (NamedValue n) = LLVMCrucibleSetupM $
resolve env n >>=
\case
Val x -> return x -- TODO add cases for the named server values that
-- are not coming from the setup monad
-- (e.g. surrounding context)
getSetupVal (_, cenv) (CryptolExpr expr) = LLVMCrucibleSetupM $
do res <- liftIO $ getTypedTermOfCExp fileReader (biSharedContext bic) cenv expr
-- TODO: add warnings (snd res)
case fst res of
Right (t, _) -> return (CMS.anySetupTerm t)
Left err -> error $ "Cryptol error: " ++ show err -- TODO: report properly
getTypedTerm ::
(Map ServerName ServerSetupVal, CryptolEnv) ->
P.Expr P.PName ->
LLVMCrucibleSetupM TypedTerm
getTypedTerm (_, cenv) expr = LLVMCrucibleSetupM $
do res <- liftIO $ getTypedTermOfCExp fileReader (biSharedContext bic) cenv expr
-- TODO: add warnings (snd res)
case fst res of
Right (t, _) -> return t
Left err -> error $ "Cryptol error: " ++ show err -- TODO: report properly
resolve env name =
case Map.lookup name env of
Just v -> return v
Nothing -> error "Server value not found - impossible!" -- rule out elsewhere
data LLVMLoadModuleParams
= LLVMLoadModuleParams ServerName FilePath
instance FromJSON LLVMLoadModuleParams where
parseJSON =
withObject "params for \"SAW/LLVM/load module\"" $ \o ->
LLVMLoadModuleParams <$> o .: "name" <*> o .: "bitcode file"
instance Doc.DescribedParams LLVMLoadModuleParams where
parameterFieldDescription =
[ ("name",
Doc.Paragraph [Doc.Text "The name to refer to the loaded module by later."])
, ("bitcode file",
Doc.Paragraph [Doc.Text "The file containing the bitcode LLVM module to load."])
]
llvmLoadModuleDescr :: Doc.Block
llvmLoadModuleDescr =
Doc.Paragraph [Doc.Text "Load the specified LLVM module."]
llvmLoadModule :: LLVMLoadModuleParams -> Method SAWState OK
llvmLoadModule (LLVMLoadModuleParams serverName fileName) =
do tasks <- view sawTask <$> getState
case tasks of
(_:_) -> raise $ notAtTopLevel $ map fst tasks
[] ->
do let ?laxArith = False -- TODO read from config
halloc <- getHandleAlloc
loaded <- liftIO (CMS.loadLLVMModule fileName halloc)
case loaded of
Left err -> raise (cantLoadLLVMModule (LLVM.formatError err))
Right llvmMod ->
do setServerVal serverName llvmMod
trackFile fileName
ok