aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCory Barker <87733503+TheCoryBarker@users.noreply.github.com>2023-06-08 18:40:13 +0000
committerGitHub <noreply@github.com>2023-06-08 18:40:13 +0000
commit14855310ea4b85b4571c02e1e89ff64307286d05 (patch)
tree76f8f19ea0b06e6db8286f8cf0347e402fe3d604
parent1ba2ae03222fa60bff118ae054560fce643bf408 (diff)
downloadjazzer-api-14855310ea4b85b4571c02e1e89ff64307286d05.tar.gz
Add ability to fuzz bootstrap classes in Android (#737)
-rw-r--r--WORKSPACE.bazel9
-rw-r--r--repositories.bzl12
-rw-r--r--src/main/java/com/code_intelligence/jazzer/BUILD.bazel1
-rw-r--r--src/main/java/com/code_intelligence/jazzer/Jazzer.java43
-rw-r--r--src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel11
-rw-r--r--src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel57
-rw-r--r--src/main/java/com/code_intelligence/jazzer/android/DexFileManager.java68
-rw-r--r--src/main/java/com/code_intelligence/jazzer/driver/Opt.java8
-rw-r--r--src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel6
-rw-r--r--src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java6
-rw-r--r--src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java27
-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
-rw-r--r--src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel1
-rw-r--r--third_party/android/android_configure.bzl7
-rw-r--r--third_party/slicer.BUILD31
19 files changed, 929 insertions, 15 deletions
diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel
index b53d7b78..ab0ed1cf 100644
--- a/WORKSPACE.bazel
+++ b/WORKSPACE.bazel
@@ -3,7 +3,7 @@ workspace(name = "jazzer")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file", "http_jar")
load("//:repositories.bzl", "jazzer_dependencies")
-jazzer_dependencies()
+jazzer_dependencies(android = True)
load("//:init.bzl", "jazzer_init")
@@ -291,3 +291,10 @@ cc_library(
strip_prefix = "libprotobuf-mutator-1.1",
urls = ["https://github.com/google/libprotobuf-mutator/archive/refs/tags/v1.1.tar.gz"],
)
+
+http_file(
+ name = "android_jvmti",
+ downloaded_file_path = "jvmti.encoded",
+ sha256 = "95bd6fb4f296ff1c49b893c1d3a665de3c2b1beaa3cc8fc570dea992202daa35",
+ url = "https://android.googlesource.com/platform/art/+/1cff8449bac0fdab6e84dc9255c3cccd504c1705/openjdkjvmti/include/jvmti.h?format=TEXT",
+)
diff --git a/repositories.bzl b/repositories.bzl
index 2d13fba3..46451614 100644
--- a/repositories.bzl
+++ b/repositories.bzl
@@ -16,8 +16,9 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_jar")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
-def jazzer_dependencies():
+def jazzer_dependencies(android = False):
maybe(
http_archive,
name = "platforms",
@@ -182,3 +183,12 @@ def jazzer_dependencies():
strip_prefix = "llvm-project-jazzer-2023-04-25/compiler-rt/lib/fuzzer",
url = "https://github.com/CodeIntelligenceTesting/llvm-project-jazzer/archive/refs/tags/2023-04-25.tar.gz",
)
+
+ if android:
+ maybe(
+ git_repository,
+ name = "jazzer_slicer",
+ remote = "https://android.googlesource.com/platform/tools/dexter",
+ build_file = "//third_party:slicer.BUILD",
+ commit = "0fe35538da107ff48da6e9f9b92b55b014973bf8",
+ )
diff --git a/src/main/java/com/code_intelligence/jazzer/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/BUILD.bazel
index ea3eaa6b..660e4b14 100644
--- a/src/main/java/com/code_intelligence/jazzer/BUILD.bazel
+++ b/src/main/java/com/code_intelligence/jazzer/BUILD.bazel
@@ -119,6 +119,7 @@ java_library(
"//src/main/java/com/code_intelligence/jazzer/driver",
"//src/main/java/com/code_intelligence/jazzer/runtime:constants",
"//src/main/java/com/code_intelligence/jazzer/utils:log",
+ "//src/main/java/com/code_intelligence/jazzer/utils:zip_utils",
"@fmeum_rules_jni//jni/tools/native_loader",
],
)
diff --git a/src/main/java/com/code_intelligence/jazzer/Jazzer.java b/src/main/java/com/code_intelligence/jazzer/Jazzer.java
index 449e78c3..e50693c7 100644
--- a/src/main/java/com/code_intelligence/jazzer/Jazzer.java
+++ b/src/main/java/com/code_intelligence/jazzer/Jazzer.java
@@ -26,6 +26,7 @@ import static java.util.stream.Collectors.toSet;
import com.code_intelligence.jazzer.driver.Driver;
import com.code_intelligence.jazzer.utils.Log;
+import com.code_intelligence.jazzer.utils.ZipUtils;
import com.github.fmeum.rules_jni.RulesJni;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -265,7 +266,42 @@ public class Jazzer {
return Paths.get(System.getProperty("java.home"), "bin", javaBinaryName);
}
- private static Stream<String> javaBinaryArgs() {
+ private static Stream<String> javaBinaryArgs() throws IOException {
+ if (IS_ANDROID) {
+ // Add Android specific args
+ Path agentPath =
+ RulesJni.extractLibrary("android_native_agent", "/com/code_intelligence/jazzer/android");
+
+ String jazzerAgentPath = System.getProperty("jazzer.agent_path");
+ String bootclassClassOverrides =
+ System.getProperty("jazzer.android_bootpath_classes_overrides");
+
+ String jazzerBootstrapJarPath =
+ "com/code_intelligence/jazzer/android/jazzer_bootstrap_android.jar";
+ String jazzerBootstrapJarOut = "/data/local/tmp/jazzer_bootstrap_android.jar";
+
+ try {
+ ZipUtils.extractFile(jazzerAgentPath, jazzerBootstrapJarPath, jazzerBootstrapJarOut);
+ } catch (IOException ioe) {
+ Log.error(
+ "Could not extract jazzer_bootstrap_android.jar from Jazzer standalone agent", ioe);
+ exit(1);
+ }
+
+ String nativeAgentOptions = "injectJars=" + jazzerBootstrapJarOut;
+ if (bootclassClassOverrides != null && !bootclassClassOverrides.isEmpty()) {
+ nativeAgentOptions += ",bootstrapClassOverrides=" + bootclassClassOverrides;
+ }
+
+ // ManagementFactory wont work with Android
+ Stream<String> stream =
+ Stream.of("-cp", System.getProperty("java.class.path"), "-Xplugin:libopenjdkjvmti.so",
+ "-agentpath:" + agentPath.toString() + "=" + nativeAgentOptions, "-Xcompiler-option",
+ "--debuggable", "-Djdk.attach.allowAttachSelf=true", Jazzer.class.getName());
+
+ return stream;
+ }
+
Stream<String> stream = Stream.of("-cp", System.getProperty("java.class.path"),
// Make ByteBuddyAgent's job simpler by allowing it to attach directly to the JVM
// rather than relying on an external helper. The latter fails on macOS 12 with JDK 11+
@@ -277,11 +313,6 @@ public class Jazzer {
// entitlements required for library insertion.
"-Djdk.attach.allowAttachSelf=true", Jazzer.class.getName());
- if (IS_ANDROID) {
- // ManagementFactory wont work with Android
- return stream;
- }
-
return Stream.concat(ManagementFactory.getRuntimeMXBean().getInputArguments().stream(), stream);
}
diff --git a/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel
index 3106acb7..89acbda3 100644
--- a/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel
+++ b/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel
@@ -4,9 +4,14 @@ load("//bazel:kotlin.bzl", "ktlint")
java_library(
name = "agent_installer",
srcs = ["AgentInstaller.java"],
- resources = [
- "//src/main/java/com/code_intelligence/jazzer/runtime:jazzer_bootstrap",
- ],
+ resources = select({
+ "@platforms//os:android": [
+ "//src/main/java/com/code_intelligence/jazzer/android:jazzer_bootstrap_android",
+ ],
+ "//conditions:default": [
+ "//src/main/java/com/code_intelligence/jazzer/runtime:jazzer_bootstrap",
+ ],
+ }),
visibility = ["//visibility:public"],
deps = [
":agent_lib",
diff --git a/src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel
index 627544a7..98b7d11f 100644
--- a/src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel
+++ b/src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel
@@ -1,4 +1,57 @@
load("//bazel:compat.bzl", "SKIP_ON_WINDOWS")
+load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
+load("@fmeum_rules_jni//jni:defs.bzl", "java_jni_library")
+
+java_import(
+ name = "jazzer_bootstrap_android_import",
+ jars = [
+ "//src/main/java/com/code_intelligence/jazzer/runtime:jazzer_bootstrap",
+ ],
+ tags = ["manual"],
+ target_compatible_with = SKIP_ON_WINDOWS,
+)
+
+android_library(
+ name = "jazzer_bootstrap_android_lib",
+ tags = ["manual"],
+ target_compatible_with = SKIP_ON_WINDOWS,
+ visibility = [
+ "//src/main/java/com/code_intelligence/jazzer/agent:__pkg__",
+ ],
+ exports = [
+ ":jazzer_bootstrap_android_import",
+ ],
+)
+
+android_binary(
+ name = "jazzer_bootstrap_android_bin",
+ manifest = "//launcher/android:android_manifest",
+ min_sdk_version = 26,
+ tags = ["manual"],
+ target_compatible_with = SKIP_ON_WINDOWS,
+ deps = [
+ ":jazzer_bootstrap_android_lib",
+ ],
+)
+
+copy_file(
+ name = "jazzer_bootstrap_android",
+ src = "jazzer_bootstrap_android_bin.apk",
+ out = "jazzer_bootstrap_android.jar",
+ tags = ["manual"],
+ target_compatible_with = SKIP_ON_WINDOWS,
+ visibility = [
+ "//src/main/java/com/code_intelligence/jazzer/agent:__pkg__",
+ ],
+)
+
+java_jni_library(
+ name = "dex_file_manager",
+ srcs = ["DexFileManager.java"],
+ native_libs = [
+ "//src/main/native/com/code_intelligence/jazzer/android:android_native_agent",
+ ],
+)
android_library(
name = "jazzer_standalone_library",
@@ -6,10 +59,7 @@ android_library(
target_compatible_with = SKIP_ON_WINDOWS,
exports = [
"//deploy:jazzer-api",
- "//sanitizers:offline_only_sanitizers",
"//src/main/java/com/code_intelligence/jazzer:jazzer_import",
- "//src/main/java/com/code_intelligence/jazzer/runtime",
- "//src/main/java/com/code_intelligence/jazzer/utils:unsafe_provider",
],
)
@@ -24,6 +74,7 @@ android_binary(
"//launcher/android:__pkg__",
],
deps = [
+ ":dex_file_manager",
":jazzer_standalone_library",
],
)
diff --git a/src/main/java/com/code_intelligence/jazzer/android/DexFileManager.java b/src/main/java/com/code_intelligence/jazzer/android/DexFileManager.java
new file mode 100644
index 00000000..23d2eeec
--- /dev/null
+++ b/src/main/java/com/code_intelligence/jazzer/android/DexFileManager.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.code_intelligence.jazzer.android;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+public class DexFileManager {
+ private final static int MAX_READ_LENGTH = 2000000;
+
+ public static byte[] getBytecodeFromDex(String jarPath, String dexFile) throws IOException {
+ try (JarFile jarFile = new JarFile(jarPath)) {
+ JarEntry entry = jarFile.stream()
+ .filter(jarEntry -> jarEntry.getName().equals(dexFile))
+ .findFirst()
+ .orElse(null);
+
+ if (entry == null) {
+ throw new IOException("Could not find dex file: " + dexFile);
+ }
+
+ try (InputStream is = jarFile.getInputStream(entry)) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ byte[] buffer = new byte[64 * 104 * 1024];
+ int read;
+ while ((read = is.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+
+ return out.toByteArray();
+ }
+ }
+ }
+
+ public static String[] getDexFilesForJar(String jarpath) throws IOException {
+ try (JarFile jarFile = new JarFile(jarpath)) {
+ return jarFile.stream()
+ .map(JarEntry::getName)
+ .filter(entry -> entry.endsWith(".dex"))
+ .toArray(String[] ::new);
+ }
+ }
+}
diff --git a/src/main/java/com/code_intelligence/jazzer/driver/Opt.java b/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
index 78accfd6..723540a8 100644
--- a/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
+++ b/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
@@ -153,6 +153,14 @@ public final class Opt {
public static final boolean dedup =
boolSetting("dedup", hooks, "Compute and print a deduplication token for every finding");
+ public static final String androidBootclassJarPath = stringSetting("android_bootclass_jar_path",
+ null,
+ "Full path to booclass jar path that will be used on Android runs. If you are using the launcher this will be set for you.");
+
+ public static final String androidBootclassClassesOverrides = stringSetting(
+ "android_bootpath_classes_overrides", null,
+ "Used for fuzzing classes loaded in through the bootstrap class loader on Android. Full path to jar file with the instrumented versions of the classes you want to override.");
+
// Whether hook instrumentation should add a check for JazzerInternal#hooksEnabled before
// executing hooks. Used to disable hooks during non-fuzz JUnit tests.
public static final boolean conditionalHooks =
diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
index 6198f085..c31c86e4 100644
--- a/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
+++ b/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
@@ -53,6 +53,7 @@ strip_jar(
],
visibility = [
"//src/main/java/com/code_intelligence/jazzer/agent:__pkg__",
+ "//src/main/java/com/code_intelligence/jazzer/android:__pkg__",
],
)
@@ -98,12 +99,17 @@ java_library(
java_jni_library(
name = "coverage_map",
srcs = ["CoverageMap.java"],
+ native_libs = select({
+ "@platforms//os:android": ["//src/main/native/com/code_intelligence/jazzer/driver:jazzer_driver"],
+ "//conditions:default": [],
+ }),
visibility = [
"//src/jmh/java/com/code_intelligence/jazzer/instrumentor:__pkg__",
"//src/main/native/com/code_intelligence/jazzer/driver:__pkg__",
"//src/test:__subpackages__",
],
deps = [
+ "//src/main/java/com/code_intelligence/jazzer/runtime:constants",
"//src/main/java/com/code_intelligence/jazzer/utils:unsafe_provider",
],
)
diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java b/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java
index d62465b9..a945a30a 100644
--- a/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java
+++ b/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java
@@ -14,6 +14,8 @@
package com.code_intelligence.jazzer.runtime;
+import static com.code_intelligence.jazzer.runtime.Constants.IS_ANDROID;
+
import com.code_intelligence.jazzer.utils.UnsafeProvider;
import com.github.fmeum.rules_jni.RulesJni;
import java.lang.invoke.MethodHandle;
@@ -110,6 +112,10 @@ final public class CoverageMap {
// Called by the coverage instrumentation.
@SuppressWarnings("unused")
public static void recordCoverage(final int id) {
+ if (IS_ANDROID) {
+ enlargeIfNeeded(id);
+ }
+
final long address = countersAddress + id;
final byte counter = UNSAFE.getByte(address);
UNSAFE.putByte(address, (byte) (counter == -1 ? 1 : counter + 1));
diff --git a/src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java b/src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java
index c637e616..4da35c3f 100644
--- a/src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java
+++ b/src/main/java/com/code_intelligence/jazzer/utils/ZipUtils.java
@@ -16,12 +16,15 @@
package com.code_intelligence.jazzer.utils;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.IllegalArgumentException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
@@ -38,6 +41,7 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public final class ZipUtils {
@@ -96,4 +100,27 @@ public final class ZipUtils {
return filesAdded;
}
+
+ public static void extractFile(String srcZip, String targetFile, String outputFilePath)
+ throws IOException {
+ try (OutputStream out = new FileOutputStream(outputFilePath);
+ ZipInputStream zis = new ZipInputStream(new FileInputStream(srcZip));) {
+ ZipEntry ze = zis.getNextEntry();
+ while (ze != null) {
+ if (ze.getName().equals(targetFile)) {
+ byte[] buf = new byte[1024];
+ int read = 0;
+
+ while ((read = zis.read(buf)) > -1) {
+ out.write(buf, 0, read);
+ }
+
+ out.close();
+ break;
+ }
+
+ ze = zis.getNextEntry();
+ }
+ }
+ }
}
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;
+}
diff --git a/src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel b/src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel
index 7e233b91..59c62aed 100644
--- a/src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel
+++ b/src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel
@@ -8,6 +8,7 @@ cc_jni_library(
"//src/jmh:__subpackages__",
"//src/main/java/com/code_intelligence/jazzer/driver:__pkg__",
"//src/main/java/com/code_intelligence/jazzer/junit:__pkg__",
+ "//src/main/java/com/code_intelligence/jazzer/runtime:__pkg__",
"//src/test:__subpackages__",
],
deps = [
diff --git a/third_party/android/android_configure.bzl b/third_party/android/android_configure.bzl
index 4b634b4f..33118904 100644
--- a/third_party/android/android_configure.bzl
+++ b/third_party/android/android_configure.bzl
@@ -20,13 +20,18 @@ def _is_windows(repository_ctx):
"""Returns true if the current platform is Windows"""
return repository_ctx.os.name.lower().startswith("windows")
+def _supports_android(repository_ctx):
+ sdk_home = repository_ctx.os.environ.get(_ANDROID_SDK_HOME)
+ ndk_home = repository_ctx.os.environ.get(_ANDROID_NDK_HOME)
+ return sdk_home and ndk_home and not _is_windows(repository_ctx)
+
def _android_autoconf_impl(repository_ctx):
"""Implementation of the android_autoconf repo rule"""
sdk_home = repository_ctx.os.environ.get(_ANDROID_SDK_HOME)
ndk_home = repository_ctx.os.environ.get(_ANDROID_NDK_HOME)
# rules_android_ndk does not support Windows yet.
- if sdk_home and ndk_home and not _is_windows(repository_ctx):
+ if _supports_android(repository_ctx):
repos = _ANDROID_REPOS_TEMPLATE.format(
sdk_home = repr(sdk_home),
ndk_home = repr(ndk_home),
diff --git a/third_party/slicer.BUILD b/third_party/slicer.BUILD
new file mode 100644
index 00000000..a7bc7b67
--- /dev/null
+++ b/third_party/slicer.BUILD
@@ -0,0 +1,31 @@
+cc_library(
+ name = "jazzer_slicer",
+ srcs = [
+ "slicer/bytecode_encoder.cc",
+ "slicer/code_ir.cc",
+ "slicer/common.cc",
+ "slicer/control_flow_graph.cc",
+ "slicer/debuginfo_encoder.cc",
+ "slicer/dex_bytecode.cc",
+ "slicer/dex_format.cc",
+ "slicer/dex_ir.cc",
+ "slicer/dex_ir_builder.cc",
+ "slicer/dex_utf8.cc",
+ "slicer/instrumentation.cc",
+ "slicer/reader.cc",
+ "slicer/tryblocks_encoder.cc",
+ "slicer/writer.cc",
+ ],
+ hdrs = glob(["slicer/export/slicer/*.h"]),
+ copts = [
+ "-Wall",
+ "-Wno-sign-compare",
+ "-Wno-unused-parameter",
+ "-Wno-shift-count-overflow",
+ "-Wno-missing-braces",
+ ],
+ includes = ["slicer/export"],
+ visibility = [
+ "//visibility:public",
+ ],
+)