| ... | @@ -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 | } |
| 46 | fn loadLocationInfo(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo) !void { | 46 | fn 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 | } |
| | 76 | fn 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); |
| 49 | | 79 | |
| ... | @@ -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); |
| 151 | | 181 | |
| 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 | } |
| 159 | fn 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 | } | | |
| 191 | pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { | 189 | pub 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 |
| 202 | | 202 | |
| 203 | // Take the symbol name from the N_FUN STAB entry, we're going to | 203 | // Take the symbol name from the N_FUN STAB entry, we're going to |
| 204 | // use it if we fail to find the DWARF infos | 204 | // 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); | | |
| 207 | | 206 | |
| 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 | }; |
| 214 | | 213 | |
| 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 | }; |
| 230 | | 230 | |
| 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; |
| 232 | | 236 | |
| 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 | } |
| 260 | pub 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 will | 265 | /// 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. |
| 275 | fn unwindFrameMachO( | 267 | pub 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); |
| 284 | | 275 | |
| ... | @@ -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; |
| 289 | | 280 | |
| 290 | // offset of the PC into the `__TEXT` segment | 281 | // 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; |
| 292 | | 283 | |
| 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 instruction | 440 | // `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. |
| 581 | pub const LookupCache = void; | 572 | pub const LookupCache = void; |
| 582 | pub const DebugInfo = struct { | 573 | pub 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 name | 575 | // 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 | }, | | |
| 596 | | 577 | |
| 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 | } |
| 613 | | 594 | |
| | 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 | }; |
| 619 | | 632 | |
| 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 { |
| 645 | | 658 | |
| 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]; |
| 649 | | 662 | |
| 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]); |
| 653 | | 666 | |
| 654 | // TODO handle tentative (common) symbols | 667 | // 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 symbols | 674 | .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 | } |
| 670 | | 686 | |
| 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 { |
| 697 | | 713 | |
| 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 | }; |