From 8a3035c7ac3e62027fb941d09a5c43123fd2009f Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 10 May 2026 20:04:33 +0200 Subject: [PATCH 01/24] build: switched from clang 14 to 19 for op_helper.bc Signed-off-by: Vitaly Chipounov --- Makefile | 12 ++++++------ libcpu/src/CMakeLists.txt | 4 ++-- libtcg/src/CMakeLists.txt | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index dfb1b484..6c7fc321 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,8 @@ # USE_Z3_BINARY=yes # Whether to use the Z3 binary or to build Z3 from source # -# SYSTEM_LLVM_CONFIG=llvm-config-14 -# Override the default llvm-config command (defaults to llvm-config-14) +# SYSTEM_LLVM_CONFIG=llvm-config-19 +# Override the default llvm-config command (defaults to llvm-config-19) # # SYSTEM_CLANG_VERSION=19 # Override the default clang version (defaults to clang-19) @@ -62,17 +62,17 @@ S2E_BUILD:=$(CURDIR) USE_Z3_BINARY?=yes # System LLVM variables -SYSTEM_LLVM_CONFIG?=llvm-config-14 +SYSTEM_LLVM_CONFIG?=llvm-config-19 LLVM_VERSION := $(shell $(SYSTEM_LLVM_CONFIG) --version) LLVM_PREFIX := $(shell $(SYSTEM_LLVM_CONFIG) --prefix) LLVM_INCLUDEDIR := $(shell $(SYSTEM_LLVM_CONFIG) --includedir) LLVM_LIBDIR := $(shell $(SYSTEM_LLVM_CONFIG) --libdir) LLVM_CMAKE_DIR := $(LLVM_LIBDIR)/cmake/llvm -# Verify LLVM version is 14.x +# Verify LLVM version is 19.x LLVM_MAJOR_VERSION := $(shell echo $(LLVM_VERSION) | cut -d. -f1) -ifneq ($(LLVM_MAJOR_VERSION),14) -$(error LLVM version $(LLVM_VERSION) found, but version 14.x is required) +ifneq ($(LLVM_MAJOR_VERSION),19) +$(error LLVM version $(LLVM_VERSION) found, but version 19.x is required) endif KLEE_DIRS=$(foreach suffix,-debug -release -coverage,$(addsuffix $(suffix),klee)) diff --git a/libcpu/src/CMakeLists.txt b/libcpu/src/CMakeLists.txt index ad53e1fb..e8f13a8e 100644 --- a/libcpu/src/CMakeLists.txt +++ b/libcpu/src/CMakeLists.txt @@ -39,8 +39,8 @@ separate_arguments(BC_INC_DIRS) add_custom_target( op_helper_bc ALL - clang-14 ${CMAKE_CURRENT_SOURCE_DIR}/target-i386/op_helper.c -c ${CARGS_LIST} -Wno-unused-function -O3 -DSYMBEX_LLVM_LIB -march=x86-64 -emit-llvm -o ${CMAKE_CURRENT_BINARY_DIR}/op_helper-org.bc ${BC_INC_DIRS} -fno-slp-vectorize -fno-tree-vectorize - COMMAND llvm-link-14 ${CMAKE_CURRENT_BINARY_DIR}/op_helper-org.bc ${LIBTCG_DIR}/src/softfloat.bc -o ${CMAKE_CURRENT_BINARY_DIR}/op_helper.bc + clang-19 ${CMAKE_CURRENT_SOURCE_DIR}/target-i386/op_helper.c -c ${CARGS_LIST} -Wno-unused-function -O3 -DSYMBEX_LLVM_LIB -march=x86-64 -emit-llvm -o ${CMAKE_CURRENT_BINARY_DIR}/op_helper-org.bc ${BC_INC_DIRS} -fno-slp-vectorize -fno-tree-vectorize + COMMAND llvm-link-19 ${CMAKE_CURRENT_BINARY_DIR}/op_helper-org.bc ${LIBTCG_DIR}/src/softfloat.bc -o ${CMAKE_CURRENT_BINARY_DIR}/op_helper.bc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/target-i386/op_helper.c ) diff --git a/libtcg/src/CMakeLists.txt b/libtcg/src/CMakeLists.txt index b898639d..1ac0c8fc 100644 --- a/libtcg/src/CMakeLists.txt +++ b/libtcg/src/CMakeLists.txt @@ -80,7 +80,7 @@ separate_arguments(BC_INC_DIRS) add_custom_target( softfloat_bc ALL - clang-14 ${CMAKE_CURRENT_SOURCE_DIR}/fpu/softfloat.c -c ${CARGS_LIST} -I${LIBTCG_INCLUDE_DIR} ${BC_INC_DIRS} -Wno-unused-function -O3 -DSYMBEX_LLVM_LIB -march=x86-64 -emit-llvm -o ${CMAKE_CURRENT_BINARY_DIR}/softfloat.bc ${BC_INC_DIRS} -fno-slp-vectorize -fno-tree-vectorize + clang-19 ${CMAKE_CURRENT_SOURCE_DIR}/fpu/softfloat.c -c ${CARGS_LIST} -I${LIBTCG_INCLUDE_DIR} ${BC_INC_DIRS} -Wno-unused-function -O3 -DSYMBEX_LLVM_LIB -march=x86-64 -emit-llvm -o ${CMAKE_CURRENT_BINARY_DIR}/softfloat.bc ${BC_INC_DIRS} -fno-slp-vectorize -fno-tree-vectorize DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/fpu/softfloat.c ) endif (WITH_SYMBEX) \ No newline at end of file From f188f13deef38e06d9aef92382416bed26dd278f Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 10 May 2026 20:05:08 +0200 Subject: [PATCH 02/24] libs2e: print error when op_helper.bc failed to load Signed-off-by: Vitaly Chipounov --- libs2e/src/s2e-kvm.cpp | 4 ++++ libtcg/src/tcg-llvm.cpp | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libs2e/src/s2e-kvm.cpp b/libs2e/src/s2e-kvm.cpp index 01e034ee..00e56647 100644 --- a/libs2e/src/s2e-kvm.cpp +++ b/libs2e/src/s2e-kvm.cpp @@ -246,6 +246,10 @@ void S2EKVM::init(void) { auto bc = getBitcodeLibrary(shared_dir); fprintf(stdout, "Using module %s\n", bc.c_str()); tcg_llvm_translator = TCGLLVMTranslator::create(bc); + if (!tcg_llvm_translator) { + fprintf(stderr, "Could not create the LLVM tanslator\n"); + exit(-1); + } if (monitor_init() < 0) { exit(-1); diff --git a/libtcg/src/tcg-llvm.cpp b/libtcg/src/tcg-llvm.cpp index cc48a2b5..af13682c 100644 --- a/libtcg/src/tcg-llvm.cpp +++ b/libtcg/src/tcg-llvm.cpp @@ -112,12 +112,14 @@ TCGLLVMTranslator *TCGLLVMTranslator::create(const std::string &bitcodeLibraryPa // Read the helper bitcode file auto ErrorOrMemBuff = MemoryBuffer::getFile(bitcodeLibraryPath); if (std::error_code EC = ErrorOrMemBuff.getError()) { - llvm::errs() << "Reading " << bitcodeLibraryPath << " failed!\n"; + llvm::errs() << "Reading " << bitcodeLibraryPath << " failed: " << EC.message() << "\n"; return nullptr; } auto ErrorOrMod = parseBitcodeFile(ErrorOrMemBuff.get()->getMemBufferRef(), *ctx); if (!ErrorOrMod) { + llvm::errs() << "Parsing " << bitcodeLibraryPath << " failed: " << llvm::toString(ErrorOrMod.takeError()) + << "\n"; return nullptr; } From 7d891ed655cd60c69f1b448c973104b073aaa18b Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Mon, 11 May 2026 00:44:29 +0200 Subject: [PATCH 03/24] CMakeLists: use llvm 19.1 instead of 14 Signed-off-by: Vitaly Chipounov --- klee/CMakeLists.txt | 2 +- klee/cmake/find_llvm.cmake | 2 -- libs2e/CMakeLists.txt | 2 +- libs2e/src/CMakeLists.txt | 2 +- libs2ecore/CMakeLists.txt | 2 +- libs2eplugins/CMakeLists.txt | 2 +- libtcg/CMakeLists.txt | 2 +- libvmi/CMakeLists.txt | 2 +- tools/CMakeLists.txt | 2 +- tools/LLVMBitcode.cmake | 2 +- 10 files changed, 9 insertions(+), 11 deletions(-) diff --git a/klee/CMakeLists.txt b/klee/CMakeLists.txt index 34c8d650..8f8aa4d5 100644 --- a/klee/CMakeLists.txt +++ b/klee/CMakeLists.txt @@ -168,8 +168,8 @@ set(KLEE_COMPONENT_EXTRA_LIBRARIES "") ################################################################################ # Find LLVM ################################################################################ +find_package(LLVM 19.1...<20 REQUIRED) include(${CMAKE_SOURCE_DIR}/cmake/find_llvm.cmake) -find_package(LLVM 14...<15 REQUIRED) foreach (vname ${NEEDED_LLVM_VARS}) diff --git a/klee/cmake/find_llvm.cmake b/klee/cmake/find_llvm.cmake index 66cf7ef8..49fd56d3 100644 --- a/klee/cmake/find_llvm.cmake +++ b/klee/cmake/find_llvm.cmake @@ -7,8 +7,6 @@ # #===------------------------------------------------------------------------===# -find_package(LLVM 14...<15 CONFIG REQUIRED) - # Provide function to map LLVM components to libraries. function(klee_get_llvm_libs output_var) llvm_map_components_to_libnames(${output_var} ${ARGN}) diff --git a/libs2e/CMakeLists.txt b/libs2e/CMakeLists.txt index 2d0eb2c7..c3e72848 100644 --- a/libs2e/CMakeLists.txt +++ b/libs2e/CMakeLists.txt @@ -51,7 +51,7 @@ find_package(Protobuf REQUIRED) ##### LLVM ##### find_package(CURL REQUIRED) -find_package(LLVM 14...<15 REQUIRED) +find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) diff --git a/libs2e/src/CMakeLists.txt b/libs2e/src/CMakeLists.txt index 62b88da8..21b7eca7 100644 --- a/libs2e/src/CMakeLists.txt +++ b/libs2e/src/CMakeLists.txt @@ -45,7 +45,7 @@ endif() set(LIBS ${LIBS} ${LIBCPU_LIBRARY_DIR}/libcpu.a ${LIBTCG_LIBRARY_DIR}/libtcg.a) -llvm_map_components_to_libnames(LLVM_LIBS core support bitreader bitstreamreader bitwriter transformutils scalaropts codegen) +llvm_map_components_to_libnames(LLVM_LIBS core passes support bitreader bitstreamreader bitwriter transformutils scalaropts codegen) if(WITH_TARGET MATCHES "s2e") set(LIBS ${LIBS} ${KLEE_LIBRARY_DIR}/libkleeCore.a diff --git a/libs2ecore/CMakeLists.txt b/libs2ecore/CMakeLists.txt index 2aec5514..9e612f7d 100644 --- a/libs2ecore/CMakeLists.txt +++ b/libs2ecore/CMakeLists.txt @@ -44,7 +44,7 @@ pkg_check_modules(GLIB_PKG glib-2.0) include_directories(${GLIB_PKG_INCLUDE_DIRS}) ##### LLVM ##### -find_package(LLVM 14...<15 REQUIRED) +find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) diff --git a/libs2eplugins/CMakeLists.txt b/libs2eplugins/CMakeLists.txt index f1f2b47e..487d3861 100644 --- a/libs2eplugins/CMakeLists.txt +++ b/libs2eplugins/CMakeLists.txt @@ -43,7 +43,7 @@ find_package(PkgConfig REQUIRED) include_directories("include") ##### LLVM ##### -find_package(LLVM 14...<15 REQUIRED) +find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) diff --git a/libtcg/CMakeLists.txt b/libtcg/CMakeLists.txt index 17073412..84f71826 100644 --- a/libtcg/CMakeLists.txt +++ b/libtcg/CMakeLists.txt @@ -45,7 +45,7 @@ find_package(LIBQ REQUIRED) message(STATUS "Found libq ${LIBQ_PACKAGE_VERSION}") ##### LLVM ##### -find_package(LLVM 14...<15 REQUIRED) +find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) diff --git a/libvmi/CMakeLists.txt b/libvmi/CMakeLists.txt index 37e36de1..303a2dc9 100644 --- a/libvmi/CMakeLists.txt +++ b/libvmi/CMakeLists.txt @@ -38,7 +38,7 @@ set(VMI_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include") set(VMI_LIBRARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/src") configure_file(VMIConfig.cmake.in ${CMAKE_CONFIG_FILE} @ONLY) -find_package(LLVM 14...<15 REQUIRED) +find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 57ec8a01..23399efe 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -46,7 +46,7 @@ find_package(Protobuf REQUIRED) # Old CMake does not support static libraries, so we have to patch the library manually string(REPLACE .so .a PROTOBUF_LIBRARIES "${PROTOBUF_LIBRARIES}") -find_package(LLVM 14...<15 REQUIRED) +find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") find_package(VMI REQUIRED) diff --git a/tools/LLVMBitcode.cmake b/tools/LLVMBitcode.cmake index f7345859..7b07396f 100644 --- a/tools/LLVMBitcode.cmake +++ b/tools/LLVMBitcode.cmake @@ -208,4 +208,4 @@ endfunction(optimize_llvm_bitcode) ############################################################################### # Ensure that LLVM is available -find_package(LLVM 14...<15 REQUIRED) +find_package(LLVM 19.1...<20 REQUIRED) From 31d90f320f30eb04f27f86a897698ebacf3191da Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Mon, 11 May 2026 00:45:51 +0200 Subject: [PATCH 04/24] CMakeLists: removed unused code Signed-off-by: Vitaly Chipounov --- libs2ecore/CMakeLists.txt | 7 ------- libs2eplugins/CMakeLists.txt | 7 ------- libtcg/CMakeLists.txt | 7 ------- libvmi/CMakeLists.txt | 8 +------- 4 files changed, 1 insertion(+), 28 deletions(-) diff --git a/libs2ecore/CMakeLists.txt b/libs2ecore/CMakeLists.txt index 9e612f7d..d675571c 100644 --- a/libs2ecore/CMakeLists.txt +++ b/libs2ecore/CMakeLists.txt @@ -54,13 +54,6 @@ include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) llvm_map_components_to_libnames(LLVM_LIBS core) -set(LLVM_CONFIG "${LLVM_TOOLS_BINARY_DIR}/llvm-config" - CACHE PATH "Path to llvm-config") -execute_process(COMMAND ${LLVM_CONFIG} "--cxxflags" - RESULT_VARIABLE LLVM_CONFIG_RESULT - OUTPUT_VARIABLE LLVM_CXXFLAGS - OUTPUT_STRIP_TRAILING_WHITESPACE) - ################## find_package(LIBQ REQUIRED) diff --git a/libs2eplugins/CMakeLists.txt b/libs2eplugins/CMakeLists.txt index 487d3861..4c3aed8a 100644 --- a/libs2eplugins/CMakeLists.txt +++ b/libs2eplugins/CMakeLists.txt @@ -67,13 +67,6 @@ include_directories(${LIBDWARF_INCLUDE_DIRS}) llvm_map_components_to_libnames(LLVM_LIBS core) -set(LLVM_CONFIG "${LLVM_TOOLS_BINARY_DIR}/llvm-config" - CACHE PATH "Path to llvm-config") -execute_process(COMMAND ${LLVM_CONFIG} "--cxxflags" - RESULT_VARIABLE LLVM_CONFIG_RESULT - OUTPUT_VARIABLE LLVM_CXXFLAGS - OUTPUT_STRIP_TRAILING_WHITESPACE) - ################## include(FindProtobuf) diff --git a/libtcg/CMakeLists.txt b/libtcg/CMakeLists.txt index 84f71826..bd3f53c0 100644 --- a/libtcg/CMakeLists.txt +++ b/libtcg/CMakeLists.txt @@ -55,13 +55,6 @@ include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) llvm_map_components_to_libnames(LLVM_LIBS core) -set(LLVM_CONFIG "${LLVM_TOOLS_BINARY_DIR}/llvm-config" - CACHE PATH "Path to llvm-config") -execute_process(COMMAND ${LLVM_CONFIG} "--cxxflags" - RESULT_VARIABLE LLVM_CONFIG_RESULT - OUTPUT_VARIABLE LLVM_CXXFLAGS - OUTPUT_STRIP_TRAILING_WHITESPACE) - ################## option(WITH_GUEST "Type of guest target to support") diff --git a/libvmi/CMakeLists.txt b/libvmi/CMakeLists.txt index 303a2dc9..39268793 100644 --- a/libvmi/CMakeLists.txt +++ b/libvmi/CMakeLists.txt @@ -71,13 +71,7 @@ link_directories(${LIBDWARF_LIB_DIRS}) llvm_map_components_to_libnames(LLVM_LIBS core support) -set(LLVM_CONFIG "${LLVM_TOOLS_BINARY_DIR}/llvm-config" - CACHE PATH "Path to llvm-config") -execute_process(COMMAND ${LLVM_CONFIG} "--cxxflags" - RESULT_VARIABLE LLVM_CONFIG_RESULT - OUTPUT_VARIABLE LLVM_CXXFLAGS - OUTPUT_STRIP_TRAILING_WHITESPACE) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_CXXFLAGS} -Wall -std=c++20") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++20") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-gnu-anonymous-struct -Wno-nested-anon-types -Wno-c99-extensions") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-address-of-packed-member") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O2") From dc2a54c88344ae40aec2b9c6fec20f33703a510b Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Mon, 11 May 2026 00:46:09 +0200 Subject: [PATCH 05/24] CMakeLists: fixed conflict with system and llvm's cxxabi.h Move llvm headers at the very end of the header search list, so that they come after the system's headers. Signed-off-by: Vitaly Chipounov --- klee/unittests/CMakeLists.txt | 9 +++++++++ libs2e/CMakeLists.txt | 5 ++++- libs2ecore/CMakeLists.txt | 4 +++- libs2eplugins/CMakeLists.txt | 4 +++- libtcg/CMakeLists.txt | 4 +++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/klee/unittests/CMakeLists.txt b/klee/unittests/CMakeLists.txt index ce624a82..4cc1ab06 100644 --- a/klee/unittests/CMakeLists.txt +++ b/klee/unittests/CMakeLists.txt @@ -23,6 +23,15 @@ llvm_map_components_to_libnames(LLVM_LIBS core) set(LIBS ${LIBS} ${LLVM_LIBS} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES} gmock) + +# Prevent googletest from including libc++abi's cxxabi.h, since it has a +# conflicting declaration of the __cxa_init_primary_exception symbol with +# libstdc++. It's very unclear why googletest unconditionally includes this +# header when the build is not set up to use libc++abi otherwise. We could +# add detection here to keep it when using libc++abi but we currently +# don't. See https://github.com/llvm/llvm-project/issues/121300 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_HAS_CXXABI_H_=0") + function(add_klee_unit_test target_name) add_executable(${target_name} ${ARGN} ${PROJECT_SOURCE_DIR}/unittests/TestMain.cpp) target_link_libraries(${target_name} PRIVATE ${LIBS}) diff --git a/libs2e/CMakeLists.txt b/libs2e/CMakeLists.txt index c3e72848..0081a388 100644 --- a/libs2e/CMakeLists.txt +++ b/libs2e/CMakeLists.txt @@ -57,7 +57,10 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) include_directories("include") -include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) + +foreach(dir ${LLVM_INCLUDE_DIRS}) + add_compile_options("SHELL:-idirafter ${dir}") +endforeach() ################## diff --git a/libs2ecore/CMakeLists.txt b/libs2ecore/CMakeLists.txt index d675571c..3a407ee8 100644 --- a/libs2ecore/CMakeLists.txt +++ b/libs2ecore/CMakeLists.txt @@ -50,7 +50,9 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) include_directories("include") -include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) +foreach(dir ${LLVM_INCLUDE_DIRS}) + add_compile_options("SHELL:-idirafter ${dir}") +endforeach() llvm_map_components_to_libnames(LLVM_LIBS core) diff --git a/libs2eplugins/CMakeLists.txt b/libs2eplugins/CMakeLists.txt index 4c3aed8a..c3ef33ba 100644 --- a/libs2eplugins/CMakeLists.txt +++ b/libs2eplugins/CMakeLists.txt @@ -48,7 +48,9 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) -include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) +foreach(dir ${LLVM_INCLUDE_DIRS}) + add_compile_options("SHELL:-idirafter ${dir}") +endforeach() ##### glib ##### diff --git a/libtcg/CMakeLists.txt b/libtcg/CMakeLists.txt index bd3f53c0..f7d05d12 100644 --- a/libtcg/CMakeLists.txt +++ b/libtcg/CMakeLists.txt @@ -51,7 +51,9 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") add_definitions(${LLVM_DEFINITIONS}) include_directories("include") -include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) +foreach(dir ${LLVM_INCLUDE_DIRS}) + add_compile_options("SHELL:-idirafter ${dir}") +endforeach() llvm_map_components_to_libnames(LLVM_LIBS core) From 0efed47a7eacb66aef0c3d19b666e949dc5cec59 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Mon, 11 May 2026 00:48:15 +0200 Subject: [PATCH 06/24] libs2ecore: cleaned up Utils.cpp Signed-off-by: Vitaly Chipounov --- libs2ecore/src/Utils.cpp | 51 +++++++--------------------------------- 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/libs2ecore/src/Utils.cpp b/libs2ecore/src/Utils.cpp index 7f06aa42..2580c648 100644 --- a/libs2ecore/src/Utils.cpp +++ b/libs2ecore/src/Utils.cpp @@ -20,22 +20,14 @@ /// SOFTWARE. /// -#include -#include - -#if defined(CONFIG_WIN32) -static void print_stacktrace(const char *reason) { - std::ostream &os = g_s2e->getDebugStream(); - os << "Stack trace printing unsupported on Windows" << '\n'; -} -#else +#include #include #include -#include -#include -#include -#include #include +#include + +#include +#include #include @@ -111,7 +103,6 @@ void print_stacktrace(void (*print_func)(const char *fmt, ...), const char *reas free(funcname); free(symbollist); } -#endif // CONFIG_WIN32 namespace s2e { @@ -125,12 +116,12 @@ std::string compress_file(const std::string &path) { } // Check that the file was compressed - llvm::SmallString<128> compressed(path); - llvm::sys::path::replace_extension(compressed, "gz"); + std::filesystem::path compressed(path); + compressed.replace_extension("gz"); - if (llvm::sys::fs::exists(compressed)) { + if (std::filesystem::exists(compressed)) { unlink(path.c_str()); - return compressed.c_str(); + return compressed.string(); } return path; @@ -157,29 +148,6 @@ bool ReadLines(const std::string &file, std::vector &lines, bool do } uint64_t GetProcessMemoryUsage() { -#if defined(CONFIG_WIN32) - - PROCESS_MEMORY_COUNTERS Memory; - HANDLE CurrentProcess = GetCurrentProcess(); - - if (!GetProcessMemoryInfo(CurrentProcess, &Memory, sizeof(Memory))) { - return 0; - } - - return Memory.PagefileUsage; - -#elif defined(CONFIG_DARWIN) - struct task_basic_info t_info; - mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; - - if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t) &t_info, &t_info_count)) { - return -1; - } - // resident size is in t_info.resident_size; - // return t_info.virtual_size; - return t_info.resident_size; - -#else pid_t myPid = getpid(); std::stringstream ss; ss << "/proc/" << myPid << "/status"; @@ -201,7 +169,6 @@ uint64_t GetProcessMemoryUsage() { fclose(fp); return peakMem * 1024; -#endif } } // namespace s2e From d69d6b672e2c6576067eac1ce72660637f25abee Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 17:34:52 +0200 Subject: [PATCH 07/24] klee/tests: added tests for the llvm interpreter Signed-off-by: Vitaly Chipounov --- klee/unittests/Core/CMakeLists.txt | 27 +- klee/unittests/Core/ExecutorTest.cpp | 1554 ++++++++++++++++++++++++++ klee/unittests/Core/LLVM/add.c | 3 + klee/unittests/Core/LLVM/compute.c | 86 ++ klee/unittests/Core/LLVM/function.c | 62 + klee/unittests/Expr/CMakeLists.txt | 2 +- 6 files changed, 1732 insertions(+), 2 deletions(-) create mode 100644 klee/unittests/Core/ExecutorTest.cpp create mode 100644 klee/unittests/Core/LLVM/add.c create mode 100644 klee/unittests/Core/LLVM/compute.c create mode 100644 klee/unittests/Core/LLVM/function.c diff --git a/klee/unittests/Core/CMakeLists.txt b/klee/unittests/Core/CMakeLists.txt index ce0be7c8..fd6fe33f 100644 --- a/klee/unittests/Core/CMakeLists.txt +++ b/klee/unittests/Core/CMakeLists.txt @@ -1,5 +1,30 @@ add_klee_unit_test(CoreTest AddressSpaceTest.cpp) +add_klee_unit_test(ExecutorTest ExecutorTest.cpp) -llvm_map_components_to_libnames(LLVM_LIBS core support transformutils scalaropts codegen) +llvm_map_components_to_libnames(LLVM_LIBS core support transformutils scalaropts codegen passes analysis) +llvm_map_components_to_libnames(LLVM_EXECUTOR_LIBS core support transformutils scalaropts codegen passes analysis ipo bitreader) target_link_libraries(CoreTest PRIVATE kleeCore kleeModule kleaverSolver kleaverExpr kleeSupport kleeBasic ${LLVM_LIBS}) +target_link_libraries(ExecutorTest PRIVATE kleeCore kleeModule kleaverSolver kleaverExpr kleeSupport kleeBasic ${LLVM_EXECUTOR_LIBS}) + +# Compile C sources to LLVM bitcode and expose their paths to the test. +find_program(CLANG_EXECUTABLE NAMES clang-19 clang REQUIRED) + +foreach(src add compute function) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${src}.bc + COMMAND ${CLANG_EXECUTABLE} -emit-llvm -c -O0 + ${CMAKE_CURRENT_SOURCE_DIR}/LLVM/${src}.c + -o ${CMAKE_CURRENT_BINARY_DIR}/${src}.bc + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/LLVM/${src}.c + COMMENT "Compiling ${src}.c to LLVM bitcode" + ) + add_custom_target(${src}_bc DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${src}.bc) + add_dependencies(ExecutorTest ${src}_bc) +endforeach() + +target_compile_definitions(ExecutorTest PRIVATE + ADD_BC_PATH="${CMAKE_CURRENT_BINARY_DIR}/add.bc" + COMPUTE_BC_PATH="${CMAKE_CURRENT_BINARY_DIR}/compute.bc" + FUNCTION_BC_PATH="${CMAKE_CURRENT_BINARY_DIR}/function.bc" +) diff --git a/klee/unittests/Core/ExecutorTest.cpp b/klee/unittests/Core/ExecutorTest.cpp new file mode 100644 index 00000000..eec722ff --- /dev/null +++ b/klee/unittests/Core/ExecutorTest.cpp @@ -0,0 +1,1554 @@ +/// +/// Copyright (C) 2026, Vitaly Chipounov +/// +/// Permission is hereby granted, free of charge, to any person obtaining a copy +/// of this software and associated documentation files (the "Software"), to deal +/// in the Software without restriction, including without limitation the rights +/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +/// copies of the Software, and to permit persons to whom the Software is +/// furnished to do so, subject to the following conditions: +/// +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. +/// + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" + +#include +#include +#include +#include +#include +#include + +using namespace klee; +using namespace llvm; + +/// Native helper used as a concrete function pointer in FunctionPointerIndirectCall. +/// +/// Must live outside the anonymous namespace and carry C linkage so that its +/// address is a callable symbol the external dispatcher can invoke directly. +extern "C" int native_triple(int x) { + return x * 3; +} + +namespace { + +/// Minimal Executor subclass for unit testing. +/// +/// Implements fork() as a no-op since tests only use concrete values. +/// Exposes the protected eval() and executeInstruction() to drive a +/// step-by-step execution loop from outside the class. +/// +/// The constructor either creates an empty module (with a standard x86-64 data +/// layout) or loads one from a bitcode file. In both cases the module is owned +/// by the executor and activated automatically; callers cannot replace it. +class TestExecutor : public Executor { + llvm::LLVMContext &m_ctx; + std::unique_ptr m_ownedModule; + KFunction *m_dummyMain = nullptr; + + static constexpr const char *kStdLayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"; + + /// Override setModule to inject a no-op dummy entry-point function. + /// + /// The dummy serves as the initial stack frame (matching S2E's m_dummyMain + /// pattern) so that runFunction() can push real functions on top of it via + /// pushFrame without underflowing the stack. + /// + /// Private: callers must not replace the module after construction. + const llvm::Module *setModule(llvm::Module *module) override { + auto *result = Executor::setModule(module); + + auto *dummyFn = llvm::Function::Create(llvm::FunctionType::get(llvm::Type::getVoidTy(m_ctx), false), + llvm::Function::ExternalLinkage, "__test_main", module); + llvm::ReturnInst::Create(m_ctx, llvm::BasicBlock::Create(m_ctx, "entry", dummyFn)); + m_dummyMain = m_kmodule->updateModuleWithFunction(dummyFn); + + return result; + } + + void initEmpty() { + m_ownedModule = std::make_unique("", m_ctx); + m_ownedModule->setDataLayout(kStdLayout); + setModule(m_ownedModule.get()); + } + + void initFromBitcode(const char *path) { + auto bufOrErr = llvm::MemoryBuffer::getFile(path); + assert(bufOrErr && "failed to open bitcode file"); + auto modOrErr = llvm::parseBitcodeFile(bufOrErr.get()->getMemBufferRef(), m_ctx); + assert(modOrErr && "failed to parse bitcode file"); + m_ownedModule = std::move(modOrErr.get()); + setModule(m_ownedModule.get()); + } + +public: + /// Construct an executor backed by an empty module (no bitcodeFile) or by + /// the module loaded from \p bitcodeFile. The module is owned and + /// activated by the constructor; setModule() is not accessible to callers. + explicit TestExecutor(llvm::LLVMContext &ctx, const char *bitcodeFile = nullptr) : Executor(ctx), m_ctx(ctx) { + if (bitcodeFile) { + initFromBitcode(bitcodeFile); + } else { + initEmpty(); + } + } + + StatePair fork(ExecutionState ¤t, const ref &condition, bool keepConditionTrueInCurrentState, + std::function onBeforeNotify) override { + StatePair sp; + if (auto *ce = dyn_cast(condition)) { + sp = ce->isTrue() ? StatePair(¤t, nullptr) : StatePair(nullptr, ¤t); + } else { + abort(); + } + onBeforeNotify(¤t, sp); + return sp; + } + + /// Return the underlying LLVM module. + /// + /// Tests that build IR programmatically use this to add functions before + /// passing them to runFunction(). + llvm::Module *getLLVMModule() { + return m_ownedModule.get(); + } + + /// Create an execution state whose initial frame is the dummy entry point. + /// + /// Globals are initialized and module constants bound before returning. + ExecutionStatePtr createState() { + auto state = ExecutionStatePtr(new ExecutionState(m_dummyMain)); + initializeGlobals(*state); + m_kmodule->bindModuleConstants(m_globalAddresses); + return state; + } + + /// Set up \p fn as a call from the current dummy frame (mirroring + /// S2EExecutor::prepareFunctionExecution), execute it, and return its + /// return-value expression. nullptr is returned for void functions. + ref runFunction(ExecutionState &state, llvm::Function *fn, std::vector> args) { + auto *kf = m_kmodule->bindFunctionConstants(m_globalAddresses, fn); + + state.llvm.prevPC = state.llvm.pc; + state.llvm.pushFrame(state.llvm.pc, kf); + state.llvm.pc = kf->getInstructions(); + + for (unsigned i = 0; i < args.size(); ++i) + state.llvm.bindArgument(kf, i, args[i]); + + // Run until the Ret that belongs to this invocation. + unsigned targetDepth = state.llvm.stack.size(); + while (state.llvm.stack.size() >= targetDepth) { + KInstruction *ki = state.llvm.pc; + + if (state.llvm.stack.size() == targetDepth && ki->inst->getOpcode() == Instruction::Ret) { + auto *ri = cast(ki->inst); + if (ri->getNumOperands() == 0) + return nullptr; + return eval(ki, 0, state.llvm).value; + } + + state.llvm.stepInstruction(); + executeInstruction(state, ki); + } + return nullptr; + } + + /// Execute the state until a ret instruction is reached. + /// + /// Stops just before executing ret so that the caller can inspect + /// the return value without triggering the stack-underflow check + /// inside executeInstruction(). + /// + /// \returns the return-value expression, or nullptr for void returns. + ref runUntilReturn(ExecutionState &state) { + while (state.llvm.pc) { + KInstruction *ki = state.llvm.pc; + // llvm::outs() << *ki->inst << "\n"; + + if (ki->inst->getOpcode() == Instruction::Ret) { + auto *ri = cast(ki->inst); + if (ri->getNumOperands() == 0) { + return nullptr; + } + // Read the return value from the virtual register file before + // the frame is popped. + return eval(ki, 0, state.llvm).value; + } + + state.llvm.stepInstruction(); + executeInstruction(state, ki); + } + return nullptr; + } +}; + +// ============================================================ +// Test: add(int, int) - simple two-value addition +// ============================================================ + +TEST(ExecutorTest, ConcreteAddReturnsCorrectValue) { + llvm::LLVMContext ctx; + TestExecutor executor(ctx, ADD_BC_PATH); + + llvm::Function *fn = executor.getLLVMModule()->getFunction("add"); + ASSERT_NE(fn, nullptr) << "Function 'add' not found in module"; + + ExecutionStatePtr state = executor.createState(); + + auto result = executor.runFunction( + *state, fn, {klee::ConstantExpr::create(5, Expr::Int32), klee::ConstantExpr::create(3, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr) << "Expected a constant (concrete) expression"; + EXPECT_EQ(ce->getZExtValue(), 8u); +} + +// ============================================================ +// Test: GEP struct field offset +// +// Builds the following IR programmatically and executes it: +// +// %S = type { [8 x i32], i32, i32 } +// define ptr @gep_field(ptr %p) { +// %r = getelementptr inbounds %S, ptr %p, i32 0, i32 1 +// ret ptr %r +// } +// +// Field 1 of { [8 x i32], i32, i32 } is at byte offset 32. +// The test asserts that the returned address equals the base + 32. +// ============================================================ + +TEST(ExecutorTest, GEPStructFieldReturnsCorrectOffset) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *arrTy = llvm::ArrayType::get(i32Ty, 8); + auto *structTy = llvm::StructType::create(ctx, {arrTy, i32Ty, i32Ty}, "S"); + + auto *ptrTy = llvm::PointerType::getUnqual(ctx); + auto *fnTy = llvm::FunctionType::get(ptrTy, {ptrTy}, /*isVarArg=*/false); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + + auto *fn = llvm::Function::Create(fnTy, llvm::Function::ExternalLinkage, "gep_field", mod); + auto *bb = llvm::BasicBlock::Create(ctx, "entry", fn); + llvm::IRBuilder<> builder(bb); + auto *gep = builder.CreateStructGEP(structTy, fn->getArg(0), 1); + builder.CreateRet(gep); + + ExecutionStatePtr state = executor.createState(); + + constexpr uint64_t fakeBase = 0x1000; + unsigned ptrWidth = executor.getModule()->getDataLayout()->getPointerSizeInBits(); + auto result = executor.runFunction(*state, fn, {klee::ConstantExpr::create(fakeBase, ptrWidth)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr) << "Expected a concrete pointer expression"; + // { [8 x i32], i32, i32 }: field 1 starts after 8*4 = 32 bytes. + EXPECT_EQ(ce->getZExtValue(), fakeBase + 32u); +} + +// ============================================================ +// Test: compute(Input*, Output*) - aggregate statistics +// ============================================================ + +/// Must match the layout declared in compute.c exactly. +struct Input { + int values[8]; + int n; + unsigned flags; +}; + +struct Output { + int sum; + int min_val; + int max_val; + unsigned xor_all; + unsigned and_masked; + int alternating; + int dot_self; +}; + +TEST(ExecutorTest, ComputeAggregateStatistics) { + llvm::LLVMContext ctx; + TestExecutor executor(ctx, COMPUTE_BC_PATH); + + llvm::Function *fn = executor.getLLVMModule()->getFunction("compute"); + ASSERT_NE(fn, nullptr) << "Function 'compute' not found in module"; + + // Set up concrete input/output in host memory and expose them to KLEE. + Input in = {}; + in.values[0] = 1; + in.values[1] = -2; + in.values[2] = 3; + in.values[3] = -4; + in.values[4] = 5; + in.n = 5; + in.flags = 0xFFFFFFFFu; + + Output out = {}; + + ExecutionStatePtr state = executor.createState(); + + // Register host structs in the KLEE address space. + // isSharedConcrete=false: KLEE copies `in` into its own concrete buffer + // (reads will be served from that buffer) and writes to `out` go into + // KLEE's buffer (read back via the address space after execution). + state->addExternalObject(&in, sizeof(in), /*isReadOnly=*/true, /*isSharedConcrete=*/false); + state->addExternalObject(&out, sizeof(out), /*isReadOnly=*/false, /*isSharedConcrete=*/false); + + // Pass pointer arguments as concrete addresses matching the host addresses + // that were registered with the address space above. + unsigned ptrWidth = executor.getModule()->getDataLayout()->getPointerSizeInBits(); + executor.runFunction( + *state, fn, + {klee::ConstantExpr::create((uint64_t) &in, ptrWidth), klee::ConstantExpr::create((uint64_t) &out, ptrWidth)}); + + // Read output fields back from the KLEE address space. + auto readI32 = [&](uint64_t addr) -> int32_t { + auto e = state->addressSpace().read(addr, klee::Expr::Int32); + auto *ce = dyn_cast(e); + return ce ? (int32_t) ce->getZExtValue() : 0; + }; + auto readU32 = [&](uint64_t addr) -> uint32_t { + auto e = state->addressSpace().read(addr, klee::Expr::Int32); + auto *ce = dyn_cast(e); + return ce ? (uint32_t) ce->getZExtValue() : 0; + }; + + uint64_t outAddr = (uint64_t) &out; + + // Compute expected values for {1, -2, 3, -4, 5}, flags=0xFFFFFFFF. + // sum = 1 + (-2) + 3 + (-4) + 5 = 3 + // min_val = -4 + // max_val = 5 + // xor_all = 1 ^ (-2) ^ 3 ^ (-4) ^ 5 (unsigned bitwise) + // and_masked = 0xFFFFFFFF & 1 & (-2) & 3 & (-4) & 5 + // alternating = 1 - (-2) + 3 - (-4) + 5 = 15 + // dot_self = 1 + 4 + 9 + 16 + 25 = 55 + + unsigned xor_expected = 1u ^ (unsigned) -2 ^ 3u ^ (unsigned) -4 ^ 5u; + unsigned and_expected = 0xFFFFFFFFu & 1u & (unsigned) -2 & 3u & (unsigned) -4 & 5u; + + EXPECT_EQ(readI32(outAddr + offsetof(Output, sum)), 3); + EXPECT_EQ(readI32(outAddr + offsetof(Output, min_val)), -4); + EXPECT_EQ(readI32(outAddr + offsetof(Output, max_val)), 5); + EXPECT_EQ(readU32(outAddr + offsetof(Output, xor_all)), xor_expected); + EXPECT_EQ(readU32(outAddr + offsetof(Output, and_masked)), and_expected); + EXPECT_EQ(readI32(outAddr + offsetof(Output, alternating)), 15); + EXPECT_EQ(readI32(outAddr + offsetof(Output, dot_self)), 55); +} + +// ============================================================ +// ExtractValue tests +// +// Struct { i32, i32 } bitvector layout (64-bit, little-endian): +// bits [ 0,32) = field 0 +// bits [32,64) = field 1 +// ============================================================ + +TEST(ExecutorTest, ExtractValueStructFirstField) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *structTy = llvm::StructType::get(ctx, {i32Ty, i32Ty}); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {structTy}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateExtractValue(fn->getArg(0), {0u})); + + ExecutionStatePtr state = executor.createState(); + + // { field0=0x1234, field1=0x5678 } packed little-endian into 64 bits. + uint64_t agg = (0x5678ULL << 32) | 0x1234ULL; + auto result = executor.runFunction(*state, fn, {klee::ConstantExpr::create(agg, Expr::Int64)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 0x1234u); +} + +TEST(ExecutorTest, ExtractValueStructSecondField) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *structTy = llvm::StructType::get(ctx, {i32Ty, i32Ty}); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {structTy}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateExtractValue(fn->getArg(0), {1u})); + + ExecutionStatePtr state = executor.createState(); + + // Field 1 is at byte offset 4 → bits [32,64). Expected: 0x5678. + uint64_t agg = (0x5678ULL << 32) | 0x1234ULL; + auto result = executor.runFunction(*state, fn, {klee::ConstantExpr::create(agg, Expr::Int64)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 0x5678u); +} + +TEST(ExecutorTest, ExtractValueArrayElement) { + llvm::LLVMContext ctx; + + // [4 x i8] bitvector (32-bit): bits [16,24) = element 2. + auto *i8Ty = llvm::Type::getInt8Ty(ctx); + auto *arrTy = llvm::ArrayType::get(i8Ty, 4); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i8Ty, {arrTy}, false), llvm::Function::ExternalLinkage, + "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateExtractValue(fn->getArg(0), {2u})); + + ExecutionStatePtr state = executor.createState(); + + // [0x01, 0x02, 0x03, 0x04] packed little-endian into 32 bits. + uint64_t arr = (0x04u << 24) | (0x03u << 16) | (0x02u << 8) | 0x01u; + auto result = executor.runFunction(*state, fn, {klee::ConstantExpr::create(arr, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 0x03u); +} + +// ============================================================ +// InsertValue tests +// +// Four code paths inside executeInstruction: +// 1. only field → result = val (no l, no r) +// 2. first field → result = concat(r, val) (no l, r exists) +// 3. last field → result = concat(val, l) (l exists, no r) +// 4. middle field→ result = concat(r, val, l)(both l and r exist) +// ============================================================ + +// Path 1: only field – { i32 }. Entire aggregate is replaced. +TEST(ExecutorTest, InsertValueOnlyField) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *structTy = llvm::StructType::get(ctx, llvm::ArrayRef({i32Ty})); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(structTy, {structTy, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateInsertValue(fn->getArg(0), fn->getArg(1), {0u})); + + ExecutionStatePtr state = executor.createState(); + + auto result = executor.runFunction( + *state, fn, + {klee::ConstantExpr::create(0xDEADBEEFu, Expr::Int32), klee::ConstantExpr::create(0xCAFEBABEu, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 0xCAFEBABEu); +} + +// Path 2: first field of { i32, i32 } – right remainder (field1) preserved. +TEST(ExecutorTest, InsertValueFirstField) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *structTy = llvm::StructType::get(ctx, {i32Ty, i32Ty}); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(structTy, {structTy, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateInsertValue(fn->getArg(0), fn->getArg(1), {0u})); + + ExecutionStatePtr state = executor.createState(); + + // agg = { field0=0xAAAA, field1=0xBBBB }, replace field0 with 0x1234. + uint64_t agg = (0xBBBBULL << 32) | 0xAAAAULL; + auto result = executor.runFunction( + *state, fn, {klee::ConstantExpr::create(agg, Expr::Int64), klee::ConstantExpr::create(0x1234u, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), (0xBBBBULL << 32) | 0x1234ULL); +} + +// Path 3: last field of { i32, i32 } – left remainder (field0) preserved. +TEST(ExecutorTest, InsertValueLastField) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *structTy = llvm::StructType::get(ctx, {i32Ty, i32Ty}); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(structTy, {structTy, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateInsertValue(fn->getArg(0), fn->getArg(1), {1u})); + + ExecutionStatePtr state = executor.createState(); + + // agg = { field0=0xAAAA, field1=0xBBBB }, replace field1 with 0x5678. + uint64_t agg = (0xBBBBULL << 32) | 0xAAAAULL; + auto result = executor.runFunction( + *state, fn, {klee::ConstantExpr::create(agg, Expr::Int64), klee::ConstantExpr::create(0x5678u, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), (0x5678ULL << 32) | 0xAAAAULL); +} + +// Path 4: middle field of { i16, i16, i16, i16 } – both left and right remainders exist. +// +// { i16, i16, i16, i16 } bitvector (64-bit, little-endian): +// bits [ 0,16) = field0, [16,32) = field1, [32,48) = field2, [48,64) = field3 +// Insert field1 (byte offset 2): lOffset=16, rOffset=32. +// l = bits [0,16), r = bits [32,64) +TEST(ExecutorTest, InsertValueMiddleField) { + llvm::LLVMContext ctx; + + auto *i16Ty = llvm::Type::getInt16Ty(ctx); + auto *structTy = llvm::StructType::get(ctx, {i16Ty, i16Ty, i16Ty, i16Ty}); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(structTy, {structTy, i16Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateInsertValue(fn->getArg(0), fn->getArg(1), {1u})); + + ExecutionStatePtr state = executor.createState(); + + // { 0x1111, 0x2222, 0x3333, 0x4444 } packed little-endian into 64 bits. + uint64_t agg = (0x4444ULL << 48) | (0x3333ULL << 32) | (0x2222ULL << 16) | 0x1111ULL; + auto result = executor.runFunction( + *state, fn, {klee::ConstantExpr::create(agg, Expr::Int64), klee::ConstantExpr::create(0xBBBBu, Expr::Int16)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + // field1 replaced, fields 0/2/3 preserved. + uint64_t expected = (0x4444ULL << 48) | (0x3333ULL << 32) | (0xBBBBULL << 16) | 0x1111ULL; + EXPECT_EQ(ce->getZExtValue(), expected); +} + +// ============================================================ +// ExtractElement tests +// +// Vector bitvector layout (little-endian): +// element i occupies bits [i*EltBits, (i+1)*EltBits) +// +// <2 x i32> [0x1234, 0x5678] packed as a 64-bit integer: +// bits [ 0,32) = 0x1234 (element 0) +// bits [32,64) = 0x5678 (element 1) +// ============================================================ + +// Extract element 0 from <2 x i32> → bits [0,32) = 0x1234. +TEST(ExecutorTest, ExtractElementFirstElement) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *vecTy = llvm::FixedVectorType::get(i32Ty, 2); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {vecTy}, false), llvm::Function::ExternalLinkage, + "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateExtractElement(fn->getArg(0), b.getInt32(0))); + + ExecutionStatePtr state = executor.createState(); + + // <2 x i32> [0x1234, 0x5678] packed little-endian into 64 bits. + uint64_t vec = (0x5678ULL << 32) | 0x1234ULL; + auto result = executor.runFunction(*state, fn, {klee::ConstantExpr::create(vec, Expr::Int64)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 0x1234u); +} + +// Extract element 1 from <2 x i32> → bits [32,64) = 0x5678. +TEST(ExecutorTest, ExtractElementLastElement) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *vecTy = llvm::FixedVectorType::get(i32Ty, 2); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {vecTy}, false), llvm::Function::ExternalLinkage, + "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateExtractElement(fn->getArg(0), b.getInt32(1))); + + ExecutionStatePtr state = executor.createState(); + + // <2 x i32> [0x1234, 0x5678] packed little-endian into 64 bits. + uint64_t vec = (0x5678ULL << 32) | 0x1234ULL; + auto result = executor.runFunction(*state, fn, {klee::ConstantExpr::create(vec, Expr::Int64)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 0x5678u); +} + +// Extract element 2 from <4 x i8> → bits [16,24) = 0x03. +// +// <4 x i8> [0x01, 0x02, 0x03, 0x04] packed as a 32-bit integer: +// bits [ 0, 8) = 0x01, [8,16) = 0x02, [16,24) = 0x03, [24,32) = 0x04 +TEST(ExecutorTest, ExtractElementMiddleElement) { + llvm::LLVMContext ctx; + + auto *i8Ty = llvm::Type::getInt8Ty(ctx); + auto *vecTy = llvm::FixedVectorType::get(i8Ty, 4); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i8Ty, {vecTy}, false), llvm::Function::ExternalLinkage, + "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateExtractElement(fn->getArg(0), b.getInt32(2))); + + ExecutionStatePtr state = executor.createState(); + + // <4 x i8> [0x01, 0x02, 0x03, 0x04] packed little-endian into 32 bits. + uint32_t vec = (0x04u << 24) | (0x03u << 16) | (0x02u << 8) | 0x01u; + auto result = executor.runFunction(*state, fn, {klee::ConstantExpr::create(vec, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 0x03u); +} + +// ============================================================ +// InsertElement tests +// +// Four code paths (mirroring InsertValue, but over vector lanes): +// 1. single-element vector → result = newElt +// 2. first element → l absent, r = remaining high lanes +// 3. last element → l = remaining low lanes, r absent +// 4. middle element → both l and r exist +// ============================================================ + +// Path 1: single-element <1 x i32>. Entire vector is replaced. +TEST(ExecutorTest, InsertElementOnlyElement) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *vecTy = llvm::FixedVectorType::get(i32Ty, 1); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(vecTy, {vecTy, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateInsertElement(fn->getArg(0), fn->getArg(1), b.getInt32(0))); + + ExecutionStatePtr state = executor.createState(); + + auto result = executor.runFunction( + *state, fn, + {klee::ConstantExpr::create(0xDEADBEEFu, Expr::Int32), klee::ConstantExpr::create(0xCAFEBABEu, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 0xCAFEBABEu); +} + +// Path 2: insert element 0 of <2 x i32> – high lane (element 1) preserved. +TEST(ExecutorTest, InsertElementFirstElement) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *vecTy = llvm::FixedVectorType::get(i32Ty, 2); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(vecTy, {vecTy, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateInsertElement(fn->getArg(0), fn->getArg(1), b.getInt32(0))); + + ExecutionStatePtr state = executor.createState(); + + // vec = [0xAAAA, 0xBBBB]; replace element 0 with 0x1234. + uint64_t vec = (0xBBBBULL << 32) | 0xAAAAULL; + auto result = executor.runFunction( + *state, fn, {klee::ConstantExpr::create(vec, Expr::Int64), klee::ConstantExpr::create(0x1234u, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), (0xBBBBULL << 32) | 0x1234ULL); +} + +// Path 3: insert element 1 of <2 x i32> – low lane (element 0) preserved. +TEST(ExecutorTest, InsertElementLastElement) { + llvm::LLVMContext ctx; + + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *vecTy = llvm::FixedVectorType::get(i32Ty, 2); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(vecTy, {vecTy, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateInsertElement(fn->getArg(0), fn->getArg(1), b.getInt32(1))); + + ExecutionStatePtr state = executor.createState(); + + // vec = [0xAAAA, 0xBBBB]; replace element 1 with 0x5678. + uint64_t vec = (0xBBBBULL << 32) | 0xAAAAULL; + auto result = executor.runFunction( + *state, fn, {klee::ConstantExpr::create(vec, Expr::Int64), klee::ConstantExpr::create(0x5678u, Expr::Int32)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), (0x5678ULL << 32) | 0xAAAAULL); +} + +// Path 4: insert element 2 of <4 x i8> – lanes 0/1 (l) and lane 3 (r) preserved. +// +// <4 x i8> [0x01, 0x02, 0x03, 0x04] packed as 32-bit little-endian integer. +// Replace element 2 (bits [16,24)) with 0xFF. +TEST(ExecutorTest, InsertElementMiddleElement) { + llvm::LLVMContext ctx; + + auto *i8Ty = llvm::Type::getInt8Ty(ctx); + auto *vecTy = llvm::FixedVectorType::get(i8Ty, 4); + + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(vecTy, {vecTy, i8Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateInsertElement(fn->getArg(0), fn->getArg(1), b.getInt32(2))); + + ExecutionStatePtr state = executor.createState(); + + // vec = [0x01, 0x02, 0x03, 0x04]; replace element 2 with 0xFF. + uint32_t vec = (0x04u << 24) | (0x03u << 16) | (0x02u << 8) | 0x01u; + auto result = executor.runFunction( + *state, fn, {klee::ConstantExpr::create(vec, Expr::Int32), klee::ConstantExpr::create(0xFFu, Expr::Int8)}); + + ASSERT_NE(result.get(), nullptr); + auto *ce = dyn_cast(result); + ASSERT_NE(ce, nullptr); + // Elements 0/1/3 unchanged; element 2 = 0xFF. + uint32_t expected = (0x04u << 24) | (0xFFu << 16) | (0x02u << 8) | 0x01u; + EXPECT_EQ(ce->getZExtValue(), expected); +} + +// ============================================================ +// function.bc tests: call, switch, trunc, ptr conversions +// ============================================================ + +// ── helper ────────────────────────────────────────────────── +// Load function.bc once per test, look up a named function, run it with +// one i32 argument, and return the i32 result. +static uint64_t evalFunctionI32(const char *name, uint32_t arg) { + llvm::LLVMContext ctx; + TestExecutor executor(ctx, FUNCTION_BC_PATH); + llvm::Function *fn = executor.getLLVMModule()->getFunction(name); + assert(fn && "function not found in function.bc"); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(arg, Expr::Int32)}); + return dyn_cast(r)->getZExtValue(); +} + +// ── call + return ──────────────────────────────────────────── +// call_chain(x) = double_it(x) + 1 = 2*x + 1 +TEST(ExecutorTest, FunctionCallChain) { + EXPECT_EQ(evalFunctionI32("call_chain", 5), 11u); // 2*5 + 1 + EXPECT_EQ(evalFunctionI32("call_chain", 0), 1u); // 2*0 + 1 + EXPECT_EQ(evalFunctionI32("call_chain", -1u), (uint64_t) (uint32_t) (-1u)); // wraps +} + +// ── switch ─────────────────────────────────────────────────── +// classify: 0→10, 1→20, 2→30, else→-1 +TEST(ExecutorTest, SwitchStatement) { + EXPECT_EQ(evalFunctionI32("classify", 0), 10u); + EXPECT_EQ(evalFunctionI32("classify", 1), 20u); + EXPECT_EQ(evalFunctionI32("classify", 2), 30u); + EXPECT_EQ(evalFunctionI32("classify", 99), 0xFFFFFFFFu); // -1 zero-extended +} + +// ── truncation ─────────────────────────────────────────────── +// Trunc drops high bits: only the low N bits survive. +TEST(ExecutorTest, TruncToU8) { + EXPECT_EQ(evalFunctionI32("trunc_to_u8", 0x12345678u), 0x78u); + EXPECT_EQ(evalFunctionI32("trunc_to_u8", 0x00000100u), 0x00u); // high byte only +} + +TEST(ExecutorTest, TruncToU16) { + EXPECT_EQ(evalFunctionI32("trunc_to_u16", 0x12345678u), 0x5678u); + EXPECT_EQ(evalFunctionI32("trunc_to_u16", 0xFFFF0000u), 0x0000u); +} + +// ── pointer conversions ────────────────────────────────────── +// PtrToInt: the pointer value passes through as an integer of pointer width. +TEST(ExecutorTest, PtrToInt) { + llvm::LLVMContext ctx; + TestExecutor executor(ctx, FUNCTION_BC_PATH); + llvm::Function *fn = executor.getLLVMModule()->getFunction("ptr_to_int"); + ASSERT_NE(fn, nullptr); + + unsigned ptrWidth = executor.getModule()->getDataLayout()->getPointerSizeInBits(); + constexpr uint64_t fakePtr = 0xDEADBEEFCAFE0000ULL; + + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(fakePtr, ptrWidth)}); + auto *ce = dyn_cast(r); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), fakePtr); +} + +// IntToPtr: the integer value passes through as a pointer-width expression. +TEST(ExecutorTest, IntToPtr) { + llvm::LLVMContext ctx; + TestExecutor executor(ctx, FUNCTION_BC_PATH); + llvm::Function *fn = executor.getLLVMModule()->getFunction("int_to_ptr"); + ASSERT_NE(fn, nullptr); + + unsigned ptrWidth = executor.getModule()->getDataLayout()->getPointerSizeInBits(); + constexpr uint64_t fakeAddr = 0x00007FFF12345678ULL; + + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(fakeAddr, Expr::Int64)}); + auto *ce = dyn_cast(r); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), fakeAddr); + EXPECT_EQ(ce->getWidth(), ptrWidth); +} + +// ── function pointer (indirect call) ──────────────────────── +// +// apply(fn, x) calls fn(x) through a non-constant SSA value, which forces +// the executor down the indirect-call path (getTargetFunction returns null). +// The executor evaluates the concrete pointer, creates an external stub via +// DynamicLibrary::AddSymbol, and dispatches the call natively. +// +// native_triple (defined above the anonymous namespace with C linkage) is a +// real callable function; passing its host address as the fn argument gives +// the external dispatcher a valid target to call. +TEST(ExecutorTest, FunctionPointerIndirectCall) { + llvm::LLVMContext ctx; + TestExecutor executor(ctx, FUNCTION_BC_PATH); + llvm::Function *fn = executor.getLLVMModule()->getFunction("apply"); + ASSERT_NE(fn, nullptr); + + unsigned ptrWidth = executor.getModule()->getDataLayout()->getPointerSizeInBits(); + + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, + {klee::ConstantExpr::create((uint64_t) (void *) native_triple, ptrWidth), + klee::ConstantExpr::create(7u, Expr::Int32)}); + auto *ce = dyn_cast(r); + ASSERT_NE(ce, nullptr); + EXPECT_EQ(ce->getZExtValue(), 21u); // native_triple(7) = 3*7 = 21 +} + +// ============================================================ +// Arithmetic and logic binary-op tests +// +// SDiv/SRem use -7 (= 0xFFFFFFF9) to verify signed semantics diverge +// from unsigned: -7 /u 2 = 0x7FFFFFFC, -7 /s 2 = -3 = 0xFFFFFFFD. +// AShr uses 0x80000008 to verify sign-extension: logical shift gives +// 0x40000004, arithmetic shift gives 0xC0000004. +// ============================================================ + +/// Build `i32 f(i32 %a, i32 %b) { ret i32 (op %a, %b) }`, run it with +/// concrete operands, and return the i32 result. +static uint64_t evalBinop(llvm::Instruction::BinaryOps op, uint32_t a, uint32_t b) { + llvm::LLVMContext ctx; + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {i32Ty, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateBinOp(op, fn->getArg(0), fn->getArg(1))); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction( + *s, fn, {klee::ConstantExpr::create(a, Expr::Int32), klee::ConstantExpr::create(b, Expr::Int32)}); + return dyn_cast(r)->getZExtValue(); +} + +TEST(ExecutorTest, Add) { + EXPECT_EQ(evalBinop(Instruction::Add, 5, 3), 8u); + EXPECT_EQ(evalBinop(Instruction::Add, 0xFFFFFFFFu, 1), 0u); // wraps +} + +TEST(ExecutorTest, Sub) { + EXPECT_EQ(evalBinop(Instruction::Sub, 5, 3), 2u); + EXPECT_EQ(evalBinop(Instruction::Sub, 0u, 1u), 0xFFFFFFFFu); // wraps +} + +TEST(ExecutorTest, Mul) { + EXPECT_EQ(evalBinop(Instruction::Mul, 6, 7), 42u); + EXPECT_EQ(evalBinop(Instruction::Mul, 0x80000000u, 2), 0u); // wraps +} + +TEST(ExecutorTest, UDiv) { + EXPECT_EQ(evalBinop(Instruction::UDiv, 7, 2), 3u); + // 0xFFFFFFF9 (= -7 as signed) /u 2 = 0x7FFFFFFCu (unsigned semantics) + EXPECT_EQ(evalBinop(Instruction::UDiv, 0xFFFFFFF9u, 2), 0x7FFFFFFCu); +} + +TEST(ExecutorTest, SDiv) { + EXPECT_EQ(evalBinop(Instruction::SDiv, 7, 2), 3u); + // -7 /s 2 = -3 = 0xFFFFFFFD (signed semantics, truncates toward zero) + EXPECT_EQ(evalBinop(Instruction::SDiv, 0xFFFFFFF9u, 2), 0xFFFFFFFDu); +} + +TEST(ExecutorTest, URem) { + EXPECT_EQ(evalBinop(Instruction::URem, 7, 3), 1u); + // 0xFFFFFFF9u % 3 = 0xFFFFFFF9u - 3*(0xFFFFFFF9u/3) + EXPECT_EQ(evalBinop(Instruction::URem, 0xFFFFFFF9u, 3), 0xFFFFFFF9u % 3u); +} + +TEST(ExecutorTest, SRem) { + EXPECT_EQ(evalBinop(Instruction::SRem, 7, 3), 1u); + // -7 %s 3 = -1 = 0xFFFFFFFF (sign follows dividend) + EXPECT_EQ(evalBinop(Instruction::SRem, 0xFFFFFFF9u, 3), 0xFFFFFFFFu); +} + +TEST(ExecutorTest, And) { + EXPECT_EQ(evalBinop(Instruction::And, 0xF0F0F0F0u, 0x0F0F0F0Fu), 0u); + EXPECT_EQ(evalBinop(Instruction::And, 0xFFFF0000u, 0xFFFFFFFFu), 0xFFFF0000u); +} + +TEST(ExecutorTest, Or) { + EXPECT_EQ(evalBinop(Instruction::Or, 0xF0F0F0F0u, 0x0F0F0F0Fu), 0xFFFFFFFFu); + EXPECT_EQ(evalBinop(Instruction::Or, 0u, 0u), 0u); +} + +TEST(ExecutorTest, Xor) { + EXPECT_EQ(evalBinop(Instruction::Xor, 0xFFFFFFFFu, 0xFFFFFFFFu), 0u); + EXPECT_EQ(evalBinop(Instruction::Xor, 0xF0F0F0F0u, 0x0F0F0F0Fu), 0xFFFFFFFFu); +} + +TEST(ExecutorTest, Shl) { + EXPECT_EQ(evalBinop(Instruction::Shl, 1, 4), 16u); + EXPECT_EQ(evalBinop(Instruction::Shl, 0xFFFFFFFFu, 1), 0xFFFFFFFEu); // high bit shifted out +} + +TEST(ExecutorTest, LShr) { + EXPECT_EQ(evalBinop(Instruction::LShr, 0x80000008u, 1), 0x40000004u); // zero-fills high bit + EXPECT_EQ(evalBinop(Instruction::LShr, 16, 4), 1u); +} + +TEST(ExecutorTest, AShr) { + EXPECT_EQ(evalBinop(Instruction::AShr, 0x80000008u, 1), 0xC0000004u); // sign-extends high bit + EXPECT_EQ(evalBinop(Instruction::AShr, 16, 4), 1u); // positive: same as LShr +} + +// ============================================================ +// Floating-point instruction tests +// +// All operations use f32 (32 bits) unless the instruction requires two +// precisions (FPTrunc: f64→f32, FPExt: f32→f64). +// +// Key bit patterns (IEEE 754): +// f32: 1.0=0x3F800000 1.5=0x3FC00000 2.0=0x40000000 +// 3.0=0x40400000 3.5=0x40600000 -2.0=0xC0000000 +// qNaN=0x7FC00000 +// f64: 3.0=0x4008000000000000 +// +// FCmp predicates are exercised in ordered/unordered pairs; a NaN operand +// distinguishes the two (O* returns false when NaN is present; U* returns true). +// ============================================================ + +/// Build `f32 f(f32, f32) { ret op(%a, %b) }` and run with f32 bit patterns. +static uint32_t evalFBinop(llvm::Instruction::BinaryOps op, uint32_t a, uint32_t b) { + llvm::LLVMContext ctx; + auto *f32Ty = llvm::Type::getFloatTy(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(f32Ty, {f32Ty, f32Ty}, false), + llvm::Function::ExternalLinkage, "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateBinOp(op, fn->getArg(0), fn->getArg(1))); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction( + *s, fn, {klee::ConstantExpr::create(a, Expr::Int32), klee::ConstantExpr::create(b, Expr::Int32)}); + return (uint32_t) dyn_cast(r)->getZExtValue(); +} + +/// Build `i1 f(f32, f32) { ret fcmp %a, %b }` and run with f32 bit patterns. +static uint64_t evalFCmp(llvm::FCmpInst::Predicate pred, uint32_t a, uint32_t b) { + llvm::LLVMContext ctx; + auto *f32Ty = llvm::Type::getFloatTy(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(llvm::Type::getInt1Ty(ctx), {f32Ty, f32Ty}, false), + llvm::Function::ExternalLinkage, "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateFCmp(pred, fn->getArg(0), fn->getArg(1))); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction( + *s, fn, {klee::ConstantExpr::create(a, Expr::Int32), klee::ConstantExpr::create(b, Expr::Int32)}); + return dyn_cast(r)->getZExtValue(); +} + +/// FPTrunc: f64 → f32 (narrowing conversion). +static uint32_t evalFPTrunc(uint64_t f64bits) { + llvm::LLVMContext ctx; + auto *f64Ty = llvm::Type::getDoubleTy(ctx), *f32Ty = llvm::Type::getFloatTy(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(f32Ty, {f64Ty}, false), llvm::Function::ExternalLinkage, + "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateFPTrunc(fn->getArg(0), f32Ty)); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(f64bits, Expr::Int64)}); + return (uint32_t) dyn_cast(r)->getZExtValue(); +} + +/// FPExt: f32 → f64 (widening conversion). +static uint64_t evalFPExt(uint32_t f32bits) { + llvm::LLVMContext ctx; + auto *f32Ty = llvm::Type::getFloatTy(ctx), *f64Ty = llvm::Type::getDoubleTy(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(f64Ty, {f32Ty}, false), llvm::Function::ExternalLinkage, + "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateFPExt(fn->getArg(0), f64Ty)); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(f32bits, Expr::Int32)}); + return dyn_cast(r)->getZExtValue(); +} + +/// FPToUI: f32 → u32 (truncation toward zero). +static uint32_t evalFPToUI(uint32_t f32bits) { + llvm::LLVMContext ctx; + auto *f32Ty = llvm::Type::getFloatTy(ctx); + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {f32Ty}, false), llvm::Function::ExternalLinkage, + "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateFPToUI(fn->getArg(0), i32Ty)); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(f32bits, Expr::Int32)}); + return (uint32_t) dyn_cast(r)->getZExtValue(); +} + +/// FPToSI: f32 → i32 (truncation toward zero, signed). +static uint32_t evalFPToSI(uint32_t f32bits) { + llvm::LLVMContext ctx; + auto *f32Ty = llvm::Type::getFloatTy(ctx); + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {f32Ty}, false), llvm::Function::ExternalLinkage, + "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateFPToSI(fn->getArg(0), i32Ty)); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(f32bits, Expr::Int32)}); + return (uint32_t) dyn_cast(r)->getZExtValue(); +} + +/// UIToFP: u32 → f32. +static uint32_t evalUIToFP(uint32_t val) { + llvm::LLVMContext ctx; + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *f32Ty = llvm::Type::getFloatTy(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(f32Ty, {i32Ty}, false), llvm::Function::ExternalLinkage, + "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateUIToFP(fn->getArg(0), f32Ty)); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(val, Expr::Int32)}); + return (uint32_t) dyn_cast(r)->getZExtValue(); +} + +/// SIToFP: i32 → f32. +static uint32_t evalSIToFP(uint32_t val) { + llvm::LLVMContext ctx; + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + auto *f32Ty = llvm::Type::getFloatTy(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(f32Ty, {i32Ty}, false), llvm::Function::ExternalLinkage, + "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateSIToFP(fn->getArg(0), f32Ty)); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(val, Expr::Int32)}); + return (uint32_t) dyn_cast(r)->getZExtValue(); +} + +// ── binary arithmetic ──────────────────────────────────────── + +TEST(ExecutorTest, FAdd) { + EXPECT_EQ(evalFBinop(Instruction::FAdd, 0x3F800000u, 0x40000000u), 0x40400000u); // 1+2=3 + EXPECT_EQ(evalFBinop(Instruction::FAdd, 0x00000000u, 0x3F800000u), 0x3F800000u); // 0+1=1 +} + +TEST(ExecutorTest, FSub) { + EXPECT_EQ(evalFBinop(Instruction::FSub, 0x40400000u, 0x3F800000u), 0x40000000u); // 3-1=2 + EXPECT_EQ(evalFBinop(Instruction::FSub, 0x3F800000u, 0x3F800000u), 0x00000000u); // 1-1=0 +} + +TEST(ExecutorTest, FMul) { + EXPECT_EQ(evalFBinop(Instruction::FMul, 0x40000000u, 0x3FC00000u), 0x40400000u); // 2*1.5=3 + EXPECT_EQ(evalFBinop(Instruction::FMul, 0x3F800000u, 0x00000000u), 0x00000000u); // 1*0=0 +} + +TEST(ExecutorTest, FDiv) { + EXPECT_EQ(evalFBinop(Instruction::FDiv, 0x40400000u, 0x40000000u), 0x3FC00000u); // 3/2=1.5 + EXPECT_EQ(evalFBinop(Instruction::FDiv, 0x3F800000u, 0x3F800000u), 0x3F800000u); // 1/1=1 +} + +TEST(ExecutorTest, FRem) { + EXPECT_EQ(evalFBinop(Instruction::FRem, 0x40600000u, 0x40000000u), 0x3FC00000u); // 3.5%2=1.5 + EXPECT_EQ(evalFBinop(Instruction::FRem, 0x40000000u, 0x3F800000u), 0x00000000u); // 2%1=0 +} + +// ── conversions ────────────────────────────────────────────── + +TEST(ExecutorTest, FPTrunc) { + // 3.0 (f64=0x4008000000000000) → 3.0f (f32=0x40400000) + EXPECT_EQ(evalFPTrunc(0x4008000000000000ULL), 0x40400000u); + // 1.0 (f64=0x3FF0000000000000) → 1.0f (f32=0x3F800000) + EXPECT_EQ(evalFPTrunc(0x3FF0000000000000ULL), 0x3F800000u); +} + +TEST(ExecutorTest, FPExt) { + // 3.0f → 3.0 + EXPECT_EQ(evalFPExt(0x40400000u), 0x4008000000000000ULL); + // 1.0f → 1.0 + EXPECT_EQ(evalFPExt(0x3F800000u), 0x3FF0000000000000ULL); +} + +TEST(ExecutorTest, FPToUI) { + EXPECT_EQ(evalFPToUI(0x40400000u), 3u); // 3.0f → 3 + EXPECT_EQ(evalFPToUI(0x00000000u), 0u); // 0.0f → 0 +} + +TEST(ExecutorTest, FPToSI) { + EXPECT_EQ(evalFPToSI(0x40400000u), 3u); // 3.0f → 3 + EXPECT_EQ(evalFPToSI(0xC0000000u), 0xFFFFFFFEu); // -2.0f → -2 +} + +TEST(ExecutorTest, UIToFP) { + EXPECT_EQ(evalUIToFP(3u), 0x40400000u); // 3 → 3.0f + EXPECT_EQ(evalUIToFP(0u), 0x00000000u); // 0 → 0.0f +} + +TEST(ExecutorTest, SIToFP) { + EXPECT_EQ(evalSIToFP(3u), 0x40400000u); // 3 → 3.0f + EXPECT_EQ(evalSIToFP(0xFFFFFFFEu), 0xC0000000u); // -2 → -2.0f +} + +// ── FCmp predicates ────────────────────────────────────────── +// +// Operand constants: +// lo = 1.0f = 0x3F800000 +// hi = 2.0f = 0x40000000 +// nan = quiet NaN = 0x7FC00000 +// +// Each test covers the ordered (O*) and unordered (U*) twin predicates. +// The NaN case is always included because that is the only input that +// distinguishes O* (returns false) from U* (returns true). + +static constexpr uint32_t kFCmpLo = 0x3F800000u; // 1.0f +static constexpr uint32_t kFCmpHi = 0x40000000u; // 2.0f +static constexpr uint32_t kFCmpNaN = 0x7FC00000u; // quiet NaN + +TEST(ExecutorTest, FCmpFalseTrue) { + using P = llvm::FCmpInst; + EXPECT_EQ(evalFCmp(P::FCMP_FALSE, kFCmpLo, kFCmpHi), 0u); // always false + EXPECT_EQ(evalFCmp(P::FCMP_FALSE, kFCmpLo, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_FALSE, kFCmpNaN, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_TRUE, kFCmpLo, kFCmpHi), 1u); // always true + EXPECT_EQ(evalFCmp(P::FCMP_TRUE, kFCmpLo, kFCmpLo), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_TRUE, kFCmpNaN, kFCmpLo), 1u); +} + +TEST(ExecutorTest, FCmpOrdUno) { + using P = llvm::FCmpInst; + // ORD: true iff neither operand is NaN + EXPECT_EQ(evalFCmp(P::FCMP_ORD, kFCmpLo, kFCmpHi), 1u); // no NaN + EXPECT_EQ(evalFCmp(P::FCMP_ORD, kFCmpNaN, kFCmpLo), 0u); // NaN present + // UNO: true iff at least one operand is NaN + EXPECT_EQ(evalFCmp(P::FCMP_UNO, kFCmpLo, kFCmpHi), 0u); // no NaN + EXPECT_EQ(evalFCmp(P::FCMP_UNO, kFCmpNaN, kFCmpLo), 1u); // NaN present +} + +TEST(ExecutorTest, FCmpOeqUeq) { + using P = llvm::FCmpInst; + EXPECT_EQ(evalFCmp(P::FCMP_OEQ, kFCmpLo, kFCmpLo), 1u); // equal, ordered + EXPECT_EQ(evalFCmp(P::FCMP_OEQ, kFCmpLo, kFCmpHi), 0u); // unequal + EXPECT_EQ(evalFCmp(P::FCMP_OEQ, kFCmpNaN, kFCmpLo), 0u); // NaN → false + EXPECT_EQ(evalFCmp(P::FCMP_UEQ, kFCmpLo, kFCmpLo), 1u); // equal + EXPECT_EQ(evalFCmp(P::FCMP_UEQ, kFCmpLo, kFCmpHi), 0u); // unequal, ordered + EXPECT_EQ(evalFCmp(P::FCMP_UEQ, kFCmpNaN, kFCmpLo), 1u); // NaN → true +} + +TEST(ExecutorTest, FCmpOgtUgt) { + using P = llvm::FCmpInst; + EXPECT_EQ(evalFCmp(P::FCMP_OGT, kFCmpHi, kFCmpLo), 1u); // 2 > 1 + EXPECT_EQ(evalFCmp(P::FCMP_OGT, kFCmpLo, kFCmpHi), 0u); // 1 > 2 → false + EXPECT_EQ(evalFCmp(P::FCMP_OGT, kFCmpLo, kFCmpLo), 0u); // equal → false + EXPECT_EQ(evalFCmp(P::FCMP_OGT, kFCmpNaN, kFCmpLo), 0u); // NaN → false + EXPECT_EQ(evalFCmp(P::FCMP_UGT, kFCmpHi, kFCmpLo), 1u); // 2 > 1 + EXPECT_EQ(evalFCmp(P::FCMP_UGT, kFCmpLo, kFCmpHi), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_UGT, kFCmpNaN, kFCmpLo), 1u); // NaN → true +} + +TEST(ExecutorTest, FCmpOgeUge) { + using P = llvm::FCmpInst; + EXPECT_EQ(evalFCmp(P::FCMP_OGE, kFCmpHi, kFCmpLo), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_OGE, kFCmpLo, kFCmpLo), 1u); // equal counts + EXPECT_EQ(evalFCmp(P::FCMP_OGE, kFCmpLo, kFCmpHi), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_OGE, kFCmpNaN, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_UGE, kFCmpHi, kFCmpLo), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_UGE, kFCmpLo, kFCmpLo), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_UGE, kFCmpLo, kFCmpHi), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_UGE, kFCmpNaN, kFCmpLo), 1u); +} + +TEST(ExecutorTest, FCmpOltUlt) { + using P = llvm::FCmpInst; + EXPECT_EQ(evalFCmp(P::FCMP_OLT, kFCmpLo, kFCmpHi), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_OLT, kFCmpHi, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_OLT, kFCmpLo, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_OLT, kFCmpNaN, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_ULT, kFCmpLo, kFCmpHi), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_ULT, kFCmpHi, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_ULT, kFCmpNaN, kFCmpLo), 1u); +} + +TEST(ExecutorTest, FCmpOleUle) { + using P = llvm::FCmpInst; + EXPECT_EQ(evalFCmp(P::FCMP_OLE, kFCmpLo, kFCmpHi), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_OLE, kFCmpLo, kFCmpLo), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_OLE, kFCmpHi, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_OLE, kFCmpNaN, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_ULE, kFCmpLo, kFCmpHi), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_ULE, kFCmpLo, kFCmpLo), 1u); + EXPECT_EQ(evalFCmp(P::FCMP_ULE, kFCmpHi, kFCmpLo), 0u); + EXPECT_EQ(evalFCmp(P::FCMP_ULE, kFCmpNaN, kFCmpLo), 1u); +} + +TEST(ExecutorTest, FCmpOneUne) { + using P = llvm::FCmpInst; + EXPECT_EQ(evalFCmp(P::FCMP_ONE, kFCmpLo, kFCmpHi), 1u); // ordered not-equal + EXPECT_EQ(evalFCmp(P::FCMP_ONE, kFCmpLo, kFCmpLo), 0u); // equal → false + EXPECT_EQ(evalFCmp(P::FCMP_ONE, kFCmpNaN, kFCmpLo), 0u); // NaN → false (not ordered) + EXPECT_EQ(evalFCmp(P::FCMP_UNE, kFCmpLo, kFCmpHi), 1u); // unordered not-equal + EXPECT_EQ(evalFCmp(P::FCMP_UNE, kFCmpLo, kFCmpLo), 0u); // equal → false + EXPECT_EQ(evalFCmp(P::FCMP_UNE, kFCmpNaN, kFCmpLo), 1u); // NaN → true +} + +// ============================================================ +// Intrinsic tests +// +// Covered: llvm.fabs (f32/f64), llvm.abs (i32), llvm.smax, llvm.smin, +// llvm.umax, llvm.umin, llvm.fshl, llvm.fshr. +// Skipped: llvm.vastart / llvm.vaend (require a varargs function setup). +// +// Float values are passed and returned as their IEEE 754 bit patterns: +// -3.0f = 0xC0400000, 3.0f = 0x40400000, -0.0f = 0x80000000 +// -1.0 = 0xBFF0000000000000, 1.0 = 0x3FF0000000000000 +// +// fshl(a, b, amt): concat [a:b] (a in high bits), shift left by amt%w, take high w bits. +// fshr(a, b, amt): concat [a:b] (a in high bits), shift right by amt%w, take low w bits. +// ============================================================ + +/// Build `i32 f(i32) { ret llvm.abs.i32(arg, ) }` and run it. +static uint64_t evalAbs(uint32_t x, bool poison) { + llvm::LLVMContext ctx; + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *intrFn = llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::abs, {i32Ty}); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {i32Ty}, false), llvm::Function::ExternalLinkage, + "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateCall(intrFn, {fn->getArg(0), llvm::ConstantInt::get(llvm::Type::getInt1Ty(ctx), poison)})); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(x, Expr::Int32)}); + return dyn_cast(r)->getZExtValue(); +} + +/// Build `i32 f(i32, i32) { ret llvm..i32(a, b) }` for smax/smin/umax/umin and run it. +static uint64_t evalMinMax(llvm::Intrinsic::ID id, uint32_t a, uint32_t b) { + llvm::LLVMContext ctx; + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *intrFn = llvm::Intrinsic::getDeclaration(mod, id, {i32Ty}); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {i32Ty, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateCall(intrFn, {fn->getArg(0), fn->getArg(1)})); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction( + *s, fn, {klee::ConstantExpr::create(a, Expr::Int32), klee::ConstantExpr::create(b, Expr::Int32)}); + return dyn_cast(r)->getZExtValue(); +} + +/// Build `i32 f(i32, i32, i32) { ret llvm..i32(a, b, amt) }` for fshl/fshr and run it. +static uint64_t evalFunnel(llvm::Intrinsic::ID id, uint32_t a, uint32_t b, uint32_t amt) { + llvm::LLVMContext ctx; + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *intrFn = llvm::Intrinsic::getDeclaration(mod, id, {i32Ty}); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(i32Ty, {i32Ty, i32Ty, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateCall(intrFn, {fn->getArg(0), fn->getArg(1), fn->getArg(2)})); + ExecutionStatePtr s = executor.createState(); + auto r = + executor.runFunction(*s, fn, + {klee::ConstantExpr::create(a, Expr::Int32), klee::ConstantExpr::create(b, Expr::Int32), + klee::ConstantExpr::create(amt, Expr::Int32)}); + return dyn_cast(r)->getZExtValue(); +} + +/// Build `fN f(fN) { ret llvm.fabs.fN(arg) }` and run it with the given bit pattern. +/// width must be 32 (float) or 64 (double). +static uint64_t evalFabs(unsigned width, uint64_t bits) { + llvm::LLVMContext ctx; + llvm::Type *fTy = width == 32 ? llvm::Type::getFloatTy(ctx) : llvm::Type::getDoubleTy(ctx); + TestExecutor executor(ctx); + auto *mod = executor.getLLVMModule(); + auto *intrFn = llvm::Intrinsic::getDeclaration(mod, llvm::Intrinsic::fabs, {fTy}); + auto *fn = + llvm::Function::Create(llvm::FunctionType::get(fTy, {fTy}, false), llvm::Function::ExternalLinkage, "f", mod); + llvm::IRBuilder<> b(llvm::BasicBlock::Create(ctx, "entry", fn)); + b.CreateRet(b.CreateCall(intrFn, {fn->getArg(0)})); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction(*s, fn, {klee::ConstantExpr::create(bits, width)}); + return dyn_cast(r)->getZExtValue(); +} + +// fabs f32: negative → positive; positive unchanged; -0.0 → 0.0. +TEST(ExecutorTest, IntrinsicFabsF32) { + EXPECT_EQ(evalFabs(32, 0xC0400000u), 0x40400000u); // -3.0f → 3.0f + EXPECT_EQ(evalFabs(32, 0x40400000u), 0x40400000u); // 3.0f unchanged + EXPECT_EQ(evalFabs(32, 0x80000000u), 0x00000000u); // -0.0f → 0.0f +} + +// fabs f64: same semantics as f32 but 64-bit. +TEST(ExecutorTest, IntrinsicFabsF64) { + EXPECT_EQ(evalFabs(64, 0xBFF0000000000000ULL), 0x3FF0000000000000ULL); // -1.0 → 1.0 + EXPECT_EQ(evalFabs(64, 0x3FF0000000000000ULL), 0x3FF0000000000000ULL); // 1.0 unchanged + EXPECT_EQ(evalFabs(64, 0x8000000000000000ULL), 0x0000000000000000ULL); // -0.0 → 0.0 +} + +// abs i32: positive unchanged; negative flipped; INT_MIN with poison=false stays as INT_MIN. +TEST(ExecutorTest, IntrinsicAbs) { + EXPECT_EQ(evalAbs(5, false), 5u); + EXPECT_EQ(evalAbs((uint32_t) -3, false), 3u); + EXPECT_EQ(evalAbs(0, false), 0u); + // abs(INT_MIN, poison=false): notsmin=false → cond=false → result = op = INT_MIN + EXPECT_EQ(evalAbs(0x80000000u, false), 0x80000000u); +} + +// smax: signed greatest of two i32s (-1 = 0xFFFFFFFF is less than 1 in signed). +TEST(ExecutorTest, IntrinsicSmax) { + EXPECT_EQ(evalMinMax(Intrinsic::smax, 5, 3), 5u); + EXPECT_EQ(evalMinMax(Intrinsic::smax, 3, 5), 5u); + EXPECT_EQ(evalMinMax(Intrinsic::smax, 5, 5), 5u); + EXPECT_EQ(evalMinMax(Intrinsic::smax, 1, 0xFFFFFFFFu), 1u); // 1 >s -1 + EXPECT_EQ(evalMinMax(Intrinsic::smax, 0xFFFFFFFFu, 1), 1u); +} + +// smin: signed least of two i32s. +TEST(ExecutorTest, IntrinsicSmin) { + EXPECT_EQ(evalMinMax(Intrinsic::smin, 5, 3), 3u); + EXPECT_EQ(evalMinMax(Intrinsic::smin, 3, 5), 3u); + EXPECT_EQ(evalMinMax(Intrinsic::smin, 5, 5), 5u); + EXPECT_EQ(evalMinMax(Intrinsic::smin, 0xFFFFFFFFu, 1), 0xFFFFFFFFu); // -1 u 1, opposite of signed). +TEST(ExecutorTest, IntrinsicUmax) { + EXPECT_EQ(evalMinMax(Intrinsic::umax, 5, 3), 5u); + EXPECT_EQ(evalMinMax(Intrinsic::umax, 3, 5), 5u); + EXPECT_EQ(evalMinMax(Intrinsic::umax, 5, 5), 5u); + EXPECT_EQ(evalMinMax(Intrinsic::umax, 0xFFFFFFFFu, 1), 0xFFFFFFFFu); // 0xFFFF… >u 1 + EXPECT_EQ(evalMinMax(Intrinsic::umax, 1, 0xFFFFFFFFu), 0xFFFFFFFFu); +} + +// umin: unsigned least. +TEST(ExecutorTest, IntrinsicUmin) { + EXPECT_EQ(evalMinMax(Intrinsic::umin, 5, 3), 3u); + EXPECT_EQ(evalMinMax(Intrinsic::umin, 3, 5), 3u); + EXPECT_EQ(evalMinMax(Intrinsic::umin, 5, 5), 5u); + EXPECT_EQ(evalMinMax(Intrinsic::umin, 0xFFFFFFFFu, 1), 1u); // 1 >8 → lo32=0x78ABCDEF + EXPECT_EQ(evalFunnel(Intrinsic::fshr, 0x12345678u, 0xABCDEF00u, 8), 0x78ABCDEFu); + // shift by 0: lo32 = b (unchanged) + EXPECT_EQ(evalFunnel(Intrinsic::fshr, 0xAAAAAAAAu, 0xBBBBBBBBu, 0), 0xBBBBBBBBu); + // shift by 32 ≡ 0 mod 32: same as shift by 0 + EXPECT_EQ(evalFunnel(Intrinsic::fshr, 0xAAAAAAAAu, 0xBBBBBBBBu, 32), 0xBBBBBBBBu); + // shift by 1: low bit of a feeds into high bit of result + // concat=0x00000001_00000000, >>1 → 0x00000000_80000000, lo32=0x80000000 + EXPECT_EQ(evalFunnel(Intrinsic::fshr, 0x00000001u, 0x00000000u, 1), 0x80000000u); +} + +// ============================================================ +// ICmp tests +// +// Signed-comparison cases use -1 (= 0xFFFFFFFF) to verify that signed +// and unsigned orderings diverge: 0xFFFFFFFF >u 1 but -1 %a, %b) }`, execute it +/// with concrete operands, and return the 1-bit result as 0 or 1. +static uint64_t evalICmp(llvm::CmpInst::Predicate pred, uint32_t a, uint32_t b) { + llvm::LLVMContext ctx; + auto *i32Ty = llvm::Type::getInt32Ty(ctx); + TestExecutor executor(ctx); + auto *fn = llvm::Function::Create(llvm::FunctionType::get(llvm::Type::getInt1Ty(ctx), {i32Ty, i32Ty}, false), + llvm::Function::ExternalLinkage, "f", executor.getLLVMModule()); + llvm::IRBuilder<> builder(llvm::BasicBlock::Create(ctx, "entry", fn)); + builder.CreateRet(builder.CreateICmp(pred, fn->getArg(0), fn->getArg(1))); + ExecutionStatePtr s = executor.createState(); + auto r = executor.runFunction( + *s, fn, {klee::ConstantExpr::create(a, Expr::Int32), klee::ConstantExpr::create(b, Expr::Int32)}); + return dyn_cast(r)->getZExtValue(); +} + +TEST(ExecutorTest, ICmpEQ) { + EXPECT_EQ(evalICmp(ICmpInst::ICMP_EQ, 5, 5), 1u); // equal → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_EQ, 5, 3), 0u); // unequal → false +} + +TEST(ExecutorTest, ICmpNE) { + EXPECT_EQ(evalICmp(ICmpInst::ICMP_NE, 5, 3), 1u); // unequal → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_NE, 5, 5), 0u); // equal → false +} + +TEST(ExecutorTest, ICmpUGT) { + EXPECT_EQ(evalICmp(ICmpInst::ICMP_UGT, 5, 3), 1u); // 5 >u 3 → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_UGT, 3, 5), 0u); // 3 >u 5 → false + EXPECT_EQ(evalICmp(ICmpInst::ICMP_UGT, 5, 5), 0u); // equal → false + EXPECT_EQ(evalICmp(ICmpInst::ICMP_UGT, 0xFFFFFFFFu, 1), 1u); // 0xFFFF… >u 1 → true (differs from signed) +} + +TEST(ExecutorTest, ICmpUGE) { + EXPECT_EQ(evalICmp(ICmpInst::ICMP_UGE, 5, 3), 1u); // 5 >=u 3 → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_UGE, 5, 5), 1u); // equal → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_UGE, 3, 5), 0u); // 3 >=u 5 → false +} + +TEST(ExecutorTest, ICmpULT) { + EXPECT_EQ(evalICmp(ICmpInst::ICMP_ULT, 3, 5), 1u); // 3 s 3 → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGT, 3, 5), 0u); // 3 >s 5 → false + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGT, 5, 5), 0u); // equal → false + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGT, 1, 0xFFFFFFFFu), 1u); // 1 >s -1 → true (differs from unsigned) + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGT, 0xFFFFFFFFu, 1), 0u); // -1 >s 1 → false +} + +TEST(ExecutorTest, ICmpSGE) { + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGE, 5, 3), 1u); // 5 >=s 3 → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGE, 5, 5), 1u); // equal → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGE, 3, 5), 0u); // 3 >=s 5 → false + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGE, 1, 0xFFFFFFFFu), 1u); // 1 >=s -1 → true + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGE, 0xFFFFFFFFu, 0xFFFFFFFFu), 1u); // -1 >=s -1 → true (equal) + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SGE, 0xFFFFFFFFu, 1), 0u); // -1 >=s 1 → false +} + +TEST(ExecutorTest, ICmpSLT) { + EXPECT_EQ(evalICmp(ICmpInst::ICMP_SLT, 3, 5), 1u); // 3 n; + if (n < 0) + n = 0; + if (n > 8) + n = 8; + + int sum = 0; + int min_val = 0x7fffffff; + int max_val = -0x7fffffff - 1; + unsigned xor_all = 0u; + unsigned and_masked = in->flags; + int alternating = 0; + int dot_self = 0; + + /// Main loop: exercises phi nodes, GEP with variable index, select. + for (int i = 0; i < n; i++) { + int v = in->values[i]; + + sum += v; + + /// min/max via ternary (select instruction). + min_val = v < min_val ? v : min_val; + max_val = v > max_val ? v : max_val; + + /// Bitwise: XOR accumulator, AND with flags. + xor_all ^= (unsigned) v; + and_masked &= (unsigned) v; + + /// Alternating sum: even indices add, odd indices subtract. + /// Uses bitwise AND to test parity, then select. + int sign = (i & 1) ? -1 : 1; + alternating += sign * v; + + /// Dot product with itself: exercises MUL. + dot_self += v * v; + } + + /// Reset min/max to zero for empty input. + if (n == 0) { + min_val = 0; + max_val = 0; + } + + /// Shift-based post-processing: exercises SHL and ASHR. + int shifted_sum = sum << 2; /// sum * 4 + int recovered = shifted_sum >> 2; /// should equal sum (arithmetic shift) + + /// Mix shifted results back in to keep the compiler from optimising out. + out->sum = recovered; + out->min_val = min_val; + out->max_val = max_val; + out->xor_all = xor_all; + out->and_masked = and_masked; + out->alternating = alternating; + out->dot_self = dot_self; +} diff --git a/klee/unittests/Core/LLVM/function.c b/klee/unittests/Core/LLVM/function.c new file mode 100644 index 00000000..a72821c6 --- /dev/null +++ b/klee/unittests/Core/LLVM/function.c @@ -0,0 +1,62 @@ +/// +/// Functions exercising: function calls, returns, switch statements, +/// integer truncation, pointer-integer conversions, and function pointers. +/// +/// No standard-library headers are included; unsigned long is used as a +/// pointer-sized integer (64-bit on Linux x86-64). +/// + +/// Helper called by call_chain – exercises Instruction::Call internally. +static int double_it(int x) { + return x * 2; +} + +/// Calls double_it and adds an offset. +/// Exercises Instruction::Call and the matching Instruction::Ret paths. +int call_chain(int x) { + return double_it(x) + 1; +} + +/// Multi-way branch – exercises Instruction::Switch. +/// Returns 10/20/30 for cases 0/1/2 and -1 for the default arm. +int classify(int x) { + switch (x) { + case 0: + return 10; + case 1: + return 20; + case 2: + return 30; + default: + return -1; + } +} + +/// Truncate i32 → i8 – exercises Instruction::Trunc. +unsigned char trunc_to_u8(unsigned int x) { + return (unsigned char) x; +} + +/// Truncate i32 → i16 – exercises Instruction::Trunc. +unsigned short trunc_to_u16(unsigned int x) { + return (unsigned short) x; +} + +/// Convert pointer to integer – exercises Instruction::PtrToInt. +unsigned long ptr_to_int(void *p) { + return (unsigned long) p; +} + +/// Convert integer to pointer – exercises Instruction::IntToPtr. +void *int_to_ptr(unsigned long x) { + return (void *) x; +} + +/// Call fn(x) through a function pointer – exercises an indirect +/// Instruction::Call where the callee is a non-constant SSA value. +/// +/// The caller must supply a concrete (non-symbolic) function pointer so +/// that the executor can resolve the callee at interpretation time. +int apply(int (*fn)(int), int x) { + return fn(x); +} diff --git a/klee/unittests/Expr/CMakeLists.txt b/klee/unittests/Expr/CMakeLists.txt index 4c4c84b3..75c88591 100644 --- a/klee/unittests/Expr/CMakeLists.txt +++ b/klee/unittests/Expr/CMakeLists.txt @@ -1,5 +1,5 @@ add_klee_unit_test(ExprTest ExprTest.cpp BitfieldSimplifier.cpp) -llvm_map_components_to_libnames(LLVM_LIBS core support transformutils scalaropts codegen) +llvm_map_components_to_libnames(LLVM_LIBS core passes support transformutils scalaropts codegen) target_link_libraries(ExprTest PRIVATE kleeCore kleaverSolver kleaverExpr kleeModule kleeSupport kleeBasic ${LLVM_LIBS}) From 5ea14b5f568cfe70030979ae3d9d7dafce10a645 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 17:35:38 +0200 Subject: [PATCH 08/24] claude.md: updates Signed-off-by: Vitaly Chipounov --- CLAUDE.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9759dd20..3f63d2d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,13 +29,16 @@ S2E is a symbolic execution platform. This repository builds `libs2e.so`, a shar - Use C++20 in all components. - **Header guards**: `#ifndef S2E_*_H` / `#define` / `#endif` - Use `///` for license headers +- Use braces for all if/for/while, etc. blocks, even when there is only a single instructions. +- **Member variables**: + - `m_` prefix + `snake_case` (e.g., `m_devices`) + - Class member variables should go at the beginning of the class. +- **Namespaces**: lowercase (e.g., `s2e::kvm`) +- **Class names**: `PascalCase` (e.g., `VirtualDeviceManager`) ## Code Style for `libs2e/*` - -- **Class names**: `PascalCase` (e.g., `VirtualDeviceManager`) - **Method/function names**: `snake_case` (e.g., `mmio_read`, `find_device`) -- **Member variables**: `m_` prefix + `snake_case` (e.g., `m_devices`) -- **Namespaces**: lowercase (e.g., `s2e::kvm`) + ## Commit Messages From 0a519e1157c8bca432492be4fe589bbdc91e9e0c Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 17:36:00 +0200 Subject: [PATCH 09/24] scripts: updated coverage script Signed-off-by: Vitaly Chipounov --- klee/unittests/coverage.sh | 10 ---------- scripts/coverage.sh | 24 +++++++++++++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) delete mode 100755 klee/unittests/coverage.sh diff --git a/klee/unittests/coverage.sh b/klee/unittests/coverage.sh deleted file mode 100755 index be835172..00000000 --- a/klee/unittests/coverage.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -set -x - -LLVM_BIN=~/s2e/env/build/llvm-release/bin - -$1 - -"$LLVM_BIN/llvm-profdata" merge -sparse default.profraw -o default.profdata -"$LLVM_BIN/llvm-cov" show $1 --instr-profile=default.profdata -format=html -output-dir=coverage diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 45af93ab..51317d9a 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -22,17 +22,27 @@ set -e -if [ -z "${LLVM_BIN}" ]; then - echo "Usage: LLVM_BIN=/path/to/llvm/bin ${0}" +if [ $# -eq 0 ]; then + echo "Usage: [LLVM_BIN=/path/to/llvm/bin] [LLVM_PROFDATA=llvm-profdata-19] [LLVM_COV=llvm-cov-19] ${0} [args...]" echo "" - echo " LLVM_BIN - Path to the LLVM's binaries" + echo " Defaults to llvm-profdata-19 and llvm-cov-19." + echo " Set LLVM_BIN to override both tools' directory (uses unversioned names)." + echo " Set LLVM_PROFDATA or LLVM_COV individually to override specific tools." + echo "" + echo " The binary must be compiled with: -fprofile-instr-generate -fcoverage-mapping" exit 1 fi -# Compile your binary with -fprofile-instr-generate -fcoverage-mapping +LLVM_PROFDATA="${LLVM_PROFDATA:-llvm-profdata-19}" +LLVM_COV="${LLVM_COV:-llvm-cov-19}" + +if [ -n "${LLVM_BIN}" ]; then + LLVM_PROFDATA="${LLVM_BIN}/llvm-profdata" + LLVM_COV="${LLVM_BIN}/llvm-cov" +fi LLVM_PROFILE_FILE="profile.profraw" $* -$LLVM_BIN/llvm-profdata merge -sparse profile.profraw -o profile.profdata -$LLVM_BIN/llvm-cov export --format=lcov $1 -instr-profile=profile.profdata > $1.coverage.lcov -genhtml -o $1.coverage $1.coverage.lcov +"${LLVM_PROFDATA}" merge -sparse profile.profraw -o profile.profdata +"${LLVM_COV}" export --format=lcov $1 -instr-profile=profile.profdata > $1.coverage.lcov +genhtml --ignore-errors unsupported,inconsistent,category -o $1.coverage $1.coverage.lcov From 044d02387e2b022c711ccc21a3f951fed2497b92 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 17:40:13 +0200 Subject: [PATCH 10/24] libtcg: updated for llvm 19 There is no more CPUX86State structure, LLVM doesn't pass types in pointers anymore. Signed-off-by: Vitaly Chipounov --- libtcg/include/tcg/tcg-llvm.h | 8 +- libtcg/src/tcg-llvm.cpp | 141 ++++++---------------------------- 2 files changed, 25 insertions(+), 124 deletions(-) diff --git a/libtcg/include/tcg/tcg-llvm.h b/libtcg/include/tcg/tcg-llvm.h index ed8b1d40..5a3de73f 100644 --- a/libtcg/include/tcg/tcg-llvm.h +++ b/libtcg/include/tcg/tcg-llvm.h @@ -104,10 +104,7 @@ class TCGLLVMTranslator { std::unordered_map m_labels; llvm::FunctionType *m_tbType; - llvm::Type *m_cpuType; llvm::Value *m_cpuState; - // Represents CPU state pointer cast to an int - llvm::Instruction *m_cpuStateInt; // This instruction is a no-op in the entry block, we use it // in order to simplify instruction insertion. @@ -116,7 +113,7 @@ class TCGLLVMTranslator { llvm::Value *m_ccop; llvm::BasicBlock *m_abortBB; - static unsigned m_eip_last_gep_index; + static uint64_t m_eip_byte_offset; typedef llvm::DenseMap, llvm::Instruction *> GepMap; GepMap m_registers; @@ -154,7 +151,7 @@ class TCGLLVMTranslator { return llvm::IntegerType::get(getContext(), w); } llvm::Type *intPtrType(int w) { - return llvm::PointerType::get(intType(w), 0); + return llvm::PointerType::getUnqual(getContext()); } llvm::Type *wordType() { return intType(TCG_TARGET_REG_BITS); @@ -221,7 +218,6 @@ class TCGLLVMTranslator { llvm::Function *generateCode(TCGContext *s, TranslationBlock *tb); void removeInterruptExit(); - bool getCpuFieldGepIndexes(unsigned offset, unsigned sizeInBytes, llvm::SmallVector &gepIndexes); static bool GetStaticBranchTarget(const llvm::BasicBlock *bb, uint64_t *target); }; diff --git a/libtcg/src/tcg-llvm.cpp b/libtcg/src/tcg-llvm.cpp index af13682c..5a1375c3 100644 --- a/libtcg/src/tcg-llvm.cpp +++ b/libtcg/src/tcg-llvm.cpp @@ -83,7 +83,7 @@ void *tcg_llvm_translator = 0; using namespace llvm; -unsigned TCGLLVMTranslator::m_eip_last_gep_index = 0; +uint64_t TCGLLVMTranslator::m_eip_byte_offset = 0; TCGLLVMTranslator::TCGLLVMTranslator(const std::string &bitcodeLibraryPath, std::unique_ptr module) : m_bitcodeLibraryPath(bitcodeLibraryPath), m_module(std::move(module)), m_builder(m_module->getContext()), @@ -92,7 +92,6 @@ TCGLLVMTranslator::TCGLLVMTranslator(const std::string &bitcodeLibraryPath, std: std::memset(m_memValuesPtr, 0, sizeof(m_memValuesPtr)); std::memset(m_globalsIdx, 0, sizeof(m_globalsIdx)); - m_cpuType = NULL; m_cpuState = NULL; m_eip = NULL; m_ccop = NULL; @@ -137,7 +136,7 @@ llvm::FunctionType *TCGLLVMTranslator::tbType() { return m_tbType; } llvm::SmallVector args; - args.push_back(m_cpuType->getPointerTo()); + args.push_back(llvm::PointerType::getUnqual(getContext())); m_tbType = llvm::FunctionType::get(wordType(), args, false); return m_tbType; } @@ -186,9 +185,6 @@ uint64_t TCGLLVMTranslator::toInteger(llvm::Value *v) const { #ifdef CONFIG_SYMBEX void TCGLLVMTranslator::initializeNativeCpuState() { - auto &ctx = m_module->getContext(); - m_cpuType = StructType::getTypeByName(ctx, "struct.CPUX86State"); - assert(m_cpuType && "Could not find CPUX86State in LLVM bitcode"); } void TCGLLVMTranslator::initializeHelpers() { @@ -239,7 +235,7 @@ Value *TCGLLVMTranslator::getPtrForValue(int idx) { assert(idx == 0); // Assume we access CPUState Value *v = &*m_tbFunction->arg_begin(); - m_memValuesPtr[idx] = m_builder.CreatePointerCast(v, tcgPtrType(temp.type), StringRef(temp.name) + "_ptr"); + m_memValuesPtr[idx] = v; } else { m_memValuesPtr[idx] = generateCpuStatePtr(temp.mem_offset, tcgType(temp.type)->getScalarSizeInBits() / 8); } @@ -283,8 +279,7 @@ Value *TCGLLVMTranslator::getValue(TCGArg arg) { case TEMP_GLOBAL: { assert(idx < m_tcgContext->nb_globals); auto ptr = getPtrForValue(idx); - m_values[idx] = - m_builder.CreateLoad(ptr->getType()->getPointerElementType(), ptr, StringRef(temp.name) + "_v"); + m_values[idx] = m_builder.CreateLoad(tcgType(temp.type), ptr, StringRef(temp.name) + "_v"); } break; case TEMP_FIXED: { @@ -295,7 +290,7 @@ Value *TCGLLVMTranslator::getValue(TCGArg arg) { case TEMP_TB: { auto ptr = getPtrForValue(idx); - m_values[idx] = m_builder.CreateLoad(ptr->getType()->getPointerElementType(), ptr); + m_values[idx] = m_builder.CreateLoad(tcgType(temp.type), ptr); std::ostringstream name; name << "loc" << (idx - m_tcgContext->nb_globals) << "_v"; m_values[idx]->setName(name.str()); @@ -413,7 +408,6 @@ void TCGLLVMTranslator::initGlobalsAndLocalTemps() { void TCGLLVMTranslator::loadNativeCpuState(Function *f) { m_cpuState = &*(f->arg_begin()); - m_cpuStateInt = dyn_cast(m_builder.CreatePtrToInt(m_cpuState, wordType())); auto ci = ConstantInt::get(wordType(), 0); auto add = BinaryOperator::Create(Instruction::Add, ci, ci, ""); @@ -422,14 +416,8 @@ void TCGLLVMTranslator::loadNativeCpuState(Function *f) { m_eip = generateCpuStatePtr(m_tcgContext->env_offset_eip, m_tcgContext->env_sizeof_eip); m_ccop = generateCpuStatePtr(m_tcgContext->env_offset_ccop, m_tcgContext->env_sizeof_ccop); - if (m_eip_last_gep_index == 0) { - SmallVector gepElements; - bool ok = getCpuFieldGepIndexes(m_tcgContext->env_offset_eip, sizeof(target_ulong), gepElements); - if (!ok) { - abort(); - } - - m_eip_last_gep_index = (unsigned) dyn_cast(gepElements.back())->getZExtValue(); + if (m_eip_byte_offset == 0) { + m_eip_byte_offset = m_tcgContext->env_offset_eip; } } @@ -459,7 +447,7 @@ void TCGLLVMTranslator::startNewBasicBlock(BasicBlock *bb) { m_builder.CreateBr(bb); } - m_tbFunction->getBasicBlockList().push_back(bb); + bb->insertInto(m_tbFunction); m_builder.SetInsertPoint(bb); /* Invalidate all temps */ @@ -477,44 +465,18 @@ void TCGLLVMTranslator::startNewBasicBlock(BasicBlock *bb) { } Value *TCGLLVMTranslator::generateCpuStatePtr(uint64_t registerOffset, unsigned sizeInBytes) { - SmallVector gepElements; - Instruction *ret = nullptr; auto regsz = std::make_pair(registerOffset, sizeInBytes); - // XXX: assumes x86 - static unsigned TARGET_LONG_BYTES = TARGET_LONG_BITS / 8; - - if ((registerOffset % (TARGET_LONG_BITS / 8)) == 0) { - auto &instList = m_tbFunction->begin()->getInstList(); - auto it = m_registers.find(regsz); - - if (it != m_registers.end()) { - return (*it).second; - } else { - bool ok = getCpuFieldGepIndexes(registerOffset, sizeInBytes, gepElements); - if (ok) { - ret = GetElementPtrInst::Create(m_cpuState->getType()->getPointerElementType(), m_cpuState, - ArrayRef(gepElements.begin(), gepElements.end())); - instList.push_front(ret); - m_registers[regsz] = ret; - } - } - } - - if (ret && sizeInBytes < TARGET_LONG_BYTES) { - auto ty = intPtrType(sizeInBytes * 8); - ret = CastInst::CreatePointerCast(ret, ty, "", ret->getNextNode()); - m_registers[regsz] = ret; - return ret; + auto it = m_registers.find(regsz); + if (it != m_registers.end()) { + return it->second; } - if (!ret) { - // If gep fails, fallback to pointer arithmetic - auto ci = ConstantInt::get(wordType(), registerOffset); - auto add = BinaryOperator::Create(Instruction::Add, m_cpuStateInt, ci, "", m_noop); - ret = CastInst::CreateBitOrPointerCast(add, intPtrType(sizeInBytes * 8), "", m_noop); - m_registers[regsz] = ret; - } + auto i8Ty = Type::getInt8Ty(getContext()); + auto offsetVal = ConstantInt::get(wordType(), registerOffset); + Instruction *ret = GetElementPtrInst::Create(i8Ty, m_cpuState, {offsetVal}); + ret->insertBefore(m_noop); + m_registers[regsz] = ret; return ret; } @@ -525,7 +487,7 @@ void TCGLLVMTranslator::generateQemuCpuLoad(const TCGArg *args, unsigned memBits Value *gep = generateCpuStatePtr(args[2], memBits / 8); Value *v; - v = m_builder.CreateLoad(gep->getType()->getPointerElementType(), gep); + v = m_builder.CreateLoad(intType(memBits), gep); v = m_builder.CreateTrunc(v, intType(memBits)); if (signExtend) { @@ -544,11 +506,7 @@ void TCGLLVMTranslator::generateQemuCpuStore(const TCGArg *args, unsigned memBit } Value *gep = generateCpuStatePtr(offset, memBits / 8); - Value *v = NULL; - - v = m_builder.CreatePointerCast(gep, intType(memBits)->getPointerTo()); - - m_builder.CreateStore(m_builder.CreateTrunc(valueToStore, intType(memBits)), v); + m_builder.CreateStore(m_builder.CreateTrunc(valueToStore, intType(memBits)), gep); } Value *TCGLLVMTranslator::generateQemuMemOp(bool ld, Value *value, Value *addr, int mem_index, int bits) { @@ -656,7 +614,7 @@ int TCGLLVMTranslator::generateOperation(const TCGOp *op) { sys::DynamicLibrary::AddSymbol(funcName, (void *) helperAddress); } - FunctionType *FTy = cast(cast(helperFunc->getType())->getPointerElementType()); + FunctionType *FTy = helperFunc->getFunctionType(); /** * Cast arguments to target function type. @@ -1155,8 +1113,7 @@ void TCGLLVMTranslator::removeInterruptExit() { auto br = dyn_cast(bb.getTerminator()); auto target = br->getSuccessor(1); br->eraseFromParent(); - auto newBr = BranchInst::Create(target); - bb.getInstList().push_back(newBr); + BranchInst::Create(target, &bb); } Function *TCGLLVMTranslator::generateCode(TCGContext *s, TranslationBlock *tb) { @@ -1301,52 +1258,6 @@ Function *TCGLLVMTranslator::generateCode(TCGContext *s, TranslationBlock *tb) { return m_tbFunction; } -bool TCGLLVMTranslator::getCpuFieldGepIndexes(unsigned offset, unsigned sizeInBytes, - SmallVector &gepIndexes) { - - Type *curType = m_cpuType; - auto &dataLayout = m_module->getDataLayout(); - auto I32Ty = Type::getInt32Ty(m_module->getContext()); - - auto coffset = offset; - gepIndexes.push_back(ConstantInt::get(I32Ty, 0)); - - do { - bool compositeType = false; - - if (curType->isStructTy()) { - compositeType = true; - StructType *curStructTy = dyn_cast(curType); - const StructLayout *curStructLayout = dataLayout.getStructLayout(curStructTy); - - auto curIdx = curStructLayout->getElementContainingOffset(coffset); - - gepIndexes.push_back(ConstantInt::get(I32Ty, curIdx)); - curType = curStructTy->getTypeAtIndex(curIdx); - coffset -= curStructLayout->getElementOffset(curIdx); - } else if (curType->isArrayTy()) { - compositeType = true; - ArrayType *curArrayTy = dyn_cast(curType); - auto elemSize = dataLayout.getTypeAllocSize(curArrayTy->getElementType()); - auto curIdx = coffset / elemSize; - assert(curIdx < curArrayTy->getNumElements() && "Illegal field offset into CPUState!"); - - gepIndexes.push_back(ConstantInt::get(I32Ty, curIdx)); - coffset %= elemSize; - curType = curArrayTy->getElementType(); - } - - if (!compositeType) { - // Offset may point in the middle of a structure/union, make sure - // that the element size matches the requested size. - auto typeSz = dataLayout.getTypeAllocSize(curType); - return coffset == 0 && typeSz == sizeInBytes; - } - } while (true); - - return false; -} - bool TCGLLVMTranslator::GetStaticBranchTarget(const llvm::BasicBlock *BB, uint64_t *target) { if (!isa(BB->getTerminator())) { return false; @@ -1369,18 +1280,12 @@ bool TCGLLVMTranslator::GetStaticBranchTarget(const llvm::BasicBlock *BB, uint64 continue; } - if (gep->getNumOperands() != 3) { - continue; - } - - const llvm::ConstantInt *go1 = dyn_cast(gep->getOperand(1)); - const llvm::ConstantInt *go2 = dyn_cast(gep->getOperand(2)); - if (!go1 || !go2) { + if (gep->getNumOperands() != 2) { continue; } - // XXX: hard-coded pc index - if (!go1->isZero() || go2->getZExtValue() != TCGLLVMTranslator::m_eip_last_gep_index) { + const llvm::ConstantInt *offset = dyn_cast(gep->getOperand(1)); + if (!offset || offset->getZExtValue() != TCGLLVMTranslator::m_eip_byte_offset) { continue; } From a00616f488535077601ce4bf29cbd67d22f381a9 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 17:40:59 +0200 Subject: [PATCH 11/24] klee: upgraded to llvm 19 Signed-off-by: Vitaly Chipounov --- klee/include/klee/Expr.h | 2 +- .../klee/Internal/Module/KInstruction.h | 18 ++- klee/include/klee/Internal/Module/KModule.h | 11 +- .../klee/util/GetElementPtrTypeIterator.h | 151 ------------------ klee/lib/Core/Executor.cpp | 29 ++-- klee/lib/Expr/BitfieldSimplifier.cpp | 36 ++--- klee/lib/Module/CMakeLists.txt | 4 + klee/lib/Module/IntrinsicCleaner.cpp | 19 ++- klee/lib/Module/KModule.cpp | 133 +++++++++------ klee/lib/Module/LowerSwitch.cpp | 9 +- 10 files changed, 149 insertions(+), 263 deletions(-) delete mode 100644 klee/include/klee/util/GetElementPtrTypeIterator.h diff --git a/klee/include/klee/Expr.h b/klee/include/klee/Expr.h index 1503f587..5a24d2a1 100644 --- a/klee/include/klee/Expr.h +++ b/klee/include/klee/Expr.h @@ -508,7 +508,7 @@ class ConstantExpr : public Expr { /// isAllOnes - Is this constant all ones. bool isAllOnes() const { - return getAPValue().isAllOnesValue(); + return getAPValue().isAllOnes(); } /* Constant Operations */ diff --git a/klee/include/klee/Internal/Module/KInstruction.h b/klee/include/klee/Internal/Module/KInstruction.h index b5d05aba..5cc27f6e 100644 --- a/klee/include/klee/Internal/Module/KInstruction.h +++ b/klee/include/klee/Internal/Module/KInstruction.h @@ -44,13 +44,21 @@ struct KInstruction { struct KGEPInstruction : KInstruction { /// indices - The list of variable sized adjustments to add to the pointer - /// operand to execute the instruction. The first element is the operand - /// index into the GetElementPtr instruction, and the second element is the - /// element size to multiple that index by. + /// operand to execute the getelementptr instruction. The first element is + /// the operand index and the second is the element size to multiply by. std::vector> indices; - /// offset - A constant offset to add to the pointer operand to execute the - /// insturction. + /// offset - A constant byte offset to add to the pointer operand. + uint64_t offset; +}; + +struct KInsertValueInstruction : KInstruction { + /// offset - Constant byte offset of the insertion point within the aggregate. + uint64_t offset; +}; + +struct KExtractValueInstruction : KInstruction { + /// offset - Constant byte offset of the extracted element within the aggregate. uint64_t offset; }; diff --git a/klee/include/klee/Internal/Module/KModule.h b/klee/include/klee/Internal/Module/KModule.h index 4681c9e8..2adec9ca 100644 --- a/klee/include/klee/Internal/Module/KModule.h +++ b/klee/include/klee/Internal/Module/KModule.h @@ -38,6 +38,8 @@ class Executor; class Expr; struct KInstruction; struct KGEPInstruction; +struct KInsertValueInstruction; +struct KExtractValueInstruction; class KModule; template class ref; @@ -119,12 +121,9 @@ class KModule { KModule() { } - template - void computeOffsetsSeqTy(const GlobalAddresses &globalAddresses, KGEPInstruction *kgepi, - ref &constantOffset, uint64_t index, const TypeIt it); - - template - void computeOffsets(const GlobalAddresses &globalAddresses, KGEPInstruction *kgepi, TypeIt ib, TypeIt ie); + void bindGEPInstructionConstants(KGEPInstruction *kgepi); + void bindInsertValueConstants(KInsertValueInstruction *kivi); + void bindExtractValueConstants(KExtractValueInstruction *kevi); /// bindInstructionConstants - Initialize any necessary per instruction /// constant values. diff --git a/klee/include/klee/util/GetElementPtrTypeIterator.h b/klee/include/klee/util/GetElementPtrTypeIterator.h deleted file mode 100644 index 8c2b0d4c..00000000 --- a/klee/include/klee/util/GetElementPtrTypeIterator.h +++ /dev/null @@ -1,151 +0,0 @@ -//===-- GetElementPtrTypeIterator.h -----------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements an iterator for walking through the types indexed by -// getelementptr, insertvalue and extractvalue instructions. -// -// It is an enhanced version of llvm::gep_type_iterator which only handles -// getelementptr. -// -//===----------------------------------------------------------------------===// - -#ifndef KLEE_GETELEMENTPTRTYPEITERATOR_H -#define KLEE_GETELEMENTPTRTYPEITERATOR_H - -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/User.h" - -namespace klee { -template class generic_gep_type_iterator { - using iterator_category = std::forward_iterator_tag; - using value_type = llvm::Type *; - using difference_type = ptrdiff_t; - - ItTy OpIt; - llvm::Type *CurTy; - generic_gep_type_iterator() { - } - - llvm::Value *asValue(llvm::Value *V) const { - return V; - } - llvm::Value *asValue(unsigned U) const { - return llvm::ConstantInt::get(CurTy->getContext(), llvm::APInt(32, U)); - } - -public: - static generic_gep_type_iterator begin(llvm::Type *Ty, ItTy It) { - generic_gep_type_iterator I; - I.CurTy = Ty; - I.OpIt = It; - return I; - } - static generic_gep_type_iterator end(ItTy It) { - generic_gep_type_iterator I; - I.CurTy = 0; - I.OpIt = It; - return I; - } - - bool operator==(const generic_gep_type_iterator &x) const { - return OpIt == x.OpIt; - } - bool operator!=(const generic_gep_type_iterator &x) const { - return !operator==(x); - } - - llvm::Type *operator*() const { - return CurTy; - } - - llvm::Type *getIndexedType() const { - return llvm::GetElementPtrInst::getTypeAtIndex(CurTy, getOperand()); - } - - // This is a non-standard operator->. It allows you to call methods on the - // current type directly. - llvm::Type *operator->() const { - return operator*(); - } - - llvm::Value *getOperand() const { - return asValue(*OpIt); - } - - generic_gep_type_iterator &operator++() { // Preincrement - if (isa(CurTy) || isa(CurTy) || isa(CurTy)) { - CurTy = llvm::GetElementPtrInst::getTypeAtIndex(CurTy, getOperand()); - - } else if (auto ptr = dyn_cast(CurTy)) { - CurTy = ptr->getPointerElementType(); - } else { - CurTy = 0; - } - ++OpIt; - return *this; - } - - generic_gep_type_iterator operator++(int) { // Postincrement - generic_gep_type_iterator tmp = *this; - ++*this; - return tmp; - } -}; - -typedef generic_gep_type_iterator<> gep_type_iterator; -typedef generic_gep_type_iterator ev_type_iterator; -typedef generic_gep_type_iterator iv_type_iterator; -typedef generic_gep_type_iterator::const_iterator> vce_type_iterator; - -inline gep_type_iterator gep_type_begin(const llvm::User *GEP) { - return gep_type_iterator::begin(GEP->getOperand(0)->getType(), GEP->op_begin() + 1); -} -inline gep_type_iterator gep_type_end(const llvm::User *GEP) { - return gep_type_iterator::end(GEP->op_end()); -} -inline gep_type_iterator gep_type_begin(const llvm::User &GEP) { - return gep_type_iterator::begin(GEP.getOperand(0)->getType(), GEP.op_begin() + 1); -} -inline gep_type_iterator gep_type_end(const llvm::User &GEP) { - return gep_type_iterator::end(GEP.op_end()); -} - -inline ev_type_iterator ev_type_begin(const llvm::ExtractValueInst *EV) { - return ev_type_iterator::begin(EV->getOperand(0)->getType(), EV->idx_begin()); -} -inline ev_type_iterator ev_type_end(const llvm::ExtractValueInst *EV) { - return ev_type_iterator::end(EV->idx_end()); -} - -inline iv_type_iterator iv_type_begin(const llvm::InsertValueInst *IV) { - return iv_type_iterator::begin(IV->getType(), IV->idx_begin()); -} -inline iv_type_iterator iv_type_end(const llvm::InsertValueInst *IV) { - return iv_type_iterator::end(IV->idx_end()); -} - -inline vce_type_iterator vce_type_begin(const llvm::ConstantExpr *CE) { - return vce_type_iterator::begin(CE->getOperand(0)->getType(), CE->getIndices().begin()); -} -inline vce_type_iterator vce_type_end(const llvm::ConstantExpr *CE) { - return vce_type_iterator::end(CE->getIndices().end()); -} - -template inline generic_gep_type_iterator gep_type_begin(llvm::Type *Op0, ItTy I, ItTy E) { - return generic_gep_type_iterator::begin(Op0, I); -} - -template inline generic_gep_type_iterator gep_type_end(llvm::Type *Op0, ItTy I, ItTy E) { - return generic_gep_type_iterator::end(E); -} -} // end namespace klee - -#endif /* KLEE_GETELEMENTPTRTYPEITERATOR_H */ diff --git a/klee/lib/Core/Executor.cpp b/klee/lib/Core/Executor.cpp index 3bb5c010..d9a60cdd 100644 --- a/klee/lib/Core/Executor.cpp +++ b/klee/lib/Core/Executor.cpp @@ -34,7 +34,6 @@ #include "klee/util/Assignment.h" #include "klee/util/ExprPPrinter.h" #include "klee/util/ExprUtil.h" -#include "klee/util/GetElementPtrTypeIterator.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/Attributes.h" @@ -63,7 +62,6 @@ #include -#include #include #include @@ -215,7 +213,7 @@ void Executor::initializeGlobals(ExecutionState &state) { // better we could support user definition, or use the EXE style // hack where we check the object file information. - Type *ty = i->getType()->getPointerElementType(); + Type *ty = i->getValueType(); uint64_t size = m_kmodule->getDataLayout()->getTypeStoreSize(ty); // XXX - DWD - hardcode some things until we decide how to fix. @@ -253,7 +251,7 @@ void Executor::initializeGlobals(ExecutionState &state) { } } } else { - Type *ty = i->getType()->getPointerElementType(); + Type *ty = i->getValueType(); uint64_t size = m_kmodule->getDataLayout()->getTypeStoreSize(ty); auto mo = ObjectState::allocate(0, size, false); @@ -735,10 +733,8 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (ce && ce->getOpcode() == Instruction::BitCast) { f = dyn_cast(ce->getOperand(0)); assert(f && "XXX unrecognized constant expression in call"); - const FunctionType *fType = - dyn_cast(cast(f->getType())->getPointerElementType()); - const FunctionType *ceType = - dyn_cast(cast(ce->getType())->getPointerElementType()); + const FunctionType *fType = f->getFunctionType(); + const FunctionType *ceType = f->getFunctionType(); check(fType && ceType, "unable to get function type"); // XXX check result coercion @@ -1055,16 +1051,14 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { KGEPInstruction *kgepi = static_cast(ki); ref base = eval(ki, 0, llvmState).value; - for (std::vector>::iterator it = kgepi->indices.begin(), - ie = kgepi->indices.end(); - it != ie; ++it) { - uint64_t elementSize = it->second; - ref index = eval(ki, it->first, llvmState).value; + for (auto &[operandIdx, elementSize] : kgepi->indices) { + ref index = eval(ki, operandIdx, llvmState).value; base = AddExpr::create( base, MulExpr::create(Expr::createCoerceToPointerType(index), Expr::createPointer(elementSize))); } if (kgepi->offset) base = AddExpr::create(base, Expr::createPointer(kgepi->offset)); + llvmState.bindLocal(ki, base); break; } @@ -1313,13 +1307,13 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { } case Instruction::InsertValue: { - KGEPInstruction *kgepi = static_cast(ki); + KInsertValueInstruction *kivi = static_cast(ki); ref agg = eval(ki, 0, llvmState).value; ref val = eval(ki, 1, llvmState).value; ref l = NULL, r = NULL; - unsigned lOffset = kgepi->offset * 8, rOffset = kgepi->offset * 8 + val->getWidth(); + unsigned lOffset = kivi->offset * 8, rOffset = kivi->offset * 8 + val->getWidth(); if (lOffset > 0) l = ExtractExpr::create(agg, 0, lOffset); @@ -1340,12 +1334,11 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { break; } case Instruction::ExtractValue: { - KGEPInstruction *kgepi = static_cast(ki); + KExtractValueInstruction *kevi = static_cast(ki); ref agg = eval(ki, 0, llvmState).value; - ref result = - ExtractExpr::create(agg, kgepi->offset * 8, m_kmodule->getWidthForLLVMType(i->getType())); + ref result = ExtractExpr::create(agg, kevi->offset * 8, m_kmodule->getWidthForLLVMType(i->getType())); llvmState.bindLocal(ki, result); break; diff --git a/klee/lib/Expr/BitfieldSimplifier.cpp b/klee/lib/Expr/BitfieldSimplifier.cpp index 0566618b..c689fc80 100644 --- a/klee/lib/Expr/BitfieldSimplifier.cpp +++ b/klee/lib/Expr/BitfieldSimplifier.cpp @@ -124,7 +124,7 @@ BitfieldSimplifier::ExprBitsInfo BitfieldSimplifier::doSimplifyBits(const refgetKid(i), APInt::getNullValue(e->getKid(i)->getWidth())); + ExprBitsInfo r = doSimplifyBits(e->getKid(i), APInt::getZero(e->getKid(i)->getWidth())); kids[i] = r.first; bits[i] = r.second; @@ -153,8 +153,8 @@ BitfieldSimplifier::ExprBitsInfo BitfieldSimplifier::doSimplifyBits(const refgetWidth()); - bits[i].knownZeroBits = APInt::getNullValue(e->getWidth()); + bits[i].knownOneBits = APInt::getAllOnes(e->getWidth()); + bits[i].knownZeroBits = APInt::getZero(e->getWidth()); } } @@ -171,8 +171,8 @@ BitfieldSimplifier::ExprBitsInfo BitfieldSimplifier::doSimplifyBits(const refgetWidth()); - bits[i].knownZeroBits = APInt::getAllOnesValue(e->getWidth()); + bits[i].knownOneBits = APInt::getZero(e->getWidth()); + bits[i].knownZeroBits = APInt::getAllOnes(e->getWidth()); } } @@ -216,14 +216,14 @@ BitfieldSimplifier::ExprBitsInfo BitfieldSimplifier::doSimplifyBits(const ref= the expression's width, the result is always 0 - rbits.knownOneBits = APInt::getNullValue(width); - rbits.knownZeroBits = APInt::getAllOnesValue(width); - bits[0].ignoredBits = APInt::getAllOnesValue(width); + rbits.knownOneBits = APInt::getZero(width); + rbits.knownZeroBits = APInt::getAllOnes(width); + bits[0].ignoredBits = APInt::getAllOnes(width); } } else { // This is the most general assumption - rbits.knownOneBits = APInt::getNullValue(width); - rbits.knownZeroBits = APInt::getNullValue(width); + rbits.knownOneBits = APInt::getZero(width); + rbits.knownZeroBits = APInt::getZero(width); } } break; @@ -246,14 +246,14 @@ BitfieldSimplifier::ExprBitsInfo BitfieldSimplifier::doSimplifyBits(const ref= the expression's width, the result is always 0 - rbits.knownOneBits = APInt::getNullValue(width); - rbits.knownZeroBits = APInt::getAllOnesValue(width); - bits[0].ignoredBits = APInt::getAllOnesValue(width); + rbits.knownOneBits = APInt::getZero(width); + rbits.knownZeroBits = APInt::getAllOnes(width); + bits[0].ignoredBits = APInt::getAllOnes(width); } } else { // This is the most general assumption - rbits.knownOneBits = APInt::getNullValue(width); - rbits.knownZeroBits = APInt::getNullValue(width); + rbits.knownOneBits = APInt::getZero(width); + rbits.knownZeroBits = APInt::getZero(width); } } break; @@ -340,8 +340,8 @@ BitfieldSimplifier::ExprBitsInfo BitfieldSimplifier::doSimplifyBits(const refgetWidth()); - rbits.knownZeroBits = APInt::getNullValue(e->getWidth()); + rbits.knownOneBits = APInt::getZero(e->getWidth()); + rbits.knownZeroBits = APInt::getZero(e->getWidth()); break; } @@ -417,7 +417,7 @@ ref BitfieldSimplifier::simplify(const ref &e, APInt *knownZeroBits) ++m_cacheMisses; - ExprBitsInfo ret = doSimplifyBits(e, APInt::getNullValue(e->getWidth())); + ExprBitsInfo ret = doSimplifyBits(e, APInt::getZero(e->getWidth())); m_simplifiedExpressions[e] = ret; diff --git a/klee/lib/Module/CMakeLists.txt b/klee/lib/Module/CMakeLists.txt index 328d324f..7da7831b 100644 --- a/klee/lib/Module/CMakeLists.txt +++ b/klee/lib/Module/CMakeLists.txt @@ -16,3 +16,7 @@ klee_add_component(kleeModule PhiCleaner.cpp RaiseAsm.cpp ) + +set(LLVM_COMPONENTS core passes support) +klee_get_llvm_libs(LLVM_LIBS ${LLVM_COMPONENTS}) +target_link_libraries(kleeModule PUBLIC ${LLVM_LIBS}) diff --git a/klee/lib/Module/IntrinsicCleaner.cpp b/klee/lib/Module/IntrinsicCleaner.cpp index e3108695..df1b566f 100644 --- a/klee/lib/Module/IntrinsicCleaner.cpp +++ b/klee/lib/Module/IntrinsicCleaner.cpp @@ -137,8 +137,8 @@ void IntrinsicCleanerPass::replaceIntrinsicAdd(Module &M, CallInst *CI) { // Store the values in the aggregated type Value *aggrValPtr = new AllocaInst(aggregate, as, NULL, "", CI); - Value *aggrVal = new LoadInst(aggrValPtr->getType()->getPointerElementType(), aggrValPtr, "", CI); - Value *addResult = new LoadInst(alloca->getType()->getPointerElementType(), alloca, "", CI); + Value *aggrVal = new LoadInst(aggregate, aggrValPtr, "", CI); + Value *addResult = new LoadInst(itype, alloca, "", CI); InsertValueInst *insRes = InsertValueInst::Create(aggrVal, addResult, 0, "", CI); InsertValueInst *insOverflow = InsertValueInst::Create(insRes, overflow, 1, "", CI); CI->replaceAllUsesWith(insOverflow); @@ -174,9 +174,9 @@ void IntrinsicCleanerPass::injectIntrinsicAddImplementation(Module &M, const std LLVMContext &ctx = M.getContext(); std::vector argTypes; - argTypes.push_back(Type::getIntNPtrTy(ctx, bits)); // Result - argTypes.push_back(Type::getIntNTy(ctx, bits)); // a - argTypes.push_back(Type::getIntNTy(ctx, bits)); // b + argTypes.push_back(PointerType::getUnqual(ctx)); // Result + argTypes.push_back(Type::getIntNTy(ctx, bits)); // a + argTypes.push_back(Type::getIntNTy(ctx, bits)); // b FunctionType *type = FunctionType::get(Type::getInt1Ty(ctx), ArrayRef(argTypes), false); f = dyn_cast(M.getOrInsertFunction(name, type).getCallee()); @@ -253,8 +253,7 @@ bool IntrinsicCleanerPass::runOnBasicBlock(BasicBlock &b, Module &M) { Type *i8pp = PointerType::getUnqual(PointerType::getUnqual(Type::getInt8Ty(ctx))); auto castedDst = Builder.CreatePointerCast(dst, i8pp, "vacopy.cast.dst"); auto castedSrc = Builder.CreatePointerCast(src, i8pp, "vacopy.cast.src"); - auto load = - Builder.CreateLoad(castedSrc->getType()->getPointerElementType(), castedSrc, "vacopy.read"); + auto load = Builder.CreateLoad(PointerType::getUnqual(ctx), castedSrc, "vacopy.read"); Builder.CreateStore(load, castedDst, false /* isVolatile */); } else { assert(WordSize == 8 && "Invalid word size!"); @@ -262,8 +261,8 @@ bool IntrinsicCleanerPass::runOnBasicBlock(BasicBlock &b, Module &M) { auto pDst = Builder.CreatePointerCast(dst, i64p, "vacopy.cast.dst"); auto pSrc = Builder.CreatePointerCast(src, i64p, "vacopy.cast.src"); - auto pSrcType = pSrc->getType()->getPointerElementType(); - auto pDstType = pDst->getType()->getPointerElementType(); + auto pSrcType = Type::getInt64Ty(ctx); + auto pDstType = Type::getInt64Ty(ctx); auto val = Builder.CreateLoad(pSrcType, pSrc); Builder.CreateStore(val, pDst, ii); @@ -495,7 +494,7 @@ bool IntrinsicCleanerPass::runOnBasicBlock(BasicBlock &b, Module &M) { case Intrinsic::exp: case Intrinsic::expect: case Intrinsic::floor: - case Intrinsic::flt_rounds: + case Intrinsic::get_rounding: case Intrinsic::frameaddress: case Intrinsic::get_dynamic_area_offset: case Intrinsic::invariant_end: diff --git a/klee/lib/Module/KModule.cpp b/klee/lib/Module/KModule.cpp index 1642e9ff..1efc6677 100644 --- a/klee/lib/Module/KModule.cpp +++ b/klee/lib/Module/KModule.cpp @@ -34,6 +34,8 @@ #include "llvm/IR/Operator.h" #include "llvm/IR/ValueSymbolTable.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Passes/PassBuilder.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_os_ostream.h" @@ -42,12 +44,31 @@ #include "llvm/Transforms/Scalar/Scalarizer.h" #include "llvm/Transforms/Utils.h" -#include "klee/util/GetElementPtrTypeIterator.h" +#include "llvm/ADT/MapVector.h" #include using namespace llvm; +namespace { +// createScalarizerPass() was removed from the legacy PM in LLVM 17. +// Wrap the new-PM ScalarizerPass for use with the legacy PM. +struct LegacyScalarizerWrapper : public FunctionPass { + static char ID; + LegacyScalarizerWrapper() : FunctionPass(ID) { + } + bool runOnFunction(Function &F) override { + FunctionAnalysisManager FAM; + PassBuilder PB; + PB.registerFunctionAnalyses(FAM); + ScalarizerPass pass; + pass.run(F, FAM); + return true; // conservative: assume modifications + } +}; +char LegacyScalarizerWrapper::ID = 0; +} // namespace + namespace { enum SwitchImplType { eSwitchTypeSimple, eSwitchTypeLLVM, eSwitchTypeInternal }; @@ -226,7 +247,7 @@ void KModule::prepare() { // // NOTE: Must come before division/overshift checks because those passes // don't know how to handle vector instructions. - pm.add(createScalarizerPass()); + pm.add(new LegacyScalarizerWrapper()); // FIXME: This false here is to work around a bug in // IntrinsicLowering which caches values which may eventually be @@ -278,7 +299,7 @@ void KModule::prepare() { } InstructionOperandTypeCheckPass *operandTypeCheckPass = new InstructionOperandTypeCheckPass(); pm3.add(new IntrinsicCleanerPass(*dataLayout)); - pm3.add(createScalarizerPass()); + pm3.add(new LegacyScalarizerWrapper()); pm3.add(new PhiCleanerPass()); pm3.add(operandTypeCheckPass); pm3.run(*module); @@ -416,57 +437,65 @@ Expr::Width KModule::getWidthForLLVMType(llvm::Type *type) const { return dataLayout->getTypeSizeInBits(type); } -template -void KModule::computeOffsetsSeqTy(const GlobalAddresses &globalAddresses, KGEPInstruction *kgepi, - ref &constantOffset, uint64_t index, const TypeIt it) { - const auto *sq = cast(*it); - auto &targetData = module->getDataLayout(); - uint64_t elementSize = targetData.getTypeStoreSize(sq->getElementType()); - const Value *operand = it.getOperand(); - if (const Constant *c = dyn_cast(operand)) { - ref index = evalConstant(globalAddresses, c)->SExt(Context::get().getPointerWidth()); - ref addend = index->Mul(ConstantExpr::alloc(elementSize, Context::get().getPointerWidth())); - constantOffset = constantOffset->Add(addend); - } else { - kgepi->indices.emplace_back(index, elementSize); +static uint64_t computeAggregateByteOffset(llvm::Type *ty, llvm::ArrayRef indices, + const llvm::DataLayout &DL) { + uint64_t offset = 0; + llvm::Type *curTy = ty; + for (unsigned idx : indices) { + if (auto *st = dyn_cast(curTy)) { + offset += DL.getStructLayout(st)->getElementOffset(idx); + curTy = st->getElementType(idx); + } else { + auto *at = cast(curTy); + offset += idx * DL.getTypeStoreSize(at->getElementType()); + curTy = at->getElementType(); + } } + return offset; } -template -void KModule::computeOffsets(const GlobalAddresses &globalAddresses, KGEPInstruction *kgepi, TypeIt ib, TypeIt ie) { - ref constantOffset = ConstantExpr::alloc(0, Context::get().getPointerWidth()); - uint64_t index = 1; - for (TypeIt ii = ib; ii != ie; ++ii) { - if (StructType *st = dyn_cast(*ii)) { - auto &targetData = module->getDataLayout(); - const StructLayout *sl = targetData.getStructLayout(st); - const ConstantInt *ci = cast(ii.getOperand()); - uint64_t addend = sl->getElementOffset((unsigned) ci->getZExtValue()); - constantOffset = constantOffset->Add(ConstantExpr::alloc(addend, Context::get().getPointerWidth())); - } else if (isa(*ii)) { - computeOffsetsSeqTy(globalAddresses, kgepi, constantOffset, index, ii); - } else if (isa(*ii)) { - computeOffsetsSeqTy(globalAddresses, kgepi, constantOffset, index, ii); - } else if (isa(*ii)) { - computeOffsetsSeqTy(globalAddresses, kgepi, constantOffset, index, ii); - } else - assert("invalid type" && 0); - index++; +void KModule::bindGEPInstructionConstants(KGEPInstruction *kgepi) { + auto *gepi = cast(kgepi->inst); + auto &DL = module->getDataLayout(); + unsigned ptrWidth = Context::get().getPointerWidth(); + + llvm::MapVector varOffsets; + llvm::APInt constOffset(ptrWidth, 0); + [[maybe_unused]] bool ok = gepi->collectOffset(DL, ptrWidth, varOffsets, constOffset); + assert(ok && "collectOffset failed on GEP instruction"); + + kgepi->offset = constOffset.getZExtValue(); + + // GEP operand 0 is the base pointer; index operands start at 1. + // For each variable index, record its operand position and scale. + for (unsigned i = 1, e = gepi->getNumOperands(); i < e; ++i) { + llvm::Value *v = gepi->getOperand(i); + auto it = varOffsets.find(v); + if (it != varOffsets.end()) { + kgepi->indices.emplace_back(i, it->second.getZExtValue()); + varOffsets.erase(it); + } } - kgepi->offset = constantOffset->getZExtValue(); +} + +void KModule::bindInsertValueConstants(KInsertValueInstruction *kivi) { + auto *ivi = cast(kivi->inst); + kivi->offset = computeAggregateByteOffset(ivi->getType(), ivi->getIndices(), module->getDataLayout()); +} + +void KModule::bindExtractValueConstants(KExtractValueInstruction *kevi) { + auto *evi = cast(kevi->inst); + kevi->offset = + computeAggregateByteOffset(evi->getOperand(0)->getType(), evi->getIndices(), module->getDataLayout()); } void KModule::bindInstructionConstants(const GlobalAddresses &globalAddresses, KInstruction *KI) { - KGEPInstruction *kgepi = static_cast(KI); - - if (GetElementPtrInst *gepi = dyn_cast(KI->inst)) { - computeOffsets(globalAddresses, kgepi, klee::gep_type_begin(gepi), klee::gep_type_end(gepi)); - } else if (InsertValueInst *ivi = dyn_cast(KI->inst)) { - computeOffsets(globalAddresses, kgepi, iv_type_begin(ivi), iv_type_end(ivi)); - assert(kgepi->indices.empty() && "InsertValue constant offset expected"); - } else if (ExtractValueInst *evi = dyn_cast(KI->inst)) { - computeOffsets(globalAddresses, kgepi, ev_type_begin(evi), ev_type_end(evi)); - assert(kgepi->indices.empty() && "ExtractValue constant offset expected"); + if (isa(KI->inst)) { + bindGEPInstructionConstants(static_cast(KI)); + } else if (isa(KI->inst)) { + bindInsertValueConstants(static_cast(KI)); + } else if (isa(KI->inst)) { + bindExtractValueConstants(static_cast(KI)); } } @@ -734,7 +763,9 @@ klee::ref KModule::evalConstantExpr(const GlobalAddresses &g } case Instruction::ICmp: { - switch (ce->getPredicate()) { + // ICmp ConstantExprs were removed in LLVM 17+; this is unreachable. + llvm_unreachable("ICmp ConstantExpr is not supported in LLVM 17+"); + switch (0) { default: assert(0 && "unhandled ICmp predicate"); case ICmpInst::ICMP_EQ: @@ -831,9 +862,13 @@ KFunction::KFunction(llvm::Function *_function, KModule *km) : function(_functio switch (it->getOpcode()) { case Instruction::GetElementPtr: + ki = new KGEPInstruction(); + break; case Instruction::InsertValue: + ki = new KInsertValueInstruction(); + break; case Instruction::ExtractValue: - ki = new KGEPInstruction(); + ki = new KExtractValueInstruction(); break; case Instruction::Call: diff --git a/klee/lib/Module/LowerSwitch.cpp b/klee/lib/Module/LowerSwitch.cpp index 64eedbf7..a944778b 100644 --- a/klee/lib/Module/LowerSwitch.cpp +++ b/klee/lib/Module/LowerSwitch.cpp @@ -62,10 +62,9 @@ void LowerSwitchPass::switchConvert(CaseItr begin, CaseItr end, Value *value, Ba // iterate through all the cases, creating a new BasicBlock for each for (CaseItr it = begin; it < end; ++it) { BasicBlock *newBlock = BasicBlock::Create(context, "NodeBlock"); - Function::iterator FI = origBlock->getIterator(); - F->getBasicBlockList().insert(++FI, newBlock); + newBlock->insertInto(F, origBlock->getNextNode()); - ICmpInst *cmpInst = new ICmpInst(*newBlock, ICmpInst::ICMP_EQ, value, it->value, "case.cmp"); + ICmpInst *cmpInst = new ICmpInst(newBlock, ICmpInst::ICMP_EQ, value, it->value, "case.cmp"); BranchInst::Create(it->block, curHead, cmpInst, newBlock); // If there were any PHI nodes in this successor, rewrite one entry @@ -99,7 +98,7 @@ void LowerSwitchPass::processSwitchInst(SwitchInst *SI) { // if-then statements go to this and the PHI nodes are happy. BasicBlock *newDefault = BasicBlock::Create(context, "newDefault"); - F->getBasicBlockList().insert(defaultBlock->getIterator(), newDefault); + newDefault->insertInto(F, defaultBlock); BranchInst::Create(defaultBlock, newDefault); // If there is an entry in any PHI nodes for the default edge, make sure @@ -126,6 +125,6 @@ void LowerSwitchPass::processSwitchInst(SwitchInst *SI) { switchConvert(cases.begin(), cases.end(), switchValue, origBlock, newDefault); // We are now done with the switch instruction, so delete it - origBlock->getInstList().erase(SI); + SI->eraseFromParent(); } } // namespace klee From 42838a3464b26d21bfaf4f44a45b5af6fa9a28c1 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 23:20:29 +0200 Subject: [PATCH 12/24] github: removed 24.04 build It is not supported anymore. Signed-off-by: Vitaly Chipounov --- .github/workflows/build.yaml | 8 +++----- .github/workflows/docker.yaml | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9961a377..94e400dc 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,8 +6,6 @@ jobs: strategy: matrix: include: - - os: ubuntu-22.04 - clang_version: 15 - os: ubuntu-24.04 clang_version: 19 steps: @@ -34,10 +32,10 @@ jobs: libcurl4-openssl-dev \ libedit-dev \ libpfm4-dev \ - llvm-14-dev \ - clang-format-14 \ + llvm-19-dev \ + clang-format-19 \ libmlir-14-dev \ - clang-14 + clang-19 - name: Install OS-specific clang version run: | diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index e78d312d..a84d7a18 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -19,6 +19,6 @@ jobs: mkdir -p $HOME/s2e/env/build cd $HOME/s2e/env/build - docker run -t --rm -e SYSTEM_CLANG_VERSION=15 -e S2E_PREFIX="$HOME/s2e/env/install" -w $(pwd) -v $HOME:$HOME s2e-build-env /run_as.sh $(id -u) $(id -g) make -f $GITHUB_WORKSPACE/Makefile.tools install + docker run -t --rm -e S2E_PREFIX="$HOME/s2e/env/install" -w $(pwd) -v $HOME:$HOME s2e-build-env /run_as.sh $(id -u) $(id -g) make -f $GITHUB_WORKSPACE/Makefile.tools install - run: echo "🍏 This job's status is ${{ job.status }}." From e5885973d83d9c5cb6116e4b0215d374fdcda1dd Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 23:21:00 +0200 Subject: [PATCH 13/24] dockerfile: upgraded to ubuntu 24.04 Signed-off-by: Vitaly Chipounov --- Dockerfile | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1b99944d..2f66ac3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ # Installs S2E and its associated libraries and tools to /opt/s2e -FROM ubuntu:22.04 AS s2e-build-env +FROM ubuntu:24.04 AS s2e-build-env # Install build dependencies. # The unzip and libgomp1 dependencies are needed to unzip and run binary Z3 @@ -41,33 +41,27 @@ RUN apt-get update && dpkg --add-architecture i386 && apt-get update && libcurl4-openssl-dev \ libedit-dev \ libpfm4-dev \ - llvm-14-dev \ - clang-format-14 \ - clang-14 \ - clang-15 + llvm-19-dev \ + clang-format-19 \ + clang-19 \ + clang-17 # This scripts allows running commands as a host user inside the container. # For example, guest tools can be built as follows: # cd $HOME/s2e/env/source/s2e # docker build --target s2e-build-env -t s2e-build-env . # cd $HOME/s2e/env/build -# docker run -ti --rm -e SYSTEM_CLANG_VERSION=15 -e S2E_PREFIX="$HOME/s2e/env/install" -w $(pwd) -v $HOME:$HOME s2e-build-env /run_as.sh $(id -u) $(id -g) make -f $HOME/s2e/env/source/s2e/Makefile.tools install +# docker run -ti --rm -e SYSTEM_CLANG_VERSION=19 -e S2E_PREFIX="$HOME/s2e/env/install" -w $(pwd) -v $HOME:$HOME s2e-build-env /run_as.sh $(id -u) $(id -g) make -f $HOME/s2e/env/source/s2e/Makefile.tools install COPY scripts/run_as.sh / ############################################################################### FROM s2e-build-env AS s2e-build-all -# Required for C++17 -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common -RUN add-apt-repository ppa:ubuntu-toolchain-r/test && apt update -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y gcc-9 g++-9 - RUN mkdir s2e && mkdir s2e-build COPY Makefile Makefile.tools Makefile.common s2e/ # Be explicit about not building Z3 from source, even though its default ARG USE_Z3_BINARY=yes -ARG SYSTEM_CLANG_VERSION=15 RUN cd s2e-build && \ make -f ../s2e/Makefile S2E_PREFIX=/opt/s2e stamps/z3 From 287ca6fa5c45ea04af91141330669a0f8444cfc7 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 23:21:40 +0200 Subject: [PATCH 14/24] dockerfile.tools: added separate file to build guest tools These require an older ubuntu version so that they can run in older guests. Signed-off-by: Vitaly Chipounov --- Dockerfile.tools | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Dockerfile.tools diff --git a/Dockerfile.tools b/Dockerfile.tools new file mode 100644 index 00000000..2f24afd5 --- /dev/null +++ b/Dockerfile.tools @@ -0,0 +1,52 @@ +# Copyright (C) 2017-2022, Cyberhaven +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# This creates the build environment for guest tools. + +FROM ubuntu:22.04 AS s2e-build-env-tools + +# Install build dependencies. +# The unzip and libgomp1 dependencies are needed to unzip and run binary Z3 +# distributions. + +RUN apt-get update && dpkg --add-architecture i386 && apt-get update && \ + apt-get -y install sudo git ca-certificates build-essential cmake curl wget texinfo flex bison \ + python-is-python3 python3-dev python3-venv python3-distro mingw-w64 lsb-release \ + autoconf libtool libprotobuf-dev protobuf-compiler protobuf-c-compiler \ + libdwarf-dev libelf-dev libelf-dev:i386 \ + libboost-dev zlib1g-dev libjemalloc-dev nasm pkg-config \ + libmemcached-dev libpq-dev libc6-dev-i386 binutils-dev \ + libboost-system-dev libboost-serialization-dev libboost-regex-dev \ + libbsd-dev libpixman-1-dev \ + libglib2.0-dev libglib2.0-dev:i386 python3-docutils libpng-dev \ + gcc-multilib g++-multilib libgomp1 unzip libzstd-dev \ + libgmock-dev libgtest-dev libsoci-dev libcapstone-dev \ + libcurl4-openssl-dev \ + libedit-dev \ + libpfm4-dev \ + clang-15 + +# This scripts allows running commands as a host user inside the container. +# For example, guest tools can be built as follows: +# cd $HOME/s2e/env/source/s2e +# docker build --target s2e-build-env -t s2e-build-env . +# cd $HOME/s2e/env/build +# docker run -ti --rm -e SYSTEM_CLANG_VERSION=19 -e S2E_PREFIX="$HOME/s2e/env/install" -w $(pwd) -v $HOME:$HOME s2e-build-env /run_as.sh $(id -u) $(id -g) make -f $HOME/s2e/env/source/s2e/Makefile.tools install +COPY scripts/run_as.sh / From 080beb7e2383e463fa522040f03dca485bbb47d0 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sun, 17 May 2026 23:22:22 +0200 Subject: [PATCH 15/24] CMakeLists.txt: cleaned up various warnings Signed-off-by: Vitaly Chipounov --- klee/CMakeLists.txt | 19 +++++-------------- libs2e/CMakeLists.txt | 4 ++++ libs2e/configure | 8 ++++++-- libs2ecore/CMakeLists.txt | 4 ++++ libs2eplugins/CMakeLists.txt | 4 ++++ libtcg/CMakeLists.txt | 4 ++++ libvmi/CMakeLists.txt | 4 ++++ tools/CMakeLists.txt | 4 ++++ 8 files changed, 35 insertions(+), 16 deletions(-) diff --git a/klee/CMakeLists.txt b/klee/CMakeLists.txt index 8f8aa4d5..f1b1d677 100644 --- a/klee/CMakeLists.txt +++ b/klee/CMakeLists.txt @@ -31,25 +31,16 @@ set(KLEE_LIBRARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/lib") configure_file(KLEEConfig.cmake.in ${CMAKE_CONFIG_FILE} @ONLY) -if (POLICY CMP0054) - # FIXME: This is horrible. With the old behaviour, - # quoted strings like "MSVC" in if() conditionals - # get implicitly dereferenced. The NEW behaviour - # doesn't do this but CMP0054 was only introduced - # in CMake 3.1 and we support lower versions as the - # minimum. We could set NEW here but it would be very - # confusing to use NEW for some builds and OLD for others - # which could lead to some subtle bugs. Instead when the - # minimum version is 3.1 change this policy to NEW and remove - # the hacks in place to work around it. - cmake_policy(SET CMP0054 OLD) -endif() - if (POLICY CMP0042) # Enable `MACOSX_RPATH` by default. cmake_policy(SET CMP0042 NEW) endif() +if (POLICY CMP0075) + # Honor CMAKE_REQUIRED_LIBRARIES in check_include_file() (required by LLVM 19's FindLibEdit). + cmake_policy(SET CMP0075 NEW) +endif() + # This overrides the default flags for the different CMAKE_BUILD_TYPEs set(CMAKE_USER_MAKE_RULES_OVERRIDE_C "${CMAKE_CURRENT_SOURCE_DIR}/cmake/c_flags_override.cmake") diff --git a/libs2e/CMakeLists.txt b/libs2e/CMakeLists.txt index 0081a388..93cefad0 100644 --- a/libs2e/CMakeLists.txt +++ b/libs2e/CMakeLists.txt @@ -50,6 +50,10 @@ include(FindProtobuf) find_package(Protobuf REQUIRED) ##### LLVM ##### +if (POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + find_package(CURL REQUIRED) find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") diff --git a/libs2e/configure b/libs2e/configure index e19163a3..286a56f5 100755 --- a/libs2e/configure +++ b/libs2e/configure @@ -319,14 +319,17 @@ fi echo "=== Configuring libs2e... ===" rm -rf libs2e && mkdir -p libs2e && cd libs2e +S2E_CMAKE_ARGS="" +if echo $target | grep -q s2e; then + S2E_CMAKE_ARGS="-DLIBS2ECORE_DIR=$(pwd)/../libs2ecore -DLIBS2EPLUGINS_DIR=$(pwd)/../libs2eplugins" +fi + cmake -DCMAKE_C_COMPILER="$CC" -DCMAKE_CXX_COMPILER="$CXX" \ -DCMAKE_C_FLAGS="$CFLAGS" -DCMAKE_CXX_FLAGS="$CXXFLAGS" \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_PREFIX_PATH="$PREFIX" \ -DLIBTCG_DIR="$(pwd)/../libtcg" \ -DLIBCPU_DIR="$(pwd)/../libcpu" \ - -DLIBS2ECORE_DIR="$(pwd)/../libs2ecore" \ - -DLIBS2EPLUGINS_DIR="$(pwd)/../libs2eplugins" \ -DLIBQ_DIR="$LIBQ_DIR" \ -DLIBCOROUTINE_DIR="$LIBCOROUTINE_DIR" \ -DKLEE_DIR="$KLEE_ROOT" \ @@ -335,6 +338,7 @@ cmake -DCMAKE_C_COMPILER="$CC" -DCMAKE_CXX_COMPILER="$CXX" \ -DFSIGCXX_DIR="$FSIGCXX_DIR" \ -DZ3_DIR="$Z3_LIBDIR" \ -DWITH_TARGET="$target" \ + $S2E_CMAKE_ARGS \ "$SRC_DIR" if [ $? -ne 0 ]; then diff --git a/libs2ecore/CMakeLists.txt b/libs2ecore/CMakeLists.txt index 3a407ee8..71c3016c 100644 --- a/libs2ecore/CMakeLists.txt +++ b/libs2ecore/CMakeLists.txt @@ -44,6 +44,10 @@ pkg_check_modules(GLIB_PKG glib-2.0) include_directories(${GLIB_PKG_INCLUDE_DIRS}) ##### LLVM ##### +if (POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") diff --git a/libs2eplugins/CMakeLists.txt b/libs2eplugins/CMakeLists.txt index c3ef33ba..a960fc39 100644 --- a/libs2eplugins/CMakeLists.txt +++ b/libs2eplugins/CMakeLists.txt @@ -43,6 +43,10 @@ find_package(PkgConfig REQUIRED) include_directories("include") ##### LLVM ##### +if (POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") diff --git a/libtcg/CMakeLists.txt b/libtcg/CMakeLists.txt index f7d05d12..f1569376 100644 --- a/libtcg/CMakeLists.txt +++ b/libtcg/CMakeLists.txt @@ -45,6 +45,10 @@ find_package(LIBQ REQUIRED) message(STATUS "Found libq ${LIBQ_PACKAGE_VERSION}") ##### LLVM ##### +if (POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") diff --git a/libvmi/CMakeLists.txt b/libvmi/CMakeLists.txt index 39268793..c9a3e68b 100644 --- a/libvmi/CMakeLists.txt +++ b/libvmi/CMakeLists.txt @@ -38,6 +38,10 @@ set(VMI_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include") set(VMI_LIBRARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/src") configure_file(VMIConfig.cmake.in ${CMAKE_CONFIG_FILE} @ONLY) +if (POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 23399efe..8bd2f833 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -46,6 +46,10 @@ find_package(Protobuf REQUIRED) # Old CMake does not support static libraries, so we have to patch the library manually string(REPLACE .so .a PROTOBUF_LIBRARIES "${PROTOBUF_LIBRARIES}") +if (POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + find_package(LLVM 19.1...<20 REQUIRED) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") From ffe92ccd989baf41489df8d895d1436d1a548949 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 21:28:43 +0200 Subject: [PATCH 16/24] libfsigc++: fixed build errors on ubuntu 26.04 Signed-off-by: Vitaly Chipounov --- libfsigc++/include/fsigc++/fsigc++.h | 57 ++++++++++++---------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/libfsigc++/include/fsigc++/fsigc++.h b/libfsigc++/include/fsigc++/fsigc++.h index 9ab72a49..e071a831 100644 --- a/libfsigc++/include/fsigc++/fsigc++.h +++ b/libfsigc++/include/fsigc++/fsigc++.h @@ -51,7 +51,30 @@ class signal_base { virtual void disconnect(void *functor) = 0; }; -class functor_refcnt; +class functor_refcnt { +private: + std::atomic m_refCount; + +protected: + functor_refcnt() : m_refCount(0) { + } + +public: + virtual ~functor_refcnt() { + assert(m_refCount == 0); + } + + friend void intrusive_ptr_add_ref(functor_refcnt *ptr) { + ++ptr->m_refCount; + } + + friend void intrusive_ptr_release(functor_refcnt *ptr) { + if (--ptr->m_refCount == 0) { + delete ptr; + } + } +}; + typedef boost::intrusive_ptr functor_refcnt_ptr; class connection { @@ -74,38 +97,6 @@ class connection { void disconnect(); }; -//************************************************* -//************************************************* -//************************************************* - -class functor_refcnt { -private: - std::atomic m_refCount; - -protected: - functor_refcnt() : m_refCount(0) { - } - -public: - virtual ~functor_refcnt() { - assert(m_refCount == 0); - } - - template friend void intrusive_ptr_add_ref(T *ptr); - - template friend void intrusive_ptr_release(T *ptr); -}; - -template inline void intrusive_ptr_add_ref(T *ptr) { - ++ptr->m_refCount; -} - -template inline void intrusive_ptr_release(T *ptr) { - if (--ptr->m_refCount == 0) { - delete ptr; - } -} - template class functor_base : public functor_refcnt { protected: functor_base() { From c1650485fe0c46ae70673a5a44e707849d394d56 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 21:29:24 +0200 Subject: [PATCH 17/24] libs2ecore: removed unnecessary headers in external dispatcher Signed-off-by: Vitaly Chipounov --- libs2ecore/include/s2e/S2EExternalDispatcher.h | 4 ++-- libs2ecore/src/S2EExternalDispatcher.cpp | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libs2ecore/include/s2e/S2EExternalDispatcher.h b/libs2ecore/include/s2e/S2EExternalDispatcher.h index 87586013..b869d8bb 100644 --- a/libs2ecore/include/s2e/S2EExternalDispatcher.h +++ b/libs2ecore/include/s2e/S2EExternalDispatcher.h @@ -26,11 +26,11 @@ #define _S2E_EXTERNAL_DISPATCHER_H #include -#include -#include #include #include +#include + namespace s2e { /* External dispatcher to convert longjmp's into C++ exceptions */ diff --git a/libs2ecore/src/S2EExternalDispatcher.cpp b/libs2ecore/src/S2EExternalDispatcher.cpp index 0deb3646..7647ab39 100644 --- a/libs2ecore/src/S2EExternalDispatcher.cpp +++ b/libs2ecore/src/S2EExternalDispatcher.cpp @@ -21,9 +21,6 @@ /// SOFTWARE. /// -#include -#include - #include #include #include From ce675fb40ead9231dd96af395f253b4e7d4aabf4 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 21:30:07 +0200 Subject: [PATCH 18/24] libs2ecore,plugins: adjustment for new boost asio apis Signed-off-by: Vitaly Chipounov --- libs2ecore/src/s2e-qmp.cpp | 14 ++++++-------- .../src/s2e/Plugins/Support/KeyValueStore.cpp | 8 +++----- .../src/s2e/Plugins/Support/KeyValueStore.h | 4 ++-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/libs2ecore/src/s2e-qmp.cpp b/libs2ecore/src/s2e-qmp.cpp index eda475a6..c075c6bb 100644 --- a/libs2ecore/src/s2e-qmp.cpp +++ b/libs2ecore/src/s2e-qmp.cpp @@ -37,10 +37,10 @@ class S2EQMPClient { std::string m_host; std::string m_port; - boost::asio::io_service m_io_service; + boost::asio::io_context m_io_context; boost::asio::ip::tcp::socket m_socket; - S2EQMPClient() : m_socket(m_io_service) { + S2EQMPClient() : m_socket(m_io_context) { } public: @@ -50,7 +50,7 @@ class S2EQMPClient { bool ready() const; }; -S2EQMPClient::S2EQMPClient(const std::string &hostPort) : m_socket(m_io_service) { +S2EQMPClient::S2EQMPClient(const std::string &hostPort) : m_socket(m_io_context) { // default port m_host = hostPort; m_port = "1234"; @@ -68,11 +68,9 @@ bool S2EQMPClient::connect() { std::cout << "QMP client connecting to " << m_host << ":" << m_port << "\n"; try { - tcp::resolver resolver(m_io_service); - tcp::resolver::query query(m_host, m_port); - tcp::resolver::iterator iter = resolver.resolve(query); - - m_socket.connect(*iter); + tcp::resolver resolver(m_io_context); + auto endpoints = resolver.resolve(m_host, m_port); + boost::asio::connect(m_socket, endpoints); } catch (std::exception &e) { std::cerr << "Exception while connecting to the QMP server: " << e.what() << "\n"; diff --git a/libs2eplugins/src/s2e/Plugins/Support/KeyValueStore.cpp b/libs2eplugins/src/s2e/Plugins/Support/KeyValueStore.cpp index 17af1067..9a9b0409 100644 --- a/libs2eplugins/src/s2e/Plugins/Support/KeyValueStore.cpp +++ b/libs2eplugins/src/s2e/Plugins/Support/KeyValueStore.cpp @@ -90,11 +90,9 @@ void KeyValueStore::connectNative() { std::stringstream sp; sp << m_port; - boost::asio::ip::tcp::resolver resolver(m_io_service); - boost::asio::ip::tcp::resolver::query query(m_server, sp.str()); - boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query); - - m_socket.connect(*iter); + boost::asio::ip::tcp::resolver resolver(m_io_context); + auto endpoints = resolver.resolve(m_server, sp.str()); + boost::asio::connect(m_socket, endpoints); std::stringstream ss; diff --git a/libs2eplugins/src/s2e/Plugins/Support/KeyValueStore.h b/libs2eplugins/src/s2e/Plugins/Support/KeyValueStore.h index 79e80d2f..76bad06f 100644 --- a/libs2eplugins/src/s2e/Plugins/Support/KeyValueStore.h +++ b/libs2eplugins/src/s2e/Plugins/Support/KeyValueStore.h @@ -95,7 +95,7 @@ class KeyValueStore : public Plugin, public IPluginInvoker { bool /* success */> onLocalPut; - KeyValueStore(S2E *s2e) : Plugin(s2e), m_socket(m_io_service) { + KeyValueStore(S2E *s2e) : Plugin(s2e), m_socket(m_io_context) { } virtual ~KeyValueStore(); @@ -117,7 +117,7 @@ class KeyValueStore : public Plugin, public IPluginInvoker { virtual void handleOpcodeInvocation(S2EExecutionState *state, uint64_t guestDataPtr, uint64_t guestDataSize); private: - boost::asio::io_service m_io_service; + boost::asio::io_context m_io_context; boost::asio::ip::tcp::socket m_socket; std::string m_session_id; From ad9a4f70a0bee4ddeadd649d40aa56c8b3f191a8 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 21:30:45 +0200 Subject: [PATCH 19/24] klee,tcg: silenced llvm warnings Signed-off-by: Vitaly Chipounov --- klee/lib/Module/Passes.h | 16 ++++++++++++++++ libtcg/include/tcg/tcg-llvm.h | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/klee/lib/Module/Passes.h b/klee/lib/Module/Passes.h index 3704f190..87dee478 100644 --- a/klee/lib/Module/Passes.h +++ b/klee/lib/Module/Passes.h @@ -10,12 +10,28 @@ #ifndef KLEE_PASSES_H #define KLEE_PASSES_H +// LLVM 19 headers include which GCC 15 flags as deprecated in C++20. +// Suppress that #warning directive only around these system headers. +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-W#warnings" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcpp" +#endif + #include "llvm/CodeGen/IntrinsicLowering.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Pass.h" +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + namespace llvm { class Function; class Instruction; diff --git a/libtcg/include/tcg/tcg-llvm.h b/libtcg/include/tcg/tcg-llvm.h index 5a3de73f..48be8184 100644 --- a/libtcg/include/tcg/tcg-llvm.h +++ b/libtcg/include/tcg/tcg-llvm.h @@ -49,6 +49,16 @@ void *tcg_llvm_gen_code(void *llvmTranslator, struct TCGContext *s, struct Trans // External interface for C++ code +// LLVM 19 headers include which GCC 15 flags as deprecated in C++20. +// Suppress that #warning directive only around these system headers. +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-W#warnings" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcpp" +#endif + #include #include #include @@ -62,6 +72,12 @@ void *tcg_llvm_gen_code(void *llvmTranslator, struct TCGContext *s, struct Trans #include #include #include + +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif #include class TCGLLVMTranslator { From 6dfe4404b56231a528f81f76a4f976c17e6646bd Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 21:31:28 +0200 Subject: [PATCH 20/24] makefile: force old cmake requirement for rapidjson Signed-off-by: Vitaly Chipounov --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 6c7fc321..d80227e2 100644 --- a/Makefile +++ b/Makefile @@ -236,9 +236,10 @@ stamps/libdwarf-make: stamps/libdwarf-configure RAPIDJSON_CONFIGURE_FLAGS = -DCMAKE_INSTALL_PREFIX=$(S2E_PREFIX) \ -DCMAKE_C_FLAGS="$(CFLAGS_ARCH) -fno-omit-frame-pointer -fPIC" \ - -DCMAKE_C_COMPILER=$(SYSTEM_CLANG_CC) \ - -DCMAKE_CXX_COMPILER=$(SYSTEM_CLANG_CXX) \ - -DRAPIDJSON_BUILD_TESTS=OFF + -DCMAKE_C_COMPILER=$(SYSTEM_CLANG_CC) \ + -DCMAKE_CXX_COMPILER=$(SYSTEM_CLANG_CXX) \ + -DRAPIDJSON_BUILD_TESTS=OFF \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 stamps/rapidjson-configure: $(RAPIDJSON_BUILD_DIR) From be036ffa538f76f09148eba472b43af0a5b80174 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 21:31:59 +0200 Subject: [PATCH 21/24] docs: dropped ubuntu 22.04, added ubuntu 26.04 Signed-off-by: Vitaly Chipounov --- docs/src/s2e-env.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/s2e-env.rst b/docs/src/s2e-env.rst index 9e6c3627..70efafdd 100644 --- a/docs/src/s2e-env.rst +++ b/docs/src/s2e-env.rst @@ -13,7 +13,7 @@ You will find in the documentation various tutorials that go deeper into various .. note:: - S2E builds and runs on Ubuntu 22.04, 24.04 and Debian 12/13 (64-bit). + S2E builds and runs on Ubuntu 24.04, 26.04 and Debian 12/13 (64-bit). Earlier versions may still work, but we do not support them anymore. From debe01cedfdf817611849e9877e23fdf4b040eb7 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 23:30:23 +0200 Subject: [PATCH 22/24] libs2e: don't link against libboost_system This library is header-only. Signed-off-by: Vitaly Chipounov --- libs2e/src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/libs2e/src/CMakeLists.txt b/libs2e/src/CMakeLists.txt index 21b7eca7..7d953ebc 100644 --- a/libs2e/src/CMakeLists.txt +++ b/libs2e/src/CMakeLists.txt @@ -63,7 +63,6 @@ if(WITH_TARGET MATCHES "s2e") gomp ${PROTOBUF_LIBRARIES}) set(LIBS ${LIBS} boost_serialization - boost_system boost_regex) endif() From 12354a3526ba09c1672a18d10021f9b8f93b7b56 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 23:51:24 +0200 Subject: [PATCH 23/24] vagrant: provisioning script fix for ubuntu 26.04 Signed-off-by: Vitaly Chipounov --- vagrant/provision-root.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vagrant/provision-root.sh b/vagrant/provision-root.sh index 247410ef..c88069c2 100755 --- a/vagrant/provision-root.sh +++ b/vagrant/provision-root.sh @@ -44,4 +44,4 @@ fi chown -R vagrant:vagrant /mnt/disk/ usermod -a -G docker vagrant -su -c "source /vagrant/provision-user.sh" vagrant +su -c "/bin/bash -c 'source /vagrant/provision-user.sh'" vagrant From 3086e987a0de36b68f32c9bec323b37422980497 Mon Sep 17 00:00:00 2001 From: Vitaly Chipounov Date: Sat, 23 May 2026 23:53:15 +0200 Subject: [PATCH 24/24] vagrant added ubuntu 26.04 Signed-off-by: Vitaly Chipounov --- vagrant/Vagrantfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile index 0ed312bd..aa2cecce 100644 --- a/vagrant/Vagrantfile +++ b/vagrant/Vagrantfile @@ -18,7 +18,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -$boxes = ["debian/bookworm64", "debian/trixie64", "generic/ubuntu2204", "bento/ubuntu-24.04"] +# Requires: +# vagrant plugin install vagrant-disksize + +# Debian 12, Debian 13 +# Note: cloud-image/ubuntu-26.04 has a 10GB disk, use qemu-img resize /path/to/disk.img +10GB. +$boxes = ["debian/bookworm64", "debian/trixie64", "bento/ubuntu-24.04", "cloud-image/ubuntu-26.04"] Vagrant.configure("2") do |config| $boxes.to_enum.with_index(1).each do |box,i|