| ... | ... | @@ -26,10 +26,13 @@ const regValueNative = Dwarf.abi.regValueNative; |
| 26 | 26 | |
| 27 | 27 | const SelfInfo = @This(); |
| 28 | 28 | |
| 29 | | /// MLUGG TODO: what if this field had a less stupid name... |
| 30 | | address_map: std.AutoHashMapUnmanaged(usize, Module.DebugInfo), |
| 31 | | |
| 32 | | module_cache: if (native_os == .windows) std.ArrayListUnmanaged(windows.MODULEENTRY32) else void, |
| 29 | modules: std.AutoHashMapUnmanaged(usize, struct { |
| 30 | di: Module.DebugInfo, |
| 31 | loaded_debug: bool, |
| 32 | loaded_unwind: bool, |
| 33 | const init: @This() = .{ .di = .init, .loaded_debug = false, .loaded_unwind = false }; |
| 34 | }), |
| 35 | lookup_cache: Module.LookupCache, |
| 33 | 36 | |
| 34 | 37 | pub const target_supported: bool = switch (native_os) { |
| 35 | 38 | .linux, |
| ... | ... | @@ -46,19 +49,19 @@ pub const target_supported: bool = switch (native_os) { |
| 46 | 49 | }; |
| 47 | 50 | |
| 48 | 51 | pub const init: SelfInfo = .{ |
| 49 | | .address_map = .empty, |
| 50 | | .module_cache = if (native_os == .windows) .empty, |
| 52 | .modules = .empty, |
| 53 | .lookup_cache = if (Module.LookupCache != void) .init, |
| 51 | 54 | }; |
| 52 | 55 | |
| 53 | 56 | pub fn deinit(self: *SelfInfo) void { |
| 54 | 57 | // MLUGG TODO: that's amusing, this function is straight-up unused. i... wonder if it even should be used anywhere? perhaps not... so perhaps it should not even exist...???? |
| 55 | | var it = self.address_map.iterator(); |
| 58 | var it = self.modules.iterator(); |
| 56 | 59 | while (it.next()) |entry| { |
| 57 | 60 | const mdi = entry.value_ptr.*; |
| 58 | 61 | mdi.deinit(self.allocator); |
| 59 | 62 | self.allocator.destroy(mdi); |
| 60 | 63 | } |
| 61 | | self.address_map.deinit(self.allocator); |
| 64 | self.modules.deinit(self.allocator); |
| 62 | 65 | if (native_os == .windows) { |
| 63 | 66 | for (self.modules.items) |module| { |
| 64 | 67 | self.allocator.free(module.name); |
| ... | ... | @@ -68,94 +71,26 @@ pub fn deinit(self: *SelfInfo) void { |
| 68 | 71 | } |
| 69 | 72 | } |
| 70 | 73 | |
| 71 | | fn lookupModuleForAddress(self: *SelfInfo, gpa: Allocator, address: usize) !Module { |
| 72 | | if (builtin.target.os.tag.isDarwin()) { |
| 73 | | return self.lookupModuleDyld(address); |
| 74 | | } else if (native_os == .windows) { |
| 75 | | return self.lookupModuleWin32(gpa, address); |
| 76 | | } else if (native_os == .haiku) { |
| 77 | | @panic("TODO implement lookup module for Haiku"); |
| 78 | | } else if (builtin.target.cpu.arch.isWasm()) { |
| 79 | | @panic("TODO implement lookup module for Wasm"); |
| 80 | | } else { |
| 81 | | return self.lookupModuleDl(address); |
| 82 | | } |
| 83 | | } |
| 84 | | |
| 85 | | fn loadModuleDebugInfo(gpa: Allocator, module: *const Module, di: *Module.DebugInfo) !void { |
| 86 | | // MLUGG TODO: this should totally just go into the `Module` impl or something, right? lol |
| 87 | | if (builtin.target.os.tag.isDarwin()) { |
| 88 | | try loadMachODebugInfo(gpa, module, di); |
| 89 | | } else if (native_os == .windows) { |
| 90 | | // MLUGG TODO: deal with 'already loaded' properly |
| 91 | | try readCoffDebugInfo(gpa, module, di); |
| 92 | | } else if (native_os == .haiku) { |
| 93 | | unreachable; |
| 94 | | } else if (builtin.target.cpu.arch.isWasm()) { |
| 95 | | unreachable; |
| 96 | | } else { |
| 97 | | if (di.mapped_memory != null) return; // already loaded |
| 98 | | const filename: ?[]const u8 = if (module.name.len > 0) module.name else null; |
| 99 | | const mapped_mem = mapFileOrSelfExe(filename) catch |err| switch (err) { |
| 100 | | error.FileNotFound => return error.MissingDebugInfo, |
| 101 | | error.FileTooBig => return error.InvalidDebugInfo, |
| 102 | | else => |e| return e, |
| 103 | | }; |
| 104 | | errdefer posix.munmap(mapped_mem); |
| 105 | | try di.load(gpa, mapped_mem, module.build_id, null, null, null, filename); |
| 106 | | assert(di.mapped_memory != null); |
| 107 | | } |
| 108 | | } |
| 109 | | |
| 110 | | fn loadModuleUnwindInfo(gpa: Allocator, module: *const Module, di: *Module.DebugInfo) !void { |
| 111 | | if (builtin.target.os.tag.isDarwin()) { |
| 112 | | // MLUGG TODO HACKHACK |
| 113 | | try loadMachODebugInfo(gpa, module, di); |
| 114 | | } else if (native_os == .windows) { |
| 115 | | comptime unreachable; // not supported |
| 116 | | } else if (native_os == .haiku) { |
| 117 | | comptime unreachable; // not supported |
| 118 | | } else if (builtin.target.cpu.arch.isWasm()) { |
| 119 | | comptime unreachable; // not supported |
| 120 | | } else { |
| 121 | | eh_frame: { |
| 122 | | if (di.unwind.eh_frame != null) break :eh_frame; // already loaded |
| 123 | | const eh_frame_hdr_bytes = module.gnu_eh_frame orelse break :eh_frame; |
| 124 | | const eh_frame_hdr: Dwarf.Unwind.EhFrameHeader = try .parse( |
| 125 | | @intFromPtr(eh_frame_hdr_bytes.ptr) - module.load_offset, |
| 126 | | eh_frame_hdr_bytes, |
| 127 | | @sizeOf(usize), |
| 128 | | native_endian, |
| 129 | | ); |
| 130 | | const eh_frame_addr = module.load_offset + @as(usize, @intCast(eh_frame_hdr.eh_frame_vaddr)); |
| 131 | | try di.unwind.scanEhFrame( |
| 132 | | gpa, |
| 133 | | eh_frame_hdr, |
| 134 | | @ptrFromInt(eh_frame_addr), |
| 135 | | null, |
| 136 | | @sizeOf(usize), |
| 137 | | native_endian, |
| 138 | | ); |
| 139 | | } |
| 140 | | } |
| 141 | | } |
| 142 | | |
| 143 | 74 | pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !usize { |
| 144 | 75 | comptime assert(target_supported); |
| 145 | | const module = try self.lookupModuleForAddress(gpa, context.pc); |
| 146 | | const gop = try self.address_map.getOrPut(gpa, module.load_offset); |
| 76 | const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); // MLUGG TODO: don't take gpa |
| 77 | const gop = try self.modules.getOrPut(gpa, module.load_offset); |
| 147 | 78 | if (!gop.found_existing) gop.value_ptr.* = .init; |
| 148 | | try loadModuleUnwindInfo(gpa, &module, gop.value_ptr); |
| 79 | if (!gop.value_ptr.loaded_unwind) { |
| 80 | try module.loadUnwindInfo(gpa, &gop.value_ptr.di); |
| 81 | gop.value_ptr.loaded_unwind = true; |
| 82 | } |
| 83 | // MLUGG TODO: the stuff below is impl! |
| 149 | 84 | if (native_os.isDarwin()) { |
| 150 | 85 | // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding |
| 151 | 86 | // via DWARF before attempting to use the compact unwind info will produce incorrect results. |
| 152 | | if (gop.value_ptr.unwind_info) |unwind_info| { |
| 87 | if (gop.value_ptr.di.unwind_info) |unwind_info| { |
| 153 | 88 | if (unwindFrameMachO( |
| 154 | 89 | module.text_base, |
| 155 | 90 | module.load_offset, |
| 156 | 91 | context, |
| 157 | 92 | unwind_info, |
| 158 | | gop.value_ptr.eh_frame, |
| 93 | gop.value_ptr.di.eh_frame, |
| 159 | 94 | )) |return_address| { |
| 160 | 95 | return return_address; |
| 161 | 96 | } else |err| { |
| ... | ... | @@ -164,7 +99,7 @@ pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !us |
| 164 | 99 | } |
| 165 | 100 | return error.MissingUnwindInfo; |
| 166 | 101 | } |
| 167 | | if (try gop.value_ptr.getDwarfUnwindForAddress(gpa, context.pc)) |unwind| { |
| 102 | if (try gop.value_ptr.di.getDwarfUnwindForAddress(gpa, context.pc)) |unwind| { |
| 168 | 103 | return unwindFrameDwarf(unwind, module.load_offset, context, null); |
| 169 | 104 | } |
| 170 | 105 | return error.MissingDebugInfo; |
| ... | ... | @@ -172,11 +107,15 @@ pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !us |
| 172 | 107 | |
| 173 | 108 | pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.debug.Symbol { |
| 174 | 109 | comptime assert(target_supported); |
| 175 | | const module = try self.lookupModuleForAddress(gpa, address); |
| 176 | | const gop = try self.address_map.getOrPut(gpa, module.key()); |
| 110 | const module: Module = try .lookup(&self.lookup_cache, gpa, address); // MLUGG TODO: don't take gpa |
| 111 | const gop = try self.modules.getOrPut(gpa, module.key()); |
| 177 | 112 | if (!gop.found_existing) gop.value_ptr.* = .init; |
| 178 | | try loadModuleDebugInfo(gpa, &module, gop.value_ptr); |
| 179 | | return module.getSymbolAtAddress(gpa, gop.value_ptr, address); |
| 113 | if (!gop.value_ptr.loaded_debug) { |
| 114 | // MLUGG TODO: this overloads the name 'debug info' with including vs excluding unwind info |
| 115 | // figure out a better name for one or the other (i think the inner one is maybe 'symbol info' or something idk) |
| 116 | try module.loadDebugInfo(gpa, &gop.value_ptr.di); |
| 117 | } |
| 118 | return module.getSymbolAtAddress(gpa, &gop.value_ptr.di, address); |
| 180 | 119 | } |
| 181 | 120 | |
| 182 | 121 | /// Returns the module name for a given address. |
| ... | ... | @@ -184,278 +123,12 @@ pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std. |
| 184 | 123 | /// a path that doesn't rely on any side-effects of a prior successful module lookup. |
| 185 | 124 | pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) error{ Unexpected, OutOfMemory, MissingDebugInfo }![]const u8 { |
| 186 | 125 | comptime assert(target_supported); |
| 187 | | const module = try self.lookupModuleForAddress(gpa, address); |
| 126 | const module: Module = try .lookup(&self.lookup_cache, gpa, address); // MLUGG TODO: don't take gpa |
| 188 | 127 | return module.name; |
| 189 | 128 | } |
| 190 | 129 | |
| 191 | | fn lookupModuleDl(self: *SelfInfo, address: usize) !Module { |
| 192 | | _ = self; // MLUGG |
| 193 | | const DlIterContext = struct { |
| 194 | | /// input |
| 195 | | address: usize, |
| 196 | | /// output |
| 197 | | module: Module, |
| 198 | | |
| 199 | | fn callback(info: *posix.dl_phdr_info, size: usize, context: *@This()) !void { |
| 200 | | _ = size; |
| 201 | | // The base address is too high |
| 202 | | if (context.address < info.addr) |
| 203 | | return; |
| 204 | | |
| 205 | | const phdrs = info.phdr[0..info.phnum]; |
| 206 | | for (phdrs) |*phdr| { |
| 207 | | if (phdr.p_type != elf.PT_LOAD) continue; |
| 208 | | |
| 209 | | // Overflowing addition is used to handle the case of VSDOs having a p_vaddr = 0xffffffffff700000 |
| 210 | | const seg_start = info.addr +% phdr.p_vaddr; |
| 211 | | const seg_end = seg_start + phdr.p_memsz; |
| 212 | | if (context.address >= seg_start and context.address < seg_end) { |
| 213 | | context.module = .{ |
| 214 | | .load_offset = info.addr, |
| 215 | | // Android libc uses NULL instead of "" to mark the main program |
| 216 | | .name = mem.sliceTo(info.name, 0) orelse "", |
| 217 | | .build_id = null, |
| 218 | | .gnu_eh_frame = null, |
| 219 | | }; |
| 220 | | break; |
| 221 | | } |
| 222 | | } else return; |
| 223 | | |
| 224 | | for (info.phdr[0..info.phnum]) |phdr| { |
| 225 | | switch (phdr.p_type) { |
| 226 | | elf.PT_NOTE => { |
| 227 | | // Look for .note.gnu.build-id |
| 228 | | const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr); |
| 229 | | var r: std.Io.Reader = .fixed(segment_ptr[0..phdr.p_memsz]); |
| 230 | | const name_size = r.takeInt(u32, native_endian) catch continue; |
| 231 | | const desc_size = r.takeInt(u32, native_endian) catch continue; |
| 232 | | const note_type = r.takeInt(u32, native_endian) catch continue; |
| 233 | | const name = r.take(name_size) catch continue; |
| 234 | | if (note_type != elf.NT_GNU_BUILD_ID) continue; |
| 235 | | if (!mem.eql(u8, name, "GNU\x00")) continue; |
| 236 | | const desc = r.take(desc_size) catch continue; |
| 237 | | context.module.build_id = desc; |
| 238 | | }, |
| 239 | | elf.PT_GNU_EH_FRAME => { |
| 240 | | const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr); |
| 241 | | context.module.gnu_eh_frame = segment_ptr[0..phdr.p_memsz]; |
| 242 | | }, |
| 243 | | else => {}, |
| 244 | | } |
| 245 | | } |
| 246 | | |
| 247 | | // Stop the iteration |
| 248 | | return error.Found; |
| 249 | | } |
| 250 | | }; |
| 251 | | var ctx: DlIterContext = .{ |
| 252 | | .address = address, |
| 253 | | .module = undefined, |
| 254 | | }; |
| 255 | | posix.dl_iterate_phdr(&ctx, error{Found}, DlIterContext.callback) catch |err| switch (err) { |
| 256 | | error.Found => return ctx.module, |
| 257 | | }; |
| 258 | | return error.MissingDebugInfo; |
| 259 | | } |
| 260 | | |
| 261 | | fn lookupModuleDyld(self: *SelfInfo, address: usize) !Module { |
| 262 | | _ = self; // MLUGG |
| 263 | | const image_count = std.c._dyld_image_count(); |
| 264 | | for (0..image_count) |image_idx| { |
| 265 | | const header = std.c._dyld_get_image_header(@intCast(image_idx)) orelse continue; |
| 266 | | const text_base = @intFromPtr(header); |
| 267 | | if (address < text_base) continue; |
| 268 | | const load_offset = std.c._dyld_get_image_vmaddr_slide(@intCast(image_idx)); |
| 269 | | |
| 270 | | // Find the __TEXT segment |
| 271 | | var it: macho.LoadCommandIterator = .{ |
| 272 | | .ncmds = header.ncmds, |
| 273 | | .buffer = @as([*]u8, @ptrCast(header))[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds], |
| 274 | | }; |
| 275 | | const text_segment_cmd, const text_sections = while (it.next()) |load_cmd| { |
| 276 | | if (load_cmd.cmd() != .SEGMENT_64) continue; |
| 277 | | const segment_cmd = load_cmd.cast(macho.segment_command_64).?; |
| 278 | | if (!mem.eql(u8, segment_cmd.segName(), "__TEXT")) continue; |
| 279 | | break .{ segment_cmd, load_cmd.getSections() }; |
| 280 | | } else continue; |
| 281 | | |
| 282 | | const seg_start = load_offset + text_segment_cmd.vmaddr; |
| 283 | | assert(seg_start == text_base); |
| 284 | | const seg_end = seg_start + text_segment_cmd.vmsize; |
| 285 | | if (address < seg_start or address >= seg_end) continue; |
| 286 | | |
| 287 | | // We've found the matching __TEXT segment. This is the image we need, but we must look |
| 288 | | // for unwind info in it before returning. |
| 289 | | |
| 290 | | var result: Module = .{ |
| 291 | | .text_base = text_base, |
| 292 | | .load_offset = load_offset, |
| 293 | | .name = mem.span(std.c._dyld_get_image_name(@intCast(image_idx))), |
| 294 | | .unwind_info = null, |
| 295 | | .eh_frame = null, |
| 296 | | }; |
| 297 | | for (text_sections) |sect| { |
| 298 | | if (mem.eql(u8, sect.sectName(), "__unwind_info")) { |
| 299 | | const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(load_offset + sect.addr))); |
| 300 | | result.unwind_info = sect_ptr[0..@intCast(sect.size)]; |
| 301 | | } else if (mem.eql(u8, sect.sectName(), "__eh_frame")) { |
| 302 | | const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(load_offset + sect.addr))); |
| 303 | | result.eh_frame = sect_ptr[0..@intCast(sect.size)]; |
| 304 | | } |
| 305 | | } |
| 306 | | return result; |
| 307 | | } |
| 308 | | return error.MissingDebugInfo; |
| 309 | | } |
| 310 | | |
| 311 | | fn lookupModuleWin32(self: *SelfInfo, gpa: Allocator, address: usize) !Module { |
| 312 | | if (self.lookupModuleWin32Cache(address)) |m| return m; |
| 313 | | |
| 314 | | { |
| 315 | | // Check a new module hasn't been loaded |
| 316 | | self.module_cache.clearRetainingCapacity(); |
| 317 | | |
| 318 | | const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0); |
| 319 | | if (handle == windows.INVALID_HANDLE_VALUE) { |
| 320 | | return windows.unexpectedError(windows.GetLastError()); |
| 321 | | } |
| 322 | | defer windows.CloseHandle(handle); |
| 323 | | |
| 324 | | var entry: windows.MODULEENTRY32 = undefined; |
| 325 | | entry.dwSize = @sizeOf(windows.MODULEENTRY32); |
| 326 | | if (windows.kernel32.Module32First(handle, &entry) != 0) { |
| 327 | | try self.module_cache.append(gpa, entry); |
| 328 | | while (windows.kernel32.Module32Next(handle, &entry) != 0) { |
| 329 | | try self.module_cache.append(gpa, entry); |
| 330 | | } |
| 331 | | } |
| 332 | | } |
| 333 | | |
| 334 | | if (self.lookupModuleWin32Cache(address)) |m| return m; |
| 335 | | return error.MissingDebugInfo; |
| 336 | | } |
| 337 | | fn lookupModuleWin32Cache(self: *SelfInfo, address: usize) ?Module { |
| 338 | | for (self.module_cache.items) |*entry| { |
| 339 | | const base_address = @intFromPtr(entry.modBaseAddr); |
| 340 | | if (address >= base_address and address < base_address + entry.modBaseSize) { |
| 341 | | return .{ |
| 342 | | .base_address = base_address, |
| 343 | | .size = entry.modBaseSize, |
| 344 | | .name = std.mem.sliceTo(&entry.szModule, 0), |
| 345 | | .handle = entry.hModule, |
| 346 | | }; |
| 347 | | } |
| 348 | | } |
| 349 | | return null; |
| 350 | | } |
| 351 | | |
| 352 | | fn readCoffDebugInfo(gpa: Allocator, module: *const Module, di: *Module.DebugInfo) !void { |
| 353 | | const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address); |
| 354 | | const mapped = mapped_ptr[0..module.size]; |
| 355 | | var coff_obj = coff.Coff.init(mapped, true) catch return error.InvalidDebugInfo; |
| 356 | | // The string table is not mapped into memory by the loader, so if a section name is in the |
| 357 | | // string table then we have to map the full image file from disk. This can happen when |
| 358 | | // a binary is produced with -gdwarf, since the section names are longer than 8 bytes. |
| 359 | | if (coff_obj.strtabRequired()) { |
| 360 | | var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined; |
| 361 | | name_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present |
| 362 | | const process_handle = windows.GetCurrentProcess(); |
| 363 | | const len = windows.kernel32.GetModuleFileNameExW( |
| 364 | | process_handle, |
| 365 | | module.handle, |
| 366 | | name_buffer[4..], |
| 367 | | windows.PATH_MAX_WIDE, |
| 368 | | ); |
| 369 | | if (len == 0) return error.MissingDebugInfo; |
| 370 | | const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) { |
| 371 | | error.FileNotFound => return error.MissingDebugInfo, |
| 372 | | else => |e| return e, |
| 373 | | }; |
| 374 | | errdefer coff_file.close(); |
| 375 | | var section_handle: windows.HANDLE = undefined; |
| 376 | | const create_section_rc = windows.ntdll.NtCreateSection( |
| 377 | | &section_handle, |
| 378 | | windows.STANDARD_RIGHTS_REQUIRED | windows.SECTION_QUERY | windows.SECTION_MAP_READ, |
| 379 | | null, |
| 380 | | null, |
| 381 | | windows.PAGE_READONLY, |
| 382 | | // The documentation states that if no AllocationAttribute is specified, then SEC_COMMIT is the default. |
| 383 | | // In practice, this isn't the case and specifying 0 will result in INVALID_PARAMETER_6. |
| 384 | | windows.SEC_COMMIT, |
| 385 | | coff_file.handle, |
| 386 | | ); |
| 387 | | if (create_section_rc != .SUCCESS) return error.MissingDebugInfo; |
| 388 | | errdefer windows.CloseHandle(section_handle); |
| 389 | | var coff_len: usize = 0; |
| 390 | | var section_view_ptr: [*]const u8 = undefined; |
| 391 | | const map_section_rc = windows.ntdll.NtMapViewOfSection( |
| 392 | | section_handle, |
| 393 | | process_handle, |
| 394 | | @ptrCast(&section_view_ptr), |
| 395 | | null, |
| 396 | | 0, |
| 397 | | null, |
| 398 | | &coff_len, |
| 399 | | .ViewUnmap, |
| 400 | | 0, |
| 401 | | windows.PAGE_READONLY, |
| 402 | | ); |
| 403 | | if (map_section_rc != .SUCCESS) return error.MissingDebugInfo; |
| 404 | | errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(section_view_ptr)) == .SUCCESS); |
| 405 | | const section_view = section_view_ptr[0..coff_len]; |
| 406 | | coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo; |
| 407 | | di.mapped_file = .{ |
| 408 | | .file = coff_file, |
| 409 | | .section_handle = section_handle, |
| 410 | | .section_view = section_view, |
| 411 | | }; |
| 412 | | } |
| 413 | | di.coff_image_base = coff_obj.getImageBase(); |
| 414 | | |
| 415 | | if (coff_obj.getSectionByName(".debug_info")) |_| { |
| 416 | | di.dwarf = .{}; |
| 417 | | |
| 418 | | inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |section, i| { |
| 419 | | di.dwarf.?.sections[i] = if (coff_obj.getSectionByName("." ++ section.name)) |section_header| blk: { |
| 420 | | break :blk .{ |
| 421 | | .data = try coff_obj.getSectionDataAlloc(section_header, gpa), |
| 422 | | .virtual_address = section_header.virtual_address, |
| 423 | | .owned = true, |
| 424 | | }; |
| 425 | | } else null; |
| 426 | | } |
| 427 | | |
| 428 | | try di.dwarf.?.open(gpa, native_endian); |
| 429 | | } |
| 430 | | |
| 431 | | if (try coff_obj.getPdbPath()) |raw_path| pdb: { |
| 432 | | const path = blk: { |
| 433 | | if (fs.path.isAbsolute(raw_path)) { |
| 434 | | break :blk raw_path; |
| 435 | | } else { |
| 436 | | const self_dir = try fs.selfExeDirPathAlloc(gpa); |
| 437 | | defer gpa.free(self_dir); |
| 438 | | break :blk try fs.path.join(gpa, &.{ self_dir, raw_path }); |
| 439 | | } |
| 440 | | }; |
| 441 | | defer if (path.ptr != raw_path.ptr) gpa.free(path); |
| 442 | | |
| 443 | | di.pdb = Pdb.init(gpa, path) catch |err| switch (err) { |
| 444 | | error.FileNotFound, error.IsDir => break :pdb, |
| 445 | | else => return err, |
| 446 | | }; |
| 447 | | try di.pdb.?.parseInfoStream(); |
| 448 | | try di.pdb.?.parseDbiStream(); |
| 449 | | |
| 450 | | if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age) |
| 451 | | return error.InvalidDebugInfo; |
| 452 | | |
| 453 | | di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(gpa); |
| 454 | | } |
| 455 | | } |
| 456 | | |
| 457 | 130 | const Module = switch (native_os) { |
| 458 | | else => "MLUGG TODO", // Dwarf, // TODO MLUGG: it's this on master but that's definitely broken atm... |
| 131 | else => {}, // Dwarf, // TODO MLUGG: it's this on master but that's definitely broken atm... |
| 459 | 132 | .macos, .ios, .watchos, .tvos, .visionos => struct { |
| 460 | 133 | /// The runtime address where __TEXT is loaded. |
| 461 | 134 | text_base: usize, |
| ... | ... | @@ -466,6 +139,63 @@ const Module = switch (native_os) { |
| 466 | 139 | fn key(m: *const Module) usize { |
| 467 | 140 | return m.text_base; |
| 468 | 141 | } |
| 142 | fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module { |
| 143 | _ = cache; |
| 144 | _ = gpa; |
| 145 | const image_count = std.c._dyld_image_count(); |
| 146 | for (0..image_count) |image_idx| { |
| 147 | const header = std.c._dyld_get_image_header(@intCast(image_idx)) orelse continue; |
| 148 | const text_base = @intFromPtr(header); |
| 149 | if (address < text_base) continue; |
| 150 | const load_offset = std.c._dyld_get_image_vmaddr_slide(@intCast(image_idx)); |
| 151 | |
| 152 | // Find the __TEXT segment |
| 153 | var it: macho.LoadCommandIterator = .{ |
| 154 | .ncmds = header.ncmds, |
| 155 | .buffer = @as([*]u8, @ptrCast(header))[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds], |
| 156 | }; |
| 157 | const text_segment_cmd, const text_sections = while (it.next()) |load_cmd| { |
| 158 | if (load_cmd.cmd() != .SEGMENT_64) continue; |
| 159 | const segment_cmd = load_cmd.cast(macho.segment_command_64).?; |
| 160 | if (!mem.eql(u8, segment_cmd.segName(), "__TEXT")) continue; |
| 161 | break .{ segment_cmd, load_cmd.getSections() }; |
| 162 | } else continue; |
| 163 | |
| 164 | const seg_start = load_offset + text_segment_cmd.vmaddr; |
| 165 | assert(seg_start == text_base); |
| 166 | const seg_end = seg_start + text_segment_cmd.vmsize; |
| 167 | if (address < seg_start or address >= seg_end) continue; |
| 168 | |
| 169 | // We've found the matching __TEXT segment. This is the image we need, but we must look |
| 170 | // for unwind info in it before returning. |
| 171 | |
| 172 | var result: Module = .{ |
| 173 | .text_base = text_base, |
| 174 | .load_offset = load_offset, |
| 175 | .name = mem.span(std.c._dyld_get_image_name(@intCast(image_idx))), |
| 176 | .unwind_info = null, |
| 177 | .eh_frame = null, |
| 178 | }; |
| 179 | for (text_sections) |sect| { |
| 180 | if (mem.eql(u8, sect.sectName(), "__unwind_info")) { |
| 181 | const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(load_offset + sect.addr))); |
| 182 | result.unwind_info = sect_ptr[0..@intCast(sect.size)]; |
| 183 | } else if (mem.eql(u8, sect.sectName(), "__eh_frame")) { |
| 184 | const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(load_offset + sect.addr))); |
| 185 | result.eh_frame = sect_ptr[0..@intCast(sect.size)]; |
| 186 | } |
| 187 | } |
| 188 | return result; |
| 189 | } |
| 190 | return error.MissingDebugInfo; |
| 191 | } |
| 192 | fn loadDebugInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void { |
| 193 | return loadMachODebugInfo(gpa, module, di); |
| 194 | } |
| 195 | fn loadUnwindInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void { |
| 196 | // MLUGG TODO HACKHACK |
| 197 | try loadMachODebugInfo(gpa, module, di); |
| 198 | } |
| 469 | 199 | fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { |
| 470 | 200 | const vaddr = address - module.load_offset; |
| 471 | 201 | const symbol = MachoSymbol.find(di.symbols, vaddr) orelse return .{}; // MLUGG TODO null? |
| ... | ... | @@ -524,8 +254,8 @@ const Module = switch (native_os) { |
| 524 | 254 | }, |
| 525 | 255 | }; |
| 526 | 256 | } |
| 257 | const LookupCache = void; |
| 527 | 258 | const DebugInfo = struct { |
| 528 | | // MLUGG TODO: these are duplicated state. i actually reckon they should be removed from Module, and loadMachODebugInfo should be the one discovering them! |
| 529 | 259 | mapped_memory: []align(std.heap.page_size_min) const u8, |
| 530 | 260 | symbols: []const MachoSymbol, |
| 531 | 261 | strings: [:0]const u8, |
| ... | ... | @@ -533,6 +263,7 @@ const Module = switch (native_os) { |
| 533 | 263 | ofiles: std.StringArrayHashMapUnmanaged(OFile), |
| 534 | 264 | |
| 535 | 265 | // Backed by the in-memory sections mapped by the loader |
| 266 | // MLUGG TODO: these are duplicated state. i actually reckon they should be removed from Module, and loadMachODebugInfo should be the one discovering them! |
| 536 | 267 | unwind_info: ?[]const u8, |
| 537 | 268 | eh_frame: ?[]const u8, |
| 538 | 269 | |
| ... | ... | @@ -642,28 +373,137 @@ const Module = switch (native_os) { |
| 642 | 373 | }; |
| 643 | 374 | }, |
| 644 | 375 | .wasi, .emscripten => struct { |
| 376 | const LookupCache = void; |
| 645 | 377 | const DebugInfo = struct { |
| 646 | 378 | const init: DebugInfo = .{}; |
| 647 | | fn getSymbolAtAddress(di: *DebugInfo, gpa: Allocator, base_address: usize, address: usize) !std.debug.Symbol { |
| 648 | | _ = di; |
| 649 | | _ = gpa; |
| 650 | | _ = base_address; |
| 651 | | _ = address; |
| 652 | | unreachable; |
| 653 | | } |
| 654 | 379 | }; |
| 380 | fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module { |
| 381 | _ = cache; |
| 382 | _ = gpa; |
| 383 | _ = address; |
| 384 | @panic("TODO implement lookup module for Wasm"); |
| 385 | } |
| 386 | fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { |
| 387 | _ = module; |
| 388 | _ = gpa; |
| 389 | _ = di; |
| 390 | _ = address; |
| 391 | unreachable; |
| 392 | } |
| 393 | fn loadDebugInfo(module: *const Module, gpa: Allocator, di: *DebugInfo) !void { |
| 394 | _ = module; |
| 395 | _ = gpa; |
| 396 | _ = di; |
| 397 | unreachable; |
| 398 | } |
| 399 | fn loadUnwindInfo(module: *const Module, gpa: Allocator, di: *DebugInfo) !void { |
| 400 | _ = module; |
| 401 | _ = gpa; |
| 402 | _ = di; |
| 403 | unreachable; |
| 404 | } |
| 655 | 405 | }, |
| 656 | 406 | .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => struct { |
| 657 | 407 | load_offset: usize, |
| 658 | 408 | name: []const u8, |
| 659 | 409 | build_id: ?[]const u8, |
| 660 | 410 | gnu_eh_frame: ?[]const u8, |
| 411 | const LookupCache = void; |
| 412 | const DebugInfo = Dwarf.ElfModule; |
| 661 | 413 | fn key(m: Module) usize { |
| 662 | 414 | return m.load_offset; // MLUGG TODO: is this technically valid? idk |
| 663 | 415 | } |
| 664 | | const DebugInfo = Dwarf.ElfModule; |
| 665 | | fn getSymbolAtAddress(mod: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { |
| 666 | | return di.getSymbolAtAddress(gpa, native_endian, mod.load_offset, address); |
| 416 | fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module { |
| 417 | _ = cache; |
| 418 | _ = gpa; |
| 419 | if (native_os == .haiku) @panic("TODO implement lookup module for Haiku"); |
| 420 | const DlIterContext = struct { |
| 421 | /// input |
| 422 | address: usize, |
| 423 | /// output |
| 424 | module: Module, |
| 425 | |
| 426 | fn callback(info: *posix.dl_phdr_info, size: usize, context: *@This()) !void { |
| 427 | _ = size; |
| 428 | // The base address is too high |
| 429 | if (context.address < info.addr) |
| 430 | return; |
| 431 | |
| 432 | const phdrs = info.phdr[0..info.phnum]; |
| 433 | for (phdrs) |*phdr| { |
| 434 | if (phdr.p_type != elf.PT_LOAD) continue; |
| 435 | |
| 436 | // Overflowing addition is used to handle the case of VSDOs having a p_vaddr = 0xffffffffff700000 |
| 437 | const seg_start = info.addr +% phdr.p_vaddr; |
| 438 | const seg_end = seg_start + phdr.p_memsz; |
| 439 | if (context.address >= seg_start and context.address < seg_end) { |
| 440 | context.module = .{ |
| 441 | .load_offset = info.addr, |
| 442 | // Android libc uses NULL instead of "" to mark the main program |
| 443 | .name = mem.sliceTo(info.name, 0) orelse "", |
| 444 | .build_id = null, |
| 445 | .gnu_eh_frame = null, |
| 446 | }; |
| 447 | break; |
| 448 | } |
| 449 | } else return; |
| 450 | |
| 451 | for (info.phdr[0..info.phnum]) |phdr| { |
| 452 | switch (phdr.p_type) { |
| 453 | elf.PT_NOTE => { |
| 454 | // Look for .note.gnu.build-id |
| 455 | const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr); |
| 456 | var r: std.Io.Reader = .fixed(segment_ptr[0..phdr.p_memsz]); |
| 457 | const name_size = r.takeInt(u32, native_endian) catch continue; |
| 458 | const desc_size = r.takeInt(u32, native_endian) catch continue; |
| 459 | const note_type = r.takeInt(u32, native_endian) catch continue; |
| 460 | const name = r.take(name_size) catch continue; |
| 461 | if (note_type != elf.NT_GNU_BUILD_ID) continue; |
| 462 | if (!mem.eql(u8, name, "GNU\x00")) continue; |
| 463 | const desc = r.take(desc_size) catch continue; |
| 464 | context.module.build_id = desc; |
| 465 | }, |
| 466 | elf.PT_GNU_EH_FRAME => { |
| 467 | const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr); |
| 468 | context.module.gnu_eh_frame = segment_ptr[0..phdr.p_memsz]; |
| 469 | }, |
| 470 | else => {}, |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | // Stop the iteration |
| 475 | return error.Found; |
| 476 | } |
| 477 | }; |
| 478 | var ctx: DlIterContext = .{ |
| 479 | .address = address, |
| 480 | .module = undefined, |
| 481 | }; |
| 482 | posix.dl_iterate_phdr(&ctx, error{Found}, DlIterContext.callback) catch |err| switch (err) { |
| 483 | error.Found => return ctx.module, |
| 484 | }; |
| 485 | return error.MissingDebugInfo; |
| 486 | } |
| 487 | fn loadDebugInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void { |
| 488 | const filename: ?[]const u8 = if (module.name.len > 0) module.name else null; |
| 489 | const mapped_mem = mapFileOrSelfExe(filename) catch |err| switch (err) { |
| 490 | error.FileNotFound => return error.MissingDebugInfo, |
| 491 | error.FileTooBig => return error.InvalidDebugInfo, |
| 492 | else => |e| return e, |
| 493 | }; |
| 494 | errdefer posix.munmap(mapped_mem); |
| 495 | try di.load(gpa, mapped_mem, module.build_id, null, null, null, filename); |
| 496 | assert(di.mapped_memory != null); |
| 497 | } |
| 498 | fn loadUnwindInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void { |
| 499 | const section_bytes = module.gnu_eh_frame orelse return error.MissingUnwindInfo; // MLUGG TODO: load from file |
| 500 | const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset; |
| 501 | const header: Dwarf.Unwind.EhFrameHeader = try .parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian); |
| 502 | try di.unwind.loadFromEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr)); |
| 503 | try di.unwind.prepareLookup(gpa, @sizeOf(usize), native_endian); |
| 504 | } |
| 505 | fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { |
| 506 | return di.getSymbolAtAddress(gpa, native_endian, module.load_offset, address); |
| 667 | 507 | } |
| 668 | 508 | }, |
| 669 | 509 | .uefi, .windows => struct { |
| ... | ... | @@ -674,6 +514,152 @@ const Module = switch (native_os) { |
| 674 | 514 | fn key(m: Module) usize { |
| 675 | 515 | return m.base_address; |
| 676 | 516 | } |
| 517 | fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !Module { |
| 518 | if (lookupInCache(cache, address)) |m| return m; |
| 519 | { |
| 520 | // Check a new module hasn't been loaded |
| 521 | cache.modules.clearRetainingCapacity(); |
| 522 | |
| 523 | const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0); |
| 524 | if (handle == windows.INVALID_HANDLE_VALUE) { |
| 525 | return windows.unexpectedError(windows.GetLastError()); |
| 526 | } |
| 527 | defer windows.CloseHandle(handle); |
| 528 | |
| 529 | var entry: windows.MODULEENTRY32 = undefined; |
| 530 | entry.dwSize = @sizeOf(windows.MODULEENTRY32); |
| 531 | if (windows.kernel32.Module32First(handle, &entry) != 0) { |
| 532 | try cache.modules.append(gpa, entry); |
| 533 | while (windows.kernel32.Module32Next(handle, &entry) != 0) { |
| 534 | try cache.modules.append(gpa, entry); |
| 535 | } |
| 536 | } |
| 537 | } |
| 538 | if (lookupInCache(cache, address)) |m| return m; |
| 539 | return error.MissingDebugInfo; |
| 540 | } |
| 541 | fn lookupInCache(cache: *const LookupCache, address: usize) ?Module { |
| 542 | for (cache.modules.items) |*entry| { |
| 543 | const base_address = @intFromPtr(entry.modBaseAddr); |
| 544 | if (address >= base_address and address < base_address + entry.modBaseSize) { |
| 545 | return .{ |
| 546 | .base_address = base_address, |
| 547 | .size = entry.modBaseSize, |
| 548 | .name = std.mem.sliceTo(&entry.szModule, 0), |
| 549 | .handle = entry.hModule, |
| 550 | }; |
| 551 | } |
| 552 | } |
| 553 | return null; |
| 554 | } |
| 555 | fn loadDebugInfo(module: *const Module, gpa: Allocator, di: *DebugInfo) !void { |
| 556 | const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address); |
| 557 | const mapped = mapped_ptr[0..module.size]; |
| 558 | var coff_obj = coff.Coff.init(mapped, true) catch return error.InvalidDebugInfo; |
| 559 | // The string table is not mapped into memory by the loader, so if a section name is in the |
| 560 | // string table then we have to map the full image file from disk. This can happen when |
| 561 | // a binary is produced with -gdwarf, since the section names are longer than 8 bytes. |
| 562 | if (coff_obj.strtabRequired()) { |
| 563 | var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined; |
| 564 | name_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present |
| 565 | const process_handle = windows.GetCurrentProcess(); |
| 566 | const len = windows.kernel32.GetModuleFileNameExW( |
| 567 | process_handle, |
| 568 | module.handle, |
| 569 | name_buffer[4..], |
| 570 | windows.PATH_MAX_WIDE, |
| 571 | ); |
| 572 | if (len == 0) return error.MissingDebugInfo; |
| 573 | const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) { |
| 574 | error.FileNotFound => return error.MissingDebugInfo, |
| 575 | else => |e| return e, |
| 576 | }; |
| 577 | errdefer coff_file.close(); |
| 578 | var section_handle: windows.HANDLE = undefined; |
| 579 | const create_section_rc = windows.ntdll.NtCreateSection( |
| 580 | &section_handle, |
| 581 | windows.STANDARD_RIGHTS_REQUIRED | windows.SECTION_QUERY | windows.SECTION_MAP_READ, |
| 582 | null, |
| 583 | null, |
| 584 | windows.PAGE_READONLY, |
| 585 | // The documentation states that if no AllocationAttribute is specified, then SEC_COMMIT is the default. |
| 586 | // In practice, this isn't the case and specifying 0 will result in INVALID_PARAMETER_6. |
| 587 | windows.SEC_COMMIT, |
| 588 | coff_file.handle, |
| 589 | ); |
| 590 | if (create_section_rc != .SUCCESS) return error.MissingDebugInfo; |
| 591 | errdefer windows.CloseHandle(section_handle); |
| 592 | var coff_len: usize = 0; |
| 593 | var section_view_ptr: [*]const u8 = undefined; |
| 594 | const map_section_rc = windows.ntdll.NtMapViewOfSection( |
| 595 | section_handle, |
| 596 | process_handle, |
| 597 | @ptrCast(&section_view_ptr), |
| 598 | null, |
| 599 | 0, |
| 600 | null, |
| 601 | &coff_len, |
| 602 | .ViewUnmap, |
| 603 | 0, |
| 604 | windows.PAGE_READONLY, |
| 605 | ); |
| 606 | if (map_section_rc != .SUCCESS) return error.MissingDebugInfo; |
| 607 | errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(section_view_ptr)) == .SUCCESS); |
| 608 | const section_view = section_view_ptr[0..coff_len]; |
| 609 | coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo; |
| 610 | di.mapped_file = .{ |
| 611 | .file = coff_file, |
| 612 | .section_handle = section_handle, |
| 613 | .section_view = section_view, |
| 614 | }; |
| 615 | } |
| 616 | di.coff_image_base = coff_obj.getImageBase(); |
| 617 | |
| 618 | if (coff_obj.getSectionByName(".debug_info")) |_| { |
| 619 | di.dwarf = .{}; |
| 620 | |
| 621 | inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |section, i| { |
| 622 | di.dwarf.?.sections[i] = if (coff_obj.getSectionByName("." ++ section.name)) |section_header| blk: { |
| 623 | break :blk .{ |
| 624 | .data = try coff_obj.getSectionDataAlloc(section_header, gpa), |
| 625 | .virtual_address = section_header.virtual_address, |
| 626 | .owned = true, |
| 627 | }; |
| 628 | } else null; |
| 629 | } |
| 630 | |
| 631 | try di.dwarf.?.open(gpa, native_endian); |
| 632 | } |
| 633 | |
| 634 | if (try coff_obj.getPdbPath()) |raw_path| pdb: { |
| 635 | const path = blk: { |
| 636 | if (fs.path.isAbsolute(raw_path)) { |
| 637 | break :blk raw_path; |
| 638 | } else { |
| 639 | const self_dir = try fs.selfExeDirPathAlloc(gpa); |
| 640 | defer gpa.free(self_dir); |
| 641 | break :blk try fs.path.join(gpa, &.{ self_dir, raw_path }); |
| 642 | } |
| 643 | }; |
| 644 | defer if (path.ptr != raw_path.ptr) gpa.free(path); |
| 645 | |
| 646 | di.pdb = Pdb.init(gpa, path) catch |err| switch (err) { |
| 647 | error.FileNotFound, error.IsDir => break :pdb, |
| 648 | else => return err, |
| 649 | }; |
| 650 | try di.pdb.?.parseInfoStream(); |
| 651 | try di.pdb.?.parseDbiStream(); |
| 652 | |
| 653 | if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age) |
| 654 | return error.InvalidDebugInfo; |
| 655 | |
| 656 | di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(gpa); |
| 657 | } |
| 658 | } |
| 659 | const LookupCache = struct { |
| 660 | modules: std.ArrayListUnmanaged(windows.MODULEENTRY32), |
| 661 | const init: LookupCache = .{ .modules = .empty }; |
| 662 | }; |
| 677 | 663 | const DebugInfo = struct { |
| 678 | 664 | coff_image_base: u64, |
| 679 | 665 | mapped_file: ?struct { |
| ... | ... | @@ -747,9 +733,9 @@ const Module = switch (native_os) { |
| 747 | 733 | } |
| 748 | 734 | }; |
| 749 | 735 | |
| 750 | | fn getSymbolAtAddress(mod: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { |
| 736 | fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { |
| 751 | 737 | // Translate the runtime address into a virtual address into the module |
| 752 | | const vaddr = address - mod.base_address; |
| 738 | const vaddr = address - module.base_address; |
| 753 | 739 | |
| 754 | 740 | if (di.pdb != null) { |
| 755 | 741 | if (try di.getSymbolFromPdb(vaddr)) |symbol| return symbol; |
| ... | ... | @@ -1091,12 +1077,12 @@ fn unwindFrameDwarf( |
| 1091 | 1077 | |
| 1092 | 1078 | const pc_vaddr = context.pc - load_offset; |
| 1093 | 1079 | |
| 1094 | | const fde_offset = explicit_fde_offset orelse try unwind.findFdeOffset( |
| 1080 | const fde_offset = explicit_fde_offset orelse try unwind.lookupPc( |
| 1095 | 1081 | pc_vaddr, |
| 1096 | 1082 | @sizeOf(usize), |
| 1097 | 1083 | native_endian, |
| 1098 | 1084 | ) orelse return error.MissingDebugInfo; |
| 1099 | | const format, const cie, const fde = try unwind.loadFde(fde_offset, @sizeOf(usize), native_endian); |
| 1085 | const format, const cie, const fde = try unwind.getFde(fde_offset, @sizeOf(usize), native_endian); |
| 1100 | 1086 | |
| 1101 | 1087 | // Check if this FDE *actually* includes the address. |
| 1102 | 1088 | if (pc_vaddr < fde.pc_begin or pc_vaddr >= fde.pc_begin + fde.pc_range) return error.MissingDebugInfo; |
| ... | ... | @@ -1294,7 +1280,7 @@ fn unwindFrameMachO( |
| 1294 | 1280 | load_offset: usize, |
| 1295 | 1281 | context: *UnwindContext, |
| 1296 | 1282 | unwind_info: []const u8, |
| 1297 | | eh_frame: ?[]const u8, |
| 1283 | opt_eh_frame: ?[]const u8, |
| 1298 | 1284 | ) !usize { |
| 1299 | 1285 | if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidUnwindInfo; |
| 1300 | 1286 | const header: *align(1) const macho.unwind_info_section_header = @ptrCast(unwind_info); |
| ... | ... | @@ -1304,20 +1290,6 @@ fn unwindFrameMachO( |
| 1304 | 1290 | const indices: []align(1) const macho.unwind_info_section_header_index_entry = @ptrCast(unwind_info[header.indexSectionOffset..][0..index_byte_count]); |
| 1305 | 1291 | if (indices.len == 0) return error.MissingUnwindInfo; |
| 1306 | 1292 | |
| 1307 | | // MLUGG TODO HACKHACK -- Unwind needs a slight refactor to make this work well |
| 1308 | | const opt_dwarf_unwind: ?Dwarf.Unwind = if (eh_frame) |eh_frame_data| .{ |
| 1309 | | .debug_frame = null, |
| 1310 | | .eh_frame = .{ |
| 1311 | | .header = .{ |
| 1312 | | .vaddr = undefined, |
| 1313 | | .eh_frame_vaddr = @intFromPtr(eh_frame_data.ptr) - load_offset, |
| 1314 | | .search_table = null, |
| 1315 | | }, |
| 1316 | | .eh_frame_data = eh_frame_data, |
| 1317 | | .sorted_fdes = null, |
| 1318 | | }, |
| 1319 | | } else null; |
| 1320 | | |
| 1321 | 1293 | // offset of the PC into the `__TEXT` segment |
| 1322 | 1294 | const pc_text_offset = context.pc - text_base; |
| 1323 | 1295 | |
| ... | ... | @@ -1533,8 +1505,11 @@ fn unwindFrameMachO( |
| 1533 | 1505 | break :ip new_ip; |
| 1534 | 1506 | }, |
| 1535 | 1507 | .DWARF => { |
| 1536 | | const dwarf_unwind = &(opt_dwarf_unwind orelse return error.MissingEhFrame); |
| 1537 | | return unwindFrameDwarf(dwarf_unwind, load_offset, context, @intCast(encoding.value.x86_64.dwarf)); |
| 1508 | const eh_frame = opt_eh_frame orelse return error.MissingEhFrame; |
| 1509 | const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - load_offset; |
| 1510 | var dwarf_unwind: Dwarf.Unwind = .init; |
| 1511 | dwarf_unwind.loadFromSection(.eh_frame, eh_frame_vaddr, eh_frame); |
| 1512 | return unwindFrameDwarf(&dwarf_unwind, load_offset, context, @intCast(encoding.value.x86_64.dwarf)); |
| 1538 | 1513 | }, |
| 1539 | 1514 | }, |
| 1540 | 1515 | .aarch64, .aarch64_be => switch (encoding.mode.arm64) { |
| ... | ... | @@ -1547,7 +1522,10 @@ fn unwindFrameMachO( |
| 1547 | 1522 | break :ip new_ip; |
| 1548 | 1523 | }, |
| 1549 | 1524 | .DWARF => { |
| 1550 | | const dwarf_unwind = &(opt_dwarf_unwind orelse return error.MissingEhFrame); |
| 1525 | const eh_frame = opt_eh_frame orelse return error.MissingEhFrame; |
| 1526 | const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - load_offset; |
| 1527 | var dwarf_unwind: Dwarf.Unwind = .init; |
| 1528 | dwarf_unwind.loadFromSection(.eh_frame, eh_frame_vaddr, eh_frame); |
| 1551 | 1529 | return unwindFrameDwarf(dwarf_unwind, load_offset, context, @intCast(encoding.value.arm64.dwarf)); |
| 1552 | 1530 | }, |
| 1553 | 1531 | .FRAME => ip: { |