Skip to content

Commit c13f9a0

Browse files
committed
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 865cdd8 commit c13f9a0

File tree

14 files changed

+269
-24
lines changed

14 files changed

+269
-24
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
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

582 Bytes
Binary file not shown.
Lines changed: 9 additions & 0 deletions
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
Lines changed: 10 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 10 additions & 2 deletions
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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,18 @@ include ../common
3838

3939
CLEANFILES = java_bytecode$(LIBEXT)
4040

41+
clean : clean_library
42+
43+
.PHONY: clean_library
44+
clean_library :
45+
$(MAKE) clean -C library
46+
4147
all: java_bytecode$(LIBEXT)
4248

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

Lines changed: 19 additions & 0 deletions
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"))
@@ -206,6 +207,24 @@ bool java_bytecode_languaget::typecheck(
206207
if(string_refinement_enabled)
207208
string_preprocess.initialize_conversion_table();
208209

210+
// Must load java.lang.Object first to avoid stubbing
211+
// This ordering could alternatively be enforced by
212+
// moving the code below to the class loader.
213+
java_class_loadert::class_mapt::const_iterator it=
214+
java_class_loader.class_map.find("java.lang.Object");
215+
if(it!=java_class_loader.class_map.end())
216+
{
217+
if(java_bytecode_convert_class(
218+
it->second,
219+
symbol_table,
220+
get_message_handler(),
221+
max_user_array_length,
222+
method_bytecode,
223+
lazy_methods_mode,
224+
string_preprocess))
225+
return true;
226+
}
227+
209228
// first generate a new struct symbol for each class and a new function symbol
210229
// for every method
211230
for(java_class_loadert::class_mapt::const_iterator

src/java_bytecode/java_bytecode_language.h

Lines changed: 16 additions & 13 deletions
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

Lines changed: 52 additions & 0 deletions
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+
}

0 commit comments

Comments
 (0)