Skip to content

Commit f66288b

Browse files
dcattaruzzaDegiorgio
authored andcommitted
Internalize core models of the Java Class Library
The 'core models' is a set of .class files that model core classes from the Java Class Library, such as java.lang.Object or java.lang.Thread. A minimum is necessary for CBMC to understand, e.g., when a new thread is created. These core classes live in `src/java_bytecode/library`. This commit adds support to compile and pack the core classes into a single jar file, core-models.jar, and a converter program that transforms that .jar file into a C-language array of data that can then be linked into CBMC, thus making the .jar file be present in the data segment of the CBMC ELF. Other modifications: - New option --no-core-models, allowing to disable the loading of the internal core models - make and cmake now compile the core models into a single core-models.jar - Add regression one regression tests ensuring the the core-models.jar is loaded by default
1 parent 34216f5 commit f66288b

File tree

14 files changed

+282
-24
lines changed

14 files changed

+282
-24
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ src/ansi-c/gcc_builtin_headers_tm.inc
4545
src/ansi-c/gcc_builtin_headers_mips.inc
4646
src/ansi-c/gcc_builtin_headers_power.inc
4747
src/ansi-c/gcc_builtin_headers_ubsan.inc
48+
src/java_bytecode/java_core_models.inc
4849

4950
# regression/test files
5051
*.out
@@ -115,6 +116,8 @@ src/ansi-c/file_converter
115116
src/ansi-c/file_converter.exe
116117
src/ansi-c/library/converter
117118
src/ansi-c/library/converter.exe
119+
src/java_bytecode/converter
120+
src/java_bytecode/converter.exe
118121
src/util/irep_ids_convert
119122
src/util/irep_ids_convert.exe
120123
build/
582 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
test.class
3+
--show-symbol-table
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^Symbol\s*\.*\: java\:\:org\.cprover\.CProver\.\<clinit\>\:\(\)V$
7+
--
8+
--
9+
tests that the core models are being loaded by checking if the static initializer for the CProver class was
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import org.cprover.CProver;
2+
class test
3+
{
4+
public static void main(String[] args)
5+
{
6+
int i=CProver.nondetInt();
7+
assert(i!=0);
8+
}
9+
}
10+

regression/jbmc-strings/java_append_char/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
CORE
22
test_append_char.class
3-
--refine-strings --string-max-length 1000
3+
--refine-strings --string-max-length 1000 --no-core-models
44
^EXIT=10$
55
^SIGNAL=0$
66
^VERIFICATION FAILED$

