diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index ab1dae9a70..7e3fafac0e 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -152,7 +152,8 @@ fn mainServer() !void { const index = try server.receiveBody_u32(); const test_fn = builtin.test_functions[index]; const entry_addr = @intFromPtr(test_fn.func); - try server.serveU64Message(.fuzz_start_addr, entry_addr); + const offset = std.c._dyld_get_image_vmaddr_slide(0); + try server.serveU64Message(.fuzz_start_addr, entry_addr - offset); defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1); is_fuzz_test = false; fuzzer_set_name(test_fn.name.ptr, test_fn.name.len); diff --git a/lib/fuzzer.zig b/lib/fuzzer.zig index 6bfd40b6f0..49049e4151 100644 --- a/lib/fuzzer.zig +++ b/lib/fuzzer.zig @@ -208,7 +208,12 @@ const Fuzzer = struct { }; f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header)); f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize)); - f.seen_pcs.appendSliceAssumeCapacity(std.mem.sliceAsBytes(pcs)); + + const offset = std.c._dyld_get_image_vmaddr_slide(0); + for (pcs) |pc| { + const value = pc - offset; + f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&value)); + } } } @@ -310,19 +315,21 @@ const Fuzzer = struct { f.input.clearRetainingCapacity(); const old_input = f.corpus.items[corpus_index].bytes; f.input.ensureTotalCapacity(old_input.len + 1) catch @panic("mmap file resize failed"); - switch (mutation) { + mut: switch (mutation) { .remove_byte => { + if (old_input.len == 0) continue :mut .add_byte; const omitted_index = rng.uintLessThanBiased(usize, old_input.len); f.input.appendSliceAssumeCapacity(old_input[0..omitted_index]); f.input.appendSliceAssumeCapacity(old_input[omitted_index + 1 ..]); }, .modify_byte => { + if (old_input.len == 0) continue :mut .add_byte; const modified_index = rng.uintLessThanBiased(usize, old_input.len); f.input.appendSliceAssumeCapacity(old_input); f.input.items[modified_index] = rng.int(u8); }, .add_byte => { - const modified_index = rng.uintLessThanBiased(usize, old_input.len); + const modified_index = if (old_input.len == 0) 0 else rng.uintLessThanBiased(usize, old_input.len); f.input.appendSliceAssumeCapacity(old_input[0..modified_index]); f.input.appendAssumeCapacity(rng.int(u8)); f.input.appendSliceAssumeCapacity(old_input[modified_index..]); diff --git a/lib/std/debug/Info.zig b/lib/std/debug/Info.zig index c809547f73..7e953ebd42 100644 --- a/lib/std/debug/Info.zig +++ b/lib/std/debug/Info.zig @@ -7,6 +7,7 @@ //! properties. const std = @import("../std.zig"); +const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const Path = std.Build.Cache.Path; const Dwarf = std.debug.Dwarf; @@ -17,7 +18,7 @@ const SourceLocation = std.debug.Coverage.SourceLocation; const Info = @This(); /// Sorted by key, ascending. -address_map: std.AutoArrayHashMapUnmanaged(u64, Dwarf.ElfModule), +address_map: std.AutoArrayHashMapUnmanaged(u64, std.debug.SelfInfo.Module), /// Externally managed, outlives this `Info` instance. coverage: *Coverage, @@ -25,19 +26,40 @@ pub const LoadError = Dwarf.ElfModule.LoadError; pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info { var sections: Dwarf.SectionArray = Dwarf.null_section_array; - var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, §ions, null); - try elf_module.dwarf.populateRanges(gpa); var info: Info = .{ .address_map = .{}, .coverage = coverage, }; - try info.address_map.put(gpa, elf_module.base_address, elf_module); + switch (builtin.os.tag) { + .linux => { + var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, §ions, null); + try elf_module.dwarf.populateRanges(gpa); + try info.address_map.put(gpa, elf_module.base_address, elf_module); + }, + .macos => { + const macho_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| switch (err) { + error.FileNotFound => return error.MissingDebugInfo, + else => return error.InvalidDebugInfo, + }; + // readMachoDebugInfo takes ownership of the file + // defer elf_file.close(); + var module = std.debug.SelfInfo.readMachODebugInfo(gpa, macho_file) catch { + return error.InvalidDebugInfo; + }; + + module.base_address = 0; + module.vmaddr_slide = 0; + + try info.address_map.put(gpa, 0, module); + }, + else => @compileError("TODO: implement debug info loading for the target platform"), + } return info; } pub fn deinit(info: *Info, gpa: Allocator) void { - for (info.address_map.values()) |*elf_module| { - elf_module.dwarf.deinit(gpa); + for (info.address_map.values()) |*module| { + module.deinit(gpa); } info.address_map.deinit(gpa); info.* = undefined; @@ -57,6 +79,53 @@ pub fn resolveAddresses( ) ResolveAddressesError!void { assert(sorted_pc_addrs.len == output.len); if (info.address_map.entries.len != 1) @panic("TODO"); - const elf_module = &info.address_map.values()[0]; - return info.coverage.resolveAddressesDwarf(gpa, sorted_pc_addrs, output, &elf_module.dwarf); + + switch (builtin.os.tag) { + else => @compileError("unsupported"), + .linux => { + const elf_module = &info.address_map.values()[0]; + return info.coverage.resolveAddressesDwarf(gpa, sorted_pc_addrs, output, &elf_module.dwarf); + }, + .macos => { + const module = &info.address_map.values()[0]; + + var idx: usize = 0; + while (idx < sorted_pc_addrs.len) : (idx += 1) { + const ofile = (module.getOFileInfoForAddress(gpa, sorted_pc_addrs[idx]) catch return error.InvalidDebugInfo); + if (ofile.o_file_info.?.di.ranges.items.len == 0) { + try ofile.o_file_info.?.di.populateRanges(gpa); + } + // const last = ofile.ranges.getLastOrNull() orelse return; + // var end_idx = idx; + // while (end_idx < sorted_pc_addrs.len and + // sorted_pc_addrs[end_idx] < last.end) end_idx += 1; + + // if (end_idx == idx) { + // std.debug.panic("made no progress", .{}); + // } + // + + const stab_symbol = std.mem.sliceTo(module.strings[ofile.symbol.?.strx..], 0); + const offset = ofile.relocated_address - ofile.symbol.?.addr; + // Translate again the address, this time into an address inside the + // .o file + const relocated_address_o = ofile.o_file_info.?.addr_table.get(stab_symbol) orelse @panic("error"); + + try info.coverage.resolveAddressesDwarf( + gpa, + &.{relocated_address_o + offset}, + output[idx..][0..1], + &ofile.o_file_info.?.di, + ); + + // std.debug.print("{x} -> {x} -> {}\n", .{ + // sorted_pc_addrs[idx], + // relocated_address_o + offset, + // output[idx], + // }); + + // idx = end_idx; + } + }, + } } diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index a05a31a6ad..b61ed548fe 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/lib/std/debug/SelfInfo.zig @@ -860,7 +860,7 @@ pub const WindowsModule = struct { /// This takes ownership of macho_file: users of this function should not close /// it themselves, even on error. /// TODO it's weird to take ownership even on error, rework this code. -fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module { +pub fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module { const mapped_mem = try mapWholeFile(macho_file); const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr)); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 2043be3e7b..e9bbb4443b 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -416,7 +416,12 @@ pub fn flushModule( } if (comp.config.any_fuzz) { - try positionals.append(try link.openObjectInput(diags, comp.fuzzer_lib.?.full_object_path)); + try positionals.append(try link.openArchiveInput( + diags, + comp.fuzzer_lib.?.full_object_path, + true, + false, + )); } if (comp.ubsan_rt_lib) |crt_file| {