diff --git a/lib/fuzzer.zig b/lib/fuzzer.zig new file mode 100644 index 000000000000..49dd33894b30 --- /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 844f69c8cdf7..635e6cc33431 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 20d68b893791..4cd5ee841d27 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 df909a623034..e7355021bb10 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 82dc3bf75997..5eedb769b571 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 e0f99b476248..88a5ca3ab240 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -387,6 +387,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)); @@ -429,9 +433,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 aa617144345f..a6be743c2bc0 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 543c2799812a..e956ffd2a9d6 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 4bea0278fa5d..fc08f9eb85db 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, @@ -263,6 +264,14 @@ pub const list = list: { .illegal_outside_function = true, }, }, + .{ + "@disableInstrumentation", + .{ + .tag = .disable_instrumentation, + .param_count = 0, + .illegal_outside_function = true, + }, + }, .{ "@mulAdd", .{ diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 128e91eef4dc..051a799db6f6 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 dbdd746bcd3c..6e573d843f5f 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 98d2fd552a10..a0141a5dad01 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -190,6 +190,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, @@ -231,6 +232,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, @@ -799,6 +804,7 @@ pub const MiscTask = enum { libcxx, libcxxabi, libtsan, + libfuzzer, wasi_libc_crt_file, compiler_rt, zig_libc, @@ -887,6 +893,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); @@ -1302,6 +1309,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; @@ -1563,6 +1571,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, @@ -1908,6 +1917,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) { @@ -1956,6 +1972,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); } @@ -2721,6 +2740,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 6e28f5028c02..63bd4c6fa203 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 c3cee5852b2b..29343400340a 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 61b7d2ac4dc8..02d9921016d4 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 fbd5d636bdc3..b01c81ff2213 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 58e71c7eda3f..e5f82500645d 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 a32f1d74bc53..f49214b6608c 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 17bcefc1b414..cd9d8c2e7647 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 7b44403c8a45..d8b28f8f6fd9 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 3e5b18e76f67..1b4a80e6fcdf 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -389,11 +389,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.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { error.MalformedObject, @@ -759,6 +762,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 561dd2f28e2a..1d9d8d94f9a6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -499,12 +499,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 @@ -1429,6 +1431,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")) { @@ -2060,11 +2066,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); } }, @@ -2642,6 +2658,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) @@ -7491,6 +7509,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 2fee5f5d835a..bd06362e624e 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 72f1026617fa..5580b6136752 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -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 89b53a802f59..7ac632fe02f1 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 78302c6a8417..0b0b5d4de020 100644 Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