src/java_bytecode/CMakeLists.txt

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
file(GLOB_RECURSE sources "*.cpp" "*.h")
2-
add_library(java_bytecode ${sources})
1+
get_filename_component(JAVA_CORE_MODELS_INC "library/java_core_models.inc" REALPATH BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
2+
set_source_files_properties("library/java_core_models.inc" GENERATED)
3+
4+
file(GLOB sources "*.cpp")
5+
file(GLOB_RECURSE headers "*.h")
6+
add_subdirectory(library)
7+
8+
add_library(java_bytecode ${sources} ${headers} )
9+
add_dependencies(java_bytecode core_models_files)
10+
target_sources(java_bytecode PUBLIC ${sources} ${headers})
311

412
generic_includes(java_bytecode)
513

src/java_bytecode/Makefile

+10
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ CLEANFILES = java_bytecode$(LIBEXT)
4040

4141
all: java_bytecode$(LIBEXT)
4242

43+
clean: clean_library
44+
45+
.PHONY: clean_library
46+
clean_library:
47+
$(MAKE) clean -C library
48+
49+
library/java_core_models.inc:
50+
$(MAKE) -C library java_core_models.inc
51+
52+
java_class_loader$(OBJEXT): library/java_core_models.inc
4353
###############################################################################
4454

4555
java_bytecode$(LIBEXT): $(OBJ)

src/java_bytecode/java_bytecode_language.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Author: Daniel Kroening, [email protected]
4242
void java_bytecode_languaget::get_language_options(const cmdlinet &cmd)
4343
{
4444
assume_inputs_non_null=cmd.isset("java-assume-inputs-non-null");
45+
java_class_loader.use_core_models=!cmd.isset("no-core-models");
4546
string_refinement_enabled=cmd.isset("refine-strings");
4647
throw_runtime_exceptions=cmd.isset("java-throw-runtime-exceptions");
4748
if(cmd.isset("java-max-input-array-length"))
@@ -267,6 +268,24 @@ bool java_bytecode_languaget::typecheck(
267268
if(string_refinement_enabled)
268269
string_preprocess.initialize_conversion_table();
269270

271+
// Must load java.lang.Object first to avoid stubbing
272+
// This ordering could alternatively be enforced by
273+
// moving the code below to the class loader.
274+
java_class_loadert::class_mapt::const_iterator it=
275+
java_class_loader.class_map.find("java.lang.Object");
276+
if(it!=java_class_loader.class_map.end())
277+
{
278+
if(java_bytecode_convert_class(
279+
it->second,
280+
symbol_table,
281+
get_message_handler(),
282+
max_user_array_length,
283+
method_bytecode,
284+
lazy_methods_mode,
285+
string_preprocess))
286+
return true;
287+
}
288+
270289
// first generate a new struct symbol for each class and a new function symbol
271290
// for every method
272291
for(java_class_loadert::class_mapt::const_iterator

src/java_bytecode/java_bytecode_language.h

+16-13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Author: Daniel Kroening, [email protected]
2424
#include <java_bytecode/select_pointer_type.h>
2525

2626
#define JAVA_BYTECODE_LANGUAGE_OPTIONS /*NOLINT*/ \
27+
"(no-core-models)" \
2728
"(java-assume-inputs-non-null)" \
2829
"(java-throw-runtime-exceptions)" \
2930
"(java-max-input-array-length):" \
@@ -35,19 +36,21 @@ Author: Daniel Kroening, [email protected]
3536
"(java-load-class):"
3637

3738
#define JAVA_BYTECODE_LANGUAGE_OPTIONS_HELP /*NOLINT*/ \
38-
" --java-assume-inputs-non-null never initialize reference-typed parameter to the\n" \
39-
" entry point with null\n" \
40-
" --java-throw-runtime-exceptions make implicit runtime exceptions explicit\n" \
41-
" --java-max-input-array-length N limit input array size to <= N\n" \
42-
" --java-max-input-tree-depth N object references are (deterministically) set to null in\n"\
43-
" the object\n" \
44-
" --java-max-vla-length limit the length of user-code-created arrays\n" \
45-
" --java-cp-include-files regexp or JSON list of files to load (with '@' prefix)\n" \
46-
" --lazy-methods only translate methods that appear to be reachable from\n" \
47-
" the --function entry point or main class\n" \
48-
" --lazy-methods-extra-entry-point METHODNAME\n" \
49-
" treat METHODNAME as a possible program entry point for\n" \
50-
" the purpose of lazy method loading\n" \
39+
" --no-core-models don't load internally provided models for core classes in\n"/* NOLINT(*) */ \
40+
" the Java Class Library\n" /* NOLINT(*) */ \
41+
" --java-assume-inputs-non-null never initialize reference-typed parameter to the\n" /* NOLINT(*) */ \
42+
" entry point with null\n" /* NOLINT(*) */ \
43+
" --java-throw-runtime-exceptions make implicit runtime exceptions explicit\n" /* NOLINT(*) */ \
44+
" --java-max-input-array-length N limit input array size to <= N\n" /* NOLINT(*) */ \
45+
" --java-max-input-tree-depth N object references are (deterministically) set to null in\n" /* NOLINT(*) */ \
46+
" the object\n" /* NOLINT(*) */ \
47+
" --java-max-vla-length limit the length of user-code-created arrays\n" /* NOLINT(*) */ \
48+
" --java-cp-include-files regexp or JSON list of files to load (with '@' prefix)\n" /* NOLINT(*) */ \
49+
" --lazy-methods only translate methods that appear to be reachable from\n" /* NOLINT(*) */ \
50+
" the --function entry point or main class\n" /* NOLINT(*) */ \
51+
" --lazy-methods-extra-entry-point METHODNAME\n" /* NOLINT(*) */ \
52+
" treat METHODNAME as a possible program entry point for\n" /* NOLINT(*) */ \
53+
" the purpose of lazy method loading\n" /* NOLINT(*) */ \
5154
" A '.*' wildcard is allowed to specify all class members\n"
5255

5356
#define MAX_NONDET_ARRAY_LENGTH_DEFAULT 5

src/java_bytecode/java_class_loader.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ Author: Daniel Kroening, [email protected]
1919
#include "java_bytecode_parser.h"
2020
#include "jar_file.h"
2121

22+
#include "library/java_core_models.inc"
23+
24+
/// This variable stores the data of the file core-models.jar. The macro
25+
/// JAVA_CORE_MODELS_SIZE is defined in the header java_core_models.inc, which
26+
/// gets generated at compile time by running a small utility (converter.cpp) on
27+
/// actual .jar file. The number of bytes in the variable is
28+
/// JAVA_CORE_MODELS_SIZE, another macro defined in java_core_models.inc.
29+
unsigned char java_core_models[] = { JAVA_CORE_MODELS_DATA };
30+
2231
java_bytecode_parse_treet &java_class_loadert::operator()(
2332
const irep_idt &class_name)
2433
{
@@ -105,6 +114,26 @@ bool java_class_loadert::get_class_file(
105114
return false;
106115
}
107116

117+
/// Retrieves a class file from the internal jar and loads it into the tree
118+
bool java_class_loadert::get_internal_class_file(
119+
java_class_loader_limitt &class_loader_limit,
120+
const irep_idt &class_name,
121+
java_bytecode_parse_treet &parse_tree)
122+
{
123+
// Add internal jar file. The name is used to load it once only and
124+
// reference it later.
125+
std::string core_models="core-models.jar";
126+
jar_pool(class_loader_limit,
127+
core_models,
128+
java_core_models,
129+
JAVA_CORE_MODELS_SIZE);
130+
131+
// This does not read from the jar file but from the jar_filet object
132+
// as we've just created it
133+
read_jar_file(class_loader_limit, core_models);
134+
return
135+
get_class_file(class_loader_limit, class_name, core_models, parse_tree);
136+
}
108137

109138
java_bytecode_parse_treet &java_class_loadert::get_parse_tree(
110139
java_class_loader_limitt &class_loader_limit,
@@ -120,6 +149,12 @@ java_bytecode_parse_treet &java_class_loadert::get_parse_tree(
120149
return parse_tree;
121150
}
122151

152+
if(use_core_models)
153+
{
154+
if(get_internal_class_file(class_loader_limit, class_name, parse_tree))
155+
return parse_tree;
156+
}
157+
123158
// See if we can find it in the class path
124159
for(const auto &cp : config.java.classpath)
125160
{
@@ -283,3 +318,20 @@ void java_class_loadert::add_load_classes(const std::vector<irep_idt> &classes)
283318
for(const auto &id : classes)
284319
java_load_classes.push_back(id);
285320
}
321+
322+
jar_filet &java_class_loadert::jar_pool(
323+
java_class_loader_limitt &class_loader_limit,
324+
const std::string &buffer_name,
325+
const void *pmem,
326+
size_t size)
327+
{
328+
const auto it=m_archives.find(buffer_name);
329+
if(it==m_archives.end())
330+
{
331+
// VS: Can't construct in place
332+
auto file=jar_filet(class_loader_limit, pmem, size);
333+
return m_archives.emplace(buffer_name, std::move(file)).first->second;
334+
}
335+
else
336+
return it->second;
337+
}

src/java_bytecode/java_class_loader.h

+29
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ Author: Daniel Kroening, [email protected]
2323
class java_class_loadert:public messaget
2424
{
2525
public:
26+
// Default constructor does not use core models
27+
// for backward compatibility of unit tests
28+
java_class_loadert() :
29+
use_core_models(true)
30+
{}
31+
2632
java_bytecode_parse_treet &operator()(const irep_idt &);
2733

2834
void set_java_cp_include_files(std::string &);
@@ -48,6 +54,13 @@ class java_class_loadert:public messaget
4854
const std::string &jar_file,
4955
java_bytecode_parse_treet &parse_tree);
5056

57+
/// Attempts to load the class from the internal core models.
58+
/// Returns true if found and loaded
59+
bool get_internal_class_file(
60+
java_class_loader_limitt &class_loader_limit,
61+
const irep_idt &class_name,
62+
java_bytecode_parse_treet &parse_tree);
63+
5164
/// Given a \p class_name (e.g. "java.lang.Thread") try to load the
5265
/// corresponding .class file by first scanning all .jar files whose
5366
/// pathname is stored in \ref jar_files, and if that doesn't work, then scan
@@ -68,6 +81,18 @@ class java_class_loadert:public messaget
6881
jar_filet &jar_pool(java_class_loader_limitt &limit,
6982
const std::string &filename);
7083

84+
/// Load jar archive(from cache if already loaded)
85+
/// \param limit
86+
/// \param buffer_name name of the original file
87+
/// \param pmem memory pointer to the contents of the file
88+
/// \param size size of the memory buffer
89+
/// Note that this mocks the existence of a file which may
90+
/// or may not exist since the actual data bis retrieved from memory.
91+
jar_filet &jar_pool(java_class_loader_limitt &limit,
92+
const std::string &buffer_name,
93+
const void *pmem,
94+
size_t size);
95+
7196
/// An object of this class represents the information of _a single JAR file_
7297
/// that is relevant for a class loader: a map associating logical class names
7398
/// with with handles (struct entryt) that describe one .class file inside the
@@ -101,6 +126,10 @@ class java_class_loadert:public messaget
101126
/// java_class_loader_limitt::setup_class_load_limit() for further
102127
/// information.
103128
std::string java_cp_include_files;
129+
130+
/// Indicates that the core models should be loaded
131+
bool use_core_models;
132+
104133
private:
105134
std::map<std::string, jar_filet> m_archives;
106135
std::vector<irep_idt> java_load_classes;
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
find_package(Java REQUIRED)
2+
include(UseJava)
3+
set(CMAKE_JAVA_COMPILE_FLAGS -sourcepath "src" -d "classes" -XDignore.symbol.file)
4+
file(GLOB_RECURSE java_sources "*.java")
5+
set(JAR_NAME "core-models")
6+
add_jar(${JAR_NAME} ${java_sources})
7+
get_filename_component(CORE_MODELS_JAR "core-models.jar" REALPATH BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
8+
get_filename_component(JAVA_CORE_MODELS_INC "java_core_models.inc" REALPATH BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
9+
file(GLOB_RECURSE jars "*.jar")
10+
add_executable(java-converter converter.cpp)
11+
12+
add_custom_target(core_models_files)
13+
add_dependencies(core_models_files ${JAR_NAME})
14+
add_custom_command(TARGET core_models_files
15+
PRE_BUILD
16+
COMMAND java-converter JAVA_CORE_MODELS core-models.jar > ${JAVA_CORE_MODELS_INC}
17+
)
18+
19+
set_source_files_properties("java_core_models.inc" GENERATED)

src/java_bytecode/library/Makefile

+32-8
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,50 @@
1-
all: org.cprover.jar
1+
.NOTPARALLEL:
2+
#javac compiles multiple classes for each source as it will compile dependent sources.
3+
#Thus we do not allow the make to run concurrently.
4+
5+
SRC = converter.cpp
6+
7+
include ../../config.inc
8+
include ../../common
29

310
SOURCE_DIR := src
411
BINARY_DIR := classes
512

613
FIND := find
714

815
JAVAC := javac
9-
JFLAGS := -sourcepath $(SOURCE_DIR) -d $(BINARY_DIR)
16+
JFLAGS := -sourcepath $(SOURCE_DIR) -d $(BINARY_DIR) -XDignore.symbol.file
1017

1118
CLASSPATH := SOURCE_DIR
1219

13-
all_javas := $(shell $(FIND) $(SOURCE_DIR) -name '*.java')
20+
ALL_JAVAS := $(wildcard $(SOURCE_DIR)/*/*.java $(SOURCE_DIR)/*/*/*.java $(SOURCE_DIR)/*/*/*/*.java)
21+
ALL_CLASSES := $(patsubst $(SOURCE_DIR)/%.java,$(BINARY_DIR)/%.class,$(ALL_JAVAS))
1422

1523
$(BINARY_DIR):
1624
mkdir -p $(BINARY_DIR)
1725

18-
.PHONY: compile
19-
compile: $(BINARY_DIR)
20-
$(JAVAC) $(JFLAGS) $(all_javas)
26+
.SUFFIXES: .java .class
27+
28+
$(BINARY_DIR)/%.class: $(SOURCE_DIR)/%.java $(BINARY_DIR)
29+
$(JAVAC) $(JFLAGS) $(patsubst $(BINARY_DIR)/%.class,$(SOURCE_DIR)/%.java,$@)
2130

2231
JAR := jar
2332
JARFLAGS := -cf
2433

25-
org.cprover.jar: compile
26-
$(JAR) $(JARFLAGS) $@ -C $(BINARY_DIR) org
34+
core-models.jar: $(ALL_CLASSES)
35+
$(JAR) $(JARFLAGS) $@ -C $(BINARY_DIR) .
36+
37+
CLEANFILES = core-models.jar converter$(EXEEXT) java_core_models.inc
38+
39+
all: java_core_models.inc
40+
41+
clean: clean_
42+
43+
clean_:
44+
$(RM) -Rf $(BINARY_DIR)
45+
46+
converter$(EXEEXT): converter.cpp
47+
$(LINKNATIVE)
48+
49+
java_core_models.inc: converter$(EXEEXT) core-models.jar
50+
./converter$(EXEEXT) JAVA_CORE_MODELS core-models.jar > $@

0 commit comments

Comments
 (0)