authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-08-27 15:36:17-04:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:48+01:00
logb706949736fe67e104a14ac1dcaac8b7eb1cc33f
tree586878099f482181f27b186d8510c7086a554842
parent7adb15892eada307b43a6a7844d3e51720f8992d
signaturelock-open Commit is signed but in an unrecognized format.

debug: refactor stack frame capturing


4 files changed, 1155 insertions(+), 1123 deletions(-)

lib/std/debug.zig+18-51
...@@ -498,10 +498,17 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT...@@ -498,10 +498,17 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT
498 }498 }
499 stack_trace.index = slice.len;499 stack_trace.index = slice.len;
500 } else {500 } else {
501 // TODO: This should use the DWARF unwinder if .eh_frame_hdr is available (so that full debug info parsing isn't required).501 if (builtin.cpu.arch == .powerpc64) {
502 // A new path for loading SelfInfo needs to be created which will only attempt to parse in-memory sections, because502 // https://github.com/ziglang/zig/issues/24970
503 // stopping to load other debug info (ie. source line info) from disk here is not required for unwinding.503 stack_trace.index = 0;
504 var it = StackIterator.init(first_address, @frameAddress());504 return;
505 }
506 var context: ThreadContext = undefined;
507 const has_context = getContext(&context);
508
509 var it = (if (has_context) blk: {
510 break :blk StackIterator.initWithContext(first_address, getSelfDebugInfo() catch break :blk null, &context) catch null;
511 } else null) orelse StackIterator.init(first_address, null);
505 defer it.deinit();512 defer it.deinit();
506 for (stack_trace.instruction_addresses, 0..) |*addr, i| {513 for (stack_trace.instruction_addresses, 0..) |*addr, i| {
507 addr.* = it.next() orelse {514 addr.* = it.next() orelse {
...@@ -764,7 +771,7 @@ pub fn writeStackTrace(...@@ -764,7 +771,7 @@ pub fn writeStackTrace(
764}771}
765772
766pub const UnwindError = if (have_ucontext)773pub const UnwindError = if (have_ucontext)
767 @typeInfo(@typeInfo(@TypeOf(StackIterator.next_unwind)).@"fn".return_type.?).error_union.error_set774 @typeInfo(@typeInfo(@TypeOf(SelfInfo.unwindFrame)).@"fn".return_type.?).error_union.error_set
768else775else
769 void;776 void;
770777
...@@ -865,11 +872,11 @@ pub const StackIterator = struct {...@@ -865,11 +872,11 @@ pub const StackIterator = struct {
865 @sizeOf(usize);872 @sizeOf(usize);
866873
867 pub fn next(it: *StackIterator) ?usize {874 pub fn next(it: *StackIterator) ?usize {
868 var address = it.next_internal() orelse return null;875 var address = it.nextInternal() orelse return null;
869876
870 if (it.first_address) |first_address| {877 if (it.first_address) |first_address| {
871 while (address != first_address) {878 while (address != first_address) {
872 address = it.next_internal() orelse return null;879 address = it.nextInternal() orelse return null;
873 }880 }
874 it.first_address = null;881 it.first_address = null;
875 }882 }
...@@ -877,48 +884,13 @@ pub const StackIterator = struct {...@@ -877,48 +884,13 @@ pub const StackIterator = struct {
877 return address;884 return address;
878 }885 }
879886
880 fn next_unwind(it: *StackIterator) !usize {887 fn nextInternal(it: *StackIterator) ?usize {
881 const unwind_state = &it.unwind_state.?;
882 const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc);
883 switch (native_os) {
884 .macos, .ios, .watchos, .tvos, .visionos => {
885 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding
886 // via DWARF before attempting to use the compact unwind info will produce incorrect results.
887 if (module.unwind_info) |unwind_info| {
888 if (SelfInfo.unwindFrameMachO(
889 unwind_state.debug_info.allocator,
890 module.base_address,
891 &unwind_state.dwarf_context,
892 unwind_info,
893 module.eh_frame,
894 )) |return_address| {
895 return return_address;
896 } else |err| {
897 if (err != error.RequiresDWARFUnwind) return err;
898 }
899 } else return error.MissingUnwindInfo;
900 },
901 else => {},
902 }
903
904 if (try module.getDwarfInfoForAddress(unwind_state.debug_info.allocator, unwind_state.dwarf_context.pc)) |di| {
905 return SelfInfo.unwindFrameDwarf(
906 unwind_state.debug_info.allocator,
907 di,
908 module.base_address,
909 &unwind_state.dwarf_context,
910 null,
911 );
912 } else return error.MissingDebugInfo;
913 }
914
915 fn next_internal(it: *StackIterator) ?usize {
916 if (have_ucontext) {888 if (have_ucontext) {
917 if (it.unwind_state) |*unwind_state| {889 if (it.unwind_state) |*unwind_state| {
918 if (!unwind_state.failed) {890 if (!unwind_state.failed) {
919 if (unwind_state.dwarf_context.pc == 0) return null;891 if (unwind_state.dwarf_context.pc == 0) return null;
920 defer it.fp = unwind_state.dwarf_context.getFp() catch 0;892 defer it.fp = unwind_state.dwarf_context.getFp() catch 0;
921 if (it.next_unwind()) |return_address| {893 if (unwind_state.debug_info.unwindFrame(&unwind_state.dwarf_context)) |return_address| {
922 return return_address;894 return return_address;
923 } else |err| {895 } else |err| {
924 unwind_state.last_error = err;896 unwind_state.last_error = err;
...@@ -948,7 +920,7 @@ pub const StackIterator = struct {...@@ -948,7 +920,7 @@ pub const StackIterator = struct {
948 // Sanity check: the stack grows down thus all the parent frames must be920 // Sanity check: the stack grows down thus all the parent frames must be
949 // be at addresses that are greater (or equal) than the previous one.921 // be at addresses that are greater (or equal) than the previous one.
950 // A zero frame pointer often signals this is the last frame, that case922 // A zero frame pointer often signals this is the last frame, that case
951 // is gracefully handled by the next call to next_internal.923 // is gracefully handled by the next call to nextInternal.
952 if (new_fp != 0 and new_fp < it.fp) return null;924 if (new_fp != 0 and new_fp < it.fp) return null;
953 const new_pc = @as(*usize, @ptrFromInt(math.add(usize, fp, pc_offset) catch return null)).*;925 const new_pc = @as(*usize, @ptrFromInt(math.add(usize, fp, pc_offset) catch return null)).*;
954926
...@@ -1099,12 +1071,7 @@ fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, err:...@@ -1099,12 +1071,7 @@ fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, err:
1099}1071}
11001072
1101pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) !void {1073pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) !void {
1102 const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {1074 const symbol_info = debug_info.getSymbolAtAddress(address) catch |err| switch (err) {
1103 error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),
1104 else => return err,
1105 };
1106
1107 const symbol_info = module.getSymbolAtAddress(debug_info.allocator, address) catch |err| switch (err) {
1108 error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),1075 error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),
1109 else => return err,1076 else => return err,
1110 };1077 };
lib/std/debug/Dwarf.zig+215-814
...@@ -16,7 +16,6 @@ const elf = std.elf;...@@ -16,7 +16,6 @@ const elf = std.elf;
16const mem = std.mem;16const mem = std.mem;
17const DW = std.dwarf;17const DW = std.dwarf;
18const AT = DW.AT;18const AT = DW.AT;
19const EH = DW.EH;
20const FORM = DW.FORM;19const FORM = DW.FORM;
21const Format = DW.Format;20const Format = DW.Format;
22const RLE = DW.RLE;21const RLE = DW.RLE;
...@@ -34,13 +33,12 @@ const Dwarf = @This();...@@ -34,13 +33,12 @@ const Dwarf = @This();
34pub const expression = @import("Dwarf/expression.zig");33pub const expression = @import("Dwarf/expression.zig");
35pub const abi = @import("Dwarf/abi.zig");34pub const abi = @import("Dwarf/abi.zig");
36pub const call_frame = @import("Dwarf/call_frame.zig");35pub const call_frame = @import("Dwarf/call_frame.zig");
36pub const Unwind = @import("Dwarf/Unwind.zig");
3737
38/// Useful to temporarily enable while working on this file.38/// Useful to temporarily enable while working on this file.
39const debug_debug_mode = false;39const debug_debug_mode = false;
4040
41endian: Endian,41sections: SectionArray = @splat(null),
42sections: SectionArray = null_section_array,
43is_macho: bool,
4442
45/// Filled later by the initializer43/// Filled later by the initializer
46abbrev_table_list: ArrayList(Abbrev.Table) = .empty,44abbrev_table_list: ArrayList(Abbrev.Table) = .empty,
...@@ -49,14 +47,6 @@ compile_unit_list: ArrayList(CompileUnit) = .empty,...@@ -49,14 +47,6 @@ compile_unit_list: ArrayList(CompileUnit) = .empty,
49/// Filled later by the initializer47/// Filled later by the initializer
50func_list: ArrayList(Func) = .empty,48func_list: ArrayList(Func) = .empty,
5149
52/// Starts out non-`null` if the `.eh_frame_hdr` section is present. May become `null` later if we
53/// find that `.eh_frame_hdr` is incomplete.
54eh_frame_hdr: ?ExceptionFrameHeader = null,
55/// These lookup tables are only used if `eh_frame_hdr` is null
56cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .empty,
57/// Sorted by start_pc
58fde_list: ArrayList(FrameDescriptionEntry) = .empty,
59
60/// Populated by `populateRanges`.50/// Populated by `populateRanges`.
61ranges: ArrayList(Range) = .empty,51ranges: ArrayList(Range) = .empty,
6252
...@@ -87,9 +77,6 @@ pub const Section = struct {...@@ -87,9 +77,6 @@ pub const Section = struct {
87 debug_rnglists,77 debug_rnglists,
88 debug_addr,78 debug_addr,
89 debug_names,79 debug_names,
90 debug_frame,
91 eh_frame,
92 eh_frame_hdr,
93 };80 };
9481
95 // For sections that are not memory mapped by the loader, this is an offset82 // For sections that are not memory mapped by the loader, this is an offset
...@@ -258,13 +245,14 @@ pub const Die = struct {...@@ -258,13 +245,14 @@ pub const Die = struct {
258 fn getAttrAddr(245 fn getAttrAddr(
259 self: *const Die,246 self: *const Die,
260 di: *const Dwarf,247 di: *const Dwarf,
248 endian: Endian,
261 id: u64,249 id: u64,
262 compile_unit: CompileUnit,250 compile_unit: *const CompileUnit,
263 ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {251 ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {
264 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;252 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
265 return switch (form_value.*) {253 return switch (form_value.*) {
266 .addr => |value| value,254 .addr => |value| value,
267 .addrx => |index| di.readDebugAddr(compile_unit, index),255 .addrx => |index| di.readDebugAddr(endian, compile_unit, index),
268 else => bad(),256 else => bad(),
269 };257 };
270 }258 }
...@@ -294,9 +282,10 @@ pub const Die = struct {...@@ -294,9 +282,10 @@ pub const Die = struct {
294 pub fn getAttrString(282 pub fn getAttrString(
295 self: *const Die,283 self: *const Die,
296 di: *Dwarf,284 di: *Dwarf,
285 endian: Endian,
297 id: u64,286 id: u64,
298 opt_str: ?[]const u8,287 opt_str: ?[]const u8,
299 compile_unit: CompileUnit,288 compile_unit: *const CompileUnit,
300 ) error{ InvalidDebugInfo, MissingDebugInfo }![]const u8 {289 ) error{ InvalidDebugInfo, MissingDebugInfo }![]const u8 {
301 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;290 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
302 switch (form_value.*) {291 switch (form_value.*) {
...@@ -309,13 +298,13 @@ pub const Die = struct {...@@ -309,13 +298,13 @@ pub const Die = struct {
309 .@"32" => {298 .@"32" => {
310 const byte_offset = compile_unit.str_offsets_base + 4 * index;299 const byte_offset = compile_unit.str_offsets_base + 4 * index;
311 if (byte_offset + 4 > debug_str_offsets.len) return bad();300 if (byte_offset + 4 > debug_str_offsets.len) return bad();
312 const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian);301 const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], endian);
313 return getStringGeneric(opt_str, offset);302 return getStringGeneric(opt_str, offset);
314 },303 },
315 .@"64" => {304 .@"64" => {
316 const byte_offset = compile_unit.str_offsets_base + 8 * index;305 const byte_offset = compile_unit.str_offsets_base + 8 * index;
317 if (byte_offset + 8 > debug_str_offsets.len) return bad();306 if (byte_offset + 8 > debug_str_offsets.len) return bad();
318 const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian);307 const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], endian);
319 return getStringGeneric(opt_str, offset);308 return getStringGeneric(opt_str, offset);
320 },309 },
321 }310 }
...@@ -326,440 +315,17 @@ pub const Die = struct {...@@ -326,440 +315,17 @@ pub const Die = struct {
326 }315 }
327};316};
328317
329/// This represents the decoded .eh_frame_hdr header
330pub const ExceptionFrameHeader = struct {
331 eh_frame_ptr: usize,
332 table_enc: u8,
333 fde_count: usize,
334 entries: []const u8,
335
336 pub fn entrySize(table_enc: u8) !u8 {
337 return switch (table_enc & EH.PE.type_mask) {
338 EH.PE.udata2,
339 EH.PE.sdata2,
340 => 4,
341 EH.PE.udata4,
342 EH.PE.sdata4,
343 => 8,
344 EH.PE.udata8,
345 EH.PE.sdata8,
346 => 16,
347 // This is a binary search table, so all entries must be the same length
348 else => return bad(),
349 };
350 }
351
352 pub fn findEntry(
353 self: ExceptionFrameHeader,
354 eh_frame_len: usize,
355 eh_frame_hdr_ptr: usize,
356 pc: usize,
357 cie: *CommonInformationEntry,
358 fde: *FrameDescriptionEntry,
359 endian: Endian,
360 ) !void {
361 const entry_size = try entrySize(self.table_enc);
362
363 var left: usize = 0;
364 var len: usize = self.fde_count;
365 var fbr: Reader = .fixed(self.entries);
366
367 while (len > 1) {
368 const mid = left + len / 2;
369
370 fbr.seek = mid * entry_size;
371 const pc_begin = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
372 .pc_rel_base = @intFromPtr(&self.entries[fbr.seek]),
373 .follow_indirect = true,
374 .data_rel_base = eh_frame_hdr_ptr,
375 }, endian) orelse return bad();
376
377 if (pc < pc_begin) {
378 len /= 2;
379 } else {
380 left = mid;
381 if (pc == pc_begin) break;
382 len -= len / 2;
383 }
384 }
385
386 if (len == 0) return missing();
387 fbr.seek = left * entry_size;
388
389 // Read past the pc_begin field of the entry
390 _ = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
391 .pc_rel_base = @intFromPtr(&self.entries[fbr.seek]),
392 .follow_indirect = true,
393 .data_rel_base = eh_frame_hdr_ptr,
394 }, endian) orelse return bad();
395
396 const fde_ptr = cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
397 .pc_rel_base = @intFromPtr(&self.entries[fbr.seek]),
398 .follow_indirect = true,
399 .data_rel_base = eh_frame_hdr_ptr,
400 }, endian) orelse return bad()) orelse return bad();
401
402 if (fde_ptr < self.eh_frame_ptr) return bad();
403
404 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0..eh_frame_len];
405
406 const fde_offset = fde_ptr - self.eh_frame_ptr;
407 var eh_frame_fbr: Reader = .fixed(eh_frame);
408 eh_frame_fbr.seek = fde_offset;
409
410 const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame, endian);
411 if (fde_entry_header.type != .fde) return bad();
412
413 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable
414 const cie_offset = fde_entry_header.type.fde;
415 eh_frame_fbr.seek = @intCast(cie_offset);
416 const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame, endian);
417 if (cie_entry_header.type != .cie) return bad();
418
419 cie.* = try CommonInformationEntry.parse(
420 cie_entry_header.entry_bytes,
421 0,
422 true,
423 cie_entry_header.format,
424 .eh_frame,
425 cie_entry_header.length_offset,
426 @sizeOf(usize),
427 endian,
428 );
429
430 fde.* = try FrameDescriptionEntry.parse(
431 fde_entry_header.entry_bytes,
432 0,
433 true,
434 cie.*,
435 @sizeOf(usize),
436 endian,
437 );
438
439 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return missing();
440 }
441};
442
443pub const EntryHeader = struct {
444 /// Offset of the length field in the backing buffer
445 length_offset: usize,
446 format: Format,
447 type: union(enum) {
448 cie,
449 /// Value is the offset of the corresponding CIE
450 fde: u64,
451 terminator,
452 },
453 /// The entry's contents, not including the ID field
454 entry_bytes: []const u8,
455
456 /// The length of the entry including the ID field, but not the length field itself
457 pub fn entryLength(self: EntryHeader) usize {
458 return self.entry_bytes.len + @as(u8, if (self.format == .@"64") 8 else 4);
459 }
460
461 /// Reads a header for either an FDE or a CIE, then advances the fbr to the
462 /// position after the trailing structure.
463 ///
464 /// `fbr` must be backed by either the .eh_frame or .debug_frame sections.
465 ///
466 /// TODO that's a bad API, don't do that. this function should neither require
467 /// a fixed reader nor depend on seeking.
468 pub fn read(fbr: *Reader, dwarf_section: Section.Id, endian: Endian) !EntryHeader {
469 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);
470
471 const length_offset = fbr.seek;
472 const unit_header = try readUnitHeader(fbr, endian);
473 const unit_length = cast(usize, unit_header.unit_length) orelse return bad();
474 if (unit_length == 0) return .{
475 .length_offset = length_offset,
476 .format = unit_header.format,
477 .type = .terminator,
478 .entry_bytes = &.{},
479 };
480 const start_offset = fbr.seek;
481 const end_offset = start_offset + unit_length;
482 defer fbr.seek = end_offset;
483
484 const id = try readAddress(fbr, unit_header.format, endian);
485 const entry_bytes = fbr.buffer[fbr.seek..end_offset];
486 const cie_id: u64 = switch (dwarf_section) {
487 .eh_frame => CommonInformationEntry.eh_id,
488 .debug_frame => switch (unit_header.format) {
489 .@"32" => CommonInformationEntry.dwarf32_id,
490 .@"64" => CommonInformationEntry.dwarf64_id,
491 },
492 else => unreachable,
493 };
494
495 return .{
496 .length_offset = length_offset,
497 .format = unit_header.format,
498 .type = if (id == cie_id) .cie else .{ .fde = switch (dwarf_section) {
499 .eh_frame => try std.math.sub(u64, start_offset, id),
500 .debug_frame => id,
501 else => unreachable,
502 } },
503 .entry_bytes = entry_bytes,
504 };
505 }
506};
507
508pub const CommonInformationEntry = struct {
509 // Used in .eh_frame
510 pub const eh_id = 0;
511
512 // Used in .debug_frame (DWARF32)
513 pub const dwarf32_id = maxInt(u32);
514
515 // Used in .debug_frame (DWARF64)
516 pub const dwarf64_id = maxInt(u64);
517
518 // Offset of the length field of this entry in the eh_frame section.
519 // This is the key that FDEs use to reference CIEs.
520 length_offset: u64,
521 version: u8,
522 address_size: u8,
523 format: Format,
524
525 // Only present in version 4
526 segment_selector_size: ?u8,
527
528 code_alignment_factor: u32,
529 data_alignment_factor: i32,
530 return_address_register: u8,
531
532 aug_str: []const u8,
533 aug_data: []const u8,
534 lsda_pointer_enc: u8,
535 personality_enc: ?u8,
536 personality_routine_pointer: ?u64,
537 fde_pointer_enc: u8,
538 initial_instructions: []const u8,
539
540 pub fn isSignalFrame(self: CommonInformationEntry) bool {
541 for (self.aug_str) |c| if (c == 'S') return true;
542 return false;
543 }
544
545 pub fn addressesSignedWithBKey(self: CommonInformationEntry) bool {
546 for (self.aug_str) |c| if (c == 'B') return true;
547 return false;
548 }
549
550 pub fn mteTaggedFrame(self: CommonInformationEntry) bool {
551 for (self.aug_str) |c| if (c == 'G') return true;
552 return false;
553 }
554
555 /// This function expects to read the CIE starting with the version field.
556 /// The returned struct references memory backed by cie_bytes.
557 ///
558 /// See the FrameDescriptionEntry.parse documentation for the description
559 /// of `pc_rel_offset` and `is_runtime`.
560 ///
561 /// `length_offset` specifies the offset of this CIE's length field in the
562 /// .eh_frame / .debug_frame section.
563 pub fn parse(
564 cie_bytes: []const u8,
565 pc_rel_offset: i64,
566 is_runtime: bool,
567 format: Format,
568 dwarf_section: Section.Id,
569 length_offset: u64,
570 addr_size_bytes: u8,
571 endian: Endian,
572 ) !CommonInformationEntry {
573 if (addr_size_bytes > 8) return error.UnsupportedAddrSize;
574
575 var fbr: Reader = .fixed(cie_bytes);
576
577 const version = try fbr.takeByte();
578 switch (dwarf_section) {
579 .eh_frame => if (version != 1 and version != 3) return error.UnsupportedDwarfVersion,
580 .debug_frame => if (version != 4) return error.UnsupportedDwarfVersion,
581 else => return error.UnsupportedDwarfSection,
582 }
583
584 var has_eh_data = false;
585 var has_aug_data = false;
586
587 var aug_str_len: usize = 0;
588 const aug_str_start = fbr.seek;
589 var aug_byte = try fbr.takeByte();
590 while (aug_byte != 0) : (aug_byte = try fbr.takeByte()) {
591 switch (aug_byte) {
592 'z' => {
593 if (aug_str_len != 0) return bad();
594 has_aug_data = true;
595 },
596 'e' => {
597 if (has_aug_data or aug_str_len != 0) return bad();
598 if (try fbr.takeByte() != 'h') return bad();
599 has_eh_data = true;
600 },
601 else => if (has_eh_data) return bad(),
602 }
603
604 aug_str_len += 1;
605 }
606
607 if (has_eh_data) {
608 // legacy data created by older versions of gcc - unsupported here
609 for (0..addr_size_bytes) |_| _ = try fbr.takeByte();
610 }
611
612 const address_size = if (version == 4) try fbr.takeByte() else addr_size_bytes;
613 const segment_selector_size = if (version == 4) try fbr.takeByte() else null;
614
615 const code_alignment_factor = try fbr.takeLeb128(u32);
616 const data_alignment_factor = try fbr.takeLeb128(i32);
617 const return_address_register = if (version == 1) try fbr.takeByte() else try fbr.takeLeb128(u8);
618
619 var lsda_pointer_enc: u8 = EH.PE.omit;
620 var personality_enc: ?u8 = null;
621 var personality_routine_pointer: ?u64 = null;
622 var fde_pointer_enc: u8 = EH.PE.absptr;
623
624 var aug_data: []const u8 = &[_]u8{};
625 const aug_str = if (has_aug_data) blk: {
626 const aug_data_len = try fbr.takeLeb128(usize);
627 const aug_data_start = fbr.seek;
628 aug_data = cie_bytes[aug_data_start..][0..aug_data_len];
629
630 const aug_str = cie_bytes[aug_str_start..][0..aug_str_len];
631 for (aug_str[1..]) |byte| {
632 switch (byte) {
633 'L' => {
634 lsda_pointer_enc = try fbr.takeByte();
635 },
636 'P' => {
637 personality_enc = try fbr.takeByte();
638 personality_routine_pointer = try readEhPointer(&fbr, personality_enc.?, addr_size_bytes, .{
639 .pc_rel_base = try pcRelBase(@intFromPtr(&cie_bytes[fbr.seek]), pc_rel_offset),
640 .follow_indirect = is_runtime,
641 }, endian);
642 },
643 'R' => {
644 fde_pointer_enc = try fbr.takeByte();
645 },
646 'S', 'B', 'G' => {},
647 else => return bad(),
648 }
649 }
650
651 // aug_data_len can include padding so the CIE ends on an address boundary
652 fbr.seek = aug_data_start + aug_data_len;
653 break :blk aug_str;
654 } else &[_]u8{};
655
656 const initial_instructions = cie_bytes[fbr.seek..];
657 return .{
658 .length_offset = length_offset,
659 .version = version,
660 .address_size = address_size,
661 .format = format,
662 .segment_selector_size = segment_selector_size,
663 .code_alignment_factor = code_alignment_factor,
664 .data_alignment_factor = data_alignment_factor,
665 .return_address_register = return_address_register,
666 .aug_str = aug_str,
667 .aug_data = aug_data,
668 .lsda_pointer_enc = lsda_pointer_enc,
669 .personality_enc = personality_enc,
670 .personality_routine_pointer = personality_routine_pointer,
671 .fde_pointer_enc = fde_pointer_enc,
672 .initial_instructions = initial_instructions,
673 };
674 }
675};
676
677pub const FrameDescriptionEntry = struct {
678 // Offset into eh_frame where the CIE for this FDE is stored
679 cie_length_offset: u64,
680
681 pc_begin: u64,
682 pc_range: u64,
683 lsda_pointer: ?u64,
684 aug_data: []const u8,
685 instructions: []const u8,
686
687 /// This function expects to read the FDE starting at the PC Begin field.
688 /// The returned struct references memory backed by `fde_bytes`.
689 ///
690 /// `pc_rel_offset` specifies an offset to be applied to pc_rel_base values
691 /// used when decoding pointers. This should be set to zero if fde_bytes is
692 /// backed by the memory of a .eh_frame / .debug_frame section in the running executable.
693 /// Otherwise, it should be the relative offset to translate addresses from
694 /// where the section is currently stored in memory, to where it *would* be
695 /// stored at runtime: section base addr - backing data base ptr.
696 ///
697 /// Similarly, `is_runtime` specifies this function is being called on a runtime
698 /// section, and so indirect pointers can be followed.
699 pub fn parse(
700 fde_bytes: []const u8,
701 pc_rel_offset: i64,
702 is_runtime: bool,
703 cie: CommonInformationEntry,
704 addr_size_bytes: u8,
705 endian: Endian,
706 ) !FrameDescriptionEntry {
707 if (addr_size_bytes > 8) return error.InvalidAddrSize;
708
709 var fbr: Reader = .fixed(fde_bytes);
710
711 const pc_begin = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
712 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.seek]), pc_rel_offset),
713 .follow_indirect = is_runtime,
714 }, endian) orelse return bad();
715
716 const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
717 .pc_rel_base = 0,
718 .follow_indirect = false,
719 }, endian) orelse return bad();
720
721 var aug_data: []const u8 = &[_]u8{};
722 const lsda_pointer = if (cie.aug_str.len > 0) blk: {
723 const aug_data_len = try fbr.takeLeb128(usize);
724 const aug_data_start = fbr.seek;
725 aug_data = fde_bytes[aug_data_start..][0..aug_data_len];
726
727 const lsda_pointer = if (cie.lsda_pointer_enc != EH.PE.omit)
728 try readEhPointer(&fbr, cie.lsda_pointer_enc, addr_size_bytes, .{
729 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.seek]), pc_rel_offset),
730 .follow_indirect = is_runtime,
731 }, endian)
732 else
733 null;
734
735 fbr.seek = aug_data_start + aug_data_len;
736 break :blk lsda_pointer;
737 } else null;
738
739 const instructions = fde_bytes[fbr.seek..];
740 return .{
741 .cie_length_offset = cie.length_offset,
742 .pc_begin = pc_begin,
743 .pc_range = pc_range,
744 .lsda_pointer = lsda_pointer,
745 .aug_data = aug_data,
746 .instructions = instructions,
747 };
748 }
749};
750
751const num_sections = std.enums.directEnumArrayLen(Section.Id, 0);318const num_sections = std.enums.directEnumArrayLen(Section.Id, 0);
752pub const SectionArray = [num_sections]?Section;319pub const SectionArray = [num_sections]?Section;
753pub const null_section_array = [_]?Section{null} ** num_sections;
754320
755pub const OpenError = ScanError;321pub const OpenError = ScanError;
756322
757/// Initialize DWARF info. The caller has the responsibility to initialize most323/// Initialize DWARF info. The caller has the responsibility to initialize most
758/// the `Dwarf` fields before calling. `binary_mem` is the raw bytes of the324/// the `Dwarf` fields before calling. `binary_mem` is the raw bytes of the
759/// main binary file (not the secondary debug info file).325/// main binary file (not the secondary debug info file).
760pub fn open(d: *Dwarf, gpa: Allocator) OpenError!void {326pub fn open(d: *Dwarf, gpa: Allocator, endian: Endian) OpenError!void {
761 try d.scanAllFunctions(gpa);327 try d.scanAllFunctions(gpa, endian);
762 try d.scanAllCompileUnits(gpa);328 try d.scanAllCompileUnits(gpa, endian);
763}329}
764330
765const PcRange = struct {331const PcRange = struct {
...@@ -825,31 +391,30 @@ pub const ScanError = error{...@@ -825,31 +391,30 @@ pub const ScanError = error{
825 StreamTooLong,391 StreamTooLong,
826} || Allocator.Error;392} || Allocator.Error;
827393
828fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {394fn scanAllFunctions(di: *Dwarf, allocator: Allocator, endian: Endian) ScanError!void {
829 const endian = di.endian;395 var fr: Reader = .fixed(di.section(.debug_info).?);
830 var fbr: Reader = .fixed(di.section(.debug_info).?);
831 var this_unit_offset: u64 = 0;396 var this_unit_offset: u64 = 0;
832397
833 while (this_unit_offset < fbr.buffer.len) {398 while (this_unit_offset < fr.buffer.len) {
834 fbr.seek = @intCast(this_unit_offset);399 fr.seek = @intCast(this_unit_offset);
835400
836 const unit_header = try readUnitHeader(&fbr, endian);401 const unit_header = try readUnitHeader(&fr, endian);
837 if (unit_header.unit_length == 0) return;402 if (unit_header.unit_length == 0) return;
838 const next_offset = unit_header.header_length + unit_header.unit_length;403 const next_offset = unit_header.header_length + unit_header.unit_length;
839404
840 const version = try fbr.takeInt(u16, endian);405 const version = try fr.takeInt(u16, endian);
841 if (version < 2 or version > 5) return bad();406 if (version < 2 or version > 5) return bad();
842407
843 var address_size: u8 = undefined;408 var address_size: u8 = undefined;
844 var debug_abbrev_offset: u64 = undefined;409 var debug_abbrev_offset: u64 = undefined;
845 if (version >= 5) {410 if (version >= 5) {
846 const unit_type = try fbr.takeByte();411 const unit_type = try fr.takeByte();
847 if (unit_type != DW.UT.compile) return bad();412 if (unit_type != DW.UT.compile) return bad();
848 address_size = try fbr.takeByte();413 address_size = try fr.takeByte();
849 debug_abbrev_offset = try readAddress(&fbr, unit_header.format, endian);414 debug_abbrev_offset = try readAddress(&fr, unit_header.format, endian);
850 } else {415 } else {
851 debug_abbrev_offset = try readAddress(&fbr, unit_header.format, endian);416 debug_abbrev_offset = try readAddress(&fr, unit_header.format, endian);
852 address_size = try fbr.takeByte();417 address_size = try fr.takeByte();
853 }418 }
854 if (address_size != @sizeOf(usize)) return bad();419 if (address_size != @sizeOf(usize)) return bad();
855420
...@@ -890,12 +455,12 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -890,12 +455,12 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
890 };455 };
891456
892 while (true) {457 while (true) {
893 fbr.seek = std.mem.indexOfNonePos(u8, fbr.buffer, fbr.seek, &.{458 fr.seek = std.mem.indexOfNonePos(u8, fr.buffer, fr.seek, &.{
894 zig_padding_abbrev_code, 0,459 zig_padding_abbrev_code, 0,
895 }) orelse fbr.buffer.len;460 }) orelse fr.buffer.len;
896 if (fbr.seek >= next_unit_pos) break;461 if (fr.seek >= next_unit_pos) break;
897 var die_obj = (try parseDie(462 var die_obj = (try parseDie(
898 &fbr,463 &fr,
899 attrs_bufs[0],464 attrs_bufs[0],
900 abbrev_table,465 abbrev_table,
901 unit_header.format,466 unit_header.format,
...@@ -920,30 +485,30 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -920,30 +485,30 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
920 // Prevent endless loops485 // Prevent endless loops
921 for (0..3) |_| {486 for (0..3) |_| {
922 if (this_die_obj.getAttr(AT.name)) |_| {487 if (this_die_obj.getAttr(AT.name)) |_| {
923 break :x try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit);488 break :x try this_die_obj.getAttrString(di, endian, AT.name, di.section(.debug_str), &compile_unit);
924 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {489 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
925 const after_die_offset = fbr.seek;490 const after_die_offset = fr.seek;
926 defer fbr.seek = after_die_offset;491 defer fr.seek = after_die_offset;
927492
928 // Follow the DIE it points to and repeat493 // Follow the DIE it points to and repeat
929 const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin, this_unit_offset, next_offset);494 const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin, this_unit_offset, next_offset);
930 fbr.seek = @intCast(ref_offset);495 fr.seek = @intCast(ref_offset);
931 this_die_obj = (try parseDie(496 this_die_obj = (try parseDie(
932 &fbr,497 &fr,
933 attrs_bufs[2],498 attrs_bufs[2],
934 abbrev_table, // wrong abbrev table for different cu499 abbrev_table, // wrong abbrev table for different cu
935 unit_header.format,500 unit_header.format,
936 endian,501 endian,
937 )) orelse return bad();502 )) orelse return bad();
938 } else if (this_die_obj.getAttr(AT.specification)) |_| {503 } else if (this_die_obj.getAttr(AT.specification)) |_| {
939 const after_die_offset = fbr.seek;504 const after_die_offset = fr.seek;
940 defer fbr.seek = after_die_offset;505 defer fr.seek = after_die_offset;
941506
942 // Follow the DIE it points to and repeat507 // Follow the DIE it points to and repeat
943 const ref_offset = try this_die_obj.getAttrRef(AT.specification, this_unit_offset, next_offset);508 const ref_offset = try this_die_obj.getAttrRef(AT.specification, this_unit_offset, next_offset);
944 fbr.seek = @intCast(ref_offset);509 fr.seek = @intCast(ref_offset);
945 this_die_obj = (try parseDie(510 this_die_obj = (try parseDie(
946 &fbr,511 &fr,
947 attrs_bufs[2],512 attrs_bufs[2],
948 abbrev_table, // wrong abbrev table for different cu513 abbrev_table, // wrong abbrev table for different cu
949 unit_header.format,514 unit_header.format,
...@@ -957,7 +522,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -957,7 +522,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
957 break :x null;522 break :x null;
958 };523 };
959524
960 var range_added = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {525 var range_added = if (die_obj.getAttrAddr(di, endian, AT.low_pc, &compile_unit)) |low_pc| blk: {
961 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {526 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
962 const pc_end = switch (high_pc_value.*) {527 const pc_end = switch (high_pc_value.*) {
963 .addr => |value| value,528 .addr => |value| value,
...@@ -983,7 +548,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -983,7 +548,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
983 };548 };
984549
985 if (die_obj.getAttr(AT.ranges)) |ranges_value| blk: {550 if (die_obj.getAttr(AT.ranges)) |ranges_value| blk: {
986 var iter = DebugRangeIterator.init(ranges_value, di, &compile_unit) catch |err| {551 var iter = DebugRangeIterator.init(ranges_value, di, endian, &compile_unit) catch |err| {
987 if (err != error.MissingDebugInfo) return err;552 if (err != error.MissingDebugInfo) return err;
988 break :blk;553 break :blk;
989 };554 };
...@@ -1015,34 +580,33 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -1015,34 +580,33 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
1015 }580 }
1016}581}
1017582
1018fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {583fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator, endian: Endian) ScanError!void {
1019 const endian = di.endian;584 var fr: Reader = .fixed(di.section(.debug_info).?);
1020 var fbr: Reader = .fixed(di.section(.debug_info).?);
1021 var this_unit_offset: u64 = 0;585 var this_unit_offset: u64 = 0;
1022586
1023 var attrs_buf = std.array_list.Managed(Die.Attr).init(allocator);587 var attrs_buf = std.array_list.Managed(Die.Attr).init(allocator);
1024 defer attrs_buf.deinit();588 defer attrs_buf.deinit();
1025589
1026 while (this_unit_offset < fbr.buffer.len) {590 while (this_unit_offset < fr.buffer.len) {
1027 fbr.seek = @intCast(this_unit_offset);591 fr.seek = @intCast(this_unit_offset);
1028592
1029 const unit_header = try readUnitHeader(&fbr, endian);593 const unit_header = try readUnitHeader(&fr, endian);
1030 if (unit_header.unit_length == 0) return;594 if (unit_header.unit_length == 0) return;
1031 const next_offset = unit_header.header_length + unit_header.unit_length;595 const next_offset = unit_header.header_length + unit_header.unit_length;
1032596
1033 const version = try fbr.takeInt(u16, endian);597 const version = try fr.takeInt(u16, endian);
1034 if (version < 2 or version > 5) return bad();598 if (version < 2 or version > 5) return bad();
1035599
1036 var address_size: u8 = undefined;600 var address_size: u8 = undefined;
1037 var debug_abbrev_offset: u64 = undefined;601 var debug_abbrev_offset: u64 = undefined;
1038 if (version >= 5) {602 if (version >= 5) {
1039 const unit_type = try fbr.takeByte();603 const unit_type = try fr.takeByte();
1040 if (unit_type != UT.compile) return bad();604 if (unit_type != UT.compile) return bad();
1041 address_size = try fbr.takeByte();605 address_size = try fr.takeByte();
1042 debug_abbrev_offset = try readAddress(&fbr, unit_header.format, endian);606 debug_abbrev_offset = try readAddress(&fr, unit_header.format, endian);
1043 } else {607 } else {
1044 debug_abbrev_offset = try readAddress(&fbr, unit_header.format, endian);608 debug_abbrev_offset = try readAddress(&fr, unit_header.format, endian);
1045 address_size = try fbr.takeByte();609 address_size = try fr.takeByte();
1046 }610 }
1047 if (address_size != @sizeOf(usize)) return bad();611 if (address_size != @sizeOf(usize)) return bad();
1048612
...@@ -1055,7 +619,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -1055,7 +619,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {
1055 try attrs_buf.resize(max_attrs);619 try attrs_buf.resize(max_attrs);
1056620
1057 var compile_unit_die = (try parseDie(621 var compile_unit_die = (try parseDie(
1058 &fbr,622 &fr,
1059 attrs_buf.items,623 attrs_buf.items,
1060 abbrev_table,624 abbrev_table,
1061 unit_header.format,625 unit_header.format,
...@@ -1080,7 +644,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {...@@ -1080,7 +644,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {
1080 };644 };
1081645
1082 compile_unit.pc_range = x: {646 compile_unit.pc_range = x: {
1083 if (compile_unit_die.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| {647 if (compile_unit_die.getAttrAddr(di, endian, AT.low_pc, &compile_unit)) |low_pc| {
1084 if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {648 if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {
1085 const pc_end = switch (high_pc_value.*) {649 const pc_end = switch (high_pc_value.*) {
1086 .addr => |value| value,650 .addr => |value| value,
...@@ -1144,10 +708,11 @@ const DebugRangeIterator = struct {...@@ -1144,10 +708,11 @@ const DebugRangeIterator = struct {
1144 base_address: u64,708 base_address: u64,
1145 section_type: Section.Id,709 section_type: Section.Id,
1146 di: *const Dwarf,710 di: *const Dwarf,
711 endian: Endian,
1147 compile_unit: *const CompileUnit,712 compile_unit: *const CompileUnit,
1148 fbr: Reader,713 fr: Reader,
1149714
1150 pub fn init(ranges_value: *const FormValue, di: *const Dwarf, compile_unit: *const CompileUnit) !@This() {715 pub fn init(ranges_value: *const FormValue, di: *const Dwarf, endian: Endian, compile_unit: *const CompileUnit) !@This() {
1151 const section_type = if (compile_unit.version >= 5) Section.Id.debug_rnglists else Section.Id.debug_ranges;716 const section_type = if (compile_unit.version >= 5) Section.Id.debug_rnglists else Section.Id.debug_ranges;
1152 const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo;717 const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo;
1153718
...@@ -1158,13 +723,13 @@ const DebugRangeIterator = struct {...@@ -1158,13 +723,13 @@ const DebugRangeIterator = struct {
1158 .@"32" => {723 .@"32" => {
1159 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx));724 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx));
1160 if (offset_loc + 4 > debug_ranges.len) return bad();725 if (offset_loc + 4 > debug_ranges.len) return bad();
1161 const offset = mem.readInt(u32, debug_ranges[offset_loc..][0..4], di.endian);726 const offset = mem.readInt(u32, debug_ranges[offset_loc..][0..4], endian);
1162 break :off compile_unit.rnglists_base + offset;727 break :off compile_unit.rnglists_base + offset;
1163 },728 },
1164 .@"64" => {729 .@"64" => {
1165 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx));730 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx));
1166 if (offset_loc + 8 > debug_ranges.len) return bad();731 if (offset_loc + 8 > debug_ranges.len) return bad();
1167 const offset = mem.readInt(u64, debug_ranges[offset_loc..][0..8], di.endian);732 const offset = mem.readInt(u64, debug_ranges[offset_loc..][0..8], endian);
1168 break :off compile_unit.rnglists_base + offset;733 break :off compile_unit.rnglists_base + offset;
1169 },734 },
1170 }735 }
...@@ -1176,42 +741,43 @@ const DebugRangeIterator = struct {...@@ -1176,42 +741,43 @@ const DebugRangeIterator = struct {
1176 // specified by DW_AT.low_pc or to some other value encoded741 // specified by DW_AT.low_pc or to some other value encoded
1177 // in the list itself.742 // in the list itself.
1178 // If no starting value is specified use zero.743 // If no starting value is specified use zero.
1179 const base_address = compile_unit.die.getAttrAddr(di, AT.low_pc, compile_unit.*) catch |err| switch (err) {744 const base_address = compile_unit.die.getAttrAddr(di, endian, AT.low_pc, compile_unit) catch |err| switch (err) {
1180 error.MissingDebugInfo => 0,745 error.MissingDebugInfo => 0,
1181 else => return err,746 else => return err,
1182 };747 };
1183748
1184 var fbr: Reader = .fixed(debug_ranges);749 var fr: Reader = .fixed(debug_ranges);
1185 fbr.seek = cast(usize, ranges_offset) orelse return bad();750 fr.seek = cast(usize, ranges_offset) orelse return bad();
1186751
1187 return .{752 return .{
1188 .base_address = base_address,753 .base_address = base_address,
1189 .section_type = section_type,754 .section_type = section_type,
1190 .di = di,755 .di = di,
756 .endian = endian,
1191 .compile_unit = compile_unit,757 .compile_unit = compile_unit,
1192 .fbr = fbr,758 .fr = fr,
1193 };759 };
1194 }760 }
1195761
1196 // Returns the next range in the list, or null if the end was reached.762 // Returns the next range in the list, or null if the end was reached.
1197 pub fn next(self: *@This()) !?PcRange {763 pub fn next(self: *@This()) !?PcRange {
1198 const endian = self.di.endian;764 const endian = self.endian;
1199 switch (self.section_type) {765 switch (self.section_type) {
1200 .debug_rnglists => {766 .debug_rnglists => {
1201 const kind = try self.fbr.takeByte();767 const kind = try self.fr.takeByte();
1202 switch (kind) {768 switch (kind) {
1203 RLE.end_of_list => return null,769 RLE.end_of_list => return null,
1204 RLE.base_addressx => {770 RLE.base_addressx => {
1205 const index = try self.fbr.takeLeb128(usize);771 const index = try self.fr.takeLeb128(usize);
1206 self.base_address = try self.di.readDebugAddr(self.compile_unit.*, index);772 self.base_address = try self.di.readDebugAddr(endian, self.compile_unit, index);
1207 return try self.next();773 return try self.next();
1208 },774 },
1209 RLE.startx_endx => {775 RLE.startx_endx => {
1210 const start_index = try self.fbr.takeLeb128(usize);776 const start_index = try self.fr.takeLeb128(usize);
1211 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);777 const start_addr = try self.di.readDebugAddr(endian, self.compile_unit, start_index);
1212778
1213 const end_index = try self.fbr.takeLeb128(usize);779 const end_index = try self.fr.takeLeb128(usize);
1214 const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index);780 const end_addr = try self.di.readDebugAddr(endian, self.compile_unit, end_index);
1215781
1216 return .{782 return .{
1217 .start = start_addr,783 .start = start_addr,
...@@ -1219,10 +785,10 @@ const DebugRangeIterator = struct {...@@ -1219,10 +785,10 @@ const DebugRangeIterator = struct {
1219 };785 };
1220 },786 },
1221 RLE.startx_length => {787 RLE.startx_length => {
1222 const start_index = try self.fbr.takeLeb128(usize);788 const start_index = try self.fr.takeLeb128(usize);
1223 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);789 const start_addr = try self.di.readDebugAddr(endian, self.compile_unit, start_index);
1224790
1225 const len = try self.fbr.takeLeb128(usize);791 const len = try self.fr.takeLeb128(usize);
1226 const end_addr = start_addr + len;792 const end_addr = start_addr + len;
1227793
1228 return .{794 return .{
...@@ -1231,8 +797,8 @@ const DebugRangeIterator = struct {...@@ -1231,8 +797,8 @@ const DebugRangeIterator = struct {
1231 };797 };
1232 },798 },
1233 RLE.offset_pair => {799 RLE.offset_pair => {
1234 const start_addr = try self.fbr.takeLeb128(usize);800 const start_addr = try self.fr.takeLeb128(usize);
1235 const end_addr = try self.fbr.takeLeb128(usize);801 const end_addr = try self.fr.takeLeb128(usize);
1236802
1237 // This is the only kind that uses the base address803 // This is the only kind that uses the base address
1238 return .{804 return .{
...@@ -1241,12 +807,12 @@ const DebugRangeIterator = struct {...@@ -1241,12 +807,12 @@ const DebugRangeIterator = struct {
1241 };807 };
1242 },808 },
1243 RLE.base_address => {809 RLE.base_address => {
1244 self.base_address = try self.fbr.takeInt(usize, endian);810 self.base_address = try self.fr.takeInt(usize, endian);
1245 return try self.next();811 return try self.next();
1246 },812 },
1247 RLE.start_end => {813 RLE.start_end => {
1248 const start_addr = try self.fbr.takeInt(usize, endian);814 const start_addr = try self.fr.takeInt(usize, endian);
1249 const end_addr = try self.fbr.takeInt(usize, endian);815 const end_addr = try self.fr.takeInt(usize, endian);
1250816
1251 return .{817 return .{
1252 .start = start_addr,818 .start = start_addr,
...@@ -1254,8 +820,8 @@ const DebugRangeIterator = struct {...@@ -1254,8 +820,8 @@ const DebugRangeIterator = struct {
1254 };820 };
1255 },821 },
1256 RLE.start_length => {822 RLE.start_length => {
1257 const start_addr = try self.fbr.takeInt(usize, endian);823 const start_addr = try self.fr.takeInt(usize, endian);
1258 const len = try self.fbr.takeLeb128(usize);824 const len = try self.fr.takeLeb128(usize);
1259 const end_addr = start_addr + len;825 const end_addr = start_addr + len;
1260826
1261 return .{827 return .{
...@@ -1267,8 +833,8 @@ const DebugRangeIterator = struct {...@@ -1267,8 +833,8 @@ const DebugRangeIterator = struct {
1267 }833 }
1268 },834 },
1269 .debug_ranges => {835 .debug_ranges => {
1270 const start_addr = try self.fbr.takeInt(usize, endian);836 const start_addr = try self.fr.takeInt(usize, endian);
1271 const end_addr = try self.fbr.takeInt(usize, endian);837 const end_addr = try self.fr.takeInt(usize, endian);
1272 if (start_addr == 0 and end_addr == 0) return null;838 if (start_addr == 0 and end_addr == 0) return null;
1273839
1274 // This entry selects a new value for the base address840 // This entry selects a new value for the base address
...@@ -1288,14 +854,14 @@ const DebugRangeIterator = struct {...@@ -1288,14 +854,14 @@ const DebugRangeIterator = struct {
1288};854};
1289855
1290/// TODO: change this to binary searching the sorted compile unit list856/// TODO: change this to binary searching the sorted compile unit list
1291pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*CompileUnit {857pub fn findCompileUnit(di: *const Dwarf, endian: Endian, target_address: u64) !*CompileUnit {
1292 for (di.compile_unit_list.items) |*compile_unit| {858 for (di.compile_unit_list.items) |*compile_unit| {
1293 if (compile_unit.pc_range) |range| {859 if (compile_unit.pc_range) |range| {
1294 if (target_address >= range.start and target_address < range.end) return compile_unit;860 if (target_address >= range.start and target_address < range.end) return compile_unit;
1295 }861 }
1296862
1297 const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue;863 const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue;
1298 var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue;864 var iter = DebugRangeIterator.init(ranges_value, di, endian, compile_unit) catch continue;
1299 while (try iter.next()) |range| {865 while (try iter.next()) |range| {
1300 if (target_address >= range.start and target_address < range.end) return compile_unit;866 if (target_address >= range.start and target_address < range.end) return compile_unit;
1301 }867 }
...@@ -1320,8 +886,8 @@ fn getAbbrevTable(di: *Dwarf, allocator: Allocator, abbrev_offset: u64) !*const...@@ -1320,8 +886,8 @@ fn getAbbrevTable(di: *Dwarf, allocator: Allocator, abbrev_offset: u64) !*const
1320}886}
1321887
1322fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table {888fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table {
1323 var fbr: Reader = .fixed(di.section(.debug_abbrev).?);889 var fr: Reader = .fixed(di.section(.debug_abbrev).?);
1324 fbr.seek = cast(usize, offset) orelse return bad();890 fr.seek = cast(usize, offset) orelse return bad();
1325891
1326 var abbrevs = std.array_list.Managed(Abbrev).init(allocator);892 var abbrevs = std.array_list.Managed(Abbrev).init(allocator);
1327 defer {893 defer {
...@@ -1335,20 +901,20 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table...@@ -1335,20 +901,20 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table
1335 defer attrs.deinit();901 defer attrs.deinit();
1336902
1337 while (true) {903 while (true) {
1338 const code = try fbr.takeLeb128(u64);904 const code = try fr.takeLeb128(u64);
1339 if (code == 0) break;905 if (code == 0) break;
1340 const tag_id = try fbr.takeLeb128(u64);906 const tag_id = try fr.takeLeb128(u64);
1341 const has_children = (try fbr.takeByte()) == DW.CHILDREN.yes;907 const has_children = (try fr.takeByte()) == DW.CHILDREN.yes;
1342908
1343 while (true) {909 while (true) {
1344 const attr_id = try fbr.takeLeb128(u64);910 const attr_id = try fr.takeLeb128(u64);
1345 const form_id = try fbr.takeLeb128(u64);911 const form_id = try fr.takeLeb128(u64);
1346 if (attr_id == 0 and form_id == 0) break;912 if (attr_id == 0 and form_id == 0) break;
1347 try attrs.append(.{913 try attrs.append(.{
1348 .id = attr_id,914 .id = attr_id,
1349 .form_id = form_id,915 .form_id = form_id,
1350 .payload = switch (form_id) {916 .payload = switch (form_id) {
1351 FORM.implicit_const => try fbr.takeLeb128(i64),917 FORM.implicit_const => try fr.takeLeb128(i64),
1352 else => undefined,918 else => undefined,
1353 },919 },
1354 });920 });
...@@ -1369,20 +935,20 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table...@@ -1369,20 +935,20 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table
1369}935}
1370936
1371fn parseDie(937fn parseDie(
1372 fbr: *Reader,938 fr: *Reader,
1373 attrs_buf: []Die.Attr,939 attrs_buf: []Die.Attr,
1374 abbrev_table: *const Abbrev.Table,940 abbrev_table: *const Abbrev.Table,
1375 format: Format,941 format: Format,
1376 endian: Endian,942 endian: Endian,
1377) ScanError!?Die {943) ScanError!?Die {
1378 const abbrev_code = try fbr.takeLeb128(u64);944 const abbrev_code = try fr.takeLeb128(u64);
1379 if (abbrev_code == 0) return null;945 if (abbrev_code == 0) return null;
1380 const table_entry = abbrev_table.get(abbrev_code) orelse return bad();946 const table_entry = abbrev_table.get(abbrev_code) orelse return bad();
1381947
1382 const attrs = attrs_buf[0..table_entry.attrs.len];948 const attrs = attrs_buf[0..table_entry.attrs.len];
1383 for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = .{949 for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = .{
1384 .id = attr.id,950 .id = attr.id,
1385 .value = try parseFormValue(fbr, attr.form_id, format, endian, attr.payload),951 .value = try parseFormValue(fr, attr.form_id, format, endian, attr.payload),
1386 };952 };
1387 return .{953 return .{
1388 .tag_id = table_entry.tag_id,954 .tag_id = table_entry.tag_id,
...@@ -1392,25 +958,24 @@ fn parseDie(...@@ -1392,25 +958,24 @@ fn parseDie(
1392}958}
1393959
1394/// Ensures that addresses in the returned LineTable are monotonically increasing.960/// Ensures that addresses in the returned LineTable are monotonically increasing.
1395fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !CompileUnit.SrcLocCache {961fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, endian: Endian, compile_unit: *const CompileUnit) !CompileUnit.SrcLocCache {
1396 const endian = d.endian;962 const compile_unit_cwd = try compile_unit.die.getAttrString(d, endian, AT.comp_dir, d.section(.debug_line_str), compile_unit);
1397 const compile_unit_cwd = try compile_unit.die.getAttrString(d, AT.comp_dir, d.section(.debug_line_str), compile_unit.*);
1398 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);963 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
1399964
1400 var fbr: Reader = .fixed(d.section(.debug_line).?);965 var fr: Reader = .fixed(d.section(.debug_line).?);
1401 fbr.seek = @intCast(line_info_offset);966 fr.seek = @intCast(line_info_offset);
1402967
1403 const unit_header = try readUnitHeader(&fbr, endian);968 const unit_header = try readUnitHeader(&fr, endian);
1404 if (unit_header.unit_length == 0) return missing();969 if (unit_header.unit_length == 0) return missing();
1405970
1406 const next_offset = unit_header.header_length + unit_header.unit_length;971 const next_offset = unit_header.header_length + unit_header.unit_length;
1407972
1408 const version = try fbr.takeInt(u16, endian);973 const version = try fr.takeInt(u16, endian);
1409 if (version < 2) return bad();974 if (version < 2) return bad();
1410975
1411 const addr_size: u8, const seg_size: u8 = if (version >= 5) .{976 const addr_size: u8, const seg_size: u8 = if (version >= 5) .{
1412 try fbr.takeByte(),977 try fr.takeByte(),
1413 try fbr.takeByte(),978 try fr.takeByte(),
1414 } else .{979 } else .{
1415 switch (unit_header.format) {980 switch (unit_header.format) {
1416 .@"32" => 4,981 .@"32" => 4,
...@@ -1421,26 +986,26 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1421,26 +986,26 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1421 _ = addr_size;986 _ = addr_size;
1422 _ = seg_size;987 _ = seg_size;
1423988
1424 const prologue_length = try readAddress(&fbr, unit_header.format, endian);989 const prologue_length = try readAddress(&fr, unit_header.format, endian);
1425 const prog_start_offset = fbr.seek + prologue_length;990 const prog_start_offset = fr.seek + prologue_length;
1426991
1427 const minimum_instruction_length = try fbr.takeByte();992 const minimum_instruction_length = try fr.takeByte();
1428 if (minimum_instruction_length == 0) return bad();993 if (minimum_instruction_length == 0) return bad();
1429994
1430 if (version >= 4) {995 if (version >= 4) {
1431 const maximum_operations_per_instruction = try fbr.takeByte();996 const maximum_operations_per_instruction = try fr.takeByte();
1432 _ = maximum_operations_per_instruction;997 _ = maximum_operations_per_instruction;
1433 }998 }
1434999
1435 const default_is_stmt = (try fbr.takeByte()) != 0;1000 const default_is_stmt = (try fr.takeByte()) != 0;
1436 const line_base = try fbr.takeByteSigned();1001 const line_base = try fr.takeByteSigned();
14371002
1438 const line_range = try fbr.takeByte();1003 const line_range = try fr.takeByte();
1439 if (line_range == 0) return bad();1004 if (line_range == 0) return bad();
14401005
1441 const opcode_base = try fbr.takeByte();1006 const opcode_base = try fr.takeByte();
14421007
1443 const standard_opcode_lengths = try fbr.take(opcode_base - 1);1008 const standard_opcode_lengths = try fr.take(opcode_base - 1);
14441009
1445 var directories: ArrayList(FileEntry) = .empty;1010 var directories: ArrayList(FileEntry) = .empty;
1446 defer directories.deinit(gpa);1011 defer directories.deinit(gpa);
...@@ -1451,17 +1016,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1451,17 +1016,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1451 try directories.append(gpa, .{ .path = compile_unit_cwd });1016 try directories.append(gpa, .{ .path = compile_unit_cwd });
14521017
1453 while (true) {1018 while (true) {
1454 const dir = try fbr.takeSentinel(0);1019 const dir = try fr.takeSentinel(0);
1455 if (dir.len == 0) break;1020 if (dir.len == 0) break;
1456 try directories.append(gpa, .{ .path = dir });1021 try directories.append(gpa, .{ .path = dir });
1457 }1022 }
14581023
1459 while (true) {1024 while (true) {
1460 const file_name = try fbr.takeSentinel(0);1025 const file_name = try fr.takeSentinel(0);
1461 if (file_name.len == 0) break;1026 if (file_name.len == 0) break;
1462 const dir_index = try fbr.takeLeb128(u32);1027 const dir_index = try fr.takeLeb128(u32);
1463 const mtime = try fbr.takeLeb128(u64);1028 const mtime = try fr.takeLeb128(u64);
1464 const size = try fbr.takeLeb128(u64);1029 const size = try fr.takeLeb128(u64);
1465 try file_entries.append(gpa, .{1030 try file_entries.append(gpa, .{
1466 .path = file_name,1031 .path = file_name,
1467 .dir_index = dir_index,1032 .dir_index = dir_index,
...@@ -1476,21 +1041,21 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1476,21 +1041,21 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1476 };1041 };
1477 {1042 {
1478 var dir_ent_fmt_buf: [10]FileEntFmt = undefined;1043 var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
1479 const directory_entry_format_count = try fbr.takeByte();1044 const directory_entry_format_count = try fr.takeByte();
1480 if (directory_entry_format_count > dir_ent_fmt_buf.len) return bad();1045 if (directory_entry_format_count > dir_ent_fmt_buf.len) return bad();
1481 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {1046 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
1482 ent_fmt.* = .{1047 ent_fmt.* = .{
1483 .content_type_code = try fbr.takeLeb128(u8),1048 .content_type_code = try fr.takeLeb128(u8),
1484 .form_code = try fbr.takeLeb128(u16),1049 .form_code = try fr.takeLeb128(u16),
1485 };1050 };
1486 }1051 }
14871052
1488 const directories_count = try fbr.takeLeb128(usize);1053 const directories_count = try fr.takeLeb128(usize);
14891054
1490 for (try directories.addManyAsSlice(gpa, directories_count)) |*e| {1055 for (try directories.addManyAsSlice(gpa, directories_count)) |*e| {
1491 e.* = .{ .path = &.{} };1056 e.* = .{ .path = &.{} };
1492 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| {1057 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| {
1493 const form_value = try parseFormValue(&fbr, ent_fmt.form_code, unit_header.format, endian, null);1058 const form_value = try parseFormValue(&fr, ent_fmt.form_code, unit_header.format, endian, null);
1494 switch (ent_fmt.content_type_code) {1059 switch (ent_fmt.content_type_code) {
1495 DW.LNCT.path => e.path = try form_value.getString(d.*),1060 DW.LNCT.path => e.path = try form_value.getString(d.*),
1496 DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),1061 DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
...@@ -1507,22 +1072,22 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1507,22 +1072,22 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1507 }1072 }
15081073
1509 var file_ent_fmt_buf: [10]FileEntFmt = undefined;1074 var file_ent_fmt_buf: [10]FileEntFmt = undefined;
1510 const file_name_entry_format_count = try fbr.takeByte();1075 const file_name_entry_format_count = try fr.takeByte();
1511 if (file_name_entry_format_count > file_ent_fmt_buf.len) return bad();1076 if (file_name_entry_format_count > file_ent_fmt_buf.len) return bad();
1512 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {1077 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
1513 ent_fmt.* = .{1078 ent_fmt.* = .{
1514 .content_type_code = try fbr.takeLeb128(u16),1079 .content_type_code = try fr.takeLeb128(u16),
1515 .form_code = try fbr.takeLeb128(u16),1080 .form_code = try fr.takeLeb128(u16),
1516 };1081 };
1517 }1082 }
15181083
1519 const file_names_count = try fbr.takeLeb128(usize);1084 const file_names_count = try fr.takeLeb128(usize);
1520 try file_entries.ensureUnusedCapacity(gpa, file_names_count);1085 try file_entries.ensureUnusedCapacity(gpa, file_names_count);
15211086
1522 for (try file_entries.addManyAsSlice(gpa, file_names_count)) |*e| {1087 for (try file_entries.addManyAsSlice(gpa, file_names_count)) |*e| {
1523 e.* = .{ .path = &.{} };1088 e.* = .{ .path = &.{} };
1524 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| {1089 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| {
1525 const form_value = try parseFormValue(&fbr, ent_fmt.form_code, unit_header.format, endian, null);1090 const form_value = try parseFormValue(&fr, ent_fmt.form_code, unit_header.format, endian, null);
1526 switch (ent_fmt.content_type_code) {1091 switch (ent_fmt.content_type_code) {
1527 DW.LNCT.path => e.path = try form_value.getString(d.*),1092 DW.LNCT.path => e.path = try form_value.getString(d.*),
1528 DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),1093 DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
...@@ -1542,17 +1107,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1542,17 +1107,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1542 var line_table: CompileUnit.SrcLocCache.LineTable = .{};1107 var line_table: CompileUnit.SrcLocCache.LineTable = .{};
1543 errdefer line_table.deinit(gpa);1108 errdefer line_table.deinit(gpa);
15441109
1545 fbr.seek = @intCast(prog_start_offset);1110 fr.seek = @intCast(prog_start_offset);
15461111
1547 const next_unit_pos = line_info_offset + next_offset;1112 const next_unit_pos = line_info_offset + next_offset;
15481113
1549 while (fbr.seek < next_unit_pos) {1114 while (fr.seek < next_unit_pos) {
1550 const opcode = try fbr.takeByte();1115 const opcode = try fr.takeByte();
15511116
1552 if (opcode == DW.LNS.extended_op) {1117 if (opcode == DW.LNS.extended_op) {
1553 const op_size = try fbr.takeLeb128(u64);1118 const op_size = try fr.takeLeb128(u64);
1554 if (op_size < 1) return bad();1119 if (op_size < 1) return bad();
1555 const sub_op = try fbr.takeByte();1120 const sub_op = try fr.takeByte();
1556 switch (sub_op) {1121 switch (sub_op) {
1557 DW.LNE.end_sequence => {1122 DW.LNE.end_sequence => {
1558 // The row being added here is an "end" address, meaning1123 // The row being added here is an "end" address, meaning
...@@ -1571,14 +1136,14 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1571,14 +1136,14 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1571 prog.reset();1136 prog.reset();
1572 },1137 },
1573 DW.LNE.set_address => {1138 DW.LNE.set_address => {
1574 const addr = try fbr.takeInt(usize, endian);1139 const addr = try fr.takeInt(usize, endian);
1575 prog.address = addr;1140 prog.address = addr;
1576 },1141 },
1577 DW.LNE.define_file => {1142 DW.LNE.define_file => {
1578 const path = try fbr.takeSentinel(0);1143 const path = try fr.takeSentinel(0);
1579 const dir_index = try fbr.takeLeb128(u32);1144 const dir_index = try fr.takeLeb128(u32);
1580 const mtime = try fbr.takeLeb128(u64);1145 const mtime = try fr.takeLeb128(u64);
1581 const size = try fbr.takeLeb128(u64);1146 const size = try fr.takeLeb128(u64);
1582 try file_entries.append(gpa, .{1147 try file_entries.append(gpa, .{
1583 .path = path,1148 .path = path,
1584 .dir_index = dir_index,1149 .dir_index = dir_index,
...@@ -1586,7 +1151,7 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1586,7 +1151,7 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1586 .size = size,1151 .size = size,
1587 });1152 });
1588 },1153 },
1589 else => try fbr.discardAll64(op_size - 1),1154 else => try fr.discardAll64(op_size - 1),
1590 }1155 }
1591 } else if (opcode >= opcode_base) {1156 } else if (opcode >= opcode_base) {
1592 // special opcodes1157 // special opcodes
...@@ -1604,19 +1169,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1604,19 +1169,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1604 prog.basic_block = false;1169 prog.basic_block = false;
1605 },1170 },
1606 DW.LNS.advance_pc => {1171 DW.LNS.advance_pc => {
1607 const arg = try fbr.takeLeb128(usize);1172 const arg = try fr.takeLeb128(usize);
1608 prog.address += arg * minimum_instruction_length;1173 prog.address += arg * minimum_instruction_length;
1609 },1174 },
1610 DW.LNS.advance_line => {1175 DW.LNS.advance_line => {
1611 const arg = try fbr.takeLeb128(i64);1176 const arg = try fr.takeLeb128(i64);
1612 prog.line += arg;1177 prog.line += arg;
1613 },1178 },
1614 DW.LNS.set_file => {1179 DW.LNS.set_file => {
1615 const arg = try fbr.takeLeb128(usize);1180 const arg = try fr.takeLeb128(usize);
1616 prog.file = arg;1181 prog.file = arg;
1617 },1182 },
1618 DW.LNS.set_column => {1183 DW.LNS.set_column => {
1619 const arg = try fbr.takeLeb128(u64);1184 const arg = try fr.takeLeb128(u64);
1620 prog.column = arg;1185 prog.column = arg;
1621 },1186 },
1622 DW.LNS.negate_stmt => {1187 DW.LNS.negate_stmt => {
...@@ -1630,13 +1195,13 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1630,13 +1195,13 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1630 prog.address += inc_addr;1195 prog.address += inc_addr;
1631 },1196 },
1632 DW.LNS.fixed_advance_pc => {1197 DW.LNS.fixed_advance_pc => {
1633 const arg = try fbr.takeInt(u16, endian);1198 const arg = try fr.takeInt(u16, endian);
1634 prog.address += arg;1199 prog.address += arg;
1635 },1200 },
1636 DW.LNS.set_prologue_end => {},1201 DW.LNS.set_prologue_end => {},
1637 else => {1202 else => {
1638 if (opcode - 1 >= standard_opcode_lengths.len) return bad();1203 if (opcode - 1 >= standard_opcode_lengths.len) return bad();
1639 try fbr.discardAll(standard_opcode_lengths[opcode - 1]);1204 try fr.discardAll(standard_opcode_lengths[opcode - 1]);
1640 },1205 },
1641 }1206 }
1642 }1207 }
...@@ -1661,18 +1226,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !...@@ -1661,18 +1226,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1661 };1226 };
1662}1227}
16631228
1664pub fn populateSrcLocCache(d: *Dwarf, gpa: Allocator, cu: *CompileUnit) ScanError!void {1229pub fn populateSrcLocCache(d: *Dwarf, gpa: Allocator, endian: Endian, cu: *CompileUnit) ScanError!void {
1665 if (cu.src_loc_cache != null) return;1230 if (cu.src_loc_cache != null) return;
1666 cu.src_loc_cache = try runLineNumberProgram(d, gpa, cu);1231 cu.src_loc_cache = try d.runLineNumberProgram(gpa, endian, cu);
1667}1232}
16681233
1669pub fn getLineNumberInfo(1234pub fn getLineNumberInfo(
1670 d: *Dwarf,1235 d: *Dwarf,
1671 gpa: Allocator,1236 gpa: Allocator,
1237 endian: Endian,
1672 compile_unit: *CompileUnit,1238 compile_unit: *CompileUnit,
1673 target_address: u64,1239 target_address: u64,
1674) !std.debug.SourceLocation {1240) !std.debug.SourceLocation {
1675 try populateSrcLocCache(d, gpa, compile_unit);1241 try d.populateSrcLocCache(gpa, endian, compile_unit);
1676 const slc = &compile_unit.src_loc_cache.?;1242 const slc = &compile_unit.src_loc_cache.?;
1677 const entry = try slc.findSource(target_address);1243 const entry = try slc.findSource(target_address);
1678 const file_index = entry.file - @intFromBool(slc.version < 5);1244 const file_index = entry.file - @intFromBool(slc.version < 5);
...@@ -1696,7 +1262,7 @@ fn getLineString(di: Dwarf, offset: u64) ![:0]const u8 {...@@ -1696,7 +1262,7 @@ fn getLineString(di: Dwarf, offset: u64) ![:0]const u8 {
1696 return getStringGeneric(di.section(.debug_line_str), offset);1262 return getStringGeneric(di.section(.debug_line_str), offset);
1697}1263}
16981264
1699fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {1265fn readDebugAddr(di: Dwarf, endian: Endian, compile_unit: *const CompileUnit, index: u64) !u64 {
1700 const debug_addr = di.section(.debug_addr) orelse return bad();1266 const debug_addr = di.section(.debug_addr) orelse return bad();
17011267
1702 // addr_base points to the first item after the header, however we1268 // addr_base points to the first item after the header, however we
...@@ -1705,7 +1271,7 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {...@@ -1705,7 +1271,7 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
1705 // The header is 8 or 12 bytes depending on is_64.1271 // The header is 8 or 12 bytes depending on is_64.
1706 if (compile_unit.addr_base < 8) return bad();1272 if (compile_unit.addr_base < 8) return bad();
17071273
1708 const version = mem.readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian);1274 const version = mem.readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], endian);
1709 if (version != 5) return bad();1275 if (version != 5) return bad();
17101276
1711 const addr_size = debug_addr[compile_unit.addr_base - 2];1277 const addr_size = debug_addr[compile_unit.addr_base - 2];
...@@ -1715,113 +1281,13 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {...@@ -1715,113 +1281,13 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
1715 if (byte_offset + addr_size > debug_addr.len) return bad();1281 if (byte_offset + addr_size > debug_addr.len) return bad();
1716 return switch (addr_size) {1282 return switch (addr_size) {
1717 1 => debug_addr[byte_offset],1283 1 => debug_addr[byte_offset],
1718 2 => mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian),1284 2 => mem.readInt(u16, debug_addr[byte_offset..][0..2], endian),
1719 4 => mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian),1285 4 => mem.readInt(u32, debug_addr[byte_offset..][0..4], endian),
1720 8 => mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian),1286 8 => mem.readInt(u64, debug_addr[byte_offset..][0..8], endian),
1721 else => bad(),1287 else => bad(),
1722 };1288 };
1723}1289}
17241290
1725/// If `.eh_frame_hdr` is present, then only the header needs to be parsed. Otherwise, `.eh_frame`
1726/// and `.debug_frame` are scanned and a sorted list of FDEs is built for binary searching during
1727/// unwinding. Even if `.eh_frame_hdr` is used, we may find during unwinding that it's incomplete,
1728/// in which case we build the sorted list of FDEs at that point.
1729///
1730/// See also `scanCieFdeInfo`.
1731pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
1732 const endian = di.endian;
1733
1734 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
1735 var fbr: Reader = .fixed(eh_frame_hdr);
1736
1737 const version = try fbr.takeByte();
1738 if (version != 1) break :blk;
1739
1740 const eh_frame_ptr_enc = try fbr.takeByte();
1741 if (eh_frame_ptr_enc == EH.PE.omit) break :blk;
1742 const fde_count_enc = try fbr.takeByte();
1743 if (fde_count_enc == EH.PE.omit) break :blk;
1744 const table_enc = try fbr.takeByte();
1745 if (table_enc == EH.PE.omit) break :blk;
1746
1747 const eh_frame_ptr = cast(usize, try readEhPointer(&fbr, eh_frame_ptr_enc, @sizeOf(usize), .{
1748 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.seek]),
1749 .follow_indirect = true,
1750 }, endian) orelse return bad()) orelse return bad();
1751
1752 const fde_count = cast(usize, try readEhPointer(&fbr, fde_count_enc, @sizeOf(usize), .{
1753 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.seek]),
1754 .follow_indirect = true,
1755 }, endian) orelse return bad()) orelse return bad();
1756
1757 const entry_size = try ExceptionFrameHeader.entrySize(table_enc);
1758 const entries_len = fde_count * entry_size;
1759 if (entries_len > eh_frame_hdr.len - fbr.seek) return bad();
1760
1761 di.eh_frame_hdr = .{
1762 .eh_frame_ptr = eh_frame_ptr,
1763 .table_enc = table_enc,
1764 .fde_count = fde_count,
1765 .entries = eh_frame_hdr[fbr.seek..][0..entries_len],
1766 };
1767
1768 // No need to scan .eh_frame, we have a binary search table already
1769 return;
1770 }
1771
1772 try di.scanCieFdeInfo(allocator, base_address);
1773}
1774
1775/// Scan `.eh_frame` and `.debug_frame` and build a sorted list of FDEs for binary searching during
1776/// unwinding.
1777pub fn scanCieFdeInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
1778 const endian = di.endian;
1779 const frame_sections = [2]Section.Id{ .eh_frame, .debug_frame };
1780 for (frame_sections) |frame_section| {
1781 if (di.section(frame_section)) |section_data| {
1782 var fbr: Reader = .fixed(section_data);
1783 while (fbr.seek < fbr.buffer.len) {
1784 const entry_header = try EntryHeader.read(&fbr, frame_section, endian);
1785 switch (entry_header.type) {
1786 .cie => {
1787 const cie = try CommonInformationEntry.parse(
1788 entry_header.entry_bytes,
1789 di.sectionVirtualOffset(frame_section, base_address).?,
1790 true,
1791 entry_header.format,
1792 frame_section,
1793 entry_header.length_offset,
1794 @sizeOf(usize),
1795 di.endian,
1796 );
1797 try di.cie_map.put(allocator, entry_header.length_offset, cie);
1798 },
1799 .fde => |cie_offset| {
1800 const cie = di.cie_map.get(cie_offset) orelse return bad();
1801 const fde = try FrameDescriptionEntry.parse(
1802 entry_header.entry_bytes,
1803 di.sectionVirtualOffset(frame_section, base_address).?,
1804 true,
1805 cie,
1806 @sizeOf(usize),
1807 di.endian,
1808 );
1809 try di.fde_list.append(allocator, fde);
1810 },
1811 .terminator => break,
1812 }
1813 }
1814
1815 std.mem.sortUnstable(FrameDescriptionEntry, di.fde_list.items, {}, struct {
1816 fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool {
1817 _ = ctx;
1818 return a.pc_begin < b.pc_begin;
1819 }
1820 }.lessThan);
1821 }
1822 }
1823}
1824
1825fn parseFormValue(1291fn parseFormValue(
1826 r: *Reader,1292 r: *Reader,
1827 form_id: u64,1293 form_id: u64,
...@@ -1946,7 +1412,7 @@ const UnitHeader = struct {...@@ -1946,7 +1412,7 @@ const UnitHeader = struct {
1946 unit_length: u64,1412 unit_length: u64,
1947};1413};
19481414
1949fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader {1415pub fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader {
1950 return switch (try r.takeInt(u32, endian)) {1416 return switch (try r.takeInt(u32, endian)) {
1951 0...0xfffffff0 - 1 => |unit_length| .{1417 0...0xfffffff0 - 1 => |unit_length| .{
1952 .format = .@"32",1418 .format = .@"32",
...@@ -1986,7 +1452,7 @@ fn invalidDebugInfoDetected() void {...@@ -1986,7 +1452,7 @@ fn invalidDebugInfoDetected() void {
1986 if (debug_debug_mode) @panic("bad dwarf");1452 if (debug_debug_mode) @panic("bad dwarf");
1987}1453}
19881454
1989fn missing() error{MissingDebugInfo} {1455pub fn missing() error{MissingDebugInfo} {
1990 if (debug_debug_mode) @panic("missing dwarf");1456 if (debug_debug_mode) @panic("missing dwarf");
1991 return error.MissingDebugInfo;1457 return error.MissingDebugInfo;
1992}1458}
...@@ -2000,94 +1466,39 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {...@@ -2000,94 +1466,39 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
2000 return str[casted_offset..last :0];1466 return str[casted_offset..last :0];
2001}1467}
20021468
2003const EhPointerContext = struct {1469pub const ElfModule = struct {
2004 // The address of the pointer field itself1470 unwind: Dwarf.Unwind,
2005 pc_rel_base: u64,1471 dwarf: Dwarf,
20061472 mapped_memory: ?[]align(std.heap.page_size_min) const u8,
2007 // Whether or not to follow indirect pointers. This should only be1473 external_mapped_memory: ?[]align(std.heap.page_size_min) const u8,
2008 // used when decoding pointers at runtime using the current process's
2009 // debug info
2010 follow_indirect: bool,
2011
2012 // These relative addressing modes are only used in specific cases, and
2013 // might not be available / required in all parsing contexts
2014 data_rel_base: ?u64 = null,
2015 text_rel_base: ?u64 = null,
2016 function_rel_base: ?u64 = null,
2017};
2018
2019fn readEhPointer(fbr: *Reader, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext, endian: Endian) !?u64 {
2020 if (enc == EH.PE.omit) return null;
2021
2022 const value: union(enum) {
2023 signed: i64,
2024 unsigned: u64,
2025 } = switch (enc & EH.PE.type_mask) {
2026 EH.PE.absptr => .{
2027 .unsigned = switch (addr_size_bytes) {
2028 2 => try fbr.takeInt(u16, endian),
2029 4 => try fbr.takeInt(u32, endian),
2030 8 => try fbr.takeInt(u64, endian),
2031 else => return error.InvalidAddrSize,
2032 },
2033 },
2034 EH.PE.uleb128 => .{ .unsigned = try fbr.takeLeb128(u64) },
2035 EH.PE.udata2 => .{ .unsigned = try fbr.takeInt(u16, endian) },
2036 EH.PE.udata4 => .{ .unsigned = try fbr.takeInt(u32, endian) },
2037 EH.PE.udata8 => .{ .unsigned = try fbr.takeInt(u64, endian) },
2038 EH.PE.sleb128 => .{ .signed = try fbr.takeLeb128(i64) },
2039 EH.PE.sdata2 => .{ .signed = try fbr.takeInt(i16, endian) },
2040 EH.PE.sdata4 => .{ .signed = try fbr.takeInt(i32, endian) },
2041 EH.PE.sdata8 => .{ .signed = try fbr.takeInt(i64, endian) },
2042 else => return bad(),
2043 };
2044
2045 const base = switch (enc & EH.PE.rel_mask) {
2046 EH.PE.pcrel => ctx.pc_rel_base,
2047 EH.PE.textrel => ctx.text_rel_base orelse return error.PointerBaseNotSpecified,
2048 EH.PE.datarel => ctx.data_rel_base orelse return error.PointerBaseNotSpecified,
2049 EH.PE.funcrel => ctx.function_rel_base orelse return error.PointerBaseNotSpecified,
2050 else => null,
2051 };
20521474
2053 const ptr: u64 = if (base) |b| switch (value) {1475 pub const Lookup = struct {
2054 .signed => |s| @intCast(try std.math.add(i64, s, @as(i64, @intCast(b)))),1476 base_address: usize,
2055 // absptr can actually contain signed values in some cases (aarch64 MachO)1477 name: []const u8,
2056 .unsigned => |u| u +% b,1478 build_id: ?[]const u8,
2057 } else switch (value) {1479 gnu_eh_frame: ?[]const u8,
2058 .signed => |s| @as(u64, @intCast(s)),
2059 .unsigned => |u| u,
2060 };1480 };
20611481
2062 if ((enc & EH.PE.indirect) > 0 and ctx.follow_indirect) {1482 pub fn init(lookup: *const Lookup) ElfModule {
2063 if (@sizeOf(usize) != addr_size_bytes) {1483 var em: ElfModule = .{
2064 // See the documentation for `follow_indirect`1484 .unwind = .{
2065 return error.NonNativeIndirection;1485 .sections = @splat(null),
2066 }1486 },
20671487 .dwarf = .{},
2068 const native_ptr = cast(usize, ptr) orelse return error.PointerOverflow;1488 .mapped_memory = null,
2069 return switch (addr_size_bytes) {1489 .external_mapped_memory = null,
2070 2, 4, 8 => return @as(*const usize, @ptrFromInt(native_ptr)).*,
2071 else => return error.UnsupportedAddrSize,
2072 };1490 };
2073 } else {1491 if (lookup.gnu_eh_frame) |eh_frame_hdr| {
2074 return ptr;1492 // This is a special case - pointer offsets inside .eh_frame_hdr
2075 }1493 // are encoded relative to its base address, so we must use the
2076}1494 // version that is already memory mapped, and not the one that
20771495 // will be mapped separately from the ELF file.
2078fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {1496 em.unwind.sections[@intFromEnum(Dwarf.Unwind.Section.Id.eh_frame_hdr)] = .{
2079 if (pc_rel_offset < 0) {1497 .data = eh_frame_hdr,
2080 return std.math.sub(usize, field_ptr, @as(usize, @intCast(-pc_rel_offset)));1498 };
2081 } else {1499 }
2082 return std.math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));1500 return em;
2083 }1501 }
2084}
2085
2086pub const ElfModule = struct {
2087 base_address: usize,
2088 dwarf: Dwarf,
2089 mapped_memory: []align(std.heap.page_size_min) const u8,
2090 external_mapped_memory: ?[]align(std.heap.page_size_min) const u8,
20911502
2092 pub fn deinit(self: *@This(), allocator: Allocator) void {1503 pub fn deinit(self: *@This(), allocator: Allocator) void {
2093 self.dwarf.deinit(allocator);1504 self.dwarf.deinit(allocator);
...@@ -2095,16 +1506,16 @@ pub const ElfModule = struct {...@@ -2095,16 +1506,16 @@ pub const ElfModule = struct {
2095 if (self.external_mapped_memory) |m| std.posix.munmap(m);1506 if (self.external_mapped_memory) |m| std.posix.munmap(m);
2096 }1507 }
20971508
2098 pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, address: usize) !std.debug.Symbol {1509 pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, endian: Endian, base_address: usize, address: usize) !std.debug.Symbol {
2099 // Translate the VA into an address into this object1510 // Translate the VA into an address into this object
2100 const relocated_address = address - self.base_address;1511 const relocated_address = address - base_address;
2101 return self.dwarf.getSymbol(allocator, relocated_address);1512 return self.dwarf.getSymbol(allocator, endian, relocated_address);
2102 }1513 }
21031514
2104 pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {1515 pub fn getDwarfUnwindForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf.Unwind {
2105 _ = allocator;1516 _ = allocator;
2106 _ = address;1517 _ = address;
2107 return &self.dwarf;1518 return &self.unwind;
2108 }1519 }
21091520
2110 pub const LoadError = error{1521 pub const LoadError = error{
...@@ -2132,6 +1543,7 @@ pub const ElfModule = struct {...@@ -2132,6 +1543,7 @@ pub const ElfModule = struct {
2132 /// info is, then this this function will recurse to attempt to load the debug1543 /// info is, then this this function will recurse to attempt to load the debug
2133 /// sections from an external file.1544 /// sections from an external file.
2134 pub fn load(1545 pub fn load(
1546 em: *ElfModule,
2135 gpa: Allocator,1547 gpa: Allocator,
2136 mapped_mem: []align(std.heap.page_size_min) const u8,1548 mapped_mem: []align(std.heap.page_size_min) const u8,
2137 build_id: ?[]const u8,1549 build_id: ?[]const u8,
...@@ -2139,7 +1551,7 @@ pub const ElfModule = struct {...@@ -2139,7 +1551,7 @@ pub const ElfModule = struct {
2139 parent_sections: *Dwarf.SectionArray,1551 parent_sections: *Dwarf.SectionArray,
2140 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,1552 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
2141 elf_filename: ?[]const u8,1553 elf_filename: ?[]const u8,
2142 ) LoadError!Dwarf.ElfModule {1554 ) LoadError!void {
2143 if (expected_crc) |crc| if (crc != std.hash.crc.Crc32.hash(mapped_mem)) return error.InvalidDebugInfo;1555 if (expected_crc) |crc| if (crc != std.hash.crc.Crc32.hash(mapped_mem)) return error.InvalidDebugInfo;
21441556
2145 const hdr: *const elf.Ehdr = @ptrCast(&mapped_mem[0]);1557 const hdr: *const elf.Ehdr = @ptrCast(&mapped_mem[0]);
...@@ -2162,7 +1574,7 @@ pub const ElfModule = struct {...@@ -2162,7 +1574,7 @@ pub const ElfModule = struct {
2162 @ptrCast(@alignCast(&mapped_mem[shoff])),1574 @ptrCast(@alignCast(&mapped_mem[shoff])),
2163 )[0..hdr.e_shnum];1575 )[0..hdr.e_shnum];
21641576
2165 var sections: Dwarf.SectionArray = Dwarf.null_section_array;1577 var sections: Dwarf.SectionArray = @splat(null);
21661578
2167 // Combine section list. This takes ownership over any owned sections from the parent scope.1579 // Combine section list. This takes ownership over any owned sections from the parent scope.
2168 for (parent_sections, &sections) |*parent, *section_elem| {1580 for (parent_sections, &sections) |*parent, *section_elem| {
...@@ -2276,7 +1688,7 @@ pub const ElfModule = struct {...@@ -2276,7 +1688,7 @@ pub const ElfModule = struct {
2276 .sub_path = filename,1688 .sub_path = filename,
2277 };1689 };
22781690
2279 return loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem) catch break :blk;1691 return em.loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem) catch break :blk;
2280 }1692 }
22811693
2282 const global_debug_directories = [_][]const u8{1694 const global_debug_directories = [_][]const u8{
...@@ -2304,7 +1716,7 @@ pub const ElfModule = struct {...@@ -2304,7 +1716,7 @@ pub const ElfModule = struct {
2304 };1716 };
2305 defer gpa.free(path.sub_path);1717 defer gpa.free(path.sub_path);
23061718
2307 return loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem) catch continue;1719 return em.loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem) catch continue;
2308 }1720 }
2309 }1721 }
23101722
...@@ -2320,7 +1732,7 @@ pub const ElfModule = struct {...@@ -2320,7 +1732,7 @@ pub const ElfModule = struct {
2320 defer exe_dir.close();1732 defer exe_dir.close();
23211733
2322 // <exe_dir>/<gnu_debuglink>1734 // <exe_dir>/<gnu_debuglink>
2323 if (loadPath(1735 if (em.loadPath(
2324 gpa,1736 gpa,
2325 .{1737 .{
2326 .root_dir = .{ .path = null, .handle = exe_dir },1738 .root_dir = .{ .path = null, .handle = exe_dir },
...@@ -2341,7 +1753,7 @@ pub const ElfModule = struct {...@@ -2341,7 +1753,7 @@ pub const ElfModule = struct {
2341 };1753 };
2342 defer gpa.free(path.sub_path);1754 defer gpa.free(path.sub_path);
23431755
2344 if (loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem)) |debug_info| return debug_info else |_| {}1756 if (em.loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem)) |debug_info| return debug_info else |_| {}
2345 }1757 }
23461758
2347 var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;1759 var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
...@@ -2354,37 +1766,27 @@ pub const ElfModule = struct {...@@ -2354,37 +1766,27 @@ pub const ElfModule = struct {
2354 .sub_path = try std.fs.path.join(gpa, &.{ global_directory, cwd_path, separate_filename }),1766 .sub_path = try std.fs.path.join(gpa, &.{ global_directory, cwd_path, separate_filename }),
2355 };1767 };
2356 defer gpa.free(path.sub_path);1768 defer gpa.free(path.sub_path);
2357 if (loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem)) |debug_info| return debug_info else |_| {}1769 if (em.loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem)) |debug_info| return debug_info else |_| {}
2358 }1770 }
2359 }1771 }
23601772
2361 return error.MissingDebugInfo;1773 return error.MissingDebugInfo;
2362 }1774 }
23631775
2364 var di: Dwarf = .{1776 em.mapped_memory = parent_mapped_mem orelse mapped_mem;
2365 .endian = endian,1777 em.external_mapped_memory = if (parent_mapped_mem != null) mapped_mem else null;
2366 .sections = sections,1778 try em.dwarf.open(gpa, endian);
2367 .is_macho = false,
2368 };
2369
2370 try Dwarf.open(&di, gpa);
2371
2372 return .{
2373 .base_address = 0,
2374 .dwarf = di,
2375 .mapped_memory = parent_mapped_mem orelse mapped_mem,
2376 .external_mapped_memory = if (parent_mapped_mem != null) mapped_mem else null,
2377 };
2378 }1779 }
23791780
2380 pub fn loadPath(1781 pub fn loadPath(
1782 em: *ElfModule,
2381 gpa: Allocator,1783 gpa: Allocator,
2382 elf_file_path: Path,1784 elf_file_path: Path,
2383 build_id: ?[]const u8,1785 build_id: ?[]const u8,
2384 expected_crc: ?u32,1786 expected_crc: ?u32,
2385 parent_sections: *Dwarf.SectionArray,1787 parent_sections: *Dwarf.SectionArray,
2386 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,1788 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
2387 ) LoadError!Dwarf.ElfModule {1789 ) LoadError!void {
2388 const elf_file = elf_file_path.root_dir.handle.openFile(elf_file_path.sub_path, .{}) catch |err| switch (err) {1790 const elf_file = elf_file_path.root_dir.handle.openFile(elf_file_path.sub_path, .{}) catch |err| switch (err) {
2389 error.FileNotFound => return missing(),1791 error.FileNotFound => return missing(),
2390 else => return err,1792 else => return err,
...@@ -2407,7 +1809,7 @@ pub const ElfModule = struct {...@@ -2407,7 +1809,7 @@ pub const ElfModule = struct {
2407 };1809 };
2408 errdefer std.posix.munmap(mapped_mem);1810 errdefer std.posix.munmap(mapped_mem);
24091811
2410 return load(1812 return em.load(
2411 gpa,1813 gpa,
2412 mapped_mem,1814 mapped_mem,
2413 build_id,1815 build_id,
...@@ -2419,22 +1821,21 @@ pub const ElfModule = struct {...@@ -2419,22 +1821,21 @@ pub const ElfModule = struct {
2419 }1821 }
2420};1822};
24211823
2422pub fn getSymbol(di: *Dwarf, allocator: Allocator, address: u64) !std.debug.Symbol {1824pub fn getSymbol(di: *Dwarf, allocator: Allocator, endian: Endian, address: u64) !std.debug.Symbol {
2423 if (di.findCompileUnit(address)) |compile_unit| {1825 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
2424 return .{
2425 .name = di.getSymbolName(address) orelse "???",
2426 .compile_unit_name = compile_unit.die.getAttrString(di, std.dwarf.AT.name, di.section(.debug_str), compile_unit.*) catch |err| switch (err) {
2427 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
2428 },
2429 .source_location = di.getLineNumberInfo(allocator, compile_unit, address) catch |err| switch (err) {
2430 error.MissingDebugInfo, error.InvalidDebugInfo => null,
2431 else => return err,
2432 },
2433 };
2434 } else |err| switch (err) {
2435 error.MissingDebugInfo, error.InvalidDebugInfo => return .{},1826 error.MissingDebugInfo, error.InvalidDebugInfo => return .{},
2436 else => return err,1827 else => return err,
2437 }1828 };
1829 return .{
1830 .name = di.getSymbolName(address) orelse "???",
1831 .compile_unit_name = compile_unit.die.getAttrString(di, endian, std.dwarf.AT.name, di.section(.debug_str), compile_unit) catch |err| switch (err) {
1832 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1833 },
1834 .source_location = di.getLineNumberInfo(allocator, endian, compile_unit, address) catch |err| switch (err) {
1835 error.MissingDebugInfo, error.InvalidDebugInfo => null,
1836 else => return err,
1837 },
1838 };
2438}1839}
24391840
2440pub fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]const u8 {1841pub fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]const u8 {
...@@ -2443,7 +1844,7 @@ pub fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]cons...@@ -2443,7 +1844,7 @@ pub fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]cons
2443 return ptr[start..end];1844 return ptr[start..end];
2444}1845}
24451846
2446fn readAddress(r: *Reader, format: std.dwarf.Format, endian: Endian) !u64 {1847pub fn readAddress(r: *Reader, format: std.dwarf.Format, endian: Endian) !u64 {
2447 return switch (format) {1848 return switch (format) {
2448 .@"32" => try r.takeInt(u32, endian),1849 .@"32" => try r.takeInt(u32, endian),
2449 .@"64" => try r.takeInt(u64, endian),1850 .@"64" => try r.takeInt(u64, endian),
lib/std/debug/Dwarf/Unwind.zig created+645
...@@ -0,0 +1,645 @@
1sections: SectionArray = @splat(null),
2
3/// Starts out non-`null` if the `.eh_frame_hdr` section is present. May become `null` later if we
4/// find that `.eh_frame_hdr` is incomplete.
5eh_frame_hdr: ?ExceptionFrameHeader = null,
6/// These lookup tables are only used if `eh_frame_hdr` is null
7cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .empty,
8/// Sorted by start_pc
9fde_list: std.ArrayList(FrameDescriptionEntry) = .empty,
10
11pub const Section = struct {
12 data: []const u8,
13
14 pub const Id = enum {
15 debug_frame,
16 eh_frame,
17 eh_frame_hdr,
18 };
19};
20
21const num_sections = std.enums.directEnumArrayLen(Section.Id, 0);
22pub const SectionArray = [num_sections]?Section;
23
24pub fn section(unwind: Unwind, dwarf_section: Section.Id) ?[]const u8 {
25 return if (unwind.sections[@intFromEnum(dwarf_section)]) |s| s.data else null;
26}
27
28/// This represents the decoded .eh_frame_hdr header
29pub const ExceptionFrameHeader = struct {
30 eh_frame_ptr: usize,
31 table_enc: u8,
32 fde_count: usize,
33 entries: []const u8,
34
35 pub fn entrySize(table_enc: u8) !u8 {
36 return switch (table_enc & EH.PE.type_mask) {
37 EH.PE.udata2,
38 EH.PE.sdata2,
39 => 4,
40 EH.PE.udata4,
41 EH.PE.sdata4,
42 => 8,
43 EH.PE.udata8,
44 EH.PE.sdata8,
45 => 16,
46 // This is a binary search table, so all entries must be the same length
47 else => return bad(),
48 };
49 }
50
51 pub fn findEntry(
52 self: ExceptionFrameHeader,
53 eh_frame_len: usize,
54 eh_frame_hdr_ptr: usize,
55 pc: usize,
56 cie: *CommonInformationEntry,
57 fde: *FrameDescriptionEntry,
58 endian: Endian,
59 ) !void {
60 const entry_size = try entrySize(self.table_enc);
61
62 var left: usize = 0;
63 var len: usize = self.fde_count;
64 var fbr: Reader = .fixed(self.entries);
65
66 while (len > 1) {
67 const mid = left + len / 2;
68
69 fbr.seek = mid * entry_size;
70 const pc_begin = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
71 .pc_rel_base = @intFromPtr(&self.entries[fbr.seek]),
72 .follow_indirect = true,
73 .data_rel_base = eh_frame_hdr_ptr,
74 }, endian) orelse return bad();
75
76 if (pc < pc_begin) {
77 len /= 2;
78 } else {
79 left = mid;
80 if (pc == pc_begin) break;
81 len -= len / 2;
82 }
83 }
84
85 if (len == 0) return missing();
86 fbr.seek = left * entry_size;
87
88 // Read past the pc_begin field of the entry
89 _ = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
90 .pc_rel_base = @intFromPtr(&self.entries[fbr.seek]),
91 .follow_indirect = true,
92 .data_rel_base = eh_frame_hdr_ptr,
93 }, endian) orelse return bad();
94
95 const fde_ptr = cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
96 .pc_rel_base = @intFromPtr(&self.entries[fbr.seek]),
97 .follow_indirect = true,
98 .data_rel_base = eh_frame_hdr_ptr,
99 }, endian) orelse return bad()) orelse return bad();
100
101 if (fde_ptr < self.eh_frame_ptr) return bad();
102
103 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0..eh_frame_len];
104
105 const fde_offset = fde_ptr - self.eh_frame_ptr;
106 var eh_frame_fbr: Reader = .fixed(eh_frame);
107 eh_frame_fbr.seek = fde_offset;
108
109 const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame, endian);
110 if (fde_entry_header.type != .fde) return bad();
111
112 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable
113 const cie_offset = fde_entry_header.type.fde;
114 eh_frame_fbr.seek = @intCast(cie_offset);
115 const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame, endian);
116 if (cie_entry_header.type != .cie) return bad();
117
118 cie.* = try CommonInformationEntry.parse(
119 cie_entry_header.entry_bytes,
120 0,
121 true,
122 cie_entry_header.format,
123 .eh_frame,
124 cie_entry_header.length_offset,
125 @sizeOf(usize),
126 endian,
127 );
128
129 fde.* = try FrameDescriptionEntry.parse(
130 fde_entry_header.entry_bytes,
131 0,
132 true,
133 cie.*,
134 @sizeOf(usize),
135 endian,
136 );
137
138 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return missing();
139 }
140};
141
142pub const EntryHeader = struct {
143 /// Offset of the length field in the backing buffer
144 length_offset: usize,
145 format: Format,
146 type: union(enum) {
147 cie,
148 /// Value is the offset of the corresponding CIE
149 fde: u64,
150 terminator,
151 },
152 /// The entry's contents, not including the ID field
153 entry_bytes: []const u8,
154
155 /// The length of the entry including the ID field, but not the length field itself
156 pub fn entryLength(self: EntryHeader) usize {
157 return self.entry_bytes.len + @as(u8, if (self.format == .@"64") 8 else 4);
158 }
159
160 /// Reads a header for either an FDE or a CIE, then advances the fbr to the
161 /// position after the trailing structure.
162 ///
163 /// `fbr` must be backed by either the .eh_frame or .debug_frame sections.
164 ///
165 /// TODO that's a bad API, don't do that. this function should neither require
166 /// a fixed reader nor depend on seeking.
167 pub fn read(fbr: *Reader, dwarf_section: Section.Id, endian: Endian) !EntryHeader {
168 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);
169
170 const length_offset = fbr.seek;
171 const unit_header = try Dwarf.readUnitHeader(fbr, endian);
172 const unit_length = cast(usize, unit_header.unit_length) orelse return bad();
173 if (unit_length == 0) return .{
174 .length_offset = length_offset,
175 .format = unit_header.format,
176 .type = .terminator,
177 .entry_bytes = &.{},
178 };
179 const start_offset = fbr.seek;
180 const end_offset = start_offset + unit_length;
181 defer fbr.seek = end_offset;
182
183 const id = try Dwarf.readAddress(fbr, unit_header.format, endian);
184 const entry_bytes = fbr.buffer[fbr.seek..end_offset];
185 const cie_id: u64 = switch (dwarf_section) {
186 .eh_frame => CommonInformationEntry.eh_id,
187 .debug_frame => switch (unit_header.format) {
188 .@"32" => CommonInformationEntry.dwarf32_id,
189 .@"64" => CommonInformationEntry.dwarf64_id,
190 },
191 else => unreachable,
192 };
193
194 return .{
195 .length_offset = length_offset,
196 .format = unit_header.format,
197 .type = if (id == cie_id) .cie else .{ .fde = switch (dwarf_section) {
198 .eh_frame => try std.math.sub(u64, start_offset, id),
199 .debug_frame => id,
200 else => unreachable,
201 } },
202 .entry_bytes = entry_bytes,
203 };
204 }
205};
206
207pub const CommonInformationEntry = struct {
208 // Used in .eh_frame
209 pub const eh_id = 0;
210
211 // Used in .debug_frame (DWARF32)
212 pub const dwarf32_id = maxInt(u32);
213
214 // Used in .debug_frame (DWARF64)
215 pub const dwarf64_id = maxInt(u64);
216
217 // Offset of the length field of this entry in the eh_frame section.
218 // This is the key that FDEs use to reference CIEs.
219 length_offset: u64,
220 version: u8,
221 address_size: u8,
222 format: Format,
223
224 // Only present in version 4
225 segment_selector_size: ?u8,
226
227 code_alignment_factor: u32,
228 data_alignment_factor: i32,
229 return_address_register: u8,
230
231 aug_str: []const u8,
232 aug_data: []const u8,
233 lsda_pointer_enc: u8,
234 personality_enc: ?u8,
235 personality_routine_pointer: ?u64,
236 fde_pointer_enc: u8,
237 initial_instructions: []const u8,
238
239 pub fn isSignalFrame(self: CommonInformationEntry) bool {
240 for (self.aug_str) |c| if (c == 'S') return true;
241 return false;
242 }
243
244 pub fn addressesSignedWithBKey(self: CommonInformationEntry) bool {
245 for (self.aug_str) |c| if (c == 'B') return true;
246 return false;
247 }
248
249 pub fn mteTaggedFrame(self: CommonInformationEntry) bool {
250 for (self.aug_str) |c| if (c == 'G') return true;
251 return false;
252 }
253
254 /// This function expects to read the CIE starting with the version field.
255 /// The returned struct references memory backed by cie_bytes.
256 ///
257 /// See the FrameDescriptionEntry.parse documentation for the description
258 /// of `pc_rel_offset` and `is_runtime`.
259 ///
260 /// `length_offset` specifies the offset of this CIE's length field in the
261 /// .eh_frame / .debug_frame section.
262 pub fn parse(
263 cie_bytes: []const u8,
264 pc_rel_offset: i64,
265 is_runtime: bool,
266 format: Format,
267 dwarf_section: Section.Id,
268 length_offset: u64,
269 addr_size_bytes: u8,
270 endian: Endian,
271 ) !CommonInformationEntry {
272 if (addr_size_bytes > 8) return error.UnsupportedAddrSize;
273
274 var fbr: Reader = .fixed(cie_bytes);
275
276 const version = try fbr.takeByte();
277 switch (dwarf_section) {
278 .eh_frame => if (version != 1 and version != 3) return error.UnsupportedDwarfVersion,
279 .debug_frame => if (version != 4) return error.UnsupportedDwarfVersion,
280 else => return error.UnsupportedDwarfSection,
281 }
282
283 var has_eh_data = false;
284 var has_aug_data = false;
285
286 var aug_str_len: usize = 0;
287 const aug_str_start = fbr.seek;
288 var aug_byte = try fbr.takeByte();
289 while (aug_byte != 0) : (aug_byte = try fbr.takeByte()) {
290 switch (aug_byte) {
291 'z' => {
292 if (aug_str_len != 0) return bad();
293 has_aug_data = true;
294 },
295 'e' => {
296 if (has_aug_data or aug_str_len != 0) return bad();
297 if (try fbr.takeByte() != 'h') return bad();
298 has_eh_data = true;
299 },
300 else => if (has_eh_data) return bad(),
301 }
302
303 aug_str_len += 1;
304 }
305
306 if (has_eh_data) {
307 // legacy data created by older versions of gcc - unsupported here
308 for (0..addr_size_bytes) |_| _ = try fbr.takeByte();
309 }
310
311 const address_size = if (version == 4) try fbr.takeByte() else addr_size_bytes;
312 const segment_selector_size = if (version == 4) try fbr.takeByte() else null;
313
314 const code_alignment_factor = try fbr.takeLeb128(u32);
315 const data_alignment_factor = try fbr.takeLeb128(i32);
316 const return_address_register = if (version == 1) try fbr.takeByte() else try fbr.takeLeb128(u8);
317
318 var lsda_pointer_enc: u8 = EH.PE.omit;
319 var personality_enc: ?u8 = null;
320 var personality_routine_pointer: ?u64 = null;
321 var fde_pointer_enc: u8 = EH.PE.absptr;
322
323 var aug_data: []const u8 = &[_]u8{};
324 const aug_str = if (has_aug_data) blk: {
325 const aug_data_len = try fbr.takeLeb128(usize);
326 const aug_data_start = fbr.seek;
327 aug_data = cie_bytes[aug_data_start..][0..aug_data_len];
328
329 const aug_str = cie_bytes[aug_str_start..][0..aug_str_len];
330 for (aug_str[1..]) |byte| {
331 switch (byte) {
332 'L' => {
333 lsda_pointer_enc = try fbr.takeByte();
334 },
335 'P' => {
336 personality_enc = try fbr.takeByte();
337 personality_routine_pointer = try readEhPointer(&fbr, personality_enc.?, addr_size_bytes, .{
338 .pc_rel_base = try pcRelBase(@intFromPtr(&cie_bytes[fbr.seek]), pc_rel_offset),
339 .follow_indirect = is_runtime,
340 }, endian);
341 },
342 'R' => {
343 fde_pointer_enc = try fbr.takeByte();
344 },
345 'S', 'B', 'G' => {},
346 else => return bad(),
347 }
348 }
349
350 // aug_data_len can include padding so the CIE ends on an address boundary
351 fbr.seek = aug_data_start + aug_data_len;
352 break :blk aug_str;
353 } else &[_]u8{};
354
355 const initial_instructions = cie_bytes[fbr.seek..];
356 return .{
357 .length_offset = length_offset,
358 .version = version,
359 .address_size = address_size,
360 .format = format,
361 .segment_selector_size = segment_selector_size,
362 .code_alignment_factor = code_alignment_factor,
363 .data_alignment_factor = data_alignment_factor,
364 .return_address_register = return_address_register,
365 .aug_str = aug_str,
366 .aug_data = aug_data,
367 .lsda_pointer_enc = lsda_pointer_enc,
368 .personality_enc = personality_enc,
369 .personality_routine_pointer = personality_routine_pointer,
370 .fde_pointer_enc = fde_pointer_enc,
371 .initial_instructions = initial_instructions,
372 };
373 }
374};
375
376pub const FrameDescriptionEntry = struct {
377 // Offset into eh_frame where the CIE for this FDE is stored
378 cie_length_offset: u64,
379
380 pc_begin: u64,
381 pc_range: u64,
382 lsda_pointer: ?u64,
383 aug_data: []const u8,
384 instructions: []const u8,
385
386 /// This function expects to read the FDE starting at the PC Begin field.
387 /// The returned struct references memory backed by `fde_bytes`.
388 ///
389 /// `pc_rel_offset` specifies an offset to be applied to pc_rel_base values
390 /// used when decoding pointers. This should be set to zero if fde_bytes is
391 /// backed by the memory of a .eh_frame / .debug_frame section in the running executable.
392 /// Otherwise, it should be the relative offset to translate addresses from
393 /// where the section is currently stored in memory, to where it *would* be
394 /// stored at runtime: section base addr - backing data base ptr.
395 ///
396 /// Similarly, `is_runtime` specifies this function is being called on a runtime
397 /// section, and so indirect pointers can be followed.
398 pub fn parse(
399 fde_bytes: []const u8,
400 pc_rel_offset: i64,
401 is_runtime: bool,
402 cie: CommonInformationEntry,
403 addr_size_bytes: u8,
404 endian: Endian,
405 ) !FrameDescriptionEntry {
406 if (addr_size_bytes > 8) return error.InvalidAddrSize;
407
408 var fbr: Reader = .fixed(fde_bytes);
409
410 const pc_begin = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
411 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.seek]), pc_rel_offset),
412 .follow_indirect = is_runtime,
413 }, endian) orelse return bad();
414
415 const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
416 .pc_rel_base = 0,
417 .follow_indirect = false,
418 }, endian) orelse return bad();
419
420 var aug_data: []const u8 = &[_]u8{};
421 const lsda_pointer = if (cie.aug_str.len > 0) blk: {
422 const aug_data_len = try fbr.takeLeb128(usize);
423 const aug_data_start = fbr.seek;
424 aug_data = fde_bytes[aug_data_start..][0..aug_data_len];
425
426 const lsda_pointer = if (cie.lsda_pointer_enc != EH.PE.omit)
427 try readEhPointer(&fbr, cie.lsda_pointer_enc, addr_size_bytes, .{
428 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.seek]), pc_rel_offset),
429 .follow_indirect = is_runtime,
430 }, endian)
431 else
432 null;
433
434 fbr.seek = aug_data_start + aug_data_len;
435 break :blk lsda_pointer;
436 } else null;
437
438 const instructions = fde_bytes[fbr.seek..];
439 return .{
440 .cie_length_offset = cie.length_offset,
441 .pc_begin = pc_begin,
442 .pc_range = pc_range,
443 .lsda_pointer = lsda_pointer,
444 .aug_data = aug_data,
445 .instructions = instructions,
446 };
447 }
448};
449
450/// If `.eh_frame_hdr` is present, then only the header needs to be parsed. Otherwise, `.eh_frame`
451/// and `.debug_frame` are scanned and a sorted list of FDEs is built for binary searching during
452/// unwinding. Even if `.eh_frame_hdr` is used, we may find during unwinding that it's incomplete,
453/// in which case we build the sorted list of FDEs at that point.
454///
455/// See also `scanCieFdeInfo`.
456pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
457 const endian = di.endian;
458
459 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
460 var fbr: Reader = .fixed(eh_frame_hdr);
461
462 const version = try fbr.takeByte();
463 if (version != 1) break :blk;
464
465 const eh_frame_ptr_enc = try fbr.takeByte();
466 if (eh_frame_ptr_enc == EH.PE.omit) break :blk;
467 const fde_count_enc = try fbr.takeByte();
468 if (fde_count_enc == EH.PE.omit) break :blk;
469 const table_enc = try fbr.takeByte();
470 if (table_enc == EH.PE.omit) break :blk;
471
472 const eh_frame_ptr = cast(usize, try readEhPointer(&fbr, eh_frame_ptr_enc, @sizeOf(usize), .{
473 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.seek]),
474 .follow_indirect = true,
475 }, endian) orelse return bad()) orelse return bad();
476
477 const fde_count = cast(usize, try readEhPointer(&fbr, fde_count_enc, @sizeOf(usize), .{
478 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.seek]),
479 .follow_indirect = true,
480 }, endian) orelse return bad()) orelse return bad();
481
482 const entry_size = try ExceptionFrameHeader.entrySize(table_enc);
483 const entries_len = fde_count * entry_size;
484 if (entries_len > eh_frame_hdr.len - fbr.seek) return bad();
485
486 di.eh_frame_hdr = .{
487 .eh_frame_ptr = eh_frame_ptr,
488 .table_enc = table_enc,
489 .fde_count = fde_count,
490 .entries = eh_frame_hdr[fbr.seek..][0..entries_len],
491 };
492
493 // No need to scan .eh_frame, we have a binary search table already
494 return;
495 }
496
497 try di.scanCieFdeInfo(allocator, base_address);
498}
499
500/// Scan `.eh_frame` and `.debug_frame` and build a sorted list of FDEs for binary searching during
501/// unwinding.
502pub fn scanCieFdeInfo(unwind: *Unwind, allocator: Allocator, endian: Endian, base_address: usize) !void {
503 const frame_sections = [2]Section.Id{ .eh_frame, .debug_frame };
504 for (frame_sections) |frame_section| {
505 if (unwind.section(frame_section)) |section_data| {
506 var fbr: Reader = .fixed(section_data);
507 while (fbr.seek < fbr.buffer.len) {
508 const entry_header = try EntryHeader.read(&fbr, frame_section, endian);
509 switch (entry_header.type) {
510 .cie => {
511 const cie = try CommonInformationEntry.parse(
512 entry_header.entry_bytes,
513 unwind.sectionVirtualOffset(frame_section, base_address).?,
514 true,
515 entry_header.format,
516 frame_section,
517 entry_header.length_offset,
518 @sizeOf(usize),
519 endian,
520 );
521 try unwind.cie_map.put(allocator, entry_header.length_offset, cie);
522 },
523 .fde => |cie_offset| {
524 const cie = unwind.cie_map.get(cie_offset) orelse return bad();
525 const fde = try FrameDescriptionEntry.parse(
526 entry_header.entry_bytes,
527 unwind.sectionVirtualOffset(frame_section, base_address).?,
528 true,
529 cie,
530 @sizeOf(usize),
531 endian,
532 );
533 try unwind.fde_list.append(allocator, fde);
534 },
535 .terminator => break,
536 }
537 }
538
539 std.mem.sortUnstable(FrameDescriptionEntry, unwind.fde_list.items, {}, struct {
540 fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool {
541 _ = ctx;
542 return a.pc_begin < b.pc_begin;
543 }
544 }.lessThan);
545 }
546 }
547}
548
549const EhPointerContext = struct {
550 // The address of the pointer field itself
551 pc_rel_base: u64,
552
553 // Whether or not to follow indirect pointers. This should only be
554 // used when decoding pointers at runtime using the current process's
555 // debug info
556 follow_indirect: bool,
557
558 // These relative addressing modes are only used in specific cases, and
559 // might not be available / required in all parsing contexts
560 data_rel_base: ?u64 = null,
561 text_rel_base: ?u64 = null,
562 function_rel_base: ?u64 = null,
563};
564
565fn readEhPointer(fbr: *Reader, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext, endian: Endian) !?u64 {
566 if (enc == EH.PE.omit) return null;
567
568 const value: union(enum) {
569 signed: i64,
570 unsigned: u64,
571 } = switch (enc & EH.PE.type_mask) {
572 EH.PE.absptr => .{
573 .unsigned = switch (addr_size_bytes) {
574 2 => try fbr.takeInt(u16, endian),
575 4 => try fbr.takeInt(u32, endian),
576 8 => try fbr.takeInt(u64, endian),
577 else => return error.InvalidAddrSize,
578 },
579 },
580 EH.PE.uleb128 => .{ .unsigned = try fbr.takeLeb128(u64) },
581 EH.PE.udata2 => .{ .unsigned = try fbr.takeInt(u16, endian) },
582 EH.PE.udata4 => .{ .unsigned = try fbr.takeInt(u32, endian) },
583 EH.PE.udata8 => .{ .unsigned = try fbr.takeInt(u64, endian) },
584 EH.PE.sleb128 => .{ .signed = try fbr.takeLeb128(i64) },
585 EH.PE.sdata2 => .{ .signed = try fbr.takeInt(i16, endian) },
586 EH.PE.sdata4 => .{ .signed = try fbr.takeInt(i32, endian) },
587 EH.PE.sdata8 => .{ .signed = try fbr.takeInt(i64, endian) },
588 else => return bad(),
589 };
590
591 const base = switch (enc & EH.PE.rel_mask) {
592 EH.PE.pcrel => ctx.pc_rel_base,
593 EH.PE.textrel => ctx.text_rel_base orelse return error.PointerBaseNotSpecified,
594 EH.PE.datarel => ctx.data_rel_base orelse return error.PointerBaseNotSpecified,
595 EH.PE.funcrel => ctx.function_rel_base orelse return error.PointerBaseNotSpecified,
596 else => null,
597 };
598
599 const ptr: u64 = if (base) |b| switch (value) {
600 .signed => |s| @intCast(try std.math.add(i64, s, @as(i64, @intCast(b)))),
601 // absptr can actually contain signed values in some cases (aarch64 MachO)
602 .unsigned => |u| u +% b,
603 } else switch (value) {
604 .signed => |s| @as(u64, @intCast(s)),
605 .unsigned => |u| u,
606 };
607
608 if ((enc & EH.PE.indirect) > 0 and ctx.follow_indirect) {
609 if (@sizeOf(usize) != addr_size_bytes) {
610 // See the documentation for `follow_indirect`
611 return error.NonNativeIndirection;
612 }
613
614 const native_ptr = cast(usize, ptr) orelse return error.PointerOverflow;
615 return switch (addr_size_bytes) {
616 2, 4, 8 => return @as(*const usize, @ptrFromInt(native_ptr)).*,
617 else => return error.UnsupportedAddrSize,
618 };
619 } else {
620 return ptr;
621 }
622}
623
624fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
625 if (pc_rel_offset < 0) {
626 return std.math.sub(usize, field_ptr, @as(usize, @intCast(-pc_rel_offset)));
627 } else {
628 return std.math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));
629 }
630}
631
632const Allocator = std.mem.Allocator;
633const assert = std.debug.assert;
634const bad = Dwarf.bad;
635const cast = std.math.cast;
636const DW = std.dwarf;
637const Dwarf = std.debug.Dwarf;
638const EH = DW.EH;
639const Endian = std.builtin.Endian;
640const Format = DW.Format;
641const maxInt = std.math.maxInt;
642const missing = Dwarf.missing;
643const Reader = std.Io.Reader;
644const std = @import("std");
645const Unwind = @This();
lib/std/debug/SelfInfo.zig+277-258
...@@ -31,7 +31,7 @@ const SelfInfo = @This();...@@ -31,7 +31,7 @@ const SelfInfo = @This();
31const root = @import("root");31const root = @import("root");
3232
33allocator: Allocator,33allocator: Allocator,
34address_map: std.AutoHashMap(usize, *Module),34address_map: std.AutoHashMapUnmanaged(usize, Module),
35modules: if (native_os == .windows) std.ArrayListUnmanaged(WindowsModule) else void,35modules: if (native_os == .windows) std.ArrayListUnmanaged(WindowsModule) else void,
3636
37pub const OpenError = error{37pub const OpenError = error{
...@@ -40,29 +40,27 @@ pub const OpenError = error{...@@ -40,29 +40,27 @@ pub const OpenError = error{
40} || @typeInfo(@typeInfo(@TypeOf(SelfInfo.init)).@"fn".return_type.?).error_union.error_set;40} || @typeInfo(@typeInfo(@TypeOf(SelfInfo.init)).@"fn".return_type.?).error_union.error_set;
4141
42pub fn open(allocator: Allocator) OpenError!SelfInfo {42pub fn open(allocator: Allocator) OpenError!SelfInfo {
43 nosuspend {43 if (builtin.strip_debug_info)
44 if (builtin.strip_debug_info)44 return error.MissingDebugInfo;
45 return error.MissingDebugInfo;45 switch (native_os) {
46 switch (native_os) {46 .linux,
47 .linux,47 .freebsd,
48 .freebsd,48 .netbsd,
49 .netbsd,49 .dragonfly,
50 .dragonfly,50 .openbsd,
51 .openbsd,51 .macos,
52 .macos,52 .solaris,
53 .solaris,53 .illumos,
54 .illumos,54 .windows,
55 .windows,55 => return try SelfInfo.init(allocator),
56 => return try SelfInfo.init(allocator),56 else => return error.UnsupportedOperatingSystem,
57 else => return error.UnsupportedOperatingSystem,
58 }
59 }57 }
60}58}
6159
62pub fn init(allocator: Allocator) !SelfInfo {60pub fn init(allocator: Allocator) !SelfInfo {
63 var debug_info: SelfInfo = .{61 var debug_info: SelfInfo = .{
64 .allocator = allocator,62 .allocator = allocator,
65 .address_map = std.AutoHashMap(usize, *Module).init(allocator),63 .address_map = .empty,
66 .modules = if (native_os == .windows) .{} else {},64 .modules = if (native_os == .windows) .{} else {},
67 };65 };
6866
...@@ -110,7 +108,7 @@ pub fn deinit(self: *SelfInfo) void {...@@ -110,7 +108,7 @@ pub fn deinit(self: *SelfInfo) void {
110 mdi.deinit(self.allocator);108 mdi.deinit(self.allocator);
111 self.allocator.destroy(mdi);109 self.allocator.destroy(mdi);
112 }110 }
113 self.address_map.deinit();111 self.address_map.deinit(self.allocator);
114 if (native_os == .windows) {112 if (native_os == .windows) {
115 for (self.modules.items) |module| {113 for (self.modules.items) |module| {
116 self.allocator.free(module.name);114 self.allocator.free(module.name);
...@@ -120,7 +118,7 @@ pub fn deinit(self: *SelfInfo) void {...@@ -120,7 +118,7 @@ pub fn deinit(self: *SelfInfo) void {
120 }118 }
121}119}
122120
123pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module {121fn lookupModuleForAddress(self: *SelfInfo, address: usize) !Module.Lookup {
124 if (builtin.target.os.tag.isDarwin()) {122 if (builtin.target.os.tag.isDarwin()) {
125 return self.lookupModuleDyld(address);123 return self.lookupModuleDyld(address);
126 } else if (native_os == .windows) {124 } else if (native_os == .windows) {
...@@ -134,21 +132,65 @@ pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module {...@@ -134,21 +132,65 @@ pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module {
134 }132 }
135}133}
136134
137// Returns the module name for a given address.135fn loadModuleDebugInfo(self: *SelfInfo, lookup: *const Module.Lookup, module: *Module) !void {
138// This can be called when getModuleForAddress fails, so implementations should provide
139// a path that doesn't rely on any side-effects of a prior successful module lookup.
140pub fn getModuleNameForAddress(self: *SelfInfo, address: usize) ?[]const u8 {
141 if (builtin.target.os.tag.isDarwin()) {136 if (builtin.target.os.tag.isDarwin()) {
142 return self.lookupModuleNameDyld(address);137 @compileError("TODO");
143 } else if (native_os == .windows) {138 } else if (native_os == .windows) {
144 return self.lookupModuleNameWin32(address);139 @compileError("TODO");
145 } else if (native_os == .haiku) {140 } else if (native_os == .haiku) {
146 return null;141 @compileError("TODO");
147 } else if (builtin.target.cpu.arch.isWasm()) {142 } else if (builtin.target.cpu.arch.isWasm()) {
148 return null;143 @compileError("TODO");
149 } else {144 } else {
150 return self.lookupModuleNameDl(address);145 if (module.mapped_memory == null) {
146 var sections: Dwarf.SectionArray = @splat(null);
147 try readElfDebugInfo(module, self.allocator, if (lookup.name.len > 0) lookup.name else null, lookup.build_id, &sections);
148 assert(module.mapped_memory != null);
149 }
150 }
151}
152
153pub fn unwindFrame(self: *SelfInfo, context: *UnwindContext) !usize {
154 const lookup = try self.lookupModuleForAddress(context.pc);
155 const gop = try self.address_map.getOrPut(self.allocator, lookup.base_address);
156 if (!gop.found_existing) gop.value_ptr.* = .init(&lookup);
157 if (native_os.isDarwin()) {
158 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding
159 // via DWARF before attempting to use the compact unwind info will produce incorrect results.
160 if (gop.value_ptr.unwind_info) |unwind_info| {
161 if (unwindFrameMachO(
162 self.allocator,
163 lookup.base_address,
164 context,
165 unwind_info,
166 gop.value_ptr.eh_frame,
167 )) |return_address| {
168 return return_address;
169 } else |err| {
170 if (err != error.RequiresDWARFUnwind) return err;
171 }
172 } else return error.MissingUnwindInfo;
151 }173 }
174 if (try gop.value_ptr.getDwarfUnwindForAddress(self.allocator, context.pc)) |unwind| {
175 return unwindFrameDwarf(self.allocator, unwind, lookup.base_address, context, null);
176 } else return error.MissingDebugInfo;
177}
178
179pub fn getSymbolAtAddress(self: *SelfInfo, address: usize) !std.debug.Symbol {
180 const lookup = try self.lookupModuleForAddress(address);
181 const gop = try self.address_map.getOrPut(self.allocator, lookup.base_address);
182 if (!gop.found_existing) gop.value_ptr.* = .init(&lookup);
183 try self.loadModuleDebugInfo(&lookup, gop.value_ptr);
184 return gop.value_ptr.getSymbolAtAddress(self.allocator, native_endian, lookup.base_address, address);
185}
186
187/// Returns the module name for a given address.
188/// This can be called when getModuleForAddress fails, so implementations should provide
189/// a path that doesn't rely on any side-effects of a prior successful module lookup.
190pub fn getModuleNameForAddress(self: *SelfInfo, address: usize) ?[]const u8 {
191 return if (self.lookupModuleForAddress(address)) |lookup| lookup.name else |err| switch (err) {
192 error.MissingDebugInfo => null,
193 };
152}194}
153195
154fn lookupModuleDyld(self: *SelfInfo, address: usize) !*Module {196fn lookupModuleDyld(self: *SelfInfo, address: usize) !*Module {
...@@ -394,19 +436,24 @@ fn lookupModuleNameDl(self: *SelfInfo, address: usize) ?[]const u8 {...@@ -394,19 +436,24 @@ fn lookupModuleNameDl(self: *SelfInfo, address: usize) ?[]const u8 {
394 return null;436 return null;
395}437}
396438
397fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {439fn lookupModuleDl(self: *SelfInfo, address: usize) !Module.Lookup {
398 var ctx: struct {440 var ctx: struct {
399 // Input441 // Input
400 address: usize,442 address: usize,
401 // Output443 // Output
402 base_address: usize = undefined,444 lookup: Module.Lookup,
403 name: []const u8 = undefined,445 } = .{
404 build_id: ?[]const u8 = null,446 .address = address,
405 gnu_eh_frame: ?[]const u8 = null,447 .lookup = .{
406 } = .{ .address = address };448 .base_address = undefined,
449 .name = undefined,
450 .build_id = null,
451 .gnu_eh_frame = null,
452 },
453 };
407 const CtxTy = @TypeOf(ctx);454 const CtxTy = @TypeOf(ctx);
408455
409 if (posix.dl_iterate_phdr(&ctx, error{Found}, struct {456 posix.dl_iterate_phdr(&ctx, error{Found}, struct {
410 fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void {457 fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void {
411 _ = size;458 _ = size;
412 // The base address is too high459 // The base address is too high
...@@ -423,8 +470,8 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {...@@ -423,8 +470,8 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {
423 if (context.address >= seg_start and context.address < seg_end) {470 if (context.address >= seg_start and context.address < seg_end) {
424 // Android libc uses NULL instead of an empty string to mark the471 // Android libc uses NULL instead of an empty string to mark the
425 // main program472 // main program
426 context.name = mem.sliceTo(info.name, 0) orelse "";473 context.lookup.name = mem.sliceTo(info.name, 0) orelse "";
427 context.base_address = info.addr;474 context.lookup.base_address = info.addr;
428 break;475 break;
429 }476 }
430 } else return;477 } else return;
...@@ -440,10 +487,10 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {...@@ -440,10 +487,10 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {
440 const note_type = mem.readInt(u32, note_bytes[8..12], native_endian);487 const note_type = mem.readInt(u32, note_bytes[8..12], native_endian);
441 if (note_type != elf.NT_GNU_BUILD_ID) continue;488 if (note_type != elf.NT_GNU_BUILD_ID) continue;
442 if (!mem.eql(u8, "GNU\x00", note_bytes[12..16])) continue;489 if (!mem.eql(u8, "GNU\x00", note_bytes[12..16])) continue;
443 context.build_id = note_bytes[16..][0..desc_size];490 context.lookup.build_id = note_bytes[16..][0..desc_size];
444 },491 },
445 elf.PT_GNU_EH_FRAME => {492 elf.PT_GNU_EH_FRAME => {
446 context.gnu_eh_frame = @as([*]const u8, @ptrFromInt(info.addr + phdr.p_vaddr))[0..phdr.p_memsz];493 context.lookup.gnu_eh_frame = @as([*]const u8, @ptrFromInt(info.addr + phdr.p_vaddr))[0..phdr.p_memsz];
447 },494 },
448 else => {},495 else => {},
449 }496 }
...@@ -452,38 +499,36 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {...@@ -452,38 +499,36 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {
452 // Stop the iteration499 // Stop the iteration
453 return error.Found;500 return error.Found;
454 }501 }
455 }.callback)) {502 }.callback) catch |err| switch (err) {
456 return error.MissingDebugInfo;503 error.Found => return ctx.lookup,
457 } else |err| switch (err) {504 };
458 error.Found => {},505 if (true) return error.MissingDebugInfo;
459 }
460506
461 if (self.address_map.get(ctx.base_address)) |obj_di| {507 if (self.address_map.get(ctx.lookup.base_address)) |obj_di| {
462 return obj_di;508 return obj_di;
463 }509 }
464510
465 const obj_di = try self.allocator.create(Module);511 var sections: Dwarf.SectionArray = @splat(null);
466 errdefer self.allocator.destroy(obj_di);512 if (ctx.lookup.gnu_eh_frame) |eh_frame_hdr| {
467
468 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
469 if (ctx.gnu_eh_frame) |eh_frame_hdr| {
470 // This is a special case - pointer offsets inside .eh_frame_hdr513 // This is a special case - pointer offsets inside .eh_frame_hdr
471 // are encoded relative to its base address, so we must use the514 // are encoded relative to its base address, so we must use the
472 // version that is already memory mapped, and not the one that515 // version that is already memory mapped, and not the one that
473 // will be mapped separately from the ELF file.516 // will be mapped separately from the ELF file.
474 sections[@intFromEnum(Dwarf.Section.Id.eh_frame_hdr)] = .{517 sections[@intFromEnum(Dwarf.Unwind.Section.Id.eh_frame_hdr)] = .{
475 .data = eh_frame_hdr,518 .data = eh_frame_hdr,
476 .owned = false,519 .owned = false,
477 };520 };
478 }521 }
479522
480 obj_di.* = try readElfDebugInfo(self.allocator, if (ctx.name.len > 0) ctx.name else null, ctx.build_id, null, &sections, null);523 const obj_di = try self.allocator.create(Module);
481 obj_di.base_address = ctx.base_address;524 errdefer self.allocator.destroy(obj_di);
525 obj_di.* = try readElfDebugInfo(self.allocator, if (ctx.lookup.name.len > 0) ctx.lookup.name else null, ctx.lookup.build_id, &sections);
526 obj_di.base_address = ctx.lookup.base_address;
482527
483 // Missing unwind info isn't treated as a failure, as the unwinder will fall back to FP-based unwinding528 // Missing unwind info isn't treated as a failure, as the unwinder will fall back to FP-based unwinding
484 obj_di.dwarf.scanAllUnwindInfo(self.allocator, ctx.base_address) catch {};529 obj_di.dwarf.scanAllUnwindInfo(self.allocator, ctx.lookup.base_address) catch {};
485530
486 try self.address_map.putNoClobber(ctx.base_address, obj_di);531 try self.address_map.putNoClobber(self.allocator, ctx.lookup.base_address, obj_di);
487532
488 return obj_di;533 return obj_di;
489}534}
...@@ -625,49 +670,47 @@ pub const Module = switch (native_os) {...@@ -625,49 +670,47 @@ pub const Module = switch (native_os) {
625 }670 }
626671
627 pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, address: usize) !std.debug.Symbol {672 pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, address: usize) !std.debug.Symbol {
628 nosuspend {673 const result = try self.getOFileInfoForAddress(allocator, address);
629 const result = try self.getOFileInfoForAddress(allocator, address);674 if (result.symbol == null) return .{};
630 if (result.symbol == null) return .{};675
631676 // Take the symbol name from the N_FUN STAB entry, we're going to
632 // Take the symbol name from the N_FUN STAB entry, we're going to677 // use it if we fail to find the DWARF infos
633 // use it if we fail to find the DWARF infos678 const stab_symbol = mem.sliceTo(self.strings[result.symbol.?.strx..], 0);
634 const stab_symbol = mem.sliceTo(self.strings[result.symbol.?.strx..], 0);679 if (result.o_file_info == null) return .{ .name = stab_symbol };
635 if (result.o_file_info == null) return .{ .name = stab_symbol };680
636681 // Translate again the address, this time into an address inside the
637 // Translate again the address, this time into an address inside the682 // .o file
638 // .o file683 const relocated_address_o = result.o_file_info.?.addr_table.get(stab_symbol) orelse return .{
639 const relocated_address_o = result.o_file_info.?.addr_table.get(stab_symbol) orelse return .{684 .name = "???",
640 .name = "???",685 };
641 };
642686
643 const addr_off = result.relocated_address - result.symbol.?.addr;687 const addr_off = result.relocated_address - result.symbol.?.addr;
644 const o_file_di = &result.o_file_info.?.di;688 const o_file_di = &result.o_file_info.?.di;
645 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {689 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {
646 return .{690 return .{
647 .name = o_file_di.getSymbolName(relocated_address_o) orelse "???",691 .name = o_file_di.getSymbolName(relocated_address_o) orelse "???",
648 .compile_unit_name = compile_unit.die.getAttrString(692 .compile_unit_name = compile_unit.die.getAttrString(
649 o_file_di,693 o_file_di,
650 std.dwarf.AT.name,694 std.dwarf.AT.name,
651 o_file_di.section(.debug_str),695 o_file_di.section(.debug_str),
652 compile_unit.*,696 compile_unit.*,
653 ) catch |err| switch (err) {697 ) catch |err| switch (err) {
654 error.MissingDebugInfo, error.InvalidDebugInfo => "???",698 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
655 },
656 .source_location = o_file_di.getLineNumberInfo(
657 allocator,
658 compile_unit,
659 relocated_address_o + addr_off,
660 ) catch |err| switch (err) {
661 error.MissingDebugInfo, error.InvalidDebugInfo => null,
662 else => return err,
663 },
664 };
665 } else |err| switch (err) {
666 error.MissingDebugInfo, error.InvalidDebugInfo => {
667 return .{ .name = stab_symbol };
668 },699 },
669 else => return err,700 .source_location = o_file_di.getLineNumberInfo(
670 }701 allocator,
702 compile_unit,
703 relocated_address_o + addr_off,
704 ) catch |err| switch (err) {
705 error.MissingDebugInfo, error.InvalidDebugInfo => null,
706 else => return err,
707 },
708 };
709 } else |err| switch (err) {
710 error.MissingDebugInfo, error.InvalidDebugInfo => {
711 return .{ .name = stab_symbol };
712 },
713 else => return err,
671 }714 }
672 }715 }
673716
...@@ -676,35 +719,33 @@ pub const Module = switch (native_os) {...@@ -676,35 +719,33 @@ pub const Module = switch (native_os) {
676 symbol: ?*const MachoSymbol = null,719 symbol: ?*const MachoSymbol = null,
677 o_file_info: ?*OFileInfo = null,720 o_file_info: ?*OFileInfo = null,
678 } {721 } {
679 nosuspend {722 // Translate the VA into an address into this object
680 // Translate the VA into an address into this object723 const relocated_address = address - self.vmaddr_slide;
681 const relocated_address = address - self.vmaddr_slide;
682724
683 // Find the .o file where this symbol is defined725 // Find the .o file where this symbol is defined
684 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{726 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{
685 .relocated_address = relocated_address,727 .relocated_address = relocated_address,
686 };728 };
687729
688 // Check if its debug infos are already in the cache730 // Check if its debug infos are already in the cache
689 const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0);731 const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0);
690 const o_file_info = self.ofiles.getPtr(o_file_path) orelse732 const o_file_info = self.ofiles.getPtr(o_file_path) orelse
691 (self.loadOFile(allocator, o_file_path) catch |err| switch (err) {733 (self.loadOFile(allocator, o_file_path) catch |err| switch (err) {
692 error.FileNotFound,734 error.FileNotFound,
693 error.MissingDebugInfo,735 error.MissingDebugInfo,
694 error.InvalidDebugInfo,736 error.InvalidDebugInfo,
695 => return .{737 => return .{
696 .relocated_address = relocated_address,738 .relocated_address = relocated_address,
697 .symbol = symbol,739 .symbol = symbol,
698 },740 },
699 else => return err,741 else => return err,
700 });742 });
701743
702 return .{744 return .{
703 .relocated_address = relocated_address,745 .relocated_address = relocated_address,
704 .symbol = symbol,746 .symbol = symbol,
705 .o_file_info = o_file_info,747 .o_file_info = o_file_info,
706 };748 };
707 }
708 }749 }
709750
710 pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {751 pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {
...@@ -974,83 +1015,68 @@ fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module {...@@ -974,83 +1015,68 @@ fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module {
974 };1015 };
975}1016}
9761017
977fn readCoffDebugInfo(gpa: Allocator, coff_obj: *coff.Coff) !Module {1018fn readCoffDebugInfo(allocator: Allocator, coff_obj: *coff.Coff) !Module {
978 nosuspend {1019 var di: Module = .{
979 var di: Module = .{1020 .base_address = undefined,
980 .base_address = undefined,1021 .coff_image_base = coff_obj.getImageBase(),
981 .coff_image_base = coff_obj.getImageBase(),1022 .coff_section_headers = undefined,
982 .coff_section_headers = undefined,1023 };
983 .pdb = null,
984 .dwarf = null,
985 };
9861024
987 if (coff_obj.getSectionByName(".debug_info")) |_| {1025 if (coff_obj.getSectionByName(".debug_info")) |_| {
988 // This coff file has embedded DWARF debug info1026 // This coff file has embedded DWARF debug info
989 var sections: Dwarf.SectionArray = Dwarf.null_section_array;1027 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
990 errdefer for (sections) |section| if (section) |s| if (s.owned) gpa.free(s.data);1028 errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
991
992 inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |section, i| {
993 sections[i] = if (coff_obj.getSectionByName("." ++ section.name)) |section_header| blk: {
994 break :blk .{
995 .data = try coff_obj.getSectionDataAlloc(section_header, gpa),
996 .virtual_address = section_header.virtual_address,
997 .owned = true,
998 };
999 } else null;
1000 }
10011029
1002 var dwarf: Dwarf = .{1030 inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |section, i| {
1003 .endian = native_endian,1031 sections[i] = if (coff_obj.getSectionByName("." ++ section.name)) |section_header| blk: {
1004 .sections = sections,1032 break :blk .{
1005 .is_macho = false,1033 .data = try coff_obj.getSectionDataAlloc(section_header, allocator),
1006 };1034 .virtual_address = section_header.virtual_address,
10071035 .owned = true,
1008 try Dwarf.open(&dwarf, gpa);1036 };
1009 di.dwarf = dwarf;1037 } else null;
1010 }1038 }
10111039
1012 const raw_path = try coff_obj.getPdbPath() orelse return di;1040 var dwarf: Dwarf = .{
1013 const path = blk: {1041 .endian = native_endian,
1014 if (fs.path.isAbsolute(raw_path)) {1042 .sections = sections,
1015 break :blk raw_path;1043 .is_macho = false,
1016 } else {
1017 const self_dir = try fs.selfExeDirPathAlloc(gpa);
1018 defer gpa.free(self_dir);
1019 break :blk try fs.path.join(gpa, &.{ self_dir, raw_path });
1020 }
1021 };
1022 defer if (path.ptr != raw_path.ptr) gpa.free(path);
1023
1024 const pdb_file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) {
1025 error.FileNotFound, error.IsDir => {
1026 if (di.dwarf == null) return error.MissingDebugInfo;
1027 return di;
1028 },
1029 else => |e| return e,
1030 };1044 };
1031 errdefer pdb_file.close();
1032
1033 const pdb_file_reader_buffer = try gpa.alloc(u8, 4096);
1034 errdefer gpa.free(pdb_file_reader_buffer);
10351045
1036 const pdb_file_reader = try gpa.create(File.Reader);1046 try Dwarf.open(&dwarf, allocator);
1037 errdefer gpa.destroy(pdb_file_reader);1047 di.dwarf = dwarf;
1048 }
10381049
1039 pdb_file_reader.* = pdb_file.reader(pdb_file_reader_buffer);1050 const raw_path = try coff_obj.getPdbPath() orelse return di;
1051 const path = blk: {
1052 if (fs.path.isAbsolute(raw_path)) {
1053 break :blk raw_path;
1054 } else {
1055 const self_dir = try fs.selfExeDirPathAlloc(allocator);
1056 defer allocator.free(self_dir);
1057 break :blk try fs.path.join(allocator, &.{ self_dir, raw_path });
1058 }
1059 };
1060 defer if (path.ptr != raw_path.ptr) allocator.free(path);
10401061
1041 di.pdb = try Pdb.init(gpa, pdb_file_reader);1062 di.pdb = Pdb.init(allocator, path) catch |err| switch (err) {
1042 try di.pdb.?.parseInfoStream();1063 error.FileNotFound, error.IsDir => {
1043 try di.pdb.?.parseDbiStream();1064 if (di.dwarf == null) return error.MissingDebugInfo;
1065 return di;
1066 },
1067 else => return err,
1068 };
1069 try di.pdb.?.parseInfoStream();
1070 try di.pdb.?.parseDbiStream();
10441071
1045 if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age)1072 if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age)
1046 return error.InvalidDebugInfo;1073 return error.InvalidDebugInfo;
10471074
1048 // Only used by the pdb path1075 // Only used by the pdb path
1049 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(gpa);1076 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator);
1050 errdefer gpa.free(di.coff_section_headers);1077 errdefer allocator.free(di.coff_section_headers);
10511078
1052 return di;1079 return di;
1053 }
1054}1080}
10551081
1056/// Reads debug info from an ELF file, or the current binary if none in specified.1082/// Reads debug info from an ELF file, or the current binary if none in specified.
...@@ -1058,32 +1084,29 @@ fn readCoffDebugInfo(gpa: Allocator, coff_obj: *coff.Coff) !Module {...@@ -1058,32 +1084,29 @@ fn readCoffDebugInfo(gpa: Allocator, coff_obj: *coff.Coff) !Module {
1058/// then this this function will recurse to attempt to load the debug sections from1084/// then this this function will recurse to attempt to load the debug sections from
1059/// an external file.1085/// an external file.
1060pub fn readElfDebugInfo(1086pub fn readElfDebugInfo(
1087 em: *Dwarf.ElfModule,
1061 allocator: Allocator,1088 allocator: Allocator,
1062 elf_filename: ?[]const u8,1089 elf_filename: ?[]const u8,
1063 build_id: ?[]const u8,1090 build_id: ?[]const u8,
1064 expected_crc: ?u32,
1065 parent_sections: *Dwarf.SectionArray,1091 parent_sections: *Dwarf.SectionArray,
1066 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,1092) !void {
1067) !Dwarf.ElfModule {1093 const elf_file = (if (elf_filename) |filename| blk: {
1068 nosuspend {1094 break :blk fs.cwd().openFile(filename, .{});
1069 const elf_file = (if (elf_filename) |filename| blk: {1095 } else fs.openSelfExe(.{})) catch |err| switch (err) {
1070 break :blk fs.cwd().openFile(filename, .{});1096 error.FileNotFound => return error.MissingDebugInfo,
1071 } else fs.openSelfExe(.{})) catch |err| switch (err) {1097 else => return err,
1072 error.FileNotFound => return error.MissingDebugInfo,1098 };
1073 else => return err,
1074 };
10751099
1076 const mapped_mem = try mapWholeFile(elf_file);1100 const mapped_mem = try mapWholeFile(elf_file);
1077 return Dwarf.ElfModule.load(1101 return em.load(
1078 allocator,1102 allocator,
1079 mapped_mem,1103 mapped_mem,
1080 build_id,1104 build_id,
1081 expected_crc,1105 null,
1082 parent_sections,1106 parent_sections,
1083 parent_mapped_mem,1107 null,
1084 elf_filename,1108 elf_filename,
1085 );1109 );
1086 }
1087}1110}
10881111
1089const MachoSymbol = struct {1112const MachoSymbol = struct {
...@@ -1106,22 +1129,20 @@ const MachoSymbol = struct {...@@ -1106,22 +1129,20 @@ const MachoSymbol = struct {
1106/// Takes ownership of file, even on error.1129/// Takes ownership of file, even on error.
1107/// TODO it's weird to take ownership even on error, rework this code.1130/// TODO it's weird to take ownership even on error, rework this code.
1108fn mapWholeFile(file: File) ![]align(std.heap.page_size_min) const u8 {1131fn mapWholeFile(file: File) ![]align(std.heap.page_size_min) const u8 {
1109 nosuspend {1132 defer file.close();
1110 defer file.close();1133
11111134 const file_len = math.cast(usize, try file.getEndPos()) orelse math.maxInt(usize);
1112 const file_len = math.cast(usize, try file.getEndPos()) orelse math.maxInt(usize);1135 const mapped_mem = try posix.mmap(
1113 const mapped_mem = try posix.mmap(1136 null,
1114 null,1137 file_len,
1115 file_len,1138 posix.PROT.READ,
1116 posix.PROT.READ,1139 .{ .TYPE = .SHARED },
1117 .{ .TYPE = .SHARED },1140 file.handle,
1118 file.handle,1141 0,
1119 0,1142 );
1120 );1143 errdefer posix.munmap(mapped_mem);
1121 errdefer posix.munmap(mapped_mem);
11221144
1123 return mapped_mem;1145 return mapped_mem;
1124 }
1125}1146}
11261147
1127fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {1148fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
...@@ -1172,7 +1193,7 @@ test machoSearchSymbols {...@@ -1172,7 +1193,7 @@ test machoSearchSymbols {
1172/// Unwind a frame using MachO compact unwind info (from __unwind_info).1193/// Unwind a frame using MachO compact unwind info (from __unwind_info).
1173/// If the compact encoding can't encode a way to unwind a frame, it will1194/// If the compact encoding can't encode a way to unwind a frame, it will
1174/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.1195/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
1175pub fn unwindFrameMachO(1196fn unwindFrameMachO(
1176 allocator: Allocator,1197 allocator: Allocator,
1177 base_address: usize,1198 base_address: usize,
1178 context: *UnwindContext,1199 context: *UnwindContext,
...@@ -1562,9 +1583,9 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {...@@ -1562,9 +1583,9 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
1562///1583///
1563/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info1584/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
1564/// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.1585/// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
1565pub fn unwindFrameDwarf(1586fn unwindFrameDwarf(
1566 allocator: Allocator,1587 allocator: Allocator,
1567 di: *Dwarf,1588 unwind: *Dwarf.Unwind,
1568 base_address: usize,1589 base_address: usize,
1569 context: *UnwindContext,1590 context: *UnwindContext,
1570 explicit_fde_offset: ?usize,1591 explicit_fde_offset: ?usize,
...@@ -1572,37 +1593,34 @@ pub fn unwindFrameDwarf(...@@ -1572,37 +1593,34 @@ pub fn unwindFrameDwarf(
1572 if (!supports_unwinding) return error.UnsupportedCpuArchitecture;1593 if (!supports_unwinding) return error.UnsupportedCpuArchitecture;
1573 if (context.pc == 0) return 0;1594 if (context.pc == 0) return 0;
15741595
1575 const endian = di.endian;
1576
1577 // Find the FDE and CIE1596 // Find the FDE and CIE
1578 const cie, const fde = if (explicit_fde_offset) |fde_offset| blk: {1597 const cie, const fde = if (explicit_fde_offset) |fde_offset| blk: {
1579 const dwarf_section: Dwarf.Section.Id = .eh_frame;1598 const frame_section = unwind.section(.eh_frame) orelse return error.MissingFDE;
1580 const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
1581 if (fde_offset >= frame_section.len) return error.MissingFDE;1599 if (fde_offset >= frame_section.len) return error.MissingFDE;
15821600
1583 var fbr: std.Io.Reader = .fixed(frame_section);1601 var fbr: std.Io.Reader = .fixed(frame_section);
1584 fbr.seek = fde_offset;1602 fbr.seek = fde_offset;
15851603
1586 const fde_entry_header = try Dwarf.EntryHeader.read(&fbr, dwarf_section, endian);1604 const fde_entry_header = try Dwarf.Unwind.EntryHeader.read(&fbr, .eh_frame, native_endian);
1587 if (fde_entry_header.type != .fde) return error.MissingFDE;1605 if (fde_entry_header.type != .fde) return error.MissingFDE;
15881606
1589 const cie_offset = fde_entry_header.type.fde;1607 const cie_offset = fde_entry_header.type.fde;
1590 fbr.seek = @intCast(cie_offset);1608 fbr.seek = @intCast(cie_offset);
15911609
1592 const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, dwarf_section, endian);1610 const cie_entry_header = try Dwarf.Unwind.EntryHeader.read(&fbr, .eh_frame, native_endian);
1593 if (cie_entry_header.type != .cie) return Dwarf.bad();1611 if (cie_entry_header.type != .cie) return Dwarf.bad();
15941612
1595 const cie = try Dwarf.CommonInformationEntry.parse(1613 const cie = try Dwarf.Unwind.CommonInformationEntry.parse(
1596 cie_entry_header.entry_bytes,1614 cie_entry_header.entry_bytes,
1597 0,1615 0,
1598 true,1616 true,
1599 cie_entry_header.format,1617 cie_entry_header.format,
1600 dwarf_section,1618 .eh_frame,
1601 cie_entry_header.length_offset,1619 cie_entry_header.length_offset,
1602 @sizeOf(usize),1620 @sizeOf(usize),
1603 native_endian,1621 native_endian,
1604 );1622 );
1605 const fde = try Dwarf.FrameDescriptionEntry.parse(1623 const fde = try Dwarf.Unwind.FrameDescriptionEntry.parse(
1606 fde_entry_header.entry_bytes,1624 fde_entry_header.entry_bytes,
1607 0,1625 0,
1608 true,1626 true,
...@@ -1616,33 +1634,33 @@ pub fn unwindFrameDwarf(...@@ -1616,33 +1634,33 @@ pub fn unwindFrameDwarf(
1616 // `.eh_frame_hdr` may be incomplete. We'll try it first, but if the lookup fails, we fall1634 // `.eh_frame_hdr` may be incomplete. We'll try it first, but if the lookup fails, we fall
1617 // back to loading `.eh_frame`/`.debug_frame` and using those from that point on.1635 // back to loading `.eh_frame`/`.debug_frame` and using those from that point on.
16181636
1619 if (di.eh_frame_hdr) |header| hdr: {1637 if (unwind.eh_frame_hdr) |header| hdr: {
1620 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else {1638 const eh_frame_len = if (unwind.section(.eh_frame)) |eh_frame| eh_frame.len else {
1621 try di.scanCieFdeInfo(allocator, base_address);1639 try unwind.scanCieFdeInfo(allocator, native_endian, base_address);
1622 di.eh_frame_hdr = null;1640 unwind.eh_frame_hdr = null;
1623 break :hdr;1641 break :hdr;
1624 };1642 };
16251643
1626 var cie: Dwarf.CommonInformationEntry = undefined;1644 var cie: Dwarf.Unwind.CommonInformationEntry = undefined;
1627 var fde: Dwarf.FrameDescriptionEntry = undefined;1645 var fde: Dwarf.Unwind.FrameDescriptionEntry = undefined;
16281646
1629 header.findEntry(1647 header.findEntry(
1630 eh_frame_len,1648 eh_frame_len,
1631 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),1649 @intFromPtr(unwind.section(.eh_frame_hdr).?.ptr),
1632 context.pc,1650 context.pc,
1633 &cie,1651 &cie,
1634 &fde,1652 &fde,
1635 endian,1653 native_endian,
1636 ) catch |err| switch (err) {1654 ) catch |err| switch (err) {
1637 error.MissingDebugInfo => {1655 error.MissingDebugInfo => {
1638 // `.eh_frame_hdr` appears to be incomplete, so go ahead and populate `cie_map`1656 // `.eh_frame_hdr` appears to be incomplete, so go ahead and populate `cie_map`
1639 // and `fde_list`, and fall back to the binary search logic below.1657 // and `fde_list`, and fall back to the binary search logic below.
1640 try di.scanCieFdeInfo(allocator, base_address);1658 try unwind.scanCieFdeInfo(allocator, native_endian, base_address);
16411659
1642 // Since `.eh_frame_hdr` is incomplete, we're very likely to get more lookup1660 // Since `.eh_frame_hdr` is incomplete, we're very likely to get more lookup
1643 // failures using it, and we've just built a complete, sorted list of FDEs1661 // failures using it, and we've just built a complete, sorted list of FDEs
1644 // anyway, so just stop using `.eh_frame_hdr` altogether.1662 // anyway, so just stop using `.eh_frame_hdr` altogether.
1645 di.eh_frame_hdr = null;1663 unwind.eh_frame_hdr = null;
16461664
1647 break :hdr;1665 break :hdr;
1648 },1666 },
...@@ -1652,8 +1670,8 @@ pub fn unwindFrameDwarf(...@@ -1652,8 +1670,8 @@ pub fn unwindFrameDwarf(
1652 break :blk .{ cie, fde };1670 break :blk .{ cie, fde };
1653 }1671 }
16541672
1655 const index = std.sort.binarySearch(Dwarf.FrameDescriptionEntry, di.fde_list.items, context.pc, struct {1673 const index = std.sort.binarySearch(Dwarf.Unwind.FrameDescriptionEntry, unwind.fde_list.items, context.pc, struct {
1656 pub fn compareFn(pc: usize, item: Dwarf.FrameDescriptionEntry) std.math.Order {1674 pub fn compareFn(pc: usize, item: Dwarf.Unwind.FrameDescriptionEntry) std.math.Order {
1657 if (pc < item.pc_begin) return .lt;1675 if (pc < item.pc_begin) return .lt;
16581676
1659 const range_end = item.pc_begin + item.pc_range;1677 const range_end = item.pc_begin + item.pc_range;
...@@ -1663,15 +1681,16 @@ pub fn unwindFrameDwarf(...@@ -1663,15 +1681,16 @@ pub fn unwindFrameDwarf(
1663 }1681 }
1664 }.compareFn);1682 }.compareFn);
16651683
1666 const fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE;1684 const fde = if (index) |i| unwind.fde_list.items[i] else return error.MissingFDE;
1667 const cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;1685 const cie = unwind.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
16681686
1669 break :blk .{ cie, fde };1687 break :blk .{ cie, fde };
1670 };1688 };
16711689
1690 // Do not set `compile_unit` because the spec states that CFIs
1691 // may not reference other debug sections anyway.
1672 var expression_context: Dwarf.expression.Context = .{1692 var expression_context: Dwarf.expression.Context = .{
1673 .format = cie.format,1693 .format = cie.format,
1674 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1675 .thread_context = context.thread_context,1694 .thread_context = context.thread_context,
1676 .reg_context = context.reg_context,1695 .reg_context = context.reg_context,
1677 .cfa = context.cfa,1696 .cfa = context.cfa,
...@@ -1679,7 +1698,7 @@ pub fn unwindFrameDwarf(...@@ -1679,7 +1698,7 @@ pub fn unwindFrameDwarf(
16791698
1680 context.vm.reset();1699 context.vm.reset();
1681 context.reg_context.eh_frame = cie.version != 4;1700 context.reg_context.eh_frame = cie.version != 4;
1682 context.reg_context.is_macho = di.is_macho;1701 context.reg_context.is_macho = native_os.isDarwin();
16831702
1684 const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde);1703 const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde);
1685 context.cfa = switch (row.cfa.rule) {1704 context.cfa = switch (row.cfa.rule) {
...@@ -2007,8 +2026,8 @@ pub const VirtualMachine = struct {...@@ -2007,8 +2026,8 @@ pub const VirtualMachine = struct {
2007 self: *VirtualMachine,2026 self: *VirtualMachine,
2008 allocator: std.mem.Allocator,2027 allocator: std.mem.Allocator,
2009 pc: u64,2028 pc: u64,
2010 cie: std.debug.Dwarf.CommonInformationEntry,2029 cie: std.debug.Dwarf.Unwind.CommonInformationEntry,
2011 fde: std.debug.Dwarf.FrameDescriptionEntry,2030 fde: std.debug.Dwarf.Unwind.FrameDescriptionEntry,
2012 addr_size_bytes: u8,2031 addr_size_bytes: u8,
2013 endian: std.builtin.Endian,2032 endian: std.builtin.Endian,
2014 ) !Row {2033 ) !Row {
...@@ -2036,8 +2055,8 @@ pub const VirtualMachine = struct {...@@ -2036,8 +2055,8 @@ pub const VirtualMachine = struct {
2036 self: *VirtualMachine,2055 self: *VirtualMachine,
2037 allocator: std.mem.Allocator,2056 allocator: std.mem.Allocator,
2038 pc: u64,2057 pc: u64,
2039 cie: std.debug.Dwarf.CommonInformationEntry,2058 cie: std.debug.Dwarf.Unwind.CommonInformationEntry,
2040 fde: std.debug.Dwarf.FrameDescriptionEntry,2059 fde: std.debug.Dwarf.Unwind.FrameDescriptionEntry,
2041 ) !Row {2060 ) !Row {
2042 return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), native_endian);2061 return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), native_endian);
2043 }2062 }
...@@ -2059,7 +2078,7 @@ pub const VirtualMachine = struct {...@@ -2059,7 +2078,7 @@ pub const VirtualMachine = struct {
2059 pub fn step(2078 pub fn step(
2060 self: *VirtualMachine,2079 self: *VirtualMachine,
2061 allocator: std.mem.Allocator,2080 allocator: std.mem.Allocator,
2062 cie: std.debug.Dwarf.CommonInformationEntry,2081 cie: std.debug.Dwarf.Unwind.CommonInformationEntry,
2063 is_initial: bool,2082 is_initial: bool,
2064 instruction: Dwarf.call_frame.Instruction,2083 instruction: Dwarf.call_frame.Instruction,
2065 ) !Row {2084 ) !Row {