Type Module [src]

Members

Source

pub const Module = switch (native_os) { .macos, .ios, .watchos, .tvos, .visionos => struct { base_address: usize, vmaddr_slide: usize, mapped_memory: []align(std.heap.page_size_min) const u8, symbols: []const MachoSymbol, strings: [:0]const u8, ofiles: OFileTable, // Backed by the in-memory sections mapped by the loader unwind_info: ?[]const u8 = null, eh_frame: ?[]const u8 = null, const OFileTable = std.StringHashMap(OFileInfo); const OFileInfo = struct { di: Dwarf, addr_table: std.StringHashMap(u64), }; pub fn deinit(self: *@This(), allocator: Allocator) void { var it = self.ofiles.iterator(); while (it.next()) |entry| { const ofile = entry.value_ptr; ofile.di.deinit(allocator); ofile.addr_table.deinit(); } self.ofiles.deinit(); allocator.free(self.symbols); posix.munmap(self.mapped_memory); } fn loadOFile(self: *@This(), allocator: Allocator, o_file_path: []const u8) !*OFileInfo { const o_file = try fs.cwd().openFile(o_file_path, .{}); const mapped_mem = try mapWholeFile(o_file); const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr)); if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo; var segcmd: ?macho.LoadCommandIterator.LoadCommand = null; var symtabcmd: ?macho.symtab_command = null; var it = macho.LoadCommandIterator{ .ncmds = hdr.ncmds, .buffer = mapped_mem[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], }; while (it.next()) |cmd| switch (cmd.cmd()) { .SEGMENT_64 => segcmd = cmd, .SYMTAB => symtabcmd = cmd.cast(macho.symtab_command).?, else => {}, }; if (segcmd == null or symtabcmd == null) return error.MissingDebugInfo; // Parse symbols const strtab = @as( [*]const u8, @ptrCast(&mapped_mem[symtabcmd.?.stroff]), )[0 .. symtabcmd.?.strsize - 1 :0]; const symtab = @as( [*]const macho.nlist_64, @ptrCast(@alignCast(&mapped_mem[symtabcmd.?.symoff])), )[0..symtabcmd.?.nsyms]; // TODO handle tentative (common) symbols var addr_table = std.StringHashMap(u64).init(allocator); try addr_table.ensureTotalCapacity(@as(u32, @intCast(symtab.len))); for (symtab) |sym| { if (sym.n_strx == 0) continue; if (sym.undf() or sym.tentative() or sym.abs()) continue; const sym_name = mem.sliceTo(strtab[sym.n_strx..], 0); // TODO is it possible to have a symbol collision? addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value); } var sections: Dwarf.SectionArray = Dwarf.null_section_array; if (self.eh_frame) |eh_frame| sections[@intFromEnum(Dwarf.Section.Id.eh_frame)] = .{ .data = eh_frame, .owned = false, }; for (segcmd.?.getSections()) |sect| { if (!std.mem.eql(u8, "__DWARF", sect.segName())) continue; var section_index: ?usize = null; inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |section, i| { if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i; } if (section_index == null) continue; const section_bytes = try Dwarf.chopSlice(mapped_mem, sect.offset, sect.size); sections[section_index.?] = .{ .data = section_bytes, .virtual_address = @intCast(sect.addr), .owned = false, }; } const missing_debug_info = sections[@intFromEnum(Dwarf.Section.Id.debug_info)] == null or sections[@intFromEnum(Dwarf.Section.Id.debug_abbrev)] == null or sections[@intFromEnum(Dwarf.Section.Id.debug_str)] == null or sections[@intFromEnum(Dwarf.Section.Id.debug_line)] == null; if (missing_debug_info) return error.MissingDebugInfo; var di: Dwarf = .{ .endian = .little, .sections = sections, .is_macho = true, }; try Dwarf.open(&di, allocator); const info = OFileInfo{ .di = di, .addr_table = addr_table, }; // Add the debug info to the cache const result = try self.ofiles.getOrPut(o_file_path); assert(!result.found_existing); result.value_ptr.* = info; return result.value_ptr; } pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, address: usize) !std.debug.Symbol { nosuspend { const result = try self.getOFileInfoForAddress(allocator, address); if (result.symbol == null) return .{}; // Take the symbol name from the N_FUN STAB entry, we're going to // use it if we fail to find the DWARF infos const stab_symbol = mem.sliceTo(self.strings[result.symbol.?.strx..], 0); if (result.o_file_info == null) return .{ .name = stab_symbol }; // Translate again the address, this time into an address inside the // .o file const relocated_address_o = result.o_file_info.?.addr_table.get(stab_symbol) orelse return .{ .name = "???", }; const addr_off = result.relocated_address - result.symbol.?.addr; const o_file_di = &result.o_file_info.?.di; if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { return .{ .name = o_file_di.getSymbolName(relocated_address_o) orelse "???", .compile_unit_name = compile_unit.die.getAttrString( o_file_di, std.dwarf.AT.name, o_file_di.section(.debug_str), compile_unit.*, ) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => "???", }, .source_location = o_file_di.getLineNumberInfo( allocator, compile_unit, relocated_address_o + addr_off, ) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => null, else => return err, }, }; } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { return .{ .name = stab_symbol }; }, else => return err, } } } pub fn getOFileInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !struct { relocated_address: usize, symbol: ?*const MachoSymbol = null, o_file_info: ?*OFileInfo = null, } { nosuspend { // Translate the VA into an address into this object const relocated_address = address - self.vmaddr_slide; // Find the .o file where this symbol is defined const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{ .relocated_address = relocated_address, }; // Check if its debug infos are already in the cache const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0); const o_file_info = self.ofiles.getPtr(o_file_path) orelse (self.loadOFile(allocator, o_file_path) catch |err| switch (err) { error.FileNotFound, error.MissingDebugInfo, error.InvalidDebugInfo, => return .{ .relocated_address = relocated_address, .symbol = symbol, }, else => return err, }); return .{ .relocated_address = relocated_address, .symbol = symbol, .o_file_info = o_file_info, }; } } pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf { return if ((try self.getOFileInfoForAddress(allocator, address)).o_file_info) |o_file_info| &o_file_info.di else null; } }, .uefi, .windows => struct { base_address: usize, pdb: ?Pdb = null, dwarf: ?Dwarf = null, coff_image_base: u64, /// Only used if pdb is non-null coff_section_headers: []coff.SectionHeader, pub fn deinit(self: *@This(), allocator: Allocator) void { if (self.dwarf) |*dwarf| { dwarf.deinit(allocator); } if (self.pdb) |*p| { p.deinit(); allocator.free(self.coff_section_headers); } } fn getSymbolFromPdb(self: *@This(), relocated_address: usize) !?std.debug.Symbol { var coff_section: *align(1) const coff.SectionHeader = undefined; const mod_index = for (self.pdb.?.sect_contribs) |sect_contrib| { if (sect_contrib.section > self.coff_section_headers.len) continue; // Remember that SectionContribEntry.Section is 1-based. coff_section = &self.coff_section_headers[sect_contrib.section - 1]; const vaddr_start = coff_section.virtual_address + sect_contrib.offset; const vaddr_end = vaddr_start + sect_contrib.size; if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { break sect_contrib.module_index; } } else { // we have no information to add to the address return null; }; const module = (try self.pdb.?.getModule(mod_index)) orelse return error.InvalidDebugInfo; const obj_basename = fs.path.basename(module.obj_file_name); const symbol_name = self.pdb.?.getSymbolName( module, relocated_address - coff_section.virtual_address, ) orelse "???"; const opt_line_info = try self.pdb.?.getLineNumberInfo( module, relocated_address - coff_section.virtual_address, ); return .{ .name = symbol_name, .compile_unit_name = obj_basename, .source_location = opt_line_info, }; } pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, address: usize) !std.debug.Symbol { // Translate the VA into an address into this object const relocated_address = address - self.base_address; if (self.pdb != null) { if (try self.getSymbolFromPdb(relocated_address)) |symbol| return symbol; } if (self.dwarf) |*dwarf| { const dwarf_address = relocated_address + self.coff_image_base; return dwarf.getSymbol(allocator, dwarf_address); } return .{}; } pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf { _ = allocator; _ = address; return switch (self.debug_data) { .dwarf => |*dwarf| dwarf, else => null, }; } }, .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => Dwarf.ElfModule, .wasi, .emscripten => struct { pub fn deinit(self: *@This(), allocator: Allocator) void { _ = self; _ = allocator; } pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, address: usize) !std.debug.Symbol { _ = self; _ = allocator; _ = address; return .{}; } pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf { _ = self; _ = allocator; _ = address; return null; } }, else => Dwarf, }