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(
582582 ws.coverage_mutex.lock();
583583 defer ws.coverage_mutex.unlock();
584584
585 std.debug.print("SET {}\n", .{coverage_id});
585586 const gop = try ws.coverage_files.getOrPut(gpa, coverage_id);
586587 if (gop.found_existing) {
587588 // We are fuzzing the same executable with multiple threads.
......@@ -676,6 +677,7 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo
676677 ws.coverage_mutex.lock();
677678 defer ws.coverage_mutex.unlock();
678679
680 std.debug.print("GET {}\n", .{coverage_id});
679681 const coverage_map = ws.coverage_files.getPtr(coverage_id).?;
680682 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
681683 const pcs = header.pcAddrs();
lib/std/debug/Info.zig+63-9
......@@ -7,6 +7,7 @@
77//! properties.
88
99const std = @import("../std.zig");
10const builtin = @import("builtin");
1011const Allocator = std.mem.Allocator;
1112const Path = std.Build.Cache.Path;
1213const Dwarf = std.debug.Dwarf;
......@@ -17,7 +18,7 @@ const SourceLocation = std.debug.Coverage.SourceLocation;
1718const Info = @This();
1819
1920/// Sorted by key, ascending.
20address_map: std.AutoArrayHashMapUnmanaged(u64, Dwarf.ElfModule),
21address_map: std.AutoArrayHashMapUnmanaged(u64, std.debug.SelfInfo.Module),
2122/// Externally managed, outlives this `Info` instance.
2223coverage: *Coverage,
2324
......@@ -25,20 +26,41 @@ pub const LoadError = Dwarf.ElfModule.LoadError;
2526
2627pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {
2728 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);
3029 var info: Info = .{
3130 .address_map = .{},
3231 .coverage = coverage,
3332 };
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 }
3557 return info;
3658}
3759
3860pub fn deinit(info: *Info, gpa: Allocator) void {
39 for (info.address_map.values()) |*elf_module| {
40 elf_module.dwarf.deinit(gpa);
41 }
61 // for (info.address_map.values()) |*module| {
62 // module.dwarf.deinit(gpa);
63 // }
4264 info.address_map.deinit(gpa);
4365 info.* = undefined;
4466}
......@@ -57,6 +79,38 @@ pub fn resolveAddresses(
5779) ResolveAddressesError!void {
5880 assert(sorted_pc_addrs.len == output.len);
5981 if (info.address_map.entries.len != 1) @panic("TODO");
60 const elf_module = &info.address_map.values()[0];
61 return info.coverage.resolveAddressesDwarf(gpa, sorted_pc_addrs, output, &elf_module.dwarf);
82 switch (builtin.os.tag) {
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 }
62116}
lib/std/debug/SelfInfo.zig+22-12
......@@ -687,18 +687,28 @@ pub const Module = switch (native_os) {
687687
688688 // Check if its debug infos are already in the cache
689689 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", .{});
702712 return .{
703713 .relocated_address = relocated_address,
704714 .symbol = symbol,
......@@ -846,7 +856,7 @@ pub const WindowsModule = struct {
846856/// This takes ownership of macho_file: users of this function should not close
847857/// it themselves, even on error.
848858/// 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 {
850860 const mapped_mem = try mapWholeFile(macho_file);
851861
852862 const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr));