authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-02 23:17:09+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:50+01:00
log4b47a377175c30ae148bae1191ae49e3ace819db
tree25f0a3c5cb0dc6cdb790bd80733519fefb8b462e
parent665f13b0cde4b9c2e69b139a87d272a67a9489e1
signaturelock-open Commit is signed but in an unrecognized format.

stash? more like no


4 files changed, 116 insertions(+), 100 deletions(-)

lib/std/debug/Dwarf/Unwind.zig+2-2
...@@ -553,8 +553,8 @@ fn readEhPointerAbs(r: *Reader, enc_ty: EH.PE.Type, addr_size_bytes: u8, endian:...@@ -553,8 +553,8 @@ fn readEhPointerAbs(r: *Reader, enc_ty: EH.PE.Type, addr_size_bytes: u8, endian:
553 };553 };
554}554}
555/// Returns `error.InvalidDebugInfo` if the encoding is `EH.PE.omit`.555/// Returns `error.InvalidDebugInfo` if the encoding is `EH.PE.omit`.
556fn readEhPointer(fbr: *Reader, enc: EH.PE, addr_size_bytes: u8, ctx: EhPointerContext, endian: Endian) !u64 {556fn readEhPointer(r: *Reader, enc: EH.PE, addr_size_bytes: u8, ctx: EhPointerContext, endian: Endian) !u64 {
557 const offset = try readEhPointerAbs(fbr, enc.type, addr_size_bytes, endian);557 const offset = try readEhPointerAbs(r, enc.type, addr_size_bytes, endian);
558 const base = switch (enc.rel) {558 const base = switch (enc.rel) {
559 .abs, .aligned => 0,559 .abs, .aligned => 0,
560 .pcrel => ctx.pc_rel_base,560 .pcrel => ctx.pc_rel_base,
lib/std/debug/SelfInfo.zig+3-5
...@@ -76,7 +76,7 @@ pub fn deinit(self: *SelfInfo, gpa: Allocator) void {...@@ -76,7 +76,7 @@ pub fn deinit(self: *SelfInfo, gpa: Allocator) void {
76 self.modules.deinit(gpa);76 self.modules.deinit(gpa);
77 if (Module.LookupCache != void) self.lookup_cache.deinit(gpa);77 if (Module.LookupCache != void) self.lookup_cache.deinit(gpa);
78}78}
79comptime {79test {
80 // `std.debug` does not currently utilize `deinit`, as it keeps hold of debug info for the80 // `std.debug` does not currently utilize `deinit`, as it keeps hold of debug info for the
81 // whole lifetime of the program. Let's try to avoid it bitrotting.81 // whole lifetime of the program. Let's try to avoid it bitrotting.
82 _ = &deinit;82 _ = &deinit;
...@@ -85,7 +85,7 @@ comptime {...@@ -85,7 +85,7 @@ comptime {
85pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !usize {85pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !usize {
86 comptime assert(supports_unwinding);86 comptime assert(supports_unwinding);
87 const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc);87 const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc);
88 const gop = try self.modules.getOrPut(gpa, module.load_offset);88 const gop = try self.modules.getOrPut(gpa, module.key());
89 self.modules.lockPointers();89 self.modules.lockPointers();
90 defer self.modules.unlockPointers();90 defer self.modules.unlockPointers();
91 if (!gop.found_existing) gop.value_ptr.* = .init;91 if (!gop.found_existing) gop.value_ptr.* = .init;
...@@ -128,9 +128,7 @@ const Module = switch (native_os) {...@@ -128,9 +128,7 @@ const Module = switch (native_os) {
128 .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"),128 .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"),
129 .uefi, .windows => @import("SelfInfo/WindowsModule.zig"),129 .uefi, .windows => @import("SelfInfo/WindowsModule.zig"),
130 .wasi, .emscripten => struct {130 .wasi, .emscripten => struct {
131 const LookupCache = struct {131 const LookupCache = void;
132 const init: LookupCache = .{};
133 };
134 fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module {132 fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module {
135 _ = cache;133 _ = cache;
136 _ = gpa;134 _ = gpa;
lib/std/debug/SelfInfo/DarwinModule.zig+109-91
...@@ -43,7 +43,37 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !DarwinModule...@@ -43,7 +43,37 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !DarwinModule
43 }43 }
44 return error.MissingDebugInfo;44 return error.MissingDebugInfo;
45}45}
46fn loadLocationInfo(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo) !void {46fn loadUnwindInfo(module: *const DarwinModule) DebugInfo.Unwind {
47 const header: *std.macho.mach_header = @ptrFromInt(module.text_base);
48
49 var it: macho.LoadCommandIterator = .{
50 .ncmds = header.ncmds,
51 .buffer = @as([*]u8, @ptrCast(header))[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
52 };
53 const sections = while (it.next()) |load_cmd| {
54 if (load_cmd.cmd() != .SEGMENT_64) continue;
55 const segment_cmd = load_cmd.cast(macho.segment_command_64).?;
56 if (!mem.eql(u8, segment_cmd.segName(), "__TEXT")) continue;
57 break load_cmd.getSections();
58 } else unreachable;
59
60 var unwind_info: ?[]const u8 = null;
61 var eh_frame: ?[]const u8 = null;
62 for (sections) |sect| {
63 if (mem.eql(u8, sect.sectName(), "__unwind_info")) {
64 const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(module.load_offset + sect.addr)));
65 unwind_info = sect_ptr[0..@intCast(sect.size)];
66 } else if (mem.eql(u8, sect.sectName(), "__eh_frame")) {
67 const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(module.load_offset + sect.addr)));
68 eh_frame = sect_ptr[0..@intCast(sect.size)];
69 }
70 }
71 return .{
72 .unwind_info = unwind_info,
73 .eh_frame = eh_frame,
74 };
75}
76fn loadFullInfo(module: *const DarwinModule, gpa: Allocator) !DebugInfo.Full {
47 const mapped_mem = try mapDebugInfoFile(module.name);77 const mapped_mem = try mapDebugInfoFile(module.name);
48 errdefer posix.munmap(mapped_mem);78 errdefer posix.munmap(mapped_mem);
4979
...@@ -149,49 +179,19 @@ fn loadLocationInfo(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo)...@@ -149,49 +179,19 @@ fn loadLocationInfo(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo)
149 // This sort is so that we can binary search later.179 // This sort is so that we can binary search later.
150 mem.sort(MachoSymbol, symbols_slice, {}, MachoSymbol.addressLessThan);180 mem.sort(MachoSymbol, symbols_slice, {}, MachoSymbol.addressLessThan);
151181
152 di.full = .{182 return .{
153 .mapped_memory = mapped_mem,183 .mapped_memory = mapped_mem,
154 .symbols = symbols_slice,184 .symbols = symbols_slice,
155 .strings = strings,185 .strings = strings,
156 .ofiles = .empty,186 .ofiles = .empty,
157 };187 };
158}188}
159fn loadUnwindInfo(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo) !void {
160 _ = gpa;
161
162 const header: *std.macho.mach_header = @ptrFromInt(module.text_base);
163
164 var it: macho.LoadCommandIterator = .{
165 .ncmds = header.ncmds,
166 .buffer = @as([*]u8, @ptrCast(header))[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
167 };
168 const sections = while (it.next()) |load_cmd| {
169 if (load_cmd.cmd() != .SEGMENT_64) continue;
170 const segment_cmd = load_cmd.cast(macho.segment_command_64).?;
171 if (!mem.eql(u8, segment_cmd.segName(), "__TEXT")) continue;
172 break load_cmd.getSections();
173 } else unreachable;
174
175 var unwind_info: ?[]const u8 = null;
176 var eh_frame: ?[]const u8 = null;
177 for (sections) |sect| {
178 if (mem.eql(u8, sect.sectName(), "__unwind_info")) {
179 const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(module.load_offset + sect.addr)));
180 unwind_info = sect_ptr[0..@intCast(sect.size)];
181 } else if (mem.eql(u8, sect.sectName(), "__eh_frame")) {
182 const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(module.load_offset + sect.addr)));
183 eh_frame = sect_ptr[0..@intCast(sect.size)];
184 }
185 }
186 di.unwind = .{
187 .unwind_info = unwind_info,
188 .eh_frame = eh_frame,
189 };
190}
191pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {189pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
192 if (di.full == null) try module.loadLocationInfo(gpa, di);190 if (di.full == null) di.full = try module.loadFullInfo(gpa);
191 const full = &di.full.?;
192
193 const vaddr = address - module.load_offset;193 const vaddr = address - module.load_offset;
194 const symbol = MachoSymbol.find(di.full.?.symbols, vaddr) orelse return .{194 const symbol = MachoSymbol.find(full.symbols, vaddr) orelse return .{
195 .name = null,195 .name = null,
196 .compile_unit_name = null,196 .compile_unit_name = null,
197 .source_location = null,197 .source_location = null,
...@@ -202,8 +202,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu...@@ -202,8 +202,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
202202
203 // Take the symbol name from the N_FUN STAB entry, we're going to203 // Take the symbol name from the N_FUN STAB entry, we're going to
204 // use it if we fail to find the DWARF infos204 // use it if we fail to find the DWARF infos
205 const stab_symbol = mem.sliceTo(di.full.?.strings[symbol.strx..], 0);205 const stab_symbol = mem.sliceTo(full.strings[symbol.strx..], 0);
206 const o_file_path = mem.sliceTo(di.full.?.strings[symbol.ofile..], 0);
207206
208 // If any information is missing, we can at least return this from now on.207 // If any information is missing, we can at least return this from now on.
209 const sym_only_result: std.debug.Symbol = .{208 const sym_only_result: std.debug.Symbol = .{
...@@ -213,10 +212,11 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu...@@ -213,10 +212,11 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
213 };212 };
214213
215 const o_file: *DebugInfo.OFile = of: {214 const o_file: *DebugInfo.OFile = of: {
216 const gop = try di.full.?.ofiles.getOrPut(gpa, o_file_path);215 const gop = try full.ofiles.getOrPut(gpa, symbol.ofile);
217 if (!gop.found_existing) {216 if (!gop.found_existing) {
217 const o_file_path = mem.sliceTo(full.strings[symbol.ofile..], 0);
218 gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| {218 gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| {
219 defer _ = di.full.?.ofiles.pop().?;219 defer _ = full.ofiles.pop().?;
220 switch (err) {220 switch (err) {
221 error.MissingDebugInfo,221 error.MissingDebugInfo,
222 error.InvalidDebugInfo,222 error.InvalidDebugInfo,
...@@ -228,7 +228,11 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu...@@ -228,7 +228,11 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
228 break :of gop.value_ptr;228 break :of gop.value_ptr;
229 };229 };
230230
231 const symbol_ofile_vaddr = o_file.addr_table.get(stab_symbol) orelse return sym_only_result;231 const symbol_index = o_file.symbols_by_name.getKeyAdapted(
232 @as([]const u8, stab_symbol),
233 @as(DebugInfo.OFile.SymbolAdapter, .{ .strtab = o_file.strtab, .symtab = o_file.symtab }),
234 ) orelse return sym_only_result;
235 const symbol_ofile_vaddr = o_file.symtab[symbol_index].n_value;
232236
233 const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch |err| switch (err) {237 const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch |err| switch (err) {
234 error.MissingDebugInfo, error.InvalidDebugInfo => return sym_only_result,238 error.MissingDebugInfo, error.InvalidDebugInfo => return sym_only_result,
...@@ -257,28 +261,15 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu...@@ -257,28 +261,15 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
257 },261 },
258 };262 };
259}263}
260pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
261 if (di.unwind == null) try module.loadUnwindInfo(gpa, di);
262 const unwind_info = di.unwind.?.unwind_info orelse return error.MissingUnwindInfo;
263 // MLUGG TODO: inline?
264 return unwindFrameMachO(
265 module.text_base,
266 module.load_offset,
267 context,
268 unwind_info,
269 di.unwind.?.eh_frame,
270 );
271}
272/// Unwind a frame using MachO compact unwind info (from __unwind_info).264/// Unwind a frame using MachO compact unwind info (from __unwind_info).
273/// If the compact encoding can't encode a way to unwind a frame, it will265/// If the compact encoding can't encode a way to unwind a frame, it will
274/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.266/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
275fn unwindFrameMachO(267pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
276 text_base: usize,268 _ = gpa;
277 load_offset: usize,269 if (di.unwind == null) di.unwind = module.loadUnwindInfo();
278 context: *UnwindContext,270 const unwind = &di.unwind.?;
279 unwind_info: []const u8,271
280 opt_eh_frame: ?[]const u8,272 const unwind_info = unwind.unwind_info orelse return error.MissingUnwindInfo;
281) !usize {
282 if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidUnwindInfo;273 if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidUnwindInfo;
283 const header: *align(1) const macho.unwind_info_section_header = @ptrCast(unwind_info);274 const header: *align(1) const macho.unwind_info_section_header = @ptrCast(unwind_info);
284275
...@@ -288,7 +279,7 @@ fn unwindFrameMachO(...@@ -288,7 +279,7 @@ fn unwindFrameMachO(
288 if (indices.len == 0) return error.MissingUnwindInfo;279 if (indices.len == 0) return error.MissingUnwindInfo;
289280
290 // offset of the PC into the `__TEXT` segment281 // offset of the PC into the `__TEXT` segment
291 const pc_text_offset = context.pc - text_base;282 const pc_text_offset = context.pc - module.text_base;
292283
293 const start_offset: u32, const first_level_offset: u32 = index: {284 const start_offset: u32, const first_level_offset: u32 = index: {
294 var left: usize = 0;285 var left: usize = 0;
...@@ -443,7 +434,7 @@ fn unwindFrameMachO(...@@ -443,7 +434,7 @@ fn unwindFrameMachO(
443 }434 }
444 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.435 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
445 const sub_offset_addr =436 const sub_offset_addr =
446 text_base +437 module.text_base +
447 entry.function_offset +438 entry.function_offset +
448 frameless.stack.indirect.sub_offset;439 frameless.stack.indirect.sub_offset;
449 // `sub_offset_addr` points to the offset of the literal within the instruction440 // `sub_offset_addr` points to the offset of the literal within the instruction
...@@ -502,11 +493,11 @@ fn unwindFrameMachO(...@@ -502,11 +493,11 @@ fn unwindFrameMachO(
502 break :ip new_ip;493 break :ip new_ip;
503 },494 },
504 .DWARF => {495 .DWARF => {
505 const eh_frame = opt_eh_frame orelse return error.MissingEhFrame;496 const eh_frame = unwind.eh_frame orelse return error.MissingEhFrame;
506 const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - load_offset;497 const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset;
507 return context.unwindFrameDwarf(498 return context.unwindFrameDwarf(
508 &.initSection(.eh_frame, eh_frame_vaddr, eh_frame),499 &.initSection(.eh_frame, eh_frame_vaddr, eh_frame),
509 load_offset,500 module.load_offset,
510 @intCast(encoding.value.x86_64.dwarf),501 @intCast(encoding.value.x86_64.dwarf),
511 );502 );
512 },503 },
...@@ -521,11 +512,11 @@ fn unwindFrameMachO(...@@ -521,11 +512,11 @@ fn unwindFrameMachO(
521 break :ip new_ip;512 break :ip new_ip;
522 },513 },
523 .DWARF => {514 .DWARF => {
524 const eh_frame = opt_eh_frame orelse return error.MissingEhFrame;515 const eh_frame = unwind.eh_frame orelse return error.MissingEhFrame;
525 const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - load_offset;516 const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset;
526 return context.unwindFrameDwarf(517 return context.unwindFrameDwarf(
527 &.initSection(.eh_frame, eh_frame_vaddr, eh_frame),518 &.initSection(.eh_frame, eh_frame_vaddr, eh_frame),
528 load_offset,519 module.load_offset,
529 @intCast(encoding.value.x86_64.dwarf),520 @intCast(encoding.value.x86_64.dwarf),
530 );521 );
531 },522 },
...@@ -580,19 +571,9 @@ fn unwindFrameMachO(...@@ -580,19 +571,9 @@ fn unwindFrameMachO(
580/// No cache needed, because `_dyld_get_image_header` etc are already fast.571/// No cache needed, because `_dyld_get_image_header` etc are already fast.
581pub const LookupCache = void;572pub const LookupCache = void;
582pub const DebugInfo = struct {573pub const DebugInfo = struct {
583 unwind: ?struct {574 unwind: ?Unwind,
584 // Backed by the in-memory sections mapped by the loader
585 unwind_info: ?[]const u8,
586 eh_frame: ?[]const u8,
587 },
588 // MLUGG TODO: awful field name575 // MLUGG TODO: awful field name
589 full: ?struct {576 full: ?Full,
590 mapped_memory: []align(std.heap.page_size_min) const u8,
591 symbols: []const MachoSymbol,
592 strings: [:0]const u8,
593 // MLUGG TODO: this could use an adapter to just index straight into `strings`!
594 ofiles: std.StringArrayHashMapUnmanaged(OFile),
595 },
596577
597 pub const init: DebugInfo = .{578 pub const init: DebugInfo = .{
598 .unwind = null,579 .unwind = null,
...@@ -603,7 +584,7 @@ pub const DebugInfo = struct {...@@ -603,7 +584,7 @@ pub const DebugInfo = struct {
603 if (di.full) |*full| {584 if (di.full) |*full| {
604 for (full.ofiles.values()) |*ofile| {585 for (full.ofiles.values()) |*ofile| {
605 ofile.dwarf.deinit(gpa);586 ofile.dwarf.deinit(gpa);
606 ofile.addr_table.deinit(gpa);587 ofile.symbols_by_name.deinit(gpa);
607 }588 }
608 full.ofiles.deinit(gpa);589 full.ofiles.deinit(gpa);
609 gpa.free(full.symbols);590 gpa.free(full.symbols);
...@@ -611,10 +592,42 @@ pub const DebugInfo = struct {...@@ -611,10 +592,42 @@ pub const DebugInfo = struct {
611 }592 }
612 }593 }
613594
595 const Unwind = struct {
596 // Backed by the in-memory sections mapped by the loader
597 unwind_info: ?[]const u8,
598 eh_frame: ?[]const u8,
599 };
600
601 const Full = struct {
602 mapped_memory: []align(std.heap.page_size_min) const u8,
603 symbols: []const MachoSymbol,
604 strings: [:0]const u8,
605 /// Key is index into `strings` of the file path.
606 ofiles: std.AutoArrayHashMapUnmanaged(u32, OFile),
607 };
608
614 const OFile = struct {609 const OFile = struct {
615 dwarf: Dwarf,610 dwarf: Dwarf,
616 // MLUGG TODO: this could use an adapter to just index straight into the strtab!611 strtab: [:0]const u8,
617 addr_table: std.StringArrayHashMapUnmanaged(u64),612 symtab: []align(1) const macho.nlist_64,
613 /// All named symbols in `symtab`. Stored `u32` key is the index into `symtab`. Accessed
614 /// through `SymbolAdapter`, so that the symbol name is used as the logical key.
615 symbols_by_name: std.ArrayHashMapUnmanaged(u32, void, void, true),
616
617 const SymbolAdapter = struct {
618 strtab: [:0]const u8,
619 symtab: []align(1) const macho.nlist_64,
620 pub fn hash(ctx: SymbolAdapter, sym_name: []const u8) u32 {
621 _ = ctx;
622 return @truncate(std.hash.Wyhash.hash(0, sym_name));
623 }
624 pub fn eql(ctx: SymbolAdapter, a_sym_name: []const u8, b_sym_index: u32, b_index: usize) bool {
625 _ = b_index;
626 const b_sym = ctx.symtab[b_sym_index];
627 const b_sym_name = std.mem.sliceTo(ctx.strtab[b_sym.n_strx..], 0);
628 return mem.eql(u8, a_sym_name, b_sym_name);
629 }
630 };
618 };631 };
619632
620 fn loadOFile(gpa: Allocator, o_file_path: []const u8) !OFile {633 fn loadOFile(gpa: Allocator, o_file_path: []const u8) !OFile {
...@@ -645,17 +658,17 @@ pub const DebugInfo = struct {...@@ -645,17 +658,17 @@ pub const DebugInfo = struct {
645658
646 if (mapped_mem.len < symtab_cmd.stroff + symtab_cmd.strsize) return error.InvalidDebugInfo;659 if (mapped_mem.len < symtab_cmd.stroff + symtab_cmd.strsize) return error.InvalidDebugInfo;
647 if (mapped_mem[symtab_cmd.stroff + symtab_cmd.strsize - 1] != 0) return error.InvalidDebugInfo;660 if (mapped_mem[symtab_cmd.stroff + symtab_cmd.strsize - 1] != 0) return error.InvalidDebugInfo;
648 const strtab = mapped_mem[symtab_cmd.stroff..][0 .. symtab_cmd.strsize - 1];661 const strtab = mapped_mem[symtab_cmd.stroff..][0 .. symtab_cmd.strsize - 1 :0];
649662
650 const n_sym_bytes = symtab_cmd.nsyms * @sizeOf(macho.nlist_64);663 const n_sym_bytes = symtab_cmd.nsyms * @sizeOf(macho.nlist_64);
651 if (mapped_mem.len < symtab_cmd.symoff + n_sym_bytes) return error.InvalidDebugInfo;664 if (mapped_mem.len < symtab_cmd.symoff + n_sym_bytes) return error.InvalidDebugInfo;
652 const symtab: []align(1) const macho.nlist_64 = @ptrCast(mapped_mem[symtab_cmd.symoff..][0..n_sym_bytes]);665 const symtab: []align(1) const macho.nlist_64 = @ptrCast(mapped_mem[symtab_cmd.symoff..][0..n_sym_bytes]);
653666
654 // TODO handle tentative (common) symbols667 // TODO handle tentative (common) symbols
655 var addr_table: std.StringArrayHashMapUnmanaged(u64) = .empty;668 var symbols_by_name: std.ArrayHashMapUnmanaged(u32, void, void, true) = .empty;
656 defer addr_table.deinit(gpa);669 defer symbols_by_name.deinit(gpa);
657 try addr_table.ensureUnusedCapacity(gpa, @intCast(symtab.len));670 try symbols_by_name.ensureUnusedCapacity(gpa, @intCast(symtab.len));
658 for (symtab) |sym| {671 for (symtab, 0..) |sym, sym_index| {
659 if (sym.n_strx == 0) continue;672 if (sym.n_strx == 0) continue;
660 switch (sym.n_type.bits.type) {673 switch (sym.n_type.bits.type) {
661 .undf => continue, // includes tentative symbols674 .undf => continue, // includes tentative symbols
...@@ -663,9 +676,12 @@ pub const DebugInfo = struct {...@@ -663,9 +676,12 @@ pub const DebugInfo = struct {
663 else => {},676 else => {},
664 }677 }
665 const sym_name = mem.sliceTo(strtab[sym.n_strx..], 0);678 const sym_name = mem.sliceTo(strtab[sym.n_strx..], 0);
666 const gop = addr_table.getOrPutAssumeCapacity(sym_name);679 const gop = symbols_by_name.getOrPutAssumeCapacityAdapted(
680 @as([]const u8, sym_name),
681 @as(DebugInfo.OFile.SymbolAdapter, .{ .strtab = strtab, .symtab = symtab }),
682 );
667 if (gop.found_existing) return error.InvalidDebugInfo;683 if (gop.found_existing) return error.InvalidDebugInfo;
668 gop.value_ptr.* = sym.n_value;684 gop.key_ptr.* = @intCast(sym_index);
669 }685 }
670686
671 var sections: Dwarf.SectionArray = @splat(null);687 var sections: Dwarf.SectionArray = @splat(null);
...@@ -697,7 +713,9 @@ pub const DebugInfo = struct {...@@ -697,7 +713,9 @@ pub const DebugInfo = struct {
697713
698 return .{714 return .{
699 .dwarf = dwarf,715 .dwarf = dwarf,
700 .addr_table = addr_table.move(),716 .strtab = strtab,
717 .symtab = symtab,
718 .symbols_by_name = symbols_by_name.move(),
701 };719 };
702 }720 }
703};721};
lib/std/debug/SelfInfo/ElfModule.zig+2-2
...@@ -92,7 +92,7 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !ElfModule {...@@ -92,7 +92,7 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !ElfModule {
92 };92 };
93 return error.MissingDebugInfo;93 return error.MissingDebugInfo;
94}94}
95fn loadLocationInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !void {95fn loadDwarf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !void {
96 if (module.name.len > 0) {96 if (module.name.len > 0) {
97 di.loaded_elf = Dwarf.ElfModule.load(gpa, .{97 di.loaded_elf = Dwarf.ElfModule.load(gpa, .{
98 .root_dir = .cwd(),98 .root_dir = .cwd(),
...@@ -116,7 +116,7 @@ fn loadLocationInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !v...@@ -116,7 +116,7 @@ fn loadLocationInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !v
116 }116 }
117}117}
118pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {118pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
119 if (di.loaded_elf == null) try module.loadLocationInfo(gpa, di);119 if (di.loaded_elf == null) try module.loadDwarf(gpa, di);
120 const vaddr = address - module.load_offset;120 const vaddr = address - module.load_offset;
121 return di.loaded_elf.?.dwarf.getSymbol(gpa, native_endian, vaddr);121 return di.loaded_elf.?.dwarf.getSymbol(gpa, native_endian, vaddr);
122}122}