authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-02 14:44:15+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:49+01:00
log55a7affea41a4a1f4e117d7ee55c1c0e8b869203
tree70b29dd2f3df6b84ad19cbfd1d76380fa81d1c4b
parent25e02bed4cebc7c90f763fae5d57e3d99a08bdfc
signaturelock-open Commit is signed but in an unrecognized format.

me when i did a thing


2 files changed, 187 insertions(+), 196 deletions(-)

lib/std/debug/Dwarf.zig+3-3
...@@ -1780,13 +1780,13 @@ pub const ElfModule = struct {...@@ -1780,13 +1780,13 @@ pub const ElfModule = struct {
17801780
1781pub fn getSymbol(di: *Dwarf, allocator: Allocator, endian: Endian, address: u64) !std.debug.Symbol {1781pub fn getSymbol(di: *Dwarf, allocator: Allocator, endian: Endian, address: u64) !std.debug.Symbol {
1782 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {1782 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
1783 error.MissingDebugInfo, error.InvalidDebugInfo => return .{},1783 error.MissingDebugInfo, error.InvalidDebugInfo => return .{ .name = null, .compile_unit_name = null, .source_location = null },
1784 else => return err,1784 else => return err,
1785 };1785 };
1786 return .{1786 return .{
1787 .name = di.getSymbolName(address) orelse "???",1787 .name = di.getSymbolName(address),
1788 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {1788 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
1789 error.MissingDebugInfo, error.InvalidDebugInfo => "???",1789 error.MissingDebugInfo, error.InvalidDebugInfo => null,
1790 },1790 },
1791 .source_location = di.getLineNumberInfo(allocator, endian, compile_unit, address) catch |err| switch (err) {1791 .source_location = di.getLineNumberInfo(allocator, endian, compile_unit, address) catch |err| switch (err) {
1792 error.MissingDebugInfo, error.InvalidDebugInfo => null,1792 error.MissingDebugInfo, error.InvalidDebugInfo => null,
lib/std/debug/SelfInfo.zig+184-193
...@@ -1,6 +1,8 @@...@@ -1,6 +1,8 @@
1//! Cross-platform abstraction for this binary's own debug information, with a1//! Cross-platform abstraction for this binary's own debug information, with a
2//! goal of minimal code bloat and compilation speed penalty.2//! goal of minimal code bloat and compilation speed penalty.
33
4// MLUGG TODO: audit use of errors in this file. ideally, introduce some concrete error sets
5
4const builtin = @import("builtin");6const builtin = @import("builtin");
5const native_os = builtin.os.tag;7const native_os = builtin.os.tag;
6const native_endian = native_arch.endian();8const native_endian = native_arch.endian();
...@@ -26,13 +28,7 @@ const regValueNative = Dwarf.abi.regValueNative;...@@ -26,13 +28,7 @@ const regValueNative = Dwarf.abi.regValueNative;
2628
27const SelfInfo = @This();29const SelfInfo = @This();
2830
29modules: std.AutoHashMapUnmanaged(usize, struct {31modules: std.AutoHashMapUnmanaged(usize, Module.DebugInfo),
30 di: Module.DebugInfo,
31 // MLUGG TODO: okay actually these should definitely go on the impl so it can share state. e.g. loading unwind info might require lodaing debug info in some cases
32 loaded_locations: bool,
33 loaded_unwind: bool,
34 const init: @This() = .{ .di = .init, .loaded_locations = false, .loaded_unwind = false };
35}),
36lookup_cache: Module.LookupCache,32lookup_cache: Module.LookupCache,
3733
38pub const target_supported: bool = switch (native_os) {34pub const target_supported: bool = switch (native_os) {
...@@ -77,11 +73,7 @@ pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !us...@@ -77,11 +73,7 @@ pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !us
77 const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc);73 const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc);
78 const gop = try self.modules.getOrPut(gpa, module.load_offset);74 const gop = try self.modules.getOrPut(gpa, module.load_offset);
79 if (!gop.found_existing) gop.value_ptr.* = .init;75 if (!gop.found_existing) gop.value_ptr.* = .init;
80 if (!gop.value_ptr.loaded_unwind) {76 return module.unwindFrame(gpa, gop.value_ptr, context);
81 try module.loadUnwindInfo(gpa, &gop.value_ptr.di);
82 gop.value_ptr.loaded_unwind = true;
83 }
84 return module.unwindFrame(gpa, &gop.value_ptr.di, context);
85}77}
8678
87pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.debug.Symbol {79pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.debug.Symbol {
...@@ -89,11 +81,7 @@ pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std....@@ -89,11 +81,7 @@ pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.
89 const module: Module = try .lookup(&self.lookup_cache, gpa, address);81 const module: Module = try .lookup(&self.lookup_cache, gpa, address);
90 const gop = try self.modules.getOrPut(gpa, module.key());82 const gop = try self.modules.getOrPut(gpa, module.key());
91 if (!gop.found_existing) gop.value_ptr.* = .init;83 if (!gop.found_existing) gop.value_ptr.* = .init;
92 if (!gop.value_ptr.loaded_locations) {84 return module.getSymbolAtAddress(gpa, gop.value_ptr, address);
93 try module.loadLocationInfo(gpa, &gop.value_ptr.di);
94 gop.value_ptr.loaded_locations = true;
95 }
96 return module.getSymbolAtAddress(gpa, &gop.value_ptr.di, address);
97}85}
9886
99/// Returns the module name for a given address.87/// Returns the module name for a given address.
...@@ -168,9 +156,125 @@ const Module = switch (native_os) {...@@ -168,9 +156,125 @@ const Module = switch (native_os) {
168 return error.MissingDebugInfo;156 return error.MissingDebugInfo;
169 }157 }
170 fn loadLocationInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {158 fn loadLocationInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {
171 try loadMachODebugInfo(gpa, module, di); // MLUGG TODO inline159 const mapped_mem = mapFileOrSelfExe(module.name) catch |err| switch (err) {
160 error.FileNotFound => return error.MissingDebugInfo,
161 error.FileTooBig => return error.InvalidDebugInfo,
162 else => |e| return e,
163 };
164 errdefer posix.munmap(mapped_mem);
165
166 const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr));
167 if (hdr.magic != macho.MH_MAGIC_64)
168 return error.InvalidDebugInfo;
169
170 const symtab: macho.symtab_command = symtab: {
171 var it: macho.LoadCommandIterator = .{
172 .ncmds = hdr.ncmds,
173 .buffer = mapped_mem[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
174 };
175 while (it.next()) |cmd| switch (cmd.cmd()) {
176 .SYMTAB => break :symtab cmd.cast(macho.symtab_command) orelse return error.InvalidDebugInfo,
177 else => {},
178 };
179 return error.MissingDebugInfo;
180 };
181
182 const syms_ptr: [*]align(1) const macho.nlist_64 = @ptrCast(mapped_mem[symtab.symoff..]);
183 const syms = syms_ptr[0..symtab.nsyms];
184 const strings = mapped_mem[symtab.stroff..][0 .. symtab.strsize - 1 :0];
185
186 // MLUGG TODO: does it really make sense to initCapacity here? how many of syms are omitted?
187 var symbols: std.ArrayList(MachoSymbol) = try .initCapacity(gpa, syms.len);
188 defer symbols.deinit(gpa);
189
190 var ofile: u32 = undefined;
191 var last_sym: MachoSymbol = undefined;
192 var state: enum {
193 init,
194 oso_open,
195 oso_close,
196 bnsym,
197 fun_strx,
198 fun_size,
199 ensym,
200 } = .init;
201
202 for (syms) |*sym| {
203 if (sym.n_type.bits.is_stab == 0) continue;
204
205 // TODO handle globals N_GSYM, and statics N_STSYM
206 switch (sym.n_type.stab) {
207 .oso => switch (state) {
208 .init, .oso_close => {
209 state = .oso_open;
210 ofile = sym.n_strx;
211 },
212 else => return error.InvalidDebugInfo,
213 },
214 .bnsym => switch (state) {
215 .oso_open, .ensym => {
216 state = .bnsym;
217 last_sym = .{
218 .strx = 0,
219 .addr = sym.n_value,
220 .size = 0,
221 .ofile = ofile,
222 };
223 },
224 else => return error.InvalidDebugInfo,
225 },
226 .fun => switch (state) {
227 .bnsym => {
228 state = .fun_strx;
229 last_sym.strx = sym.n_strx;
230 },
231 .fun_strx => {
232 state = .fun_size;
233 last_sym.size = @intCast(sym.n_value);
234 },
235 else => return error.InvalidDebugInfo,
236 },
237 .ensym => switch (state) {
238 .fun_size => {
239 state = .ensym;
240 symbols.appendAssumeCapacity(last_sym);
241 },
242 else => return error.InvalidDebugInfo,
243 },
244 .so => switch (state) {
245 .init, .oso_close => {},
246 .oso_open, .ensym => {
247 state = .oso_close;
248 },
249 else => return error.InvalidDebugInfo,
250 },
251 else => {},
252 }
253 }
254
255 switch (state) {
256 .init => return error.MissingDebugInfo,
257 .oso_close => {},
258 else => return error.InvalidDebugInfo,
259 }
260
261 const symbols_slice = try symbols.toOwnedSlice(gpa);
262 errdefer gpa.free(symbols_slice);
263
264 // Even though lld emits symbols in ascending order, this debug code
265 // should work for programs linked in any valid way.
266 // This sort is so that we can binary search later.
267 mem.sort(MachoSymbol, symbols_slice, {}, MachoSymbol.addressLessThan);
268
269 di.full = .{
270 .mapped_memory = mapped_mem,
271 .symbols = symbols_slice,
272 .strings = strings,
273 .ofiles = .empty,
274 };
172 }275 }
173 fn loadUnwindInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {276 fn loadUnwindInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {
277 if (di.unwind != null) return;
174 _ = gpa;278 _ = gpa;
175 di.unwind = .{279 di.unwind = .{
176 .unwind_info = module.unwind_info,280 .unwind_info = module.unwind_info,
...@@ -178,27 +282,39 @@ const Module = switch (native_os) {...@@ -178,27 +282,39 @@ const Module = switch (native_os) {
178 };282 };
179 }283 }
180 fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {284 fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
285 if (di.full == null) try module.loadLocationInfo(gpa, di);
181 const vaddr = address - module.load_offset;286 const vaddr = address - module.load_offset;
182 const symbol = MachoSymbol.find(di.full.symbols, vaddr) orelse return .{}; // MLUGG TODO null?287 const symbol = MachoSymbol.find(di.full.?.symbols, vaddr) orelse return .{
288 .name = null,
289 .compile_unit_name = null,
290 .source_location = null,
291 };
183292
184 // offset of `address` from start of `symbol`293 // offset of `address` from start of `symbol`
185 const address_symbol_offset = vaddr - symbol.addr;294 const address_symbol_offset = vaddr - symbol.addr;
186295
187 // Take the symbol name from the N_FUN STAB entry, we're going to296 // Take the symbol name from the N_FUN STAB entry, we're going to
188 // use it if we fail to find the DWARF infos297 // use it if we fail to find the DWARF infos
189 const stab_symbol = mem.sliceTo(di.full.strings[symbol.strx..], 0);298 const stab_symbol = mem.sliceTo(di.full.?.strings[symbol.strx..], 0);
190 const o_file_path = mem.sliceTo(di.full.strings[symbol.ofile..], 0);299 const o_file_path = mem.sliceTo(di.full.?.strings[symbol.ofile..], 0);
300
301 // If any information is missing, we can at least return this from now on.
302 const sym_only_result: std.debug.Symbol = .{
303 .name = stab_symbol,
304 .compile_unit_name = null,
305 .source_location = null,
306 };
191307
192 const o_file: *DebugInfo.OFile = of: {308 const o_file: *DebugInfo.OFile = of: {
193 const gop = try di.full.ofiles.getOrPut(gpa, o_file_path);309 const gop = try di.full.?.ofiles.getOrPut(gpa, o_file_path);
194 if (!gop.found_existing) {310 if (!gop.found_existing) {
195 gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| {311 gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| {
196 defer _ = di.full.ofiles.pop().?;312 defer _ = di.full.?.ofiles.pop().?;
197 switch (err) {313 switch (err) {
198 error.FileNotFound,314 error.FileNotFound,
199 error.MissingDebugInfo,315 error.MissingDebugInfo,
200 error.InvalidDebugInfo,316 error.InvalidDebugInfo,
201 => return .{ .name = stab_symbol },317 => return sym_only_result,
202 else => |e| return e,318 else => |e| return e,
203 }319 }
204 };320 };
...@@ -206,10 +322,10 @@ const Module = switch (native_os) {...@@ -206,10 +322,10 @@ const Module = switch (native_os) {
206 break :of gop.value_ptr;322 break :of gop.value_ptr;
207 };323 };
208324
209 const symbol_ofile_vaddr = o_file.addr_table.get(stab_symbol) orelse return .{ .name = stab_symbol };325 const symbol_ofile_vaddr = o_file.addr_table.get(stab_symbol) orelse return sym_only_result;
210326
211 const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch |err| switch (err) {327 const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch |err| switch (err) {
212 error.MissingDebugInfo, error.InvalidDebugInfo => return .{ .name = stab_symbol },328 error.MissingDebugInfo, error.InvalidDebugInfo => return sym_only_result,
213 else => |e| return e,329 else => |e| return e,
214 };330 };
215331
...@@ -222,7 +338,7 @@ const Module = switch (native_os) {...@@ -222,7 +338,7 @@ const Module = switch (native_os) {
222 o_file.dwarf.section(.debug_str),338 o_file.dwarf.section(.debug_str),
223 compile_unit,339 compile_unit,
224 ) catch |err| switch (err) {340 ) catch |err| switch (err) {
225 error.MissingDebugInfo, error.InvalidDebugInfo => "???",341 error.MissingDebugInfo, error.InvalidDebugInfo => null,
226 },342 },
227 .source_location = o_file.dwarf.getLineNumberInfo(343 .source_location = o_file.dwarf.getLineNumberInfo(
228 gpa,344 gpa,
...@@ -236,25 +352,27 @@ const Module = switch (native_os) {...@@ -236,25 +352,27 @@ const Module = switch (native_os) {
236 };352 };
237 }353 }
238 fn unwindFrame(module: *const Module, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {354 fn unwindFrame(module: *const Module, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
239 _ = gpa;355 if (di.unwind == null) try module.loadUnwindInfo(gpa, di);
240 const unwind_info = di.unwind.unwind_info orelse return error.MissingUnwindInfo;356 const unwind_info = di.unwind.?.unwind_info orelse return error.MissingUnwindInfo;
241 // MLUGG TODO: inline357 // MLUGG TODO: inline?
242 return unwindFrameMachO(358 return unwindFrameMachO(
243 module.text_base,359 module.text_base,
244 module.load_offset,360 module.load_offset,
245 context,361 context,
246 unwind_info,362 unwind_info,
247 di.unwind.eh_frame,363 di.unwind.?.eh_frame,
248 );364 );
249 }365 }
250 const LookupCache = void;366 const LookupCache = void;
251 const DebugInfo = struct {367 const DebugInfo = struct {
252 unwind: struct {368 unwind: ?struct {
369 // Backed by the in-memory sections mapped by the loader
370 // MLUGG TODO: these are duplicated state. i actually reckon they should be removed from Module, and loadLocationInfo should be the one discovering them!
253 unwind_info: ?[]const u8,371 unwind_info: ?[]const u8,
254 eh_frame: ?[]const u8,372 eh_frame: ?[]const u8,
255 },373 },
256 // MLUGG TODO: awful field name374 // MLUGG TODO: awful field name
257 full: struct {375 full: ?struct {
258 mapped_memory: []align(std.heap.page_size_min) const u8,376 mapped_memory: []align(std.heap.page_size_min) const u8,
259 symbols: []const MachoSymbol,377 symbols: []const MachoSymbol,
260 strings: [:0]const u8,378 strings: [:0]const u8,
...@@ -262,11 +380,10 @@ const Module = switch (native_os) {...@@ -262,11 +380,10 @@ const Module = switch (native_os) {
262 ofiles: std.StringArrayHashMapUnmanaged(OFile),380 ofiles: std.StringArrayHashMapUnmanaged(OFile),
263 },381 },
264382
265 // Backed by the in-memory sections mapped by the loader383 const init: DebugInfo = .{
266 // MLUGG TODO: these are duplicated state. i actually reckon they should be removed from Module, and loadMachODebugInfo should be the one discovering them!384 .unwind = null,
267385 .full = null,
268 // MLUGG TODO HACKHACK: this is awful386 };
269 const init: DebugInfo = undefined;
270387
271 const OFile = struct {388 const OFile = struct {
272 dwarf: Dwarf,389 dwarf: Dwarf,
...@@ -388,18 +505,6 @@ const Module = switch (native_os) {...@@ -388,18 +505,6 @@ const Module = switch (native_os) {
388 _ = address;505 _ = address;
389 unreachable;506 unreachable;
390 }507 }
391 fn loadLocationInfo(module: *const Module, gpa: Allocator, di: *DebugInfo) !void {
392 _ = module;
393 _ = gpa;
394 _ = di;
395 unreachable;
396 }
397 fn loadUnwindInfo(module: *const Module, gpa: Allocator, di: *DebugInfo) !void {
398 _ = module;
399 _ = gpa;
400 _ = di;
401 unreachable;
402 }
403 },508 },
404 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => struct {509 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => struct {
405 load_offset: usize,510 load_offset: usize,
...@@ -408,9 +513,12 @@ const Module = switch (native_os) {...@@ -408,9 +513,12 @@ const Module = switch (native_os) {
408 gnu_eh_frame: ?[]const u8,513 gnu_eh_frame: ?[]const u8,
409 const LookupCache = void;514 const LookupCache = void;
410 const DebugInfo = struct {515 const DebugInfo = struct {
411 const init: DebugInfo = undefined; // MLUGG TODO: this makes me sad516 em: ?Dwarf.ElfModule, // MLUGG TODO: bad field name (and, frankly, type)
412 em: Dwarf.ElfModule, // MLUGG TODO: bad field name (and, frankly, type)517 unwind: ?Dwarf.Unwind,
413 unwind: Dwarf.Unwind,518 const init: DebugInfo = .{
519 .em = null,
520 .unwind = null,
521 };
414 };522 };
415 fn key(m: Module) usize {523 fn key(m: Module) usize {
416 return m.load_offset; // MLUGG TODO: is this technically valid? idk524 return m.load_offset; // MLUGG TODO: is this technically valid? idk
...@@ -496,19 +604,20 @@ const Module = switch (native_os) {...@@ -496,19 +604,20 @@ const Module = switch (native_os) {
496 errdefer posix.munmap(mapped_mem);604 errdefer posix.munmap(mapped_mem);
497 di.em = try .load(gpa, mapped_mem, module.build_id, null, null, null, filename);605 di.em = try .load(gpa, mapped_mem, module.build_id, null, null, null, filename);
498 }606 }
607 fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
608 if (di.em == null) try module.loadLocationInfo(gpa, di);
609 return di.em.?.getSymbolAtAddress(gpa, native_endian, module.load_offset, address);
610 }
499 fn loadUnwindInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {611 fn loadUnwindInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {
500 const section_bytes = module.gnu_eh_frame orelse return error.MissingUnwindInfo; // MLUGG TODO: load from file612 const section_bytes = module.gnu_eh_frame orelse return error.MissingUnwindInfo; // MLUGG TODO: load from file
501 const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset;613 const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset;
502 const header: Dwarf.Unwind.EhFrameHeader = try .parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian);614 const header: Dwarf.Unwind.EhFrameHeader = try .parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian);
503 di.unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr));615 di.unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr));
504 try di.unwind.prepareLookup(gpa, @sizeOf(usize), native_endian);616 try di.unwind.?.prepareLookup(gpa, @sizeOf(usize), native_endian);
505 }
506 fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
507 return di.em.getSymbolAtAddress(gpa, native_endian, module.load_offset, address);
508 }617 }
509 fn unwindFrame(module: *const Module, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {618 fn unwindFrame(module: *const Module, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
510 _ = gpa;619 if (di.unwind == null) try module.loadUnwindInfo(gpa, di);
511 return unwindFrameDwarf(&di.unwind, module.load_offset, context, null);620 return unwindFrameDwarf(&di.unwind.?, module.load_offset, context, null);
512 }621 }
513 },622 },
514 .uefi, .windows => struct {623 .uefi, .windows => struct {
...@@ -660,12 +769,16 @@ const Module = switch (native_os) {...@@ -660,12 +769,16 @@ const Module = switch (native_os) {
660769
661 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(gpa);770 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(gpa);
662 }771 }
772
773 di.loaded = true;
663 }774 }
664 const LookupCache = struct {775 const LookupCache = struct {
665 modules: std.ArrayListUnmanaged(windows.MODULEENTRY32),776 modules: std.ArrayListUnmanaged(windows.MODULEENTRY32),
666 const init: LookupCache = .{ .modules = .empty };777 const init: LookupCache = .{ .modules = .empty };
667 };778 };
668 const DebugInfo = struct {779 const DebugInfo = struct {
780 loaded: bool,
781
669 coff_image_base: u64,782 coff_image_base: u64,
670 mapped_file: ?struct {783 mapped_file: ?struct {
671 file: File,784 file: File,
...@@ -686,6 +799,7 @@ const Module = switch (native_os) {...@@ -686,6 +799,7 @@ const Module = switch (native_os) {
686 coff_section_headers: []coff.SectionHeader,799 coff_section_headers: []coff.SectionHeader,
687800
688 const init: DebugInfo = .{801 const init: DebugInfo = .{
802 .loaded = false,
689 .coff_image_base = undefined,803 .coff_image_base = undefined,
690 .mapped_file = null,804 .mapped_file = null,
691 .dwarf = null,805 .dwarf = null,
...@@ -717,28 +831,24 @@ const Module = switch (native_os) {...@@ -717,28 +831,24 @@ const Module = switch (native_os) {
717 return null;831 return null;
718 };832 };
719833
720 const module = (try di.pdb.?.getModule(mod_index)) orelse834 const module = try di.pdb.?.getModule(mod_index) orelse return error.InvalidDebugInfo;
721 return error.InvalidDebugInfo;
722 const obj_basename = fs.path.basename(module.obj_file_name);
723
724 const symbol_name = di.pdb.?.getSymbolName(
725 module,
726 relocated_address - coff_section.virtual_address,
727 ) orelse "???";
728 const opt_line_info = try di.pdb.?.getLineNumberInfo(
729 module,
730 relocated_address - coff_section.virtual_address,
731 );
732835
733 return .{836 return .{
734 .name = symbol_name,837 .name = di.pdb.?.getSymbolName(
735 .compile_unit_name = obj_basename,838 module,
736 .source_location = opt_line_info,839 relocated_address - coff_section.virtual_address,
840 ),
841 .compile_unit_name = fs.path.basename(module.obj_file_name),
842 .source_location = try di.pdb.?.getLineNumberInfo(
843 module,
844 relocated_address - coff_section.virtual_address,
845 ),
737 };846 };
738 }847 }
739 };848 };
740849
741 fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {850 fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
851 if (!di.loaded) try module.loadLocationInfo(gpa, di);
742 // Translate the runtime address into a virtual address into the module852 // Translate the runtime address into a virtual address into the module
743 const vaddr = address - module.base_address;853 const vaddr = address - module.base_address;
744854
...@@ -756,125 +866,6 @@ const Module = switch (native_os) {...@@ -756,125 +866,6 @@ const Module = switch (native_os) {
756 },866 },
757};867};
758868
759fn loadMachODebugInfo(gpa: Allocator, module: *const Module, di: *Module.DebugInfo) !void {
760 const mapped_mem = mapFileOrSelfExe(module.name) catch |err| switch (err) {
761 error.FileNotFound => return error.MissingDebugInfo,
762 error.FileTooBig => return error.InvalidDebugInfo,
763 else => |e| return e,
764 };
765 errdefer posix.munmap(mapped_mem);
766
767 const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr));
768 if (hdr.magic != macho.MH_MAGIC_64)
769 return error.InvalidDebugInfo;
770
771 const symtab: macho.symtab_command = symtab: {
772 var it: macho.LoadCommandIterator = .{
773 .ncmds = hdr.ncmds,
774 .buffer = mapped_mem[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
775 };
776 while (it.next()) |cmd| switch (cmd.cmd()) {
777 .SYMTAB => break :symtab cmd.cast(macho.symtab_command) orelse return error.InvalidDebugInfo,
778 else => {},
779 };
780 return error.MissingDebugInfo;
781 };
782
783 const syms_ptr: [*]align(1) const macho.nlist_64 = @ptrCast(mapped_mem[symtab.symoff..]);
784 const syms = syms_ptr[0..symtab.nsyms];
785 const strings = mapped_mem[symtab.stroff..][0 .. symtab.strsize - 1 :0];
786
787 // MLUGG TODO: does it really make sense to initCapacity here? how many of syms are omitted?
788 var symbols: std.ArrayList(MachoSymbol) = try .initCapacity(gpa, syms.len);
789 defer symbols.deinit(gpa);
790
791 var ofile: u32 = undefined;
792 var last_sym: MachoSymbol = undefined;
793 var state: enum {
794 init,
795 oso_open,
796 oso_close,
797 bnsym,
798 fun_strx,
799 fun_size,
800 ensym,
801 } = .init;
802
803 for (syms) |*sym| {
804 if (sym.n_type.bits.is_stab == 0) continue;
805
806 // TODO handle globals N_GSYM, and statics N_STSYM
807 switch (sym.n_type.stab) {
808 .oso => switch (state) {
809 .init, .oso_close => {
810 state = .oso_open;
811 ofile = sym.n_strx;
812 },
813 else => return error.InvalidDebugInfo,
814 },
815 .bnsym => switch (state) {
816 .oso_open, .ensym => {
817 state = .bnsym;
818 last_sym = .{
819 .strx = 0,
820 .addr = sym.n_value,
821 .size = 0,
822 .ofile = ofile,
823 };
824 },
825 else => return error.InvalidDebugInfo,
826 },
827 .fun => switch (state) {
828 .bnsym => {
829 state = .fun_strx;
830 last_sym.strx = sym.n_strx;
831 },
832 .fun_strx => {
833 state = .fun_size;
834 last_sym.size = @intCast(sym.n_value);
835 },
836 else => return error.InvalidDebugInfo,
837 },
838 .ensym => switch (state) {
839 .fun_size => {
840 state = .ensym;
841 symbols.appendAssumeCapacity(last_sym);
842 },
843 else => return error.InvalidDebugInfo,
844 },
845 .so => switch (state) {
846 .init, .oso_close => {},
847 .oso_open, .ensym => {
848 state = .oso_close;
849 },
850 else => return error.InvalidDebugInfo,
851 },
852 else => {},
853 }
854 }
855
856 switch (state) {
857 .init => return error.MissingDebugInfo,
858 .oso_close => {},
859 else => return error.InvalidDebugInfo,
860 }
861
862 const symbols_slice = try symbols.toOwnedSlice(gpa);
863 errdefer gpa.free(symbols_slice);
864
865 // Even though lld emits symbols in ascending order, this debug code
866 // should work for programs linked in any valid way.
867 // This sort is so that we can binary search later.
868 mem.sort(MachoSymbol, symbols_slice, {}, MachoSymbol.addressLessThan);
869
870 di.full = .{
871 .mapped_memory = mapped_mem,
872 .symbols = symbols_slice,
873 .strings = strings,
874 .ofiles = .empty,
875 };
876}
877
878const MachoSymbol = struct {869const MachoSymbol = struct {
879 strx: u32,870 strx: u32,
880 addr: u64,871 addr: u64,