authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2025-02-28 10:34:06+01:00
committergravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2025-02-28 10:34:06+01:00
log7a50e76eb14df322033f3b96547cd62a5691df6f
treed3c41052365f0df077b3a5ca34b1af2b9a95caa0
parentf8fe50314614aae1d1540f6a5ca0505bcbf66da0

wip

this is a bad commit that contains work in progress changes to make fuzzing on macos work. more specifically it contains macho-specific code for loading debug information in the webserver. it's messy because I'm still trying to understand how this stuff works. with these changes the web server loads but the wasm code panics. it's unclear if the wasm panic is due to my dwarf loading code being wrong or if we're encountering some other latent issue.

3 files changed, 87 insertions(+), 21 deletions(-)

lib/std/Build/Fuzz/WebServer.zig+2
...@@ -582,6 +582,7 @@ fn prepareTables(...@@ -582,6 +582,7 @@ fn prepareTables(
582 ws.coverage_mutex.lock();582 ws.coverage_mutex.lock();
583 defer ws.coverage_mutex.unlock();583 defer ws.coverage_mutex.unlock();
584584
585 std.debug.print("SET {}\n", .{coverage_id});
585 const gop = try ws.coverage_files.getOrPut(gpa, coverage_id);586 const gop = try ws.coverage_files.getOrPut(gpa, coverage_id);
586 if (gop.found_existing) {587 if (gop.found_existing) {
587 // We are fuzzing the same executable with multiple threads.588 // We are fuzzing the same executable with multiple threads.
...@@ -676,6 +677,7 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo...@@ -676,6 +677,7 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo
676 ws.coverage_mutex.lock();677 ws.coverage_mutex.lock();
677 defer ws.coverage_mutex.unlock();678 defer ws.coverage_mutex.unlock();
678679
680 std.debug.print("GET {}\n", .{coverage_id});
679 const coverage_map = ws.coverage_files.getPtr(coverage_id).?;681 const coverage_map = ws.coverage_files.getPtr(coverage_id).?;
680 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);682 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
681 const pcs = header.pcAddrs();683 const pcs = header.pcAddrs();
lib/std/debug/Info.zig+63-9
...@@ -7,6 +7,7 @@...@@ -7,6 +7,7 @@
7//! properties.7//! properties.
88
9const std = @import("../std.zig");9const std = @import("../std.zig");
10const builtin = @import("builtin");
10const Allocator = std.mem.Allocator;11const Allocator = std.mem.Allocator;
11const Path = std.Build.Cache.Path;12const Path = std.Build.Cache.Path;
12const Dwarf = std.debug.Dwarf;13const Dwarf = std.debug.Dwarf;
...@@ -17,7 +18,7 @@ const SourceLocation = std.debug.Coverage.SourceLocation;...@@ -17,7 +18,7 @@ const SourceLocation = std.debug.Coverage.SourceLocation;
17const Info = @This();18const Info = @This();
1819
19/// Sorted by key, ascending.20/// Sorted by key, ascending.
20address_map: std.AutoArrayHashMapUnmanaged(u64, Dwarf.ElfModule),21address_map: std.AutoArrayHashMapUnmanaged(u64, std.debug.SelfInfo.Module),
21/// Externally managed, outlives this `Info` instance.22/// Externally managed, outlives this `Info` instance.
22coverage: *Coverage,23coverage: *Coverage,
2324
...@@ -25,20 +26,41 @@ pub const LoadError = Dwarf.ElfModule.LoadError;...@@ -25,20 +26,41 @@ pub const LoadError = Dwarf.ElfModule.LoadError;
2526
26pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {27pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {
27 var sections: Dwarf.SectionArray = Dwarf.null_section_array;28 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
28 var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, &sections, null);
29 try elf_module.dwarf.populateRanges(gpa);
30 var info: Info = .{29 var info: Info = .{
31 .address_map = .{},30 .address_map = .{},
32 .coverage = coverage,31 .coverage = coverage,
33 };32 };
34 try info.address_map.put(gpa, elf_module.base_address, elf_module);33 switch (builtin.os.tag) {
34 .linux => {
35 var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, &sections, null);
36 try elf_module.dwarf.populateRanges(gpa);
37 try info.address_map.put(gpa, elf_module.base_address, elf_module);
38 },
39 .macos => {
40 const macho_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| switch (err) {
41 error.FileNotFound => return error.MissingDebugInfo,
42 else => return error.InvalidDebugInfo,
43 };
44 // readMachoDebugInfo takes ownership of the file
45 // defer elf_file.close();
46 var module = std.debug.SelfInfo.readMachODebugInfo(gpa, macho_file) catch {
47 return error.InvalidDebugInfo;
48 };
49
50 module.base_address = 0;
51 module.vmaddr_slide = 0;
52
53 try info.address_map.put(gpa, 0, module);
54 },
55 else => @compileError("TODO: implement debug info loading for the target platform"),
56 }
35 return info;57 return info;
36}58}
3759
38pub fn deinit(info: *Info, gpa: Allocator) void {60pub fn deinit(info: *Info, gpa: Allocator) void {
39 for (info.address_map.values()) |*elf_module| {61 // for (info.address_map.values()) |*module| {
40 elf_module.dwarf.deinit(gpa);62 // module.dwarf.deinit(gpa);
41 }63 // }
42 info.address_map.deinit(gpa);64 info.address_map.deinit(gpa);
43 info.* = undefined;65 info.* = undefined;
44}66}
...@@ -57,6 +79,38 @@ pub fn resolveAddresses(...@@ -57,6 +79,38 @@ pub fn resolveAddresses(
57) ResolveAddressesError!void {79) ResolveAddressesError!void {
58 assert(sorted_pc_addrs.len == output.len);80 assert(sorted_pc_addrs.len == output.len);
59 if (info.address_map.entries.len != 1) @panic("TODO");81 if (info.address_map.entries.len != 1) @panic("TODO");
60 const elf_module = &info.address_map.values()[0];82 switch (builtin.os.tag) {
61 return info.coverage.resolveAddressesDwarf(gpa, sorted_pc_addrs, output, &elf_module.dwarf);83 else => @compileError("unsupported"),
84 .linux => {
85 const elf_module = &info.address_map.values()[0];
86 return info.coverage.resolveAddressesDwarf(gpa, sorted_pc_addrs, output, &elf_module.dwarf);
87 },
88 .macos => {
89 const module = &info.address_map.values()[0];
90
91 var idx: usize = 0;
92 while (idx < sorted_pc_addrs.len) {
93 const dw = (module.getDwarfInfoForAddress(gpa, sorted_pc_addrs[idx]) catch return error.InvalidDebugInfo).?;
94 try dw.populateRanges(gpa);
95 const last = dw.ranges.getLastOrNull() orelse return;
96 var end_idx = idx;
97 while (end_idx < sorted_pc_addrs.len and
98 sorted_pc_addrs[end_idx] < last.end) end_idx += 1;
99
100 if (end_idx == idx) {
101 std.debug.print("made no progress", .{});
102 return;
103 }
104
105 try info.coverage.resolveAddressesDwarf(
106 gpa,
107 sorted_pc_addrs[idx..end_idx],
108 output[idx..end_idx],
109 dw,
110 );
111
112 idx = end_idx;
113 }
114 },
115 }
62}116}
lib/std/debug/SelfInfo.zig+22-12
...@@ -687,18 +687,28 @@ pub const Module = switch (native_os) {...@@ -687,18 +687,28 @@ pub const Module = switch (native_os) {
687687
688 // Check if its debug infos are already in the cache688 // Check if its debug infos are already in the cache
689 const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0);689 const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0);
690 const o_file_info = self.ofiles.getPtr(o_file_path) orelse
691 (self.loadOFile(allocator, o_file_path) catch |err| switch (err) {
692 error.FileNotFound,
693 error.MissingDebugInfo,
694 error.InvalidDebugInfo,
695 => return .{
696 .relocated_address = relocated_address,
697 .symbol = symbol,
698 },
699 else => return err,
700 });
701690
691 std.debug.print("loading '{s}'\n", .{o_file_path});
692
693 const clean_name = if (std.mem.endsWith(u8, o_file_path, ".a.o)")) blk: {
694 var it = std.mem.tokenizeScalar(u8, o_file_path, '(');
695 break :blk std.fmt.allocPrint(allocator, "{s}.o", .{it.next().?}) catch unreachable;
696 } else o_file_path;
697 std.debug.print("clean '{s}'\n", .{clean_name});
698
699 const o_file_info = self.ofiles.getPtr(clean_name) orelse
700 (self.loadOFile(allocator, clean_name) catch |err| switch (err) {
701 error.FileNotFound,
702 error.MissingDebugInfo,
703 error.InvalidDebugInfo,
704 => return .{
705 .relocated_address = relocated_address,
706 .symbol = symbol,
707 },
708 else => return err,
709 });
710
711 std.debug.print("success\n", .{});
702 return .{712 return .{
703 .relocated_address = relocated_address,713 .relocated_address = relocated_address,
704 .symbol = symbol,714 .symbol = symbol,
...@@ -846,7 +856,7 @@ pub const WindowsModule = struct {...@@ -846,7 +856,7 @@ pub const WindowsModule = struct {
846/// This takes ownership of macho_file: users of this function should not close856/// This takes ownership of macho_file: users of this function should not close
847/// it themselves, even on error.857/// it themselves, even on error.
848/// TODO it's weird to take ownership even on error, rework this code.858/// TODO it's weird to take ownership even on error, rework this code.
849fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module {859pub fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module {
850 const mapped_mem = try mapWholeFile(macho_file);860 const mapped_mem = try mapWholeFile(macho_file);
851861
852 const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr));862 const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr));