Skip to content

Commit 7a8876d

Browse files
committed
WIP: Support JDK 9 or later
This allows SAW to deal with JDK 9 or later, which packages its standard library not in a JAR file, but in a JIMAGE file. Extracting `.class` files from JIMAGE files proves to be surprisingly tricky, and I've carefully documented the intricacies of doing so in `Note [Loading classes from JIMAGE files]` in `SAWScript.JavaCodebase`. This fixes #861. This depends on #1030 to work. Remaining tasks: * Ideally, the code in `SAWScript.JavaCodebase` would be upstreamed to `crucible-jvm`, where the all-important `Codebase` data type lives. Unfortunately, some parts of SAW (e.g., `java_verify` still rely on the `jvm-verifier` library, which defines a separate `Codebase` type. SAW is in the process of phasing out the use of `jvm-verifier` in favor of `crucible-jvm` (see #993), but until that happens, I needed to introduce some ugly hacks in order to make everything typecheck. In particular, the (hopefully temporary) `SAWScript.JavaCodebase` module defines a shim version of `Codebase` that puts the experimental new things that I added in an `ExperimentalCodebase` constructor, but preserving the ability to use the `jvm-verifier` version of `Codebase` in the `LegacyCodebase` constructor. If JDK 8 or earlier is used, then `LegacyCodebase` is chosen, and if JDK 9 or later is used, then `ExperimentalCodebase` is chosen. * Unfortunately, `java_verify` doesn't work with `ExperimentalCodebase`. Nor would we necessarily want to make this happen, as that would require upstreaming changes to `jvm-verifier`, which we are in the process of phasing out. As a result, this is blocked on #993. * The CI should be updated to test more versions of the JDK than just 8. Other things: * I removed the dependency on the `xdg-basedir`, as it was unused. This dependency was likely added quite some time ago, and it appears that `saw-script` switched over to using XDG-related functionality from the `directory` library since then. I opted to use `directory` to find the `.cache` directory as well, so I have made that clear in the `.cabal` file.
1 parent 41e3126 commit 7a8876d

17 files changed

+551
-61
lines changed

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## New Features
44

5+
SAW now supports verifying Java code using JDK 9 or later.
6+
57
When verifying Java code, the path to Java can be specified with the new
68
`--java-bin-dirs`/`-b` command-line option. Alternatively, if
79
`--java-bin-dirs` is not set, then SAW searches the `PATH` to find Java.

doc/manual/manual.md

+6-11
Original file line numberDiff line numberDiff line change
@@ -1544,17 +1544,12 @@ GitHub](https://github.com/GaloisInc/saw-script/issues).
15441544

15451545
Loading Java code is slightly more complex, because of the more
15461546
structured nature of Java packages. First, when running `saw`, two flags
1547-
control where to look for classes. The `-j` flag takes the name of a JAR
1548-
file as an argument and adds the contents of that file to the class
1549-
database. The `-c` flag takes the name of a directory as an argument and
1550-
adds all class files found in that directory (and its subdirectories) to
1551-
the class database. By default, the current directory is included in the
1552-
class path. However, the Java runtime and standard library (usually
1553-
called `rt.jar`) is generally required for any non-trivial Java code,
1554-
and can be installed in a wide variety of different locations.
1555-
Therefore, for most Java analysis, you must provide the `-j` argument or
1556-
the `SAW_JDK_JAR` environment variable to specify where to find this
1557-
file.
1547+
control where to look for classes. The `-b` flag takes the path where the
1548+
`java` executable lives, which is used to locate the Java standard library
1549+
classes and add them to the class database. The `-c` flag takes the name of a
1550+
directory as an argument and adds all class files found in that directory (and
1551+
its subdirectories) to the class database. By default, the current directory is
1552+
included in the class path.
15581553

15591554
Once the class path is configured, you can pass the name of a class to
15601555
the `java_load_class` function.

saw-remote-api/src/SAWServer.hs

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import Verifier.SAW.TypedTerm (TypedTerm, CryptolModule)
4747

4848
import qualified SAWScript.Crucible.Common.MethodSpec as CMS (CrucibleMethodSpecIR)
4949
import qualified SAWScript.Crucible.LLVM.MethodSpecIR as CMS (SomeLLVM, LLVMModule)
50+
import SAWScript.JavaCodebase (loadCodebase)
5051
import SAWScript.JavaExpr (JavaType(..))
5152
import SAWScript.Options (defaultOptions)
5253
import SAWScript.Position (Pos(..))
@@ -167,7 +168,8 @@ initialState readFileFn =
167168
ss <- basic_ss sc
168169
let jarFiles = []
169170
classPaths = []
170-
jcb <- JSS.loadCodebase jarFiles classPaths
171+
javaBinDirs = []
172+
jcb <- loadCodebase jarFiles classPaths javaBinDirs
171173
let bic = BuiltinContext { biSharedContext = sc
172174
, biJavaCodebase = jcb
173175
, biBasicSS = ss

saw-script.cabal

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ library
2929
base >= 4
3030
, aig
3131
, array
32+
, base16-bytestring >= 0.1 && < 1.1
3233
, binary
3334
, bimap
3435
, bytestring
@@ -41,6 +42,7 @@ library
4142
, crucible-jvm
4243
, crucible-llvm >= 0.2
4344
, crucible-saw
45+
, cryptohash-sha1 >= 0.11 && < 0.12
4446
, deepseq
4547
, either
4648
, exceptions
@@ -87,7 +89,6 @@ library
8789
, utf8-string
8890
, what4 >= 0.4
8991
, vector
90-
, xdg-basedir
9192
, GraphSCC
9293
, macaw-base
9394
, macaw-x86
@@ -117,6 +118,7 @@ library
117118
SAWScript.ImportAIG
118119
SAWScript.Interpreter
119120
SAWScript.JavaBuiltins
121+
SAWScript.JavaCodebase
120122
SAWScript.JavaExpr
121123
SAWScript.JavaMethodSpec
122124
SAWScript.JavaMethodSpec.Evaluator

src/SAWScript/Crucible/JVM/Builtins.hs

+2-5
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ import Data.Void (absurd)
6161
import Prettyprinter
6262
import System.IO
6363

64-
-- jvm-verifier
65-
-- TODO: transition to Lang.JVM.Codebase from crucible-jvm
66-
import qualified Verifier.Java.Codebase as CB
67-
6864
-- cryptol
6965
import qualified Cryptol.Eval.Type as Cryptol (evalValType)
7066
import qualified Cryptol.TypeCheck.Type as Cryptol
@@ -112,6 +108,7 @@ import qualified SAWScript.Position as SS
112108
import SAWScript.Options
113109
import SAWScript.Crucible.JVM.BuiltinsJVM (prepareClassTopLevel)
114110

111+
import SAWScript.JavaCodebase (Codebase)
115112
import SAWScript.JavaExpr (JavaType(..))
116113

117114
import qualified SAWScript.Crucible.Common as Common
@@ -143,7 +140,7 @@ ppAbortedResult _cc = Common.ppAbortedResult (\_gp -> mempty)
143140
-- number of classes we load is way too large, and the classes take a
144141
-- long time to parse and translate.
145142

146-
allClassRefs :: CB.Codebase -> J.ClassName -> IO (Set J.ClassName)
143+
allClassRefs :: Codebase -> J.ClassName -> IO (Set J.ClassName)
147144
allClassRefs cb c0 = go seen0 [c0]
148145
where
149146
seen0 = Set.fromList CJ.initClasses

src/SAWScript/Crucible/JVM/BuiltinsJVM.hs

+7-2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import Verifier.SAW.TypedTerm (TypedTerm(..), abstractTypedExts)
7373

7474
-- saw-script
7575
import SAWScript.Builtins(fixPos)
76+
import SAWScript.JavaCodebase (Codebase)
7677
import SAWScript.Value
7778
import SAWScript.Options(Options,simVerbose)
7879
import SAWScript.Crucible.LLVM.Builtins (setupArg, setupArgs, getGlobalPair, runCFG, baseCryptolType)
@@ -93,8 +94,12 @@ import Debug.Trace
9394
-- | Use the Codebase implementation from the old Java static simulator
9495
--
9596
instance IsCodebase JCB.Codebase where
96-
lookupClass cb = J.lookupClass cb fixPos
97-
findMethod cb = J.findMethod cb fixPos
97+
lookupClass cb = J.lookupClassLegacy cb fixPos
98+
findMethod cb = J.findMethodLegacy cb fixPos
99+
100+
instance IsCodebase Codebase where
101+
lookupClass cb = J.lookupClass cb fixPos
102+
findMethod cb = J.findMethod cb fixPos
98103

99104
-----------------------------------------------------------------------
100105
-- | Make sure the class is in the database and allocate handles for its

src/SAWScript/Crucible/JVM/MethodSpecIR.hs

+4-7
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ import qualified Lang.Crucible.FunctionHandle as Crucible (HandleAllocator)
3838
-- crucible-jvm
3939
import qualified Lang.Crucible.JVM as CJ
4040

41-
-- jvm-verifier
42-
-- TODO: transition to Lang.JVM.Codebase from crucible-jvm
43-
import qualified Verifier.Java.Codebase as CB
44-
4541
-- jvm-parser
4642
import qualified Language.JVM.Parser as J
4743

@@ -51,6 +47,7 @@ import Verifier.SAW.TypedTerm (TypedTerm)
5147
import SAWScript.Crucible.Common (Sym)
5248
import qualified SAWScript.Crucible.Common.MethodSpec as MS
5349
import qualified SAWScript.Crucible.Common.Setup.Type as Setup
50+
import SAWScript.JavaCodebase (Codebase)
5451

5552
--------------------------------------------------------------------------------
5653
-- ** Language features
@@ -158,12 +155,12 @@ instance PPL.Pretty JVMPointsTo where
158155
--------------------------------------------------------------------------------
159156
-- *** JVMCrucibleContext
160157

161-
type instance MS.Codebase CJ.JVM = CB.Codebase
158+
type instance MS.Codebase CJ.JVM = Codebase
162159

163160
data JVMCrucibleContext =
164161
JVMCrucibleContext
165162
{ _jccJVMClass :: J.Class
166-
, _jccCodebase :: CB.Codebase
163+
, _jccCodebase :: Codebase
167164
, _jccJVMContext :: CJ.JVMContext
168165
, _jccBackend :: Sym -- This is stored inside field _ctxSymInterface of Crucible.SimContext; why do we need another one?
169166
, _jccHandleAllocator :: Crucible.HandleAllocator
@@ -176,7 +173,7 @@ type instance MS.CrucibleContext CJ.JVM = JVMCrucibleContext
176173
--------------------------------------------------------------------------------
177174

178175
initialDefCrucibleMethodSpecIR ::
179-
CB.Codebase ->
176+
Codebase ->
180177
J.ClassName ->
181178
J.Method ->
182179
ProgramLoc ->

src/SAWScript/Interpreter.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import SAWScript.Builtins
5151
import SAWScript.Exceptions (failTypecheck)
5252
import qualified SAWScript.Import
5353
import SAWScript.JavaBuiltins
54+
import SAWScript.JavaCodebase (loadCodebase)
5455
import SAWScript.JavaExpr
5556
import SAWScript.LLVMBuiltins
5657
import SAWScript.Options
@@ -71,7 +72,6 @@ import Verifier.SAW.TypedAST
7172
import Verifier.SAW.TypedTerm
7273
import qualified Verifier.SAW.CryptolEnv as CEnv
7374

74-
import qualified Verifier.Java.Codebase as JCB
7575
import qualified Verifier.Java.SAWBackend as JavaSAW
7676

7777
import qualified Verifier.SAW.Cryptol.Prelude as CryptolSAW
@@ -421,7 +421,7 @@ buildTopLevelEnv proxy opts =
421421
simps <- scSimpset sc0 cryptolDefs [] convs
422422
let sc = rewritingSharedContext sc0 simps
423423
ss <- basic_ss sc
424-
jcb <- JCB.loadCodebase (jarList opts) (classPath opts)
424+
jcb <- loadCodebase (jarList opts) (classPath opts) (javaBinDirs opts)
425425
currDir <- getCurrentDirectory
426426
Crucible.withHandleAllocator $ \halloc -> do
427427
let ro0 = TopLevelRO

src/SAWScript/JavaBuiltins.hs

+10-9
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import Prettyprinter
3131

3232
import Language.JVM.Common
3333

34-
import Verifier.Java.Codebase hiding (lookupClass)
35-
import Verifier.Java.Simulator as JSS hiding (lookupClass)
34+
import Verifier.Java.Codebase hiding (lookupClass, Codebase)
35+
import Verifier.Java.Simulator as JSS hiding (lookupClass, Codebase)
3636
import Verifier.Java.SAWBackend
3737

3838
import Verifier.SAW.Cryptol (importType, emptyEnv, exportFirstOrderValue)
@@ -46,6 +46,7 @@ import Verifier.SAW.CryptolEnv (schemaNoUser)
4646

4747
import qualified SAWScript.CongruenceClosure as CC
4848

49+
import SAWScript.JavaCodebase (Codebase, legacyCodebaseHack)
4950
import SAWScript.JavaExpr
5051
import SAWScript.JavaMethodSpec
5152
import SAWScript.JavaMethodSpecIR
@@ -64,7 +65,7 @@ import qualified Cryptol.Eval.Concrete as Cryptol (Concrete(..))
6465
import qualified Cryptol.TypeCheck.AST as Cryptol
6566
import qualified Cryptol.Utils.PP as Cryptol (pretty)
6667

67-
loadJavaClass :: JSS.Codebase -> String -> IO Class
68+
loadJavaClass :: Codebase -> String -> IO Class
6869
loadJavaClass cb =
6970
lookupClass cb fixPos . mkClassName . dotsToSlashes
7071

@@ -112,7 +113,7 @@ symexecJava cls mname inputs outputs satBranches = do
112113
" argument in method " ++ methodName meth
113114
pidx = fromIntegral . localIndexOfParameter meth
114115
withSAWBackend Nothing $ \sbe -> io $ do
115-
runSimulator cb sbe defaultSEH (Just fl) $ do
116+
runSimulator (legacyCodebaseHack cb) sbe defaultSEH (Just fl) $ do
116117
setVerbosity (simVerbose opts)
117118
assigns <- mapM mkAssign inputs
118119
let (argAssigns, otherAssigns) = partition (isArg meth . fst) assigns
@@ -164,7 +165,7 @@ extractJava cls mname setup = do
164165
let fl = defaultSimFlags { alwaysBitBlastBranchTerms =
165166
jsSatBranches setupRes }
166167
meth = specMethod (jsSpec setupRes)
167-
io $ runSimulator cb sbe defaultSEH (Just fl) $ do
168+
io $ runSimulator (legacyCodebaseHack cb) sbe defaultSEH (Just fl) $ do
168169
setVerbosity (simVerbose opts)
169170
argTypes <- either fail return (getActualArgTypes setupRes)
170171
args <- mapM (freshJavaVal (Just argsRef) sc) argTypes
@@ -247,8 +248,8 @@ verifyJava cls mname overrides setup = do
247248
liftIO $ bsCheckAliasTypes pos bs
248249
liftIO $ printOutLn opts Debug $ "Executing " ++ specName ms ++
249250
" at PC " ++ show (bsLoc bs) ++ "."
250-
-- runDefSimulator cb sbe $ do
251-
runSimulator cb sbe defaultSEH (Just fl) $ do
251+
-- runDefSimulator (legacyCodebaseHack cb) sbe $ do
252+
runSimulator (legacyCodebaseHack cb) sbe defaultSEH (Just fl) $ do
252253
setVerbosity (simVerbose opts)
253254
let prover script vs n g = do
254255
let exts = getAllExts g
@@ -381,7 +382,7 @@ checkCompatibleType _sc msg aty schema =
381382
]
382383

383384
parseJavaExpr' :: (MonadIO m) =>
384-
JSS.Codebase -> JSS.Class -> JSS.Method -> String
385+
Codebase -> JSS.Class -> JSS.Method -> String
385386
-> m JavaExpr
386387
parseJavaExpr' cb cls meth name =
387388
liftIO (runExceptT (parseJavaExpr cb cls meth name) >>= either fail return)
@@ -401,7 +402,7 @@ getJavaExpr ctx name = do
401402
, ftext "Maybe you're missing a `java_var` or `java_class_var`?"
402403
]
403404

404-
typeJavaExpr :: JSS.Codebase -> String -> JavaType
405+
typeJavaExpr :: Codebase -> String -> JavaType
405406
-> JavaSetup (JavaExpr, JavaActualType)
406407
typeJavaExpr cb name ty = do
407408
ms <- gets jsSpec

0 commit comments

Comments
 (0)