aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/fuzzer.zig62
-rw-r--r--lib/std/Build/Module.zig4
-rw-r--r--lib/std/mem.zig12
-rw-r--r--lib/std/os/linux/start_pie.zig1
-rw-r--r--lib/std/os/linux/tls.zig72
-rw-r--r--lib/std/start.zig8
-rw-r--r--lib/std/zig/AstGen.zig14
-rw-r--r--lib/std/zig/AstRlAnnotate.zig1
-rw-r--r--lib/std/zig/BuiltinFn.zig9
-rw-r--r--lib/std/zig/Zir.zig4
-rw-r--r--src/Builtin.zig3
-rw-r--r--src/Compilation.zig100
-rw-r--r--src/Compilation/Config.zig3
-rw-r--r--src/InternPool.zig20
-rw-r--r--src/Package/Module.zig13
-rw-r--r--src/Sema.zig13
-rw-r--r--src/codegen/llvm.zig25
-rw-r--r--src/codegen/llvm/bindings.zig1
-rw-r--r--src/link/Coff/lld.zig4
-rw-r--r--src/link/Elf.zig14
-rw-r--r--src/link/MachO.zig9
-rw-r--r--src/main.zig34
-rw-r--r--src/print_zir.zig1
-rw-r--r--src/zig_llvm.cpp82
-rw-r--r--src/zig_llvm.h2
-rw-r--r--stage1/zig1.wasmbin2826391 -> 2730397 bytes
26 files changed, 406 insertions, 105 deletions
diff --git a/lib/fuzzer.zig b/lib/fuzzer.zig
new file mode 100644
index 0000000000..49dd33894b
--- /dev/null
+++ b/lib/fuzzer.zig
@@ -0,0 +1,62 @@
+const std = @import("std");
+
+export threadlocal var __sancov_lowest_stack: usize = 0;
+
+export fn __sanitizer_cov_8bit_counters_init(start: [*]u8, stop: [*]u8) void {
+ std.debug.print("__sanitizer_cov_8bit_counters_init start={*}, stop={*}\n", .{ start, stop });
+}
+
+export fn __sanitizer_cov_pcs_init(pcs_beg: [*]const usize, pcs_end: [*]const usize) void {
+ std.debug.print("__sanitizer_cov_pcs_init pcs_beg={*}, pcs_end={*}\n", .{ pcs_beg, pcs_end });
+}
+
+export fn __sanitizer_cov_trace_const_cmp1(arg1: u8, arg2: u8) void {
+ handleCmp(@returnAddress(), arg1, arg2);
+}
+
+export fn __sanitizer_cov_trace_cmp1(arg1: u8, arg2: u8) void {
+ handleCmp(@returnAddress(), arg1, arg2);
+}
+
+export fn __sanitizer_cov_trace_const_cmp2(arg1: u16, arg2: u16) void {
+ handleCmp(@returnAddress(), arg1, arg2);
+}
+
+export fn __sanitizer_cov_trace_cmp2(arg1: u16, arg2: u16) void {
+ handleCmp(@returnAddress(), arg1, arg2);
+}
+
+export fn __sanitizer_cov_trace_const_cmp4(arg1: u32, arg2: u32) void {
+ handleCmp(@returnAddress(), arg1, arg2);
+}
+
+export fn __sanitizer_cov_trace_cmp4(arg1: u32, arg2: u32) void {
+ handleCmp(@returnAddress(), arg1, arg2);
+}
+
+export fn __sanitizer_cov_trace_const_cmp8(arg1: u64, arg2: u64) void {
+ handleCmp(@returnAddress(), arg1, arg2);
+}
+
+export fn __sanitizer_cov_trace_cmp8(arg1: u64, arg2: u64) void {
+ handleCmp(@returnAddress(), arg1, arg2);
+}
+
+export fn __sanitizer_cov_trace_switch(val: u64, cases_ptr: [*]u64) void {
+ const pc = @returnAddress();
+ const len = cases_ptr[0];
+ const val_size_in_bits = cases_ptr[1];
+ const cases = cases_ptr[2..][0..len];
+ std.debug.print("0x{x}: switch on value {d} ({d} bits) with {d} cases\n", .{
+ pc, val, val_size_in_bits, cases.len,
+ });
+}
+
+export fn __sanitizer_cov_trace_pc_indir(callee: usize) void {
+ const pc = @returnAddress();
+ std.debug.print("0x{x}: indirect call to 0x{x}\n", .{ pc, callee });
+}
+
+fn handleCmp(pc: usize, arg1: u64, arg2: u64) void {
+ std.debug.print("0x{x}: comparison of {d} and {d}\n", .{ pc, arg1, arg2 });
+}
diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig
index 844f69c8cd..635e6cc334 100644
--- a/lib/std/Build/Module.zig
+++ b/lib/std/Build/Module.zig
@@ -28,6 +28,7 @@ stack_protector: ?bool,
stack_check: ?bool,
sanitize_c: ?bool,
sanitize_thread: ?bool,
+fuzz: ?bool,
code_model: std.builtin.CodeModel,
valgrind: ?bool,
pic: ?bool,
@@ -186,6 +187,7 @@ pub const CreateOptions = struct {
stack_check: ?bool = null,
sanitize_c: ?bool = null,
sanitize_thread: ?bool = null,
+ fuzz: ?bool = null,
/// Whether to emit machine code that integrates with Valgrind.
valgrind: ?bool = null,
/// Position Independent Code
@@ -228,6 +230,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St
.stack_check = options.stack_check,
.sanitize_c = options.sanitize_c,
.sanitize_thread = options.sanitize_thread,
+ .fuzz = options.fuzz,
.code_model = options.code_model,
.valgrind = options.valgrind,
.pic = options.pic,
@@ -642,6 +645,7 @@ pub fn appendZigProcessFlags(
try addFlag(zig_args, m.error_tracing, "-ferror-tracing", "-fno-error-tracing");
try addFlag(zig_args, m.sanitize_c, "-fsanitize-c", "-fno-sanitize-c");
try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread");
+ try addFlag(zig_args, m.fuzz, "-ffuzz", "-fno-fuzz");
try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind");
try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");
diff --git a/lib/std/mem.zig b/lib/std/mem.zig
index 20d68b8937..4cd5ee841d 100644
--- a/lib/std/mem.zig
+++ b/lib/std/mem.zig
@@ -636,18 +636,20 @@ test lessThan {
try testing.expect(lessThan(u8, "", "a"));
}
-const backend_can_use_eql_bytes = switch (builtin.zig_backend) {
+const eqlBytes_allowed = switch (builtin.zig_backend) {
// The SPIR-V backend does not support the optimized path yet.
.stage2_spirv64 => false,
// The RISC-V does not support vectors.
.stage2_riscv64 => false,
- else => true,
+ // The naive memory comparison implementation is more useful for fuzzers to
+ // find interesting inputs.
+ else => !builtin.fuzz,
};
/// Compares two slices and returns whether they are equal.
pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
if (@sizeOf(T) == 0) return true;
- if (!@inComptime() and std.meta.hasUniqueRepresentation(T) and backend_can_use_eql_bytes) return eqlBytes(sliceAsBytes(a), sliceAsBytes(b));
+ if (!@inComptime() and std.meta.hasUniqueRepresentation(T) and eqlBytes_allowed) return eqlBytes(sliceAsBytes(a), sliceAsBytes(b));
if (a.len != b.len) return false;
if (a.len == 0 or a.ptr == b.ptr) return true;
@@ -660,9 +662,7 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
/// std.mem.eql heavily optimized for slices of bytes.
fn eqlBytes(a: []const u8, b: []const u8) bool {
- if (!backend_can_use_eql_bytes) {
- return eql(u8, a, b);
- }
+ comptime assert(eqlBytes_allowed);
if (a.len != b.len) return false;
if (a.len == 0 or a.ptr == b.ptr) return true;
diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig
index df909a6230..e7355021bb 100644
--- a/lib/std/os/linux/start_pie.zig
+++ b/lib/std/os/linux/start_pie.zig
@@ -71,6 +71,7 @@ fn getDynamicSymbol() [*]elf.Dyn {
pub fn relocate(phdrs: []elf.Phdr) void {
@setRuntimeSafety(false);
+ @disableInstrumentation();
const dynv = getDynamicSymbol();
// Recover the delta applied by the loader by comparing the effective and
diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig
index 82dc3bf759..5eedb769b5 100644
--- a/lib/std/os/linux/tls.zig
+++ b/lib/std/os/linux/tls.zig
@@ -110,6 +110,8 @@ const TLSImage = struct {
pub var tls_image: TLSImage = undefined;
pub fn setThreadPointer(addr: usize) void {
+ @setRuntimeSafety(false);
+ @disableInstrumentation();
switch (native_arch) {
.x86 => {
var user_desc: linux.user_desc = .{
@@ -125,7 +127,7 @@ pub fn setThreadPointer(addr: usize) void {
.useable = 1,
},
};
- const rc = linux.syscall1(.set_thread_area, @intFromPtr(&user_desc));
+ const rc = @call(.always_inline, linux.syscall1, .{ .set_thread_area, @intFromPtr(&user_desc) });
assert(rc == 0);
const gdt_entry_number = user_desc.entry_number;
@@ -138,7 +140,7 @@ pub fn setThreadPointer(addr: usize) void {
);
},
.x86_64 => {
- const rc = linux.syscall2(.arch_prctl, linux.ARCH.SET_FS, addr);
+ const rc = @call(.always_inline, linux.syscall2, .{ .arch_prctl, linux.ARCH.SET_FS, addr });
assert(rc == 0);
},
.aarch64, .aarch64_be => {
@@ -149,7 +151,7 @@ pub fn setThreadPointer(addr: usize) void {
);
},
.arm, .thumb => {
- const rc = linux.syscall1(.set_tls, addr);
+ const rc = @call(.always_inline, linux.syscall1, .{ .set_tls, addr });
assert(rc == 0);
},
.riscv64 => {
@@ -160,7 +162,7 @@ pub fn setThreadPointer(addr: usize) void {
);
},
.mips, .mipsel, .mips64, .mips64el => {
- const rc = linux.syscall1(.set_thread_area, addr);
+ const rc = @call(.always_inline, linux.syscall1, .{ .set_thread_area, addr });
assert(rc == 0);
},
.powerpc, .powerpcle => {
@@ -189,6 +191,9 @@ pub fn setThreadPointer(addr: usize) void {
}
fn initTLS(phdrs: []elf.Phdr) void {
+ @setRuntimeSafety(false);
+ @disableInstrumentation();
+
var tls_phdr: ?*elf.Phdr = null;
var img_base: usize = 0;
@@ -236,7 +241,7 @@ fn initTLS(phdrs: []elf.Phdr) void {
l += tls_align_factor - delta;
l += @sizeOf(CustomData);
tcb_offset = l;
- l += mem.alignForward(usize, tls_tcb_size, tls_align_factor);
+ l += alignForward(tls_tcb_size, tls_align_factor);
data_offset = l;
l += tls_data_alloc_size;
break :blk l;
@@ -244,14 +249,14 @@ fn initTLS(phdrs: []elf.Phdr) void {
.VariantII => blk: {
var l: usize = 0;
data_offset = l;
- l += mem.alignForward(usize, tls_data_alloc_size, tls_align_factor);
+ l += alignForward(tls_data_alloc_size, tls_align_factor);
// The thread pointer is aligned to p_align
tcb_offset = l;
l += tls_tcb_size;
// The CustomData structure is right after the TCB with no padding
// in between so it can be easily found
l += @sizeOf(CustomData);
- l = mem.alignForward(usize, l, @alignOf(DTV));
+ l = alignForward(l, @alignOf(DTV));
dtv_offset = l;
l += @sizeOf(DTV);
break :blk l;
@@ -270,13 +275,28 @@ fn initTLS(phdrs: []elf.Phdr) void {
};
}
+/// Inline because TLS is not set up yet.
+inline fn alignForward(addr: usize, alignment: usize) usize {
+ return alignBackward(addr + (alignment - 1), alignment);
+}
+
+/// Inline because TLS is not set up yet.
+inline fn alignBackward(addr: usize, alignment: usize) usize {
+ return addr & ~(alignment - 1);
+}
+
+/// Inline because TLS is not set up yet.
inline fn alignPtrCast(comptime T: type, ptr: [*]u8) *T {
return @ptrCast(@alignCast(ptr));
}
/// Initializes all the fields of the static TLS area and returns the computed
/// architecture-specific value of the thread-pointer register
+///
+/// This function is inline because thread local storage is not set up yet.
pub fn prepareTLS(area: []u8) usize {
+ @setRuntimeSafety(false);
+ @disableInstrumentation();
// Clear the area we're going to use, just to be safe
@memset(area, 0);
// Prepare the DTV
@@ -310,6 +330,9 @@ pub fn prepareTLS(area: []u8) usize {
var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
pub fn initStaticTLS(phdrs: []elf.Phdr) void {
+ @setRuntimeSafety(false);
+ @disableInstrumentation();
+
initTLS(phdrs);
const tls_area = blk: {
@@ -321,22 +344,47 @@ pub fn initStaticTLS(phdrs: []elf.Phdr) void {
break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
}
- const alloc_tls_area = posix.mmap(
+ const begin_addr = mmap(
null,
tls_image.alloc_size + tls_image.alloc_align - 1,
posix.PROT.READ | posix.PROT.WRITE,
.{ .TYPE = .PRIVATE, .ANONYMOUS = true },
-1,
0,
- ) catch posix.abort();
+ );
+ if (@as(isize, @bitCast(begin_addr)) < 0) @trap();
+ const alloc_tls_area: [*]align(mem.page_size) u8 = @ptrFromInt(begin_addr);
// Make sure the slice is correctly aligned.
- const begin_addr = @intFromPtr(alloc_tls_area.ptr);
- const begin_aligned_addr = mem.alignForward(usize, begin_addr, tls_image.alloc_align);
+ const begin_aligned_addr = alignForward(begin_addr, tls_image.alloc_align);
const start = begin_aligned_addr - begin_addr;
- break :blk alloc_tls_area[start .. start + tls_image.alloc_size];
+ break :blk alloc_tls_area[start..][0..tls_image.alloc_size];
};
const tp_value = prepareTLS(tls_area);
setThreadPointer(tp_value);
}
+
+inline fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: linux.MAP, fd: i32, offset: i64) usize {
+ if (@hasField(linux.SYS, "mmap2")) {
+ return @call(.always_inline, linux.syscall6, .{
+ .mmap2,
+ @intFromPtr(address),
+ length,
+ prot,
+ @as(u32, @bitCast(flags)),
+ @as(usize, @bitCast(@as(isize, fd))),
+ @as(usize, @truncate(@as(u64, @bitCast(offset)) / linux.MMAP2_UNIT)),
+ });
+ } else {
+ return @call(.always_inline, linux.syscall6, .{
+ .mmap,
+ @intFromPtr(address),
+ length,
+ prot,
+ @as(u32, @bitCast(flags)),
+ @as(usize, @bitCast(@as(isize, fd))),
+ @as(u64, @bitCast(offset)),
+ });
+ }
+}
diff --git a/lib/std/start.zig b/lib/std/start.zig
index caca4c7be3..33099d5f30 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -411,6 +411,10 @@ fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn {
}
fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn {
+ // We're not ready to panic until thread local storage is initialized.
+ @setRuntimeSafety(false);
+ // Code coverage instrumentation might try to use thread local variables.
+ @disableInstrumentation();
const argc = argc_argv_ptr[0];
const argv = @as([*][*:0]u8, @ptrCast(argc_argv_ptr + 1));
@@ -453,9 +457,9 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.C) noreturn {
if (comptime native_arch.isARM()) {
if (at_hwcap & std.os.linux.HWCAP.TLS == 0) {
// FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls
- // For the time being use a simple abort instead of a @panic call to
+ // For the time being use a simple trap instead of a @panic call to
// keep the binary bloat under control.
- std.posix.abort();
+ @trap();
}
}
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig
index aa61714434..a6be743c2b 100644
--- a/lib/std/zig/AstGen.zig
+++ b/lib/std/zig/AstGen.zig
@@ -2817,6 +2817,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
.breakpoint,
+ .disable_instrumentation,
.fence,
.set_float_mode,
.set_align_stack,
@@ -9305,12 +9306,13 @@ fn builtinCall(
},
// zig fmt: off
- .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node),
- .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node),
- .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
- .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
- .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
- .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
+ .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node),
+ .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node),
+ .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
+ .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
+ .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
+ .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
+ .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node),
.type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
.size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig
index 543c279981..e956ffd2a9 100644
--- a/lib/std/zig/AstRlAnnotate.zig
+++ b/lib/std/zig/AstRlAnnotate.zig
@@ -877,6 +877,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
.error_return_trace,
.frame,
.breakpoint,
+ .disable_instrumentation,
.in_comptime,
.panic,
.trap,
diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig
index 4bea0278fa..fc08f9eb85 100644
--- a/lib/std/zig/BuiltinFn.zig
+++ b/lib/std/zig/BuiltinFn.zig
@@ -15,6 +15,7 @@ pub const Tag = enum {
int_from_bool,
bit_size_of,
breakpoint,
+ disable_instrumentation,
mul_add,
byte_swap,
bit_reverse,
@@ -264,6 +265,14 @@ pub const list = list: {
},
},
.{
+ "@disableInstrumentation",
+ .{
+ .tag = .disable_instrumentation,
+ .param_count = 0,
+ .illegal_outside_function = true,
+ },
+ },
+ .{
"@mulAdd",
.{
.tag = .mul_add,
diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig
index 128e91eef4..051a799db6 100644
--- a/lib/std/zig/Zir.zig
+++ b/lib/std/zig/Zir.zig
@@ -1553,7 +1553,7 @@ pub const Inst = struct {
=> false,
.extended => switch (data.extended.opcode) {
- .fence, .set_cold, .breakpoint => true,
+ .fence, .set_cold, .breakpoint, .disable_instrumentation => true,
else => false,
},
};
@@ -1973,6 +1973,8 @@ pub const Inst = struct {
/// Implements `@breakpoint`.
/// `operand` is `src_node: i32`.
breakpoint,
+ /// Implement builtin `@disableInstrumentation`. `operand` is `src_node: i32`.
+ disable_instrumentation,
/// Implements the `@select` builtin.
/// `operand` is payload index to `Select`.
select,
diff --git a/src/Builtin.zig b/src/Builtin.zig
index dbdd746bcd..6e573d843f 100644
--- a/src/Builtin.zig
+++ b/src/Builtin.zig
@@ -10,6 +10,7 @@ optimize_mode: std.builtin.OptimizeMode,
error_tracing: bool,
valgrind: bool,
sanitize_thread: bool,
+fuzz: bool,
pic: bool,
pie: bool,
strip: bool,
@@ -185,6 +186,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
\\pub const have_error_return_tracing = {};
\\pub const valgrind_support = {};
\\pub const sanitize_thread = {};
+ \\pub const fuzz = {};
\\pub const position_independent_code = {};
\\pub const position_independent_executable = {};
\\pub const strip_debug_info = {};
@@ -199,6 +201,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
opts.error_tracing,
opts.valgrind,
opts.sanitize_thread,
+ opts.fuzz,
opts.pic,
opts.pie,
opts.strip,
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 567d0b0a02..50fe8b36c3 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -191,6 +191,7 @@ debug_compile_errors: bool,
incremental: bool,
job_queued_compiler_rt_lib: bool = false,
job_queued_compiler_rt_obj: bool = false,
+job_queued_fuzzer_lib: bool = false,
job_queued_update_builtin_zig: bool,
alloc_failure_occurred: bool = false,
formatted_panics: bool = false,
@@ -232,6 +233,10 @@ compiler_rt_lib: ?CRTFile = null,
/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated
/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush().
compiler_rt_obj: ?CRTFile = null,
+/// Populated when we build the libfuzzer static library. A Job to build this
+/// is indicated by setting `job_queued_fuzzer_lib` and resolved before
+/// calling linker.flush().
+fuzzer_lib: ?CRTFile = null,
glibc_so_files: ?glibc.BuiltSharedObjects = null,
wasi_emulated_libs: []const wasi_libc.CRTFile,
@@ -800,6 +805,7 @@ pub const MiscTask = enum {
libcxx,
libcxxabi,
libtsan,
+ libfuzzer,
wasi_libc_crt_file,
compiler_rt,
zig_libc,
@@ -888,6 +894,7 @@ pub const cache_helpers = struct {
hh.add(mod.red_zone);
hh.add(mod.sanitize_c);
hh.add(mod.sanitize_thread);
+ hh.add(mod.fuzz);
hh.add(mod.unwind_tables);
hh.add(mod.structured_cfg);
hh.addListOfBytes(mod.cc_argv);
@@ -1303,6 +1310,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables;
const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;
const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;
+ const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz;
const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;
const build_id = options.build_id orelse .none;
@@ -1564,6 +1572,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
comp.config.any_unwind_tables = any_unwind_tables;
comp.config.any_non_single_threaded = any_non_single_threaded;
comp.config.any_sanitize_thread = any_sanitize_thread;
+ comp.config.any_fuzz = any_fuzz;
const lf_open_opts: link.File.OpenOptions = .{
.linker_script = options.linker_script,
@@ -1909,6 +1918,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
}
}
+ if (comp.config.any_fuzz and capable_of_building_compiler_rt) {
+ if (is_exe_or_dyn_lib) {
+ log.debug("queuing a job to build libfuzzer", .{});
+ comp.job_queued_fuzzer_lib = true;
+ }
+ }
+
if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and
!comp.config.link_libc and capable_of_building_zig_libc)
{
@@ -1957,6 +1973,9 @@ pub fn destroy(comp: *Compilation) void {
if (comp.compiler_rt_obj) |*crt_file| {
crt_file.deinit(gpa);
}
+ if (comp.fuzzer_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
if (comp.libc_static_lib) |*crt_file| {
crt_file.deinit(gpa);
}
@@ -2722,6 +2741,7 @@ pub fn emitLlvmObject(
.is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
.time_report = comp.time_report,
.sanitize_thread = comp.config.any_sanitize_thread,
+ .fuzz = comp.config.any_fuzz,
.lto = comp.config.lto,
});
}
@@ -3641,15 +3661,9 @@ fn performAllTheWorkInner(
break;
}
- if (comp.job_queued_compiler_rt_lib) {
- comp.job_queued_compiler_rt_lib = false;
- buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib, main_progress_node);
- }
-
- if (comp.job_queued_compiler_rt_obj) {
- comp.job_queued_compiler_rt_obj = false;
- buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj, main_progress_node);
- }
+ buildCompilerRtOneShot(comp, &comp.job_queued_compiler_rt_lib, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node);
+ buildCompilerRtOneShot(comp, &comp.job_queued_compiler_rt_obj, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node);
+ buildCompilerRtOneShot(comp, &comp.job_queued_fuzzer_lib, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node);
}
const JobError = Allocator.Error;
@@ -4655,23 +4669,27 @@ fn workerUpdateWin32Resource(
fn buildCompilerRtOneShot(
comp: *Compilation,
+ job_queued: *bool,
+ root_source_name: []const u8,
+ misc_task: MiscTask,
output_mode: std.builtin.OutputMode,
out: *?CRTFile,
prog_node: std.Progress.Node,
) void {
+ if (!job_queued.*) return;
+ job_queued.* = false;
+
comp.buildOutputFromZig(
- "compiler_rt.zig",
+ root_source_name,
output_mode,
out,
- .compiler_rt,
+ misc_task,
prog_node,
) catch |err| switch (err) {
error.SubCompilationFailed => return, // error reported already
- else => comp.lockAndSetMiscFailure(
- .compiler_rt,
- "unable to build compiler_rt: {s}",
- .{@errorName(err)},
- ),
+ else => comp.lockAndSetMiscFailure(misc_task, "unable to build {s}: {s}", .{
+ @tagName(misc_task), @errorName(err),
+ }),
};
}
@@ -5602,23 +5620,39 @@ pub fn addCCArgs(
try argv.append("-mthumb");
}
- if (mod.sanitize_c and !mod.sanitize_thread) {
- try argv.append("-fsanitize=undefined");
- try argv.append("-fsanitize-trap=undefined");
- // It is very common, and well-defined, for a pointer on one side of a C ABI
- // to have a different but compatible element type. Examples include:
- // `char*` vs `uint8_t*` on a system with 8-bit bytes
- // `const char*` vs `char*`
- // `char*` vs `unsigned char*`
- // Without this flag, Clang would invoke UBSAN when such an extern
- // function was called.
- try argv.append("-fno-sanitize=function");
- } else if (mod.sanitize_c and mod.sanitize_thread) {
- try argv.append("-fsanitize=undefined,thread");
- try argv.append("-fsanitize-trap=undefined");
- try argv.append("-fno-sanitize=function");
- } else if (!mod.sanitize_c and mod.sanitize_thread) {
- try argv.append("-fsanitize=thread");
+ {
+ var san_arg: std.ArrayListUnmanaged(u8) = .{};
+ const prefix = "-fsanitize=";
+ if (mod.sanitize_c) {
+ if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
+ try san_arg.appendSlice(arena, "undefined,");
+ }
+ if (mod.sanitize_thread) {
+ if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
+ try san_arg.appendSlice(arena, "thread,");
+ }
+ if (mod.fuzz) {
+ if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
+ try san_arg.appendSlice(arena, "fuzzer-no-link,");
+ }
+ // Chop off the trailing comma and append to argv.
+ if (san_arg.popOrNull()) |_| {
+ try argv.append(san_arg.items);
+
+ // These args have to be added after the `-fsanitize` arg or
+ // they won't take effect.
+ if (mod.sanitize_c) {
+ try argv.append("-fsanitize-trap=undefined");
+ // It is very common, and well-defined, for a pointer on one side of a C ABI
+ // to have a different but compatible element type. Examples include:
+ // `char*` vs `uint8_t*` on a system with 8-bit bytes
+ // `const char*` vs `char*`
+ // `char*` vs `unsigned char*`
+ // Without this flag, Clang would invoke UBSAN when such an extern
+ // function was called.
+ try argv.append("-fno-sanitize=function");
+ }
+ }
}
if (mod.red_zone) {
diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig
index 6e28f5028c..63bd4c6fa2 100644
--- a/src/Compilation/Config.zig
+++ b/src/Compilation/Config.zig
@@ -32,6 +32,7 @@ any_non_single_threaded: bool,
/// per-Module setting.
any_error_tracing: bool,
any_sanitize_thread: bool,
+any_fuzz: bool,
pie: bool,
/// If this is true then linker code is responsible for making an LLVM IR
/// Module, outputting it to an object file, and then linking that together
@@ -82,6 +83,7 @@ pub const Options = struct {
ensure_libcpp_on_non_freestanding: bool = false,
any_non_single_threaded: bool = false,
any_sanitize_thread: bool = false,
+ any_fuzz: bool = false,
any_unwind_tables: bool = false,
any_dyn_libs: bool = false,
any_c_source_files: bool = false,
@@ -486,6 +488,7 @@ pub fn resolve(options: Options) ResolveError!Config {
.any_non_single_threaded = options.any_non_single_threaded,
.any_error_tracing = any_error_tracing,
.any_sanitize_thread = options.any_sanitize_thread,
+ .any_fuzz = options.any_fuzz,
.root_error_tracing = root_error_tracing,
.pie = pie,
.lto = lto,
diff --git a/src/InternPool.zig b/src/InternPool.zig
index c3cee5852b..2934340034 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -5184,11 +5184,11 @@ pub const FuncAnalysis = packed struct(u32) {
is_noinline: bool,
calls_or_awaits_errorable_fn: bool,
stack_alignment: Alignment,
-
/// True if this function has an inferred error set.
inferred_error_set: bool,
+ disable_instrumentation: bool,
- _: u14 = 0,
+ _: u13 = 0,
pub const State = enum(u8) {
/// This function has not yet undergone analysis, because we have not
@@ -8111,6 +8111,7 @@ pub fn getFuncDecl(
.calls_or_awaits_errorable_fn = false,
.stack_alignment = .none,
.inferred_error_set = false,
+ .disable_instrumentation = false,
},
.owner_decl = key.owner_decl,
.ty = key.ty,
@@ -8214,6 +8215,7 @@ pub fn getFuncDeclIes(
.calls_or_awaits_errorable_fn = false,
.stack_alignment = .none,
.inferred_error_set = true,
+ .disable_instrumentation = false,
},
.owner_decl = key.owner_decl,
.ty = func_ty,
@@ -8405,6 +8407,7 @@ pub fn getFuncInstance(
.calls_or_awaits_errorable_fn = false,
.stack_alignment = .none,
.inferred_error_set = false,
+ .disable_instrumentation = false,
},
// This is populated after we create the Decl below. It is not read
// by equality or hashing functions.
@@ -8504,6 +8507,7 @@ pub fn getFuncInstanceIes(
.calls_or_awaits_errorable_fn = false,
.stack_alignment = .none,
.inferred_error_set = true,
+ .disable_instrumentation = false,
},
// This is populated after we create the Decl below. It is not read
// by equality or hashing functions.
@@ -11225,6 +11229,18 @@ pub fn funcSetCallsOrAwaitsErrorableFn(ip: *InternPool, func: Index) void {
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
+pub fn funcSetDisableInstrumentation(ip: *InternPool, func: Index) void {
+ const unwrapped_func = func.unwrap(ip);
+ const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
+ extra_mutex.lock();
+ defer extra_mutex.unlock();
+
+ const analysis_ptr = ip.funcAnalysisPtr(func);
+ var analysis = analysis_ptr.*;
+ analysis.disable_instrumentation = true;
+ @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
+}
+
pub fn funcSetCold(ip: *InternPool, func: Index, is_cold: bool) void {
const unwrapped_func = func.unwrap(ip);
const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
diff --git a/src/Package/Module.zig b/src/Package/Module.zig
index 61b7d2ac4d..02d9921016 100644
--- a/src/Package/Module.zig
+++ b/src/Package/Module.zig
@@ -26,6 +26,7 @@ stack_protector: u32,
red_zone: bool,
sanitize_c: bool,
sanitize_thread: bool,
+fuzz: bool,
unwind_tables: bool,
cc_argv: []const []const u8,
/// (SPIR-V) whether to generate a structured control flow graph or not
@@ -92,6 +93,7 @@ pub const CreateOptions = struct {
unwind_tables: ?bool = null,
sanitize_c: ?bool = null,
sanitize_thread: ?bool = null,
+ fuzz: ?bool = null,
structured_cfg: ?bool = null,
};
};
@@ -106,6 +108,7 @@ pub const ResolvedTarget = struct {
/// At least one of `parent` and `resolved_target` must be non-null.
pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread);
+ if (options.inherited.fuzz == true) assert(options.global.any_fuzz);
if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded);
if (options.inherited.unwind_tables == true) assert(options.global.any_unwind_tables);
if (options.inherited.error_tracing == true) assert(options.global.any_error_tracing);
@@ -210,6 +213,12 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
break :b false;
};
+ const fuzz = b: {
+ if (options.inherited.fuzz) |x| break :b x;
+ if (options.parent) |p| break :b p.fuzz;
+ break :b false;
+ };
+
const code_model = b: {
if (options.inherited.code_model) |x| break :b x;
if (options.parent) |p| break :b p.code_model;
@@ -337,6 +346,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
.red_zone = red_zone,
.sanitize_c = sanitize_c,
.sanitize_thread = sanitize_thread,
+ .fuzz = fuzz,
.unwind_tables = unwind_tables,
.cc_argv = options.cc_argv,
.structured_cfg = structured_cfg,
@@ -359,6 +369,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
.error_tracing = error_tracing,
.valgrind = valgrind,
.sanitize_thread = sanitize_thread,
+ .fuzz = fuzz,
.pic = pic,
.pie = options.global.pie,
.strip = strip,
@@ -427,6 +438,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
.red_zone = red_zone,
.sanitize_c = sanitize_c,
.sanitize_thread = sanitize_thread,
+ .fuzz = fuzz,
.unwind_tables = unwind_tables,
.cc_argv = &.{},
.structured_cfg = structured_cfg,
@@ -485,6 +497,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P
.red_zone = undefined,
.sanitize_c = undefined,
.sanitize_thread = undefined,
+ .fuzz = undefined,
.unwind_tables = undefined,
.cc_argv = undefined,
.structured_cfg = undefined,
diff --git a/src/Sema.zig b/src/Sema.zig
index fbd5d636bd..b01c81ff22 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -1316,6 +1316,11 @@ fn analyzeBodyInner(
i += 1;
continue;
},
+ .disable_instrumentation => {
+ try sema.zirDisableInstrumentation();
+ i += 1;
+ continue;
+ },
.restore_err_ret_index => {
try sema.zirRestoreErrRetIndex(block, extended);
i += 1;
@@ -6576,6 +6581,14 @@ fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData)
ip.funcSetCold(sema.func_index, is_cold);
}
+fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
+ const pt = sema.pt;
+ const mod = pt.zcu;
+ const ip = &mod.intern_pool;
+ if (sema.func_index == .none) return; // does nothing outside a function
+ ip.funcSetDisableInstrumentation(sema.func_index);
+}
+
fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = block.builtinCallArgSrc(extra.node, 0);
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 58e71c7eda..e5f8250064 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1101,6 +1101,7 @@ pub const Object = struct {
is_small: bool,
time_report: bool,
sanitize_thread: bool,
+ fuzz: bool,
lto: bool,
};
@@ -1287,6 +1288,7 @@ pub const Object = struct {
options.is_small,
options.time_report,
options.sanitize_thread,
+ options.fuzz,
options.lto,
null,
emit_bin_path,
@@ -1311,6 +1313,7 @@ pub const Object = struct {
options.is_small,
options.time_report,
options.sanitize_thread,
+ options.fuzz,
options.lto,
options.asm_path,
emit_bin_path,
@@ -1380,6 +1383,25 @@ pub const Object = struct {
_ = try attributes.removeFnAttr(.cold);
}
+ if (owner_mod.sanitize_thread and !func_analysis.disable_instrumentation) {
+ try attributes.addFnAttr(.sanitize_thread, &o.builder);
+ } else {
+ _ = try attributes.removeFnAttr(.sanitize_thread);
+ }
+ if (owner_mod.fuzz and !func_analysis.disable_instrumentation) {
+ try attributes.addFnAttr(.optforfuzzing, &o.builder);
+ if (comp.config.any_fuzz) {
+ _ = try attributes.removeFnAttr(.skipprofile);
+ _ = try attributes.removeFnAttr(.nosanitize_coverage);
+ }
+ } else {
+ _ = try attributes.removeFnAttr(.optforfuzzing);
+ if (comp.config.any_fuzz) {
+ try attributes.addFnAttr(.skipprofile, &o.builder);
+ try attributes.addFnAttr(.nosanitize_coverage, &o.builder);
+ }
+ }
+
// TODO: disable this if safety is off for the function scope
const ssp_buf_size = owner_mod.stack_protector;
if (ssp_buf_size != 0) {
@@ -2979,9 +3001,6 @@ pub const Object = struct {
try attributes.addFnAttr(.minsize, &o.builder);
try attributes.addFnAttr(.optsize, &o.builder);
}
- if (owner_mod.sanitize_thread) {
- try attributes.addFnAttr(.sanitize_thread, &o.builder);
- }
const target = owner_mod.resolved_target.result;
if (target.cpu.model.llvm_name) |s| {
try attributes.addFnAttr(.{ .string = .{
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
index a32f1d74bc..f49214b660 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -93,6 +93,7 @@ pub const TargetMachine = opaque {
is_small: bool,
time_report: bool,
tsan: bool,
+ sancov: bool,
lto: bool,
asm_filename: ?[*:0]const u8,
bin_filename: ?[*:0]const u8,
diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig
index 17bcefc1b4..cd9d8c2e76 100644
--- a/src/link/Coff/lld.zig
+++ b/src/link/Coff/lld.zig
@@ -460,6 +460,10 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
try argv.append(comp.libunwind_static_lib.?.full_object_path);
}
+ if (comp.config.any_fuzz) {
+ try argv.append(comp.fuzzer_lib.?.full_object_path);
+ }
+
if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
if (!comp.config.link_libc) {
if (comp.libc_static_lib) |lib| {
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index d2d4fd2657..8085d35a22 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -1144,11 +1144,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
_ = try rpath_table.put(rpath, {});
}
- // TSAN
if (comp.config.any_sanitize_thread) {
try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });
}
+ if (comp.config.any_fuzz) {
+ try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });
+ }
+
// libc
if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
if (comp.libc_static_lib) |lib| {
@@ -1607,6 +1610,10 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
try argv.append(comp.tsan_lib.?.full_object_path);
}
+ if (comp.config.any_fuzz) {
+ try argv.append(comp.fuzzer_lib.?.full_object_path);
+ }
+
// libc
if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
if (comp.libc_static_lib) |lib| {
@@ -2272,6 +2279,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
man.hash.add(self.bind_global_refs_locally);
man.hash.add(self.compress_debug_sections);
man.hash.add(comp.config.any_sanitize_thread);
+ man.hash.add(comp.config.any_fuzz);
man.hash.addOptionalBytes(comp.sysroot);
// We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
@@ -2616,6 +2624,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
try argv.append(comp.tsan_lib.?.full_object_path);
}
+ if (comp.config.any_fuzz) {
+ try argv.append(comp.fuzzer_lib.?.full_object_path);
+ }
+
// libc
if (is_exe_or_dyn_lib and
!comp.skip_linker_dependencies and
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index aa7a2a96aa..b70e09b84b 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -387,11 +387,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
if (module_obj_path) |path| try positionals.append(.{ .path = path });
- // TSAN
if (comp.config.any_sanitize_thread) {
try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path });
}
+ if (comp.config.any_fuzz) {
+ try positionals.append(.{ .path = comp.fuzzer_lib.?.full_object_path });
+ }
+
for (positionals.items) |obj| {
self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),
@@ -725,6 +728,10 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
try argv.appendSlice(&.{ "-rpath", std.fs.path.dirname(path) orelse "." });
}
+ if (comp.config.any_fuzz) {
+ try argv.append(comp.fuzzer_lib.?.full_object_path);
+ }
+
for (self.lib_dirs) |lib_dir| {
const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});
try argv.append(arg);
diff --git a/src/main.zig b/src/main.zig
index a715b36b5c..ae34b083ae 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -502,12 +502,14 @@ const usage_build_generic =
\\ -fno-stack-check Disable stack probing in safe builds
\\ -fstack-protector Enable stack protection in unsafe builds
\\ -fno-stack-protector Disable stack protection in safe builds
- \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
- \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
\\ -fvalgrind Include valgrind client requests in release builds
\\ -fno-valgrind Omit valgrind client requests in debug builds
+ \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
+ \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
\\ -fsanitize-thread Enable Thread Sanitizer
\\ -fno-sanitize-thread Disable Thread Sanitizer
+ \\ -ffuzz Enable fuzz testing instrumentation
+ \\ -fno-fuzz Disable fuzz testing instrumentation
\\ -funwind-tables Always produce unwind table entries for all functions
\\ -fno-unwind-tables Never produce unwind table entries
\\ -ferror-tracing Enable error tracing in ReleaseFast mode
@@ -1432,6 +1434,10 @@ fn buildOutputType(
mod_opts.sanitize_thread = true;
} else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {
mod_opts.sanitize_thread = false;
+ } else if (mem.eql(u8, arg, "-ffuzz")) {
+ mod_opts.fuzz = true;
+ } else if (mem.eql(u8, arg, "-fno-fuzz")) {
+ mod_opts.fuzz = false;
} else if (mem.eql(u8, arg, "-fllvm")) {
create_module.opts.use_llvm = true;
} else if (mem.eql(u8, arg, "-fno-llvm")) {
@@ -2063,11 +2069,21 @@ fn buildOutputType(
create_module.opts.debug_format = .{ .dwarf = .@"64" };
},
.sanitize => {
- if (mem.eql(u8, it.only_arg, "undefined")) {
- mod_opts.sanitize_c = true;
- } else if (mem.eql(u8, it.only_arg, "thread")) {
- mod_opts.sanitize_thread = true;
- } else {
+ var san_it = std.mem.splitScalar(u8, it.only_arg, ',');
+ var recognized_any = false;
+ while (san_it.next()) |sub_arg| {
+ if (mem.eql(u8, sub_arg, "undefined")) {
+ mod_opts.sanitize_c = true;
+ recognized_any = true;
+ } else if (mem.eql(u8, sub_arg, "thread")) {
+ mod_opts.sanitize_thread = true;
+ recognized_any = true;
+ } else if (mem.eql(u8, sub_arg, "fuzzer") or mem.eql(u8, sub_arg, "fuzzer-no-link")) {
+ mod_opts.fuzz = true;
+ recognized_any = true;
+ }
+ }
+ if (!recognized_any) {
try cc_argv.appendSlice(arena, it.other_args);
}
},
@@ -2645,6 +2661,8 @@ fn buildOutputType(
create_module.opts.any_non_single_threaded = true;
if (mod_opts.sanitize_thread == true)
create_module.opts.any_sanitize_thread = true;
+ if (mod_opts.fuzz == true)
+ create_module.opts.any_fuzz = true;
if (mod_opts.unwind_tables == true)
create_module.opts.any_unwind_tables = true;
if (mod_opts.strip == false)
@@ -7494,6 +7512,8 @@ fn handleModArg(
create_module.opts.any_non_single_threaded = true;
if (mod_opts.sanitize_thread == true)
create_module.opts.any_sanitize_thread = true;
+ if (mod_opts.fuzz == true)
+ create_module.opts.any_fuzz = true;
if (mod_opts.unwind_tables == true)
create_module.opts.any_unwind_tables = true;
if (mod_opts.strip == false)
diff --git a/src/print_zir.zig b/src/print_zir.zig
index 2fee5f5d83..bd06362e62 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -524,6 +524,7 @@ const Writer = struct {
.frame,
.frame_address,
.breakpoint,
+ .disable_instrumentation,
.c_va_start,
.in_comptime,
.value_placeholder,
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index 72f1026617..5580b61367 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -54,6 +54,7 @@
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/AlwaysInliner.h>
#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
+#include <llvm/Transforms/Instrumentation/SanitizerCoverage.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils.h>
#include <llvm/Transforms/Utils/AddDiscriminators.h>
@@ -188,9 +189,31 @@ struct TimeTracerRAII {
};
} // end anonymous namespace
+static SanitizerCoverageOptions getSanCovOptions(void) {
+ SanitizerCoverageOptions o;
+ o.CoverageType = SanitizerCoverageOptions::SCK_Edge;
+ o.IndirectCalls = true;
+ o.TraceBB = false;
+ o.TraceCmp = true;
+ o.TraceDiv = false;
+ o.TraceGep = false;
+ o.Use8bitCounters = false;
+ o.TracePC = false;
+ o.TracePCGuard = false;
+ o.Inline8bitCounters = true;
+ o.InlineBoolFlag = false;
+ o.PCTable = true;
+ o.NoPrune = false;
+ o.StackDepth = true;
+ o.TraceLoads = false;
+ o.TraceStores = false;
+ o.CollectControlFlow = false;
+ return o;
+}
+
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
char **error_message, bool is_debug,
- bool is_small, bool time_report, bool tsan, bool lto,
+ bool is_small, bool time_report, bool tsan, bool sancov, bool lto,
const char *asm_filename, const char *bin_filename,
const char *llvm_ir_filename, const char *bitcode_filename)
{
@@ -277,39 +300,38 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
pass_builder.registerCGSCCAnalyses(cgscc_am);
pass_builder.registerFunctionAnalyses(function_am);
pass_builder.registerLoopAnalyses(loop_am);
- pass_builder.crossRegisterProxies(loop_am, function_am,
- cgscc_am, module_am);
-
- // IR verification
- if (assertions_on) {
- // Verify the input
- pass_builder.registerPipelineStartEPCallback(
- [](ModulePassManager &module_pm, OptimizationLevel OL) {
- module_pm.addPass(VerifierPass());
- });
- // Verify the output
- pass_builder.registerOptimizerLastEPCallback(
- [](ModulePassManager &module_pm, OptimizationLevel OL) {
- module_pm.addPass(VerifierPass());
- });
- }
+ pass_builder.crossRegisterProxies(loop_am, function_am, cgscc_am, module_am);
- // Passes specific for release build
- if (!is_debug) {
- pass_builder.registerPipelineStartEPCallback(
- [](ModulePassManager &module_pm, OptimizationLevel OL) {
- module_pm.addPass(
- createModuleToFunctionPassAdaptor(AddDiscriminatorsPass()));
- });
- }
+ pass_builder.registerPipelineStartEPCallback([&](ModulePassManager &module_pm, OptimizationLevel OL) {
+ // Verify the input
+ if (assertions_on) {
+ module_pm.addPass(VerifierPass());
+ }
+
+ if (!is_debug) {
+ module_pm.addPass(createModuleToFunctionPassAdaptor(AddDiscriminatorsPass()));
+ }
+ });
+
+ pass_builder.registerOptimizerEarlyEPCallback([&](ModulePassManager &module_pm, OptimizationLevel OL) {
+ // Code coverage instrumentation.
+ if (sancov) {
+ module_pm.addPass(SanitizerCoveragePass(getSanCovOptions()));
+ }
- // Thread sanitizer
- if (tsan) {
- pass_builder.registerOptimizerLastEPCallback([](ModulePassManager &module_pm, OptimizationLevel level) {
+ // Thread sanitizer
+ if (tsan) {
module_pm.addPass(ModuleThreadSanitizerPass());
module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
- });
- }
+ }
+ });
+
+ pass_builder.registerOptimizerLastEPCallback([&](ModulePassManager &module_pm, OptimizationLevel level) {
+ // Verify the output
+ if (assertions_on) {
+ module_pm.addPass(VerifierPass());
+ }
+ });
ModulePassManager module_pm;
OptimizationLevel opt_level;
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index 89b53a802f..7ac632fe02 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -26,7 +26,7 @@
ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
char **error_message, bool is_debug,
- bool is_small, bool time_report, bool tsan, bool lto,
+ bool is_small, bool time_report, bool tsan, bool sancov, bool lto,
const char *asm_filename, const char *bin_filename,
const char *llvm_ir_filename, const char *bitcode_filename);
diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm
index 78302c6a84..0b0b5d4de0 100644
--- a/stage1/zig1.wasm
+++ b/stage1/zig1.wasm
Binary files differ