aboutsummaryrefslogtreecommitdiff
path: root/src/main/native/com/code_intelligence/jazzer/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/native/com/code_intelligence/jazzer/android')
-rw-r--r--src/main/native/com/code_intelligence/jazzer/android/BUILD.bazel47
-rw-r--r--src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp208
-rw-r--r--src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.h37
-rw-r--r--src/main/native/com/code_intelligence/jazzer/android/jazzer_jvmti_allocator.h52
-rw-r--r--src/main/native/com/code_intelligence/jazzer/android/native_agent.cpp313
5 files changed, 657 insertions, 0 deletions
diff --git a/src/main/native/com/code_intelligence/jazzer/android/BUILD.bazel b/src/main/native/com/code_intelligence/jazzer/android/BUILD.bazel
new file mode 100644
index 00000000..74f98cda
--- /dev/null
+++ b/src/main/native/com/code_intelligence/jazzer/android/BUILD.bazel
@@ -0,0 +1,47 @@
+load("//bazel:compat.bzl", "SKIP_ON_WINDOWS")
+load("@fmeum_rules_jni//jni:defs.bzl", "cc_jni_library")
+load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
+
+copy_file(
+ name = "jvmti_h_encoded",
+ src = "@android_jvmti//file",
+ out = "jvmti.encoded",
+ is_executable = False,
+ tags = ["manual"],
+ target_compatible_with = SKIP_ON_WINDOWS,
+)
+
+genrule(
+ name = "jvmti_h",
+ srcs = [
+ "jvmti.encoded",
+ ],
+ outs = ["jvmti.h"],
+ cmd = "base64 --decode $< > $(OUTS)",
+ tags = ["manual"],
+ target_compatible_with = SKIP_ON_WINDOWS,
+)
+
+cc_jni_library(
+ name = "android_native_agent",
+ srcs = [
+ "dex_file_manager.cpp",
+ "dex_file_manager.h",
+ "jazzer_jvmti_allocator.h",
+ "native_agent.cpp",
+ ":jvmti_h",
+ ],
+ includes = [
+ ".",
+ ],
+ linkopts = [
+ "-lz",
+ ],
+ tags = ["manual"],
+ target_compatible_with = SKIP_ON_WINDOWS,
+ visibility = ["//visibility:public"],
+ deps = [
+ "@com_google_absl//absl/strings",
+ "@jazzer_slicer",
+ ],
+)
diff --git a/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp b/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp
new file mode 100644
index 00000000..b409e82b
--- /dev/null
+++ b/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp
@@ -0,0 +1,208 @@
+// Copyright 2023 Code Intelligence GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dex_file_manager.h"
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "jazzer_jvmti_allocator.h"
+#include "jvmti.h"
+#include "slicer/dex_ir.h"
+#include "slicer/reader.h"
+#include "slicer/writer.h"
+
+std::string GetName(const char* name) {
+ std::stringstream ss;
+ // Class name needs to be in the format "L<class_name>;" as it is stored in
+ // the types table in the DEX file for slicer to find it
+ ss << "L" << name << ";";
+ return ss.str();
+}
+
+bool IsValidIndex(dex::u4 index) { return index != (unsigned)-1; }
+
+void DexFileManager::addDexFile(const unsigned char* bytes, int length) {
+ unsigned char* newArr = new unsigned char[length];
+ std::copy(bytes, bytes + length, newArr);
+
+ dexFiles.push_back(newArr);
+ dexFilesSize.push_back(length);
+}
+
+unsigned char* DexFileManager::getClassBytes(const char* className,
+ int dexFileIndex, jvmtiEnv* jvmti,
+ size_t* newSize) {
+ dex::Reader dexReader(dexFiles[dexFileIndex], dexFilesSize[dexFileIndex]);
+ auto descName = GetName(className);
+
+ auto classIndex = dexReader.FindClassIndex(descName.c_str());
+ if (!IsValidIndex(classIndex)) {
+ *newSize = *newSize;
+ return nullptr;
+ }
+
+ dexReader.CreateClassIr(classIndex);
+ auto oldIr = dexReader.GetIr();
+
+ dex::Writer writer(oldIr);
+ JazzerJvmtiAllocator allocator(jvmti);
+ return writer.CreateImage(&allocator, newSize);
+}
+
+uint32_t DexFileManager::findDexFileForClass(const char* className) {
+ for (int i = 0; i < dexFiles.size(); i++) {
+ dex::Reader dexReader(dexFiles[i], dexFilesSize[i]);
+
+ std::string descName = GetName(className);
+ dex::u4 classIndex = dexReader.FindClassIndex(descName.c_str());
+
+ if (IsValidIndex(classIndex)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+std::vector<std::string> getMethodDescriptions(
+ std::vector<ir::EncodedMethod*>* encMethodList) {
+ std::vector<std::string> methodDescs;
+
+ for (int i = 0; i < encMethodList->size(); i++) {
+ std::stringstream ss;
+ ss << (*encMethodList)[i]->access_flags;
+ ss << (*encMethodList)[i]->decl->name->c_str();
+ ss << (*encMethodList)[i]->decl->prototype->Signature().c_str();
+
+ methodDescs.push_back(ss.str());
+ }
+
+ sort(methodDescs.begin(), methodDescs.end());
+ return methodDescs;
+}
+
+std::vector<std::string> getFieldDescriptions(
+ std::vector<ir::EncodedField*>* encFieldList) {
+ std::vector<std::string> fieldDescs;
+
+ for (int i = 0; i < encFieldList->size(); i++) {
+ std::stringstream ss;
+ ss << (*encFieldList)[i]->access_flags;
+ ss << (*encFieldList)[i]->decl->type->descriptor->c_str();
+ ss << (*encFieldList)[i]->decl->name->c_str();
+ fieldDescs.push_back(ss.str());
+ }
+
+ sort(fieldDescs.begin(), fieldDescs.end());
+ return fieldDescs;
+}
+
+bool matchFields(std::vector<ir::EncodedField*>* encodedFieldListOne,
+ std::vector<ir::EncodedField*>* encodedFieldListTwo) {
+ std::vector<std::string> fDescListOne =
+ getFieldDescriptions(encodedFieldListOne);
+ std::vector<std::string> fDescListTwo =
+ getFieldDescriptions(encodedFieldListTwo);
+
+ if (fDescListOne.size() != fDescListTwo.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < fDescListOne.size(); i++) {
+ if (fDescListOne[i] != fDescListTwo[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool matchMethods(std::vector<ir::EncodedMethod*>* encodedMethodListOne,
+ std::vector<ir::EncodedMethod*>* encodedMethodListTwo) {
+ std::vector<std::string> mDescListOne =
+ getMethodDescriptions(encodedMethodListOne);
+ std::vector<std::string> mDescListTwo =
+ getMethodDescriptions(encodedMethodListTwo);
+
+ if (mDescListOne.size() != mDescListTwo.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < mDescListOne.size(); i++) {
+ if (mDescListOne[i] != mDescListTwo[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool classStructureMatches(ir::Class* classOne, ir::Class* classTwo) {
+ return matchMethods(&(classOne->direct_methods),
+ &(classTwo->direct_methods)) &&
+ matchMethods(&(classOne->virtual_methods),
+ &(classTwo->virtual_methods)) &&
+ matchFields(&(classOne->static_fields), &(classTwo->static_fields)) &&
+ matchFields(&(classOne->instance_fields),
+ &(classTwo->instance_fields)) &&
+ classOne->access_flags == classTwo->access_flags;
+}
+
+bool DexFileManager::structureMatches(dex::Reader* oldReader,
+ dex::Reader* newReader,
+ const char* className) {
+ std::string descName = GetName(className);
+
+ dex::u4 oldReaderIndex = oldReader->FindClassIndex(descName.c_str());
+ dex::u4 newReaderIndex = newReader->FindClassIndex(descName.c_str());
+
+ if (!IsValidIndex(oldReaderIndex) || !IsValidIndex(newReaderIndex)) {
+ return false;
+ }
+
+ oldReader->CreateClassIr(oldReaderIndex);
+ newReader->CreateClassIr(newReaderIndex);
+
+ std::shared_ptr<ir::DexFile> oldDexFile = oldReader->GetIr();
+ std::shared_ptr<ir::DexFile> newDexFile = newReader->GetIr();
+
+ for (int i = 0; i < oldDexFile->classes.size(); i++) {
+ const char* oldClassDescriptor =
+ oldDexFile->classes[i]->type->descriptor->c_str();
+ if (strcmp(oldClassDescriptor, descName.c_str()) != 0) {
+ continue;
+ }
+
+ bool match = false;
+ for (int j = 0; j < newDexFile->classes.size(); j++) {
+ const char* newClassDescriptor =
+ newDexFile->classes[j]->type->descriptor->c_str();
+ if (strcmp(oldClassDescriptor, newClassDescriptor) == 0) {
+ match = classStructureMatches(oldDexFile->classes[i].get(),
+ newDexFile->classes[j].get());
+ break;
+ }
+ }
+
+ if (!match) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.h b/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.h
new file mode 100644
index 00000000..2b7dd67a
--- /dev/null
+++ b/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 Code Intelligence GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include "jvmti.h"
+#include "slicer/reader.h"
+
+// DexFileManager will contain the contents to multiple DEX files
+class DexFileManager {
+ public:
+ DexFileManager() {}
+
+ void addDexFile(const unsigned char* bytes, int length);
+ unsigned char* getClassBytes(const char* className, int dexFileIndex,
+ jvmtiEnv* jvmti, size_t* newSize);
+ uint32_t findDexFileForClass(const char* className);
+ bool structureMatches(dex::Reader* oldReader, dex::Reader* newReader,
+ const char* className);
+
+ private:
+ std::vector<unsigned char*> dexFiles;
+ std::vector<int> dexFilesSize;
+};
diff --git a/src/main/native/com/code_intelligence/jazzer/android/jazzer_jvmti_allocator.h b/src/main/native/com/code_intelligence/jazzer/android/jazzer_jvmti_allocator.h
new file mode 100644
index 00000000..0748c177
--- /dev/null
+++ b/src/main/native/com/code_intelligence/jazzer/android/jazzer_jvmti_allocator.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 Code Intelligence GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "slicer/writer.h"
+
+class JazzerJvmtiAllocator : public dex::Writer::Allocator {
+ public:
+ JazzerJvmtiAllocator(jvmtiEnv* jvmti_env) : jvmti_env_(jvmti_env) {}
+
+ virtual void* Allocate(size_t size) {
+ unsigned char* alloc = nullptr;
+ jvmtiError error_num = jvmti_env_->Allocate(size, &alloc);
+
+ if (error_num != JVMTI_ERROR_NONE) {
+ std::cerr << "JazzerJvmtiAllocator Allocation error. JVMTI error: "
+ << error_num << std::endl;
+ }
+
+ return (void*)alloc;
+ }
+
+ virtual void Free(void* ptr) {
+ if (ptr == nullptr) {
+ return;
+ }
+
+ jvmtiError error_num = jvmti_env_->Deallocate((unsigned char*)ptr);
+
+ if (error_num != JVMTI_ERROR_NONE) {
+ std::cout << "JazzerJvmtiAllocator Free error. JVMTI error: " << error_num
+ << std::endl;
+ }
+ }
+
+ private:
+ jvmtiEnv* jvmti_env_;
+};
diff --git a/src/main/native/com/code_intelligence/jazzer/android/native_agent.cpp b/src/main/native/com/code_intelligence/jazzer/android/native_agent.cpp
new file mode 100644
index 00000000..9f0b2ad8
--- /dev/null
+++ b/src/main/native/com/code_intelligence/jazzer/android/native_agent.cpp
@@ -0,0 +1,313 @@
+// Copyright 2023 Code Intelligence GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <dlfcn.h>
+#include <jni.h>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "absl/strings/str_split.h"
+#include "dex_file_manager.h"
+#include "jazzer_jvmti_allocator.h"
+#include "jvmti.h"
+#include "slicer/arrayview.h"
+#include "slicer/dex_format.h"
+#include "slicer/reader.h"
+#include "slicer/writer.h"
+
+static std::string agentOptions;
+static DexFileManager dfm;
+
+const std::string kAndroidAgentClass =
+ "com/code_intelligence/jazzer/android/DexFileManager";
+
+void retransformLoadedClasses(jvmtiEnv* jvmti, JNIEnv* env) {
+ jint classCount = 0;
+ jclass* classes;
+
+ jvmti->GetLoadedClasses(&classCount, &classes);
+
+ std::vector<jclass> classesToRetransform;
+ for (int i = 0; i < classCount; i++) {
+ jboolean isModifiable = false;
+ jvmti->IsModifiableClass(classes[i], &isModifiable);
+
+ if ((bool)isModifiable) {
+ classesToRetransform.push_back(classes[i]);
+ }
+ }
+
+ jvmtiError errorNum = jvmti->RetransformClasses(classesToRetransform.size(),
+ &classesToRetransform[0]);
+ if (errorNum != JVMTI_ERROR_NONE) {
+ std::cerr << "Could not retransform classes. JVMTI error: " << errorNum
+ << std::endl;
+ exit(1);
+ }
+}
+
+std::vector<std::string> getDexFiles(std::string jarPath, JNIEnv* env) {
+ jclass jazzerClass = env->FindClass(kAndroidAgentClass.c_str());
+ if (jazzerClass == nullptr) {
+ std::cerr << kAndroidAgentClass << " could not be found" << std::endl;
+ exit(1);
+ }
+
+ const char* getDexFilesFunction = "getDexFilesForJar";
+ jmethodID getDexFilesForJar =
+ env->GetStaticMethodID(jazzerClass, getDexFilesFunction,
+ "(Ljava/lang/String;)[Ljava/lang/String;");
+ if (getDexFilesForJar == nullptr) {
+ std::cerr << getDexFilesFunction << " could not be found\n";
+ exit(1);
+ }
+
+ jstring jJarFile = env->NewStringUTF(jarPath.data());
+ jobjectArray dexFilesArray = (jobjectArray)env->CallStaticObjectMethod(
+ jazzerClass, getDexFilesForJar, jJarFile);
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ exit(1);
+ }
+
+ int length = env->GetArrayLength(dexFilesArray);
+
+ std::vector<std::string> dexFilesResult;
+ for (int i = 0; i < length; i++) {
+ jstring dexFileJstring =
+ (jstring)env->GetObjectArrayElement(dexFilesArray, i);
+ const char* dexFileChars = env->GetStringUTFChars(dexFileJstring, NULL);
+ std::string dexFileString(dexFileChars);
+
+ env->ReleaseStringUTFChars(dexFileJstring, dexFileChars);
+ dexFilesResult.push_back(dexFileString);
+ }
+
+ return dexFilesResult;
+}
+
+void initializeBootclassOverrideJar(std::string jarPath, JNIEnv* env) {
+ std::vector<std::string> dexFiles = getDexFiles(jarPath, env);
+
+ std::cerr << "Adding DEX files for: " << jarPath << std::endl;
+ for (int i = 0; i < dexFiles.size(); i++) {
+ std::cerr << "DEX FILE: " << dexFiles[i] << std::endl;
+ }
+
+ for (int i = 0; i < dexFiles.size(); i++) {
+ jclass bootHelperClass = env->FindClass(kAndroidAgentClass.c_str());
+ if (bootHelperClass == nullptr) {
+ std::cerr << kAndroidAgentClass << " could not be found" << std::endl;
+ exit(1);
+ }
+
+ jmethodID getBytecodeFromDex =
+ env->GetStaticMethodID(bootHelperClass, "getBytecodeFromDex",
+ "(Ljava/lang/String;Ljava/lang/String;)[B");
+ if (getBytecodeFromDex == nullptr) {
+ std::cerr << "'getBytecodeFromDex' not found\n";
+ exit(1);
+ }
+
+ jstring jjarPath = env->NewStringUTF(jarPath.data());
+ jstring jdexFile = env->NewStringUTF(dexFiles[i].data());
+
+ int length = 1;
+ std::vector<unsigned char> dexFileBytes;
+
+ jbyteArray dexBytes = (jbyteArray)env->CallStaticObjectMethod(
+ bootHelperClass, getBytecodeFromDex, jjarPath, jdexFile);
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ exit(1);
+ }
+
+ jbyte* data = new jbyte;
+ data = env->GetByteArrayElements(dexBytes, 0);
+ length = env->GetArrayLength(dexBytes);
+
+ for (int j = 0; j < length; j++) {
+ dexFileBytes.push_back(data[j]);
+ }
+
+ env->DeleteLocalRef(dexBytes);
+ env->DeleteLocalRef(jjarPath);
+ env->DeleteLocalRef(jdexFile);
+ env->DeleteLocalRef(bootHelperClass);
+
+ unsigned char* usData = reinterpret_cast<unsigned char*>(&dexFileBytes[0]);
+ dfm.addDexFile(usData, length);
+ }
+}
+
+void JNICALL jazzerClassFileLoadHook(
+ jvmtiEnv* jvmti, JNIEnv* jni_env, jclass class_being_redefined,
+ jobject loader, const char* name, jobject protection_domain,
+ jint class_data_len, const unsigned char* class_data,
+ jint* new_class_data_len, unsigned char** new_class_data) {
+ // check if Jazzer class
+ const char* prefix = "com/code_intelligence/jazzer/";
+ if (strncmp(name, prefix, 29) == 0) {
+ return;
+ }
+
+ int indx = dfm.findDexFileForClass(name);
+ if (indx < 0) {
+ return;
+ }
+
+ size_t newSize;
+ unsigned char* newClassDataResult =
+ dfm.getClassBytes(name, indx, jvmti, &newSize);
+
+ dex::Reader oldReader(const_cast<unsigned char*>(class_data),
+ (size_t)class_data_len);
+ dex::Reader newReader(newClassDataResult, newSize);
+ if (dfm.structureMatches(&oldReader, &newReader, name)) {
+ std::cout << "REDEFINING WITH INSTRUMENTATION: " << name << std::endl;
+ *new_class_data = newClassDataResult;
+ *new_class_data_len = static_cast<jint>(newSize);
+ }
+}
+
+bool fileExists(std::string filePath) { return std::ifstream(filePath).good(); }
+
+void JNICALL jazzerVMInit(jvmtiEnv* jvmti_env, JNIEnv* jni_env,
+ jthread thread) {
+ // Parse agentOptions
+
+ std::stringstream ss(agentOptions);
+ std::string token;
+
+ std::string jazzerClassesJar;
+ std::vector<std::string> bootpathClassesOverrides;
+ while (std::getline(ss, token, ',')) {
+ std::vector<std::string> split =
+ absl::StrSplit(token, absl::MaxSplits('=', 1));
+ if (split.size() < 2) {
+ std::cerr << "ERROR: no option given for: " << token;
+ exit(1);
+ }
+
+ if (split[0] == "injectJars") {
+ jazzerClassesJar = split[1];
+ } else if (split[0] == "bootstrapClassOverrides") {
+ bootpathClassesOverrides =
+ absl::StrSplit(split[1], absl::MaxSplits(':', 10));
+ }
+ }
+
+ if (!fileExists(jazzerClassesJar)) {
+ std::cerr << "ERROR: Jazzer bootstrap class file not found at: "
+ << jazzerClassesJar << std::endl;
+ exit(1);
+ }
+
+ jvmti_env->AddToBootstrapClassLoaderSearch(jazzerClassesJar.c_str());
+
+ jvmtiCapabilities jazzerJvmtiCapabilities = {
+ .can_tag_objects = 0,
+ .can_generate_field_modification_events = 0,
+ .can_generate_field_access_events = 0,
+ .can_get_bytecodes = 0,
+ .can_get_synthetic_attribute = 0,
+ .can_get_owned_monitor_info = 0,
+ .can_get_current_contended_monitor = 0,
+ .can_get_monitor_info = 0,
+ .can_pop_frame = 0,
+ .can_redefine_classes = 1,
+ .can_signal_thread = 0,
+ .can_get_source_file_name = 1,
+ .can_get_line_numbers = 0,
+ .can_get_source_debug_extension = 0,
+ .can_access_local_variables = 0,
+ .can_maintain_original_method_order = 0,
+ .can_generate_single_step_events = 0,
+ .can_generate_exception_events = 0,
+ .can_generate_frame_pop_events = 0,
+ .can_generate_breakpoint_events = 0,
+ .can_suspend = 0,
+ .can_redefine_any_class = 0,
+ .can_get_current_thread_cpu_time = 0,
+ .can_get_thread_cpu_time = 0,
+ .can_generate_method_entry_events = 0,
+ .can_generate_method_exit_events = 0,
+ .can_generate_all_class_hook_events = 0,
+ .can_generate_compiled_method_load_events = 0,
+ .can_generate_monitor_events = 0,
+ .can_generate_vm_object_alloc_events = 0,
+ .can_generate_native_method_bind_events = 0,
+ .can_generate_garbage_collection_events = 0,
+ .can_generate_object_free_events = 0,
+ .can_force_early_return = 0,
+ .can_get_owned_monitor_stack_depth_info = 0,
+ .can_get_constant_pool = 0,
+ .can_set_native_method_prefix = 0,
+ .can_retransform_classes = 1,
+ .can_retransform_any_class = 0,
+ .can_generate_resource_exhaustion_heap_events = 0,
+ .can_generate_resource_exhaustion_threads_events = 0,
+ };
+
+ jvmtiError je = jvmti_env->AddCapabilities(&jazzerJvmtiCapabilities);
+ if (je != JVMTI_ERROR_NONE) {
+ std::cerr << "JVMTI ERROR: " << je << std::endl;
+ exit(1);
+ }
+
+ for (int i = 0; i < bootpathClassesOverrides.size(); i++) {
+ if (!fileExists(bootpathClassesOverrides[i])) {
+ std::cerr << "ERROR: Bootpath Class override jar not found at: "
+ << bootpathClassesOverrides[i] << std::endl;
+ exit(1);
+ }
+
+ initializeBootclassOverrideJar(bootpathClassesOverrides[i], jni_env);
+ }
+
+ retransformLoadedClasses(jvmti_env, jni_env);
+}
+
+JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
+ jvmtiEnv* jvmti = nullptr;
+ if (vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_2) != JNI_OK) {
+ return 1;
+ }
+
+ jvmtiEventCallbacks callbacks;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.ClassFileLoadHook = jazzerClassFileLoadHook;
+ callbacks.VMInit = jazzerVMInit;
+
+ jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
+ jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
+ jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
+
+ // Save the options string here, this is the only time it will be available
+ // however, we wont be able to use this to initialize until VMInit callback is
+ // called
+ agentOptions = std::string(options);
+ return 0;
+}