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
498498 }
499499 stack_trace.index = slice.len;
500500 } else {
501 // TODO: This should use the DWARF unwinder if .eh_frame_hdr is available (so that full debug info parsing isn't required).
502 // A new path for loading SelfInfo needs to be created which will only attempt to parse in-memory sections, because
503 // stopping to load other debug info (ie. source line info) from disk here is not required for unwinding.
504 var it = StackIterator.init(first_address, @frameAddress());
501 if (builtin.cpu.arch == .powerpc64) {
502 // https://github.com/ziglang/zig/issues/24970
503 stack_trace.index = 0;
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);
505512 defer it.deinit();
506513 for (stack_trace.instruction_addresses, 0..) |*addr, i| {
507514 addr.* = it.next() orelse {
......@@ -764,7 +771,7 @@ pub fn writeStackTrace(
764771}
765772
766773pub const UnwindError = if (have_ucontext)
767 @typeInfo(@typeInfo(@TypeOf(StackIterator.next_unwind)).@"fn".return_type.?).error_union.error_set
774 @typeInfo(@typeInfo(@TypeOf(SelfInfo.unwindFrame)).@"fn".return_type.?).error_union.error_set
768775else
769776 void;
770777
......@@ -865,11 +872,11 @@ pub const StackIterator = struct {
865872 @sizeOf(usize);
866873
867874 pub fn next(it: *StackIterator) ?usize {
868 var address = it.next_internal() orelse return null;
875 var address = it.nextInternal() orelse return null;
869876
870877 if (it.first_address) |first_address| {
871878 while (address != first_address) {
872 address = it.next_internal() orelse return null;
879 address = it.nextInternal() orelse return null;
873880 }
874881 it.first_address = null;
875882 }
......@@ -877,48 +884,13 @@ pub const StackIterator = struct {
877884 return address;
878885 }
879886
880 fn next_unwind(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 {
887 fn nextInternal(it: *StackIterator) ?usize {
916888 if (have_ucontext) {
917889 if (it.unwind_state) |*unwind_state| {
918890 if (!unwind_state.failed) {
919891 if (unwind_state.dwarf_context.pc == 0) return null;
920892 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| {
922894 return return_address;
923895 } else |err| {
924896 unwind_state.last_error = err;
......@@ -948,7 +920,7 @@ pub const StackIterator = struct {
948920 // Sanity check: the stack grows down thus all the parent frames must be
949921 // be at addresses that are greater (or equal) than the previous one.
950922 // 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.
952924 if (new_fp != 0 and new_fp < it.fp) return null;
953925 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:
10991071}
11001072
11011073pub 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) {
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) {
1074 const symbol_info = debug_info.getSymbolAtAddress(address) catch |err| switch (err) {
11081075 error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),
11091076 else => return err,
11101077 };
lib/std/debug/Dwarf.zig+215-814
......@@ -16,7 +16,6 @@ const elf = std.elf;
1616const mem = std.mem;
1717const DW = std.dwarf;
1818const AT = DW.AT;
19const EH = DW.EH;
2019const FORM = DW.FORM;
2120const Format = DW.Format;
2221const RLE = DW.RLE;
......@@ -34,13 +33,12 @@ const Dwarf = @This();
3433pub const expression = @import("Dwarf/expression.zig");
3534pub const abi = @import("Dwarf/abi.zig");
3635pub const call_frame = @import("Dwarf/call_frame.zig");
36pub const Unwind = @import("Dwarf/Unwind.zig");
3737
3838/// Useful to temporarily enable while working on this file.
3939const debug_debug_mode = false;
4040
41endian: Endian,
42sections: SectionArray = null_section_array,
43is_macho: bool,
41sections: SectionArray = @splat(null),
4442
4543/// Filled later by the initializer
4644abbrev_table_list: ArrayList(Abbrev.Table) = .empty,
......@@ -49,14 +47,6 @@ compile_unit_list: ArrayList(CompileUnit) = .empty,
4947/// Filled later by the initializer
5048func_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
6050/// Populated by `populateRanges`.
6151ranges: ArrayList(Range) = .empty,
6252
......@@ -87,9 +77,6 @@ pub const Section = struct {
8777 debug_rnglists,
8878 debug_addr,
8979 debug_names,
90 debug_frame,
91 eh_frame,
92 eh_frame_hdr,
9380 };
9481
9582 // For sections that are not memory mapped by the loader, this is an offset
......@@ -258,13 +245,14 @@ pub const Die = struct {
258245 fn getAttrAddr(
259246 self: *const Die,
260247 di: *const Dwarf,
248 endian: Endian,
261249 id: u64,
262 compile_unit: CompileUnit,
250 compile_unit: *const CompileUnit,
263251 ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {
264252 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
265253 return switch (form_value.*) {
266254 .addr => |value| value,
267 .addrx => |index| di.readDebugAddr(compile_unit, index),
255 .addrx => |index| di.readDebugAddr(endian, compile_unit, index),
268256 else => bad(),
269257 };
270258 }
......@@ -294,9 +282,10 @@ pub const Die = struct {
294282 pub fn getAttrString(
295283 self: *const Die,
296284 di: *Dwarf,
285 endian: Endian,
297286 id: u64,
298287 opt_str: ?[]const u8,
299 compile_unit: CompileUnit,
288 compile_unit: *const CompileUnit,
300289 ) error{ InvalidDebugInfo, MissingDebugInfo }![]const u8 {
301290 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
302291 switch (form_value.*) {
......@@ -309,13 +298,13 @@ pub const Die = struct {
309298 .@"32" => {
310299 const byte_offset = compile_unit.str_offsets_base + 4 * index;
311300 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);
313302 return getStringGeneric(opt_str, offset);
314303 },
315304 .@"64" => {
316305 const byte_offset = compile_unit.str_offsets_base + 8 * index;
317306 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);
319308 return getStringGeneric(opt_str, offset);
320309 },
321310 }
......@@ -326,440 +315,17 @@ pub const Die = struct {
326315 }
327316};
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
751318const num_sections = std.enums.directEnumArrayLen(Section.Id, 0);
752319pub const SectionArray = [num_sections]?Section;
753pub const null_section_array = [_]?Section{null} ** num_sections;
754320
755321pub const OpenError = ScanError;
756322
757323/// Initialize DWARF info. The caller has the responsibility to initialize most
758324/// the `Dwarf` fields before calling. `binary_mem` is the raw bytes of the
759325/// main binary file (not the secondary debug info file).
760pub fn open(d: *Dwarf, gpa: Allocator) OpenError!void {
761 try d.scanAllFunctions(gpa);
762 try d.scanAllCompileUnits(gpa);
326pub fn open(d: *Dwarf, gpa: Allocator, endian: Endian) OpenError!void {
327 try d.scanAllFunctions(gpa, endian);
328 try d.scanAllCompileUnits(gpa, endian);
763329}
764330
765331const PcRange = struct {
......@@ -825,31 +391,30 @@ pub const ScanError = error{
825391 StreamTooLong,
826392} || Allocator.Error;
827393
828fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
829 const endian = di.endian;
830 var fbr: Reader = .fixed(di.section(.debug_info).?);
394fn scanAllFunctions(di: *Dwarf, allocator: Allocator, endian: Endian) ScanError!void {
395 var fr: Reader = .fixed(di.section(.debug_info).?);
831396 var this_unit_offset: u64 = 0;
832397
833 while (this_unit_offset < fbr.buffer.len) {
834 fbr.seek = @intCast(this_unit_offset);
398 while (this_unit_offset < fr.buffer.len) {
399 fr.seek = @intCast(this_unit_offset);
835400
836 const unit_header = try readUnitHeader(&fbr, endian);
401 const unit_header = try readUnitHeader(&fr, endian);
837402 if (unit_header.unit_length == 0) return;
838403 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);
841406 if (version < 2 or version > 5) return bad();
842407
843408 var address_size: u8 = undefined;
844409 var debug_abbrev_offset: u64 = undefined;
845410 if (version >= 5) {
846 const unit_type = try fbr.takeByte();
411 const unit_type = try fr.takeByte();
847412 if (unit_type != DW.UT.compile) return bad();
848 address_size = try fbr.takeByte();
849 debug_abbrev_offset = try readAddress(&fbr, unit_header.format, endian);
413 address_size = try fr.takeByte();
414 debug_abbrev_offset = try readAddress(&fr, unit_header.format, endian);
850415 } else {
851 debug_abbrev_offset = try readAddress(&fbr, unit_header.format, endian);
852 address_size = try fbr.takeByte();
416 debug_abbrev_offset = try readAddress(&fr, unit_header.format, endian);
417 address_size = try fr.takeByte();
853418 }
854419 if (address_size != @sizeOf(usize)) return bad();
855420
......@@ -890,12 +455,12 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
890455 };
891456
892457 while (true) {
893 fbr.seek = std.mem.indexOfNonePos(u8, fbr.buffer, fbr.seek, &.{
458 fr.seek = std.mem.indexOfNonePos(u8, fr.buffer, fr.seek, &.{
894459 zig_padding_abbrev_code, 0,
895 }) orelse fbr.buffer.len;
896 if (fbr.seek >= next_unit_pos) break;
460 }) orelse fr.buffer.len;
461 if (fr.seek >= next_unit_pos) break;
897462 var die_obj = (try parseDie(
898 &fbr,
463 &fr,
899464 attrs_bufs[0],
900465 abbrev_table,
901466 unit_header.format,
......@@ -920,30 +485,30 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
920485 // Prevent endless loops
921486 for (0..3) |_| {
922487 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);
924489 } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
925 const after_die_offset = fbr.seek;
926 defer fbr.seek = after_die_offset;
490 const after_die_offset = fr.seek;
491 defer fr.seek = after_die_offset;
927492
928493 // Follow the DIE it points to and repeat
929494 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);
931496 this_die_obj = (try parseDie(
932 &fbr,
497 &fr,
933498 attrs_bufs[2],
934499 abbrev_table, // wrong abbrev table for different cu
935500 unit_header.format,
936501 endian,
937502 )) orelse return bad();
938503 } else if (this_die_obj.getAttr(AT.specification)) |_| {
939 const after_die_offset = fbr.seek;
940 defer fbr.seek = after_die_offset;
504 const after_die_offset = fr.seek;
505 defer fr.seek = after_die_offset;
941506
942507 // Follow the DIE it points to and repeat
943508 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);
945510 this_die_obj = (try parseDie(
946 &fbr,
511 &fr,
947512 attrs_bufs[2],
948513 abbrev_table, // wrong abbrev table for different cu
949514 unit_header.format,
......@@ -957,7 +522,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
957522 break :x null;
958523 };
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: {
961526 if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
962527 const pc_end = switch (high_pc_value.*) {
963528 .addr => |value| value,
......@@ -983,7 +548,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
983548 };
984549
985550 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| {
987552 if (err != error.MissingDebugInfo) return err;
988553 break :blk;
989554 };
......@@ -1015,34 +580,33 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void {
1015580 }
1016581}
1017582
1018fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {
1019 const endian = di.endian;
1020 var fbr: Reader = .fixed(di.section(.debug_info).?);
583fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator, endian: Endian) ScanError!void {
584 var fr: Reader = .fixed(di.section(.debug_info).?);
1021585 var this_unit_offset: u64 = 0;
1022586
1023587 var attrs_buf = std.array_list.Managed(Die.Attr).init(allocator);
1024588 defer attrs_buf.deinit();
1025589
1026 while (this_unit_offset < fbr.buffer.len) {
1027 fbr.seek = @intCast(this_unit_offset);
590 while (this_unit_offset < fr.buffer.len) {
591 fr.seek = @intCast(this_unit_offset);
1028592
1029 const unit_header = try readUnitHeader(&fbr, endian);
593 const unit_header = try readUnitHeader(&fr, endian);
1030594 if (unit_header.unit_length == 0) return;
1031595 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);
1034598 if (version < 2 or version > 5) return bad();
1035599
1036600 var address_size: u8 = undefined;
1037601 var debug_abbrev_offset: u64 = undefined;
1038602 if (version >= 5) {
1039 const unit_type = try fbr.takeByte();
603 const unit_type = try fr.takeByte();
1040604 if (unit_type != UT.compile) return bad();
1041 address_size = try fbr.takeByte();
1042 debug_abbrev_offset = try readAddress(&fbr, unit_header.format, endian);
605 address_size = try fr.takeByte();
606 debug_abbrev_offset = try readAddress(&fr, unit_header.format, endian);
1043607 } else {
1044 debug_abbrev_offset = try readAddress(&fbr, unit_header.format, endian);
1045 address_size = try fbr.takeByte();
608 debug_abbrev_offset = try readAddress(&fr, unit_header.format, endian);
609 address_size = try fr.takeByte();
1046610 }
1047611 if (address_size != @sizeOf(usize)) return bad();
1048612
......@@ -1055,7 +619,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {
1055619 try attrs_buf.resize(max_attrs);
1056620
1057621 var compile_unit_die = (try parseDie(
1058 &fbr,
622 &fr,
1059623 attrs_buf.items,
1060624 abbrev_table,
1061625 unit_header.format,
......@@ -1080,7 +644,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void {
1080644 };
1081645
1082646 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| {
1084648 if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {
1085649 const pc_end = switch (high_pc_value.*) {
1086650 .addr => |value| value,
......@@ -1144,10 +708,11 @@ const DebugRangeIterator = struct {
1144708 base_address: u64,
1145709 section_type: Section.Id,
1146710 di: *const Dwarf,
711 endian: Endian,
1147712 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() {
1151716 const section_type = if (compile_unit.version >= 5) Section.Id.debug_rnglists else Section.Id.debug_ranges;
1152717 const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo;
1153718
......@@ -1158,13 +723,13 @@ const DebugRangeIterator = struct {
1158723 .@"32" => {
1159724 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx));
1160725 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);
1162727 break :off compile_unit.rnglists_base + offset;
1163728 },
1164729 .@"64" => {
1165730 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx));
1166731 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);
1168733 break :off compile_unit.rnglists_base + offset;
1169734 },
1170735 }
......@@ -1176,42 +741,43 @@ const DebugRangeIterator = struct {
1176741 // specified by DW_AT.low_pc or to some other value encoded
1177742 // in the list itself.
1178743 // 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) {
1180745 error.MissingDebugInfo => 0,
1181746 else => return err,
1182747 };
1183748
1184 var fbr: Reader = .fixed(debug_ranges);
1185 fbr.seek = cast(usize, ranges_offset) orelse return bad();
749 var fr: Reader = .fixed(debug_ranges);
750 fr.seek = cast(usize, ranges_offset) orelse return bad();
1186751
1187752 return .{
1188753 .base_address = base_address,
1189754 .section_type = section_type,
1190755 .di = di,
756 .endian = endian,
1191757 .compile_unit = compile_unit,
1192 .fbr = fbr,
758 .fr = fr,
1193759 };
1194760 }
1195761
1196762 // Returns the next range in the list, or null if the end was reached.
1197763 pub fn next(self: *@This()) !?PcRange {
1198 const endian = self.di.endian;
764 const endian = self.endian;
1199765 switch (self.section_type) {
1200766 .debug_rnglists => {
1201 const kind = try self.fbr.takeByte();
767 const kind = try self.fr.takeByte();
1202768 switch (kind) {
1203769 RLE.end_of_list => return null,
1204770 RLE.base_addressx => {
1205 const index = try self.fbr.takeLeb128(usize);
1206 self.base_address = try self.di.readDebugAddr(self.compile_unit.*, index);
771 const index = try self.fr.takeLeb128(usize);
772 self.base_address = try self.di.readDebugAddr(endian, self.compile_unit, index);
1207773 return try self.next();
1208774 },
1209775 RLE.startx_endx => {
1210 const start_index = try self.fbr.takeLeb128(usize);
1211 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);
776 const start_index = try self.fr.takeLeb128(usize);
777 const start_addr = try self.di.readDebugAddr(endian, self.compile_unit, start_index);
1212778
1213 const end_index = try self.fbr.takeLeb128(usize);
1214 const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index);
779 const end_index = try self.fr.takeLeb128(usize);
780 const end_addr = try self.di.readDebugAddr(endian, self.compile_unit, end_index);
1215781
1216782 return .{
1217783 .start = start_addr,
......@@ -1219,10 +785,10 @@ const DebugRangeIterator = struct {
1219785 };
1220786 },
1221787 RLE.startx_length => {
1222 const start_index = try self.fbr.takeLeb128(usize);
1223 const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index);
788 const start_index = try self.fr.takeLeb128(usize);
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);
1226792 const end_addr = start_addr + len;
1227793
1228794 return .{
......@@ -1231,8 +797,8 @@ const DebugRangeIterator = struct {
1231797 };
1232798 },
1233799 RLE.offset_pair => {
1234 const start_addr = try self.fbr.takeLeb128(usize);
1235 const end_addr = try self.fbr.takeLeb128(usize);
800 const start_addr = try self.fr.takeLeb128(usize);
801 const end_addr = try self.fr.takeLeb128(usize);
1236802
1237803 // This is the only kind that uses the base address
1238804 return .{
......@@ -1241,12 +807,12 @@ const DebugRangeIterator = struct {
1241807 };
1242808 },
1243809 RLE.base_address => {
1244 self.base_address = try self.fbr.takeInt(usize, endian);
810 self.base_address = try self.fr.takeInt(usize, endian);
1245811 return try self.next();
1246812 },
1247813 RLE.start_end => {
1248 const start_addr = try self.fbr.takeInt(usize, endian);
1249 const end_addr = try self.fbr.takeInt(usize, endian);
814 const start_addr = try self.fr.takeInt(usize, endian);
815 const end_addr = try self.fr.takeInt(usize, endian);
1250816
1251817 return .{
1252818 .start = start_addr,
......@@ -1254,8 +820,8 @@ const DebugRangeIterator = struct {
1254820 };
1255821 },
1256822 RLE.start_length => {
1257 const start_addr = try self.fbr.takeInt(usize, endian);
1258 const len = try self.fbr.takeLeb128(usize);
823 const start_addr = try self.fr.takeInt(usize, endian);
824 const len = try self.fr.takeLeb128(usize);
1259825 const end_addr = start_addr + len;
1260826
1261827 return .{
......@@ -1267,8 +833,8 @@ const DebugRangeIterator = struct {
1267833 }
1268834 },
1269835 .debug_ranges => {
1270 const start_addr = try self.fbr.takeInt(usize, endian);
1271 const end_addr = try self.fbr.takeInt(usize, endian);
836 const start_addr = try self.fr.takeInt(usize, endian);
837 const end_addr = try self.fr.takeInt(usize, endian);
1272838 if (start_addr == 0 and end_addr == 0) return null;
1273839
1274840 // This entry selects a new value for the base address
......@@ -1288,14 +854,14 @@ const DebugRangeIterator = struct {
1288854};
1289855
1290856/// 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 {
1292858 for (di.compile_unit_list.items) |*compile_unit| {
1293859 if (compile_unit.pc_range) |range| {
1294860 if (target_address >= range.start and target_address < range.end) return compile_unit;
1295861 }
1296862
1297863 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;
1299865 while (try iter.next()) |range| {
1300866 if (target_address >= range.start and target_address < range.end) return compile_unit;
1301867 }
......@@ -1320,8 +886,8 @@ fn getAbbrevTable(di: *Dwarf, allocator: Allocator, abbrev_offset: u64) !*const
1320886}
1321887
1322888fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table {
1323 var fbr: Reader = .fixed(di.section(.debug_abbrev).?);
1324 fbr.seek = cast(usize, offset) orelse return bad();
889 var fr: Reader = .fixed(di.section(.debug_abbrev).?);
890 fr.seek = cast(usize, offset) orelse return bad();
1325891
1326892 var abbrevs = std.array_list.Managed(Abbrev).init(allocator);
1327893 defer {
......@@ -1335,20 +901,20 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table
1335901 defer attrs.deinit();
1336902
1337903 while (true) {
1338 const code = try fbr.takeLeb128(u64);
904 const code = try fr.takeLeb128(u64);
1339905 if (code == 0) break;
1340 const tag_id = try fbr.takeLeb128(u64);
1341 const has_children = (try fbr.takeByte()) == DW.CHILDREN.yes;
906 const tag_id = try fr.takeLeb128(u64);
907 const has_children = (try fr.takeByte()) == DW.CHILDREN.yes;
1342908
1343909 while (true) {
1344 const attr_id = try fbr.takeLeb128(u64);
1345 const form_id = try fbr.takeLeb128(u64);
910 const attr_id = try fr.takeLeb128(u64);
911 const form_id = try fr.takeLeb128(u64);
1346912 if (attr_id == 0 and form_id == 0) break;
1347913 try attrs.append(.{
1348914 .id = attr_id,
1349915 .form_id = form_id,
1350916 .payload = switch (form_id) {
1351 FORM.implicit_const => try fbr.takeLeb128(i64),
917 FORM.implicit_const => try fr.takeLeb128(i64),
1352918 else => undefined,
1353919 },
1354920 });
......@@ -1369,20 +935,20 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table
1369935}
1370936
1371937fn parseDie(
1372 fbr: *Reader,
938 fr: *Reader,
1373939 attrs_buf: []Die.Attr,
1374940 abbrev_table: *const Abbrev.Table,
1375941 format: Format,
1376942 endian: Endian,
1377943) ScanError!?Die {
1378 const abbrev_code = try fbr.takeLeb128(u64);
944 const abbrev_code = try fr.takeLeb128(u64);
1379945 if (abbrev_code == 0) return null;
1380946 const table_entry = abbrev_table.get(abbrev_code) orelse return bad();
1381947
1382948 const attrs = attrs_buf[0..table_entry.attrs.len];
1383949 for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = .{
1384950 .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),
1386952 };
1387953 return .{
1388954 .tag_id = table_entry.tag_id,
......@@ -1392,25 +958,24 @@ fn parseDie(
1392958}
1393959
1394960/// Ensures that addresses in the returned LineTable are monotonically increasing.
1395fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !CompileUnit.SrcLocCache {
1396 const endian = d.endian;
1397 const compile_unit_cwd = try compile_unit.die.getAttrString(d, AT.comp_dir, d.section(.debug_line_str), compile_unit.*);
961fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, endian: Endian, compile_unit: *const CompileUnit) !CompileUnit.SrcLocCache {
962 const compile_unit_cwd = try compile_unit.die.getAttrString(d, endian, AT.comp_dir, d.section(.debug_line_str), compile_unit);
1398963 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
1399964
1400 var fbr: Reader = .fixed(d.section(.debug_line).?);
1401 fbr.seek = @intCast(line_info_offset);
965 var fr: Reader = .fixed(d.section(.debug_line).?);
966 fr.seek = @intCast(line_info_offset);
1402967
1403 const unit_header = try readUnitHeader(&fbr, endian);
968 const unit_header = try readUnitHeader(&fr, endian);
1404969 if (unit_header.unit_length == 0) return missing();
1405970
1406971 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);
1409974 if (version < 2) return bad();
1410975
1411976 const addr_size: u8, const seg_size: u8 = if (version >= 5) .{
1412 try fbr.takeByte(),
1413 try fbr.takeByte(),
977 try fr.takeByte(),
978 try fr.takeByte(),
1414979 } else .{
1415980 switch (unit_header.format) {
1416981 .@"32" => 4,
......@@ -1421,26 +986,26 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
1421986 _ = addr_size;
1422987 _ = seg_size;
1423988
1424 const prologue_length = try readAddress(&fbr, unit_header.format, endian);
1425 const prog_start_offset = fbr.seek + prologue_length;
989 const prologue_length = try readAddress(&fr, unit_header.format, endian);
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();
1428993 if (minimum_instruction_length == 0) return bad();
1429994
1430995 if (version >= 4) {
1431 const maximum_operations_per_instruction = try fbr.takeByte();
996 const maximum_operations_per_instruction = try fr.takeByte();
1432997 _ = maximum_operations_per_instruction;
1433998 }
1434999
1435 const default_is_stmt = (try fbr.takeByte()) != 0;
1436 const line_base = try fbr.takeByteSigned();
1000 const default_is_stmt = (try fr.takeByte()) != 0;
1001 const line_base = try fr.takeByteSigned();
14371002
1438 const line_range = try fbr.takeByte();
1003 const line_range = try fr.takeByte();
14391004 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
14451010 var directories: ArrayList(FileEntry) = .empty;
14461011 defer directories.deinit(gpa);
......@@ -1451,17 +1016,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
14511016 try directories.append(gpa, .{ .path = compile_unit_cwd });
14521017
14531018 while (true) {
1454 const dir = try fbr.takeSentinel(0);
1019 const dir = try fr.takeSentinel(0);
14551020 if (dir.len == 0) break;
14561021 try directories.append(gpa, .{ .path = dir });
14571022 }
14581023
14591024 while (true) {
1460 const file_name = try fbr.takeSentinel(0);
1025 const file_name = try fr.takeSentinel(0);
14611026 if (file_name.len == 0) break;
1462 const dir_index = try fbr.takeLeb128(u32);
1463 const mtime = try fbr.takeLeb128(u64);
1464 const size = try fbr.takeLeb128(u64);
1027 const dir_index = try fr.takeLeb128(u32);
1028 const mtime = try fr.takeLeb128(u64);
1029 const size = try fr.takeLeb128(u64);
14651030 try file_entries.append(gpa, .{
14661031 .path = file_name,
14671032 .dir_index = dir_index,
......@@ -1476,21 +1041,21 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
14761041 };
14771042 {
14781043 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();
14801045 if (directory_entry_format_count > dir_ent_fmt_buf.len) return bad();
14811046 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
14821047 ent_fmt.* = .{
1483 .content_type_code = try fbr.takeLeb128(u8),
1484 .form_code = try fbr.takeLeb128(u16),
1048 .content_type_code = try fr.takeLeb128(u8),
1049 .form_code = try fr.takeLeb128(u16),
14851050 };
14861051 }
14871052
1488 const directories_count = try fbr.takeLeb128(usize);
1053 const directories_count = try fr.takeLeb128(usize);
14891054
14901055 for (try directories.addManyAsSlice(gpa, directories_count)) |*e| {
14911056 e.* = .{ .path = &.{} };
14921057 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);
14941059 switch (ent_fmt.content_type_code) {
14951060 DW.LNCT.path => e.path = try form_value.getString(d.*),
14961061 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) !
15071072 }
15081073
15091074 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();
15111076 if (file_name_entry_format_count > file_ent_fmt_buf.len) return bad();
15121077 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
15131078 ent_fmt.* = .{
1514 .content_type_code = try fbr.takeLeb128(u16),
1515 .form_code = try fbr.takeLeb128(u16),
1079 .content_type_code = try fr.takeLeb128(u16),
1080 .form_code = try fr.takeLeb128(u16),
15161081 };
15171082 }
15181083
1519 const file_names_count = try fbr.takeLeb128(usize);
1084 const file_names_count = try fr.takeLeb128(usize);
15201085 try file_entries.ensureUnusedCapacity(gpa, file_names_count);
15211086
15221087 for (try file_entries.addManyAsSlice(gpa, file_names_count)) |*e| {
15231088 e.* = .{ .path = &.{} };
15241089 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);
15261091 switch (ent_fmt.content_type_code) {
15271092 DW.LNCT.path => e.path = try form_value.getString(d.*),
15281093 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) !
15421107 var line_table: CompileUnit.SrcLocCache.LineTable = .{};
15431108 errdefer line_table.deinit(gpa);
15441109
1545 fbr.seek = @intCast(prog_start_offset);
1110 fr.seek = @intCast(prog_start_offset);
15461111
15471112 const next_unit_pos = line_info_offset + next_offset;
15481113
1549 while (fbr.seek < next_unit_pos) {
1550 const opcode = try fbr.takeByte();
1114 while (fr.seek < next_unit_pos) {
1115 const opcode = try fr.takeByte();
15511116
15521117 if (opcode == DW.LNS.extended_op) {
1553 const op_size = try fbr.takeLeb128(u64);
1118 const op_size = try fr.takeLeb128(u64);
15541119 if (op_size < 1) return bad();
1555 const sub_op = try fbr.takeByte();
1120 const sub_op = try fr.takeByte();
15561121 switch (sub_op) {
15571122 DW.LNE.end_sequence => {
15581123 // The row being added here is an "end" address, meaning
......@@ -1571,14 +1136,14 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
15711136 prog.reset();
15721137 },
15731138 DW.LNE.set_address => {
1574 const addr = try fbr.takeInt(usize, endian);
1139 const addr = try fr.takeInt(usize, endian);
15751140 prog.address = addr;
15761141 },
15771142 DW.LNE.define_file => {
1578 const path = try fbr.takeSentinel(0);
1579 const dir_index = try fbr.takeLeb128(u32);
1580 const mtime = try fbr.takeLeb128(u64);
1581 const size = try fbr.takeLeb128(u64);
1143 const path = try fr.takeSentinel(0);
1144 const dir_index = try fr.takeLeb128(u32);
1145 const mtime = try fr.takeLeb128(u64);
1146 const size = try fr.takeLeb128(u64);
15821147 try file_entries.append(gpa, .{
15831148 .path = path,
15841149 .dir_index = dir_index,
......@@ -1586,7 +1151,7 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
15861151 .size = size,
15871152 });
15881153 },
1589 else => try fbr.discardAll64(op_size - 1),
1154 else => try fr.discardAll64(op_size - 1),
15901155 }
15911156 } else if (opcode >= opcode_base) {
15921157 // special opcodes
......@@ -1604,19 +1169,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
16041169 prog.basic_block = false;
16051170 },
16061171 DW.LNS.advance_pc => {
1607 const arg = try fbr.takeLeb128(usize);
1172 const arg = try fr.takeLeb128(usize);
16081173 prog.address += arg * minimum_instruction_length;
16091174 },
16101175 DW.LNS.advance_line => {
1611 const arg = try fbr.takeLeb128(i64);
1176 const arg = try fr.takeLeb128(i64);
16121177 prog.line += arg;
16131178 },
16141179 DW.LNS.set_file => {
1615 const arg = try fbr.takeLeb128(usize);
1180 const arg = try fr.takeLeb128(usize);
16161181 prog.file = arg;
16171182 },
16181183 DW.LNS.set_column => {
1619 const arg = try fbr.takeLeb128(u64);
1184 const arg = try fr.takeLeb128(u64);
16201185 prog.column = arg;
16211186 },
16221187 DW.LNS.negate_stmt => {
......@@ -1630,13 +1195,13 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
16301195 prog.address += inc_addr;
16311196 },
16321197 DW.LNS.fixed_advance_pc => {
1633 const arg = try fbr.takeInt(u16, endian);
1198 const arg = try fr.takeInt(u16, endian);
16341199 prog.address += arg;
16351200 },
16361201 DW.LNS.set_prologue_end => {},
16371202 else => {
16381203 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]);
16401205 },
16411206 }
16421207 }
......@@ -1661,18 +1226,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !
16611226 };
16621227}
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 {
16651230 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);
16671232}
16681233
16691234pub fn getLineNumberInfo(
16701235 d: *Dwarf,
16711236 gpa: Allocator,
1237 endian: Endian,
16721238 compile_unit: *CompileUnit,
16731239 target_address: u64,
16741240) !std.debug.SourceLocation {
1675 try populateSrcLocCache(d, gpa, compile_unit);
1241 try d.populateSrcLocCache(gpa, endian, compile_unit);
16761242 const slc = &compile_unit.src_loc_cache.?;
16771243 const entry = try slc.findSource(target_address);
16781244 const file_index = entry.file - @intFromBool(slc.version < 5);
......@@ -1696,7 +1262,7 @@ fn getLineString(di: Dwarf, offset: u64) ![:0]const u8 {
16961262 return getStringGeneric(di.section(.debug_line_str), offset);
16971263}
16981264
1699fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
1265fn readDebugAddr(di: Dwarf, endian: Endian, compile_unit: *const CompileUnit, index: u64) !u64 {
17001266 const debug_addr = di.section(.debug_addr) orelse return bad();
17011267
17021268 // 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 {
17051271 // The header is 8 or 12 bytes depending on is_64.
17061272 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);
17091275 if (version != 5) return bad();
17101276
17111277 const addr_size = debug_addr[compile_unit.addr_base - 2];
......@@ -1715,113 +1281,13 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
17151281 if (byte_offset + addr_size > debug_addr.len) return bad();
17161282 return switch (addr_size) {
17171283 1 => debug_addr[byte_offset],
1718 2 => mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
1719 4 => mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
1720 8 => mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
1284 2 => mem.readInt(u16, debug_addr[byte_offset..][0..2], endian),
1285 4 => mem.readInt(u32, debug_addr[byte_offset..][0..4], endian),
1286 8 => mem.readInt(u64, debug_addr[byte_offset..][0..8], endian),
17211287 else => bad(),
17221288 };
17231289}
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
18251291fn parseFormValue(
18261292 r: *Reader,
18271293 form_id: u64,
......@@ -1946,7 +1412,7 @@ const UnitHeader = struct {
19461412 unit_length: u64,
19471413};
19481414
1949fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader {
1415pub fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader {
19501416 return switch (try r.takeInt(u32, endian)) {
19511417 0...0xfffffff0 - 1 => |unit_length| .{
19521418 .format = .@"32",
......@@ -1986,7 +1452,7 @@ fn invalidDebugInfoDetected() void {
19861452 if (debug_debug_mode) @panic("bad dwarf");
19871453}
19881454
1989fn missing() error{MissingDebugInfo} {
1455pub fn missing() error{MissingDebugInfo} {
19901456 if (debug_debug_mode) @panic("missing dwarf");
19911457 return error.MissingDebugInfo;
19921458}
......@@ -2000,94 +1466,39 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
20001466 return str[casted_offset..last :0];
20011467}
20021468
2003const EhPointerContext = struct {
2004 // The address of the pointer field itself
2005 pc_rel_base: u64,
2006
2007 // Whether or not to follow indirect pointers. This should only be
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 };
1469pub const ElfModule = struct {
1470 unwind: Dwarf.Unwind,
1471 dwarf: Dwarf,
1472 mapped_memory: ?[]align(std.heap.page_size_min) const u8,
1473 external_mapped_memory: ?[]align(std.heap.page_size_min) const u8,
20521474
2053 const ptr: u64 = if (base) |b| switch (value) {
2054 .signed => |s| @intCast(try std.math.add(i64, s, @as(i64, @intCast(b)))),
2055 // absptr can actually contain signed values in some cases (aarch64 MachO)
2056 .unsigned => |u| u +% b,
2057 } else switch (value) {
2058 .signed => |s| @as(u64, @intCast(s)),
2059 .unsigned => |u| u,
1475 pub const Lookup = struct {
1476 base_address: usize,
1477 name: []const u8,
1478 build_id: ?[]const u8,
1479 gnu_eh_frame: ?[]const u8,
20601480 };
20611481
2062 if ((enc & EH.PE.indirect) > 0 and ctx.follow_indirect) {
2063 if (@sizeOf(usize) != addr_size_bytes) {
2064 // See the documentation for `follow_indirect`
2065 return error.NonNativeIndirection;
2066 }
2067
2068 const native_ptr = cast(usize, ptr) orelse return error.PointerOverflow;
2069 return switch (addr_size_bytes) {
2070 2, 4, 8 => return @as(*const usize, @ptrFromInt(native_ptr)).*,
2071 else => return error.UnsupportedAddrSize,
1482 pub fn init(lookup: *const Lookup) ElfModule {
1483 var em: ElfModule = .{
1484 .unwind = .{
1485 .sections = @splat(null),
1486 },
1487 .dwarf = .{},
1488 .mapped_memory = null,
1489 .external_mapped_memory = null,
20721490 };
2073 } else {
2074 return ptr;
2075 }
2076}
2077
2078fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
2079 if (pc_rel_offset < 0) {
2080 return std.math.sub(usize, field_ptr, @as(usize, @intCast(-pc_rel_offset)));
2081 } else {
2082 return std.math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));
1491 if (lookup.gnu_eh_frame) |eh_frame_hdr| {
1492 // This is a special case - pointer offsets inside .eh_frame_hdr
1493 // are encoded relative to its base address, so we must use the
1494 // version that is already memory mapped, and not the one that
1495 // will be mapped separately from the ELF file.
1496 em.unwind.sections[@intFromEnum(Dwarf.Unwind.Section.Id.eh_frame_hdr)] = .{
1497 .data = eh_frame_hdr,
1498 };
1499 }
1500 return em;
20831501 }
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
20921503 pub fn deinit(self: *@This(), allocator: Allocator) void {
20931504 self.dwarf.deinit(allocator);
......@@ -2095,16 +1506,16 @@ pub const ElfModule = struct {
20951506 if (self.external_mapped_memory) |m| std.posix.munmap(m);
20961507 }
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 {
20991510 // Translate the VA into an address into this object
2100 const relocated_address = address - self.base_address;
2101 return self.dwarf.getSymbol(allocator, relocated_address);
1511 const relocated_address = address - base_address;
1512 return self.dwarf.getSymbol(allocator, endian, relocated_address);
21021513 }
21031514
2104 pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {
1515 pub fn getDwarfUnwindForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf.Unwind {
21051516 _ = allocator;
21061517 _ = address;
2107 return &self.dwarf;
1518 return &self.unwind;
21081519 }
21091520
21101521 pub const LoadError = error{
......@@ -2132,6 +1543,7 @@ pub const ElfModule = struct {
21321543 /// info is, then this this function will recurse to attempt to load the debug
21331544 /// sections from an external file.
21341545 pub fn load(
1546 em: *ElfModule,
21351547 gpa: Allocator,
21361548 mapped_mem: []align(std.heap.page_size_min) const u8,
21371549 build_id: ?[]const u8,
......@@ -2139,7 +1551,7 @@ pub const ElfModule = struct {
21391551 parent_sections: *Dwarf.SectionArray,
21401552 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
21411553 elf_filename: ?[]const u8,
2142 ) LoadError!Dwarf.ElfModule {
1554 ) LoadError!void {
21431555 if (expected_crc) |crc| if (crc != std.hash.crc.Crc32.hash(mapped_mem)) return error.InvalidDebugInfo;
21441556
21451557 const hdr: *const elf.Ehdr = @ptrCast(&mapped_mem[0]);
......@@ -2162,7 +1574,7 @@ pub const ElfModule = struct {
21621574 @ptrCast(@alignCast(&mapped_mem[shoff])),
21631575 )[0..hdr.e_shnum];
21641576
2165 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
1577 var sections: Dwarf.SectionArray = @splat(null);
21661578
21671579 // Combine section list. This takes ownership over any owned sections from the parent scope.
21681580 for (parent_sections, &sections) |*parent, *section_elem| {
......@@ -2276,7 +1688,7 @@ pub const ElfModule = struct {
22761688 .sub_path = filename,
22771689 };
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;
22801692 }
22811693
22821694 const global_debug_directories = [_][]const u8{
......@@ -2304,7 +1716,7 @@ pub const ElfModule = struct {
23041716 };
23051717 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;
23081720 }
23091721 }
23101722
......@@ -2320,7 +1732,7 @@ pub const ElfModule = struct {
23201732 defer exe_dir.close();
23211733
23221734 // <exe_dir>/<gnu_debuglink>
2323 if (loadPath(
1735 if (em.loadPath(
23241736 gpa,
23251737 .{
23261738 .root_dir = .{ .path = null, .handle = exe_dir },
......@@ -2341,7 +1753,7 @@ pub const ElfModule = struct {
23411753 };
23421754 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 |_| {}
23451757 }
23461758
23471759 var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
......@@ -2354,37 +1766,27 @@ pub const ElfModule = struct {
23541766 .sub_path = try std.fs.path.join(gpa, &.{ global_directory, cwd_path, separate_filename }),
23551767 };
23561768 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 |_| {}
23581770 }
23591771 }
23601772
23611773 return error.MissingDebugInfo;
23621774 }
23631775
2364 var di: Dwarf = .{
2365 .endian = endian,
2366 .sections = sections,
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 };
1776 em.mapped_memory = parent_mapped_mem orelse mapped_mem;
1777 em.external_mapped_memory = if (parent_mapped_mem != null) mapped_mem else null;
1778 try em.dwarf.open(gpa, endian);
23781779 }
23791780
23801781 pub fn loadPath(
1782 em: *ElfModule,
23811783 gpa: Allocator,
23821784 elf_file_path: Path,
23831785 build_id: ?[]const u8,
23841786 expected_crc: ?u32,
23851787 parent_sections: *Dwarf.SectionArray,
23861788 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
2387 ) LoadError!Dwarf.ElfModule {
1789 ) LoadError!void {
23881790 const elf_file = elf_file_path.root_dir.handle.openFile(elf_file_path.sub_path, .{}) catch |err| switch (err) {
23891791 error.FileNotFound => return missing(),
23901792 else => return err,
......@@ -2407,7 +1809,7 @@ pub const ElfModule = struct {
24071809 };
24081810 errdefer std.posix.munmap(mapped_mem);
24091811
2410 return load(
1812 return em.load(
24111813 gpa,
24121814 mapped_mem,
24131815 build_id,
......@@ -2419,22 +1821,21 @@ pub const ElfModule = struct {
24191821 }
24201822};
24211823
2422pub fn getSymbol(di: *Dwarf, allocator: Allocator, address: u64) !std.debug.Symbol {
2423 if (di.findCompileUnit(address)) |compile_unit| {
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) {
1824pub fn getSymbol(di: *Dwarf, allocator: Allocator, endian: Endian, address: u64) !std.debug.Symbol {
1825 const compile_unit = di.findCompileUnit(endian, address) catch |err| switch (err) {
24351826 error.MissingDebugInfo, error.InvalidDebugInfo => return .{},
24361827 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 };
24381839}
24391840
24401841pub 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
24431844 return ptr[start..end];
24441845}
24451846
2446fn readAddress(r: *Reader, format: std.dwarf.Format, endian: Endian) !u64 {
1847pub fn readAddress(r: *Reader, format: std.dwarf.Format, endian: Endian) !u64 {
24471848 return switch (format) {
24481849 .@"32" => try r.takeInt(u32, endian),
24491850 .@"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();
3131const root = @import("root");
3232
3333allocator: Allocator,
34address_map: std.AutoHashMap(usize, *Module),
34address_map: std.AutoHashMapUnmanaged(usize, Module),
3535modules: if (native_os == .windows) std.ArrayListUnmanaged(WindowsModule) else void,
3636
3737pub const OpenError = error{
......@@ -40,29 +40,27 @@ pub const OpenError = error{
4040} || @typeInfo(@typeInfo(@TypeOf(SelfInfo.init)).@"fn".return_type.?).error_union.error_set;
4141
4242pub fn open(allocator: Allocator) OpenError!SelfInfo {
43 nosuspend {
44 if (builtin.strip_debug_info)
45 return error.MissingDebugInfo;
46 switch (native_os) {
47 .linux,
48 .freebsd,
49 .netbsd,
50 .dragonfly,
51 .openbsd,
52 .macos,
53 .solaris,
54 .illumos,
55 .windows,
56 => return try SelfInfo.init(allocator),
57 else => return error.UnsupportedOperatingSystem,
58 }
43 if (builtin.strip_debug_info)
44 return error.MissingDebugInfo;
45 switch (native_os) {
46 .linux,
47 .freebsd,
48 .netbsd,
49 .dragonfly,
50 .openbsd,
51 .macos,
52 .solaris,
53 .illumos,
54 .windows,
55 => return try SelfInfo.init(allocator),
56 else => return error.UnsupportedOperatingSystem,
5957 }
6058}
6159
6260pub fn init(allocator: Allocator) !SelfInfo {
6361 var debug_info: SelfInfo = .{
6462 .allocator = allocator,
65 .address_map = std.AutoHashMap(usize, *Module).init(allocator),
63 .address_map = .empty,
6664 .modules = if (native_os == .windows) .{} else {},
6765 };
6866
......@@ -110,7 +108,7 @@ pub fn deinit(self: *SelfInfo) void {
110108 mdi.deinit(self.allocator);
111109 self.allocator.destroy(mdi);
112110 }
113 self.address_map.deinit();
111 self.address_map.deinit(self.allocator);
114112 if (native_os == .windows) {
115113 for (self.modules.items) |module| {
116114 self.allocator.free(module.name);
......@@ -120,7 +118,7 @@ pub fn deinit(self: *SelfInfo) void {
120118 }
121119}
122120
123pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module {
121fn lookupModuleForAddress(self: *SelfInfo, address: usize) !Module.Lookup {
124122 if (builtin.target.os.tag.isDarwin()) {
125123 return self.lookupModuleDyld(address);
126124 } else if (native_os == .windows) {
......@@ -134,21 +132,65 @@ pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module {
134132 }
135133}
136134
137// Returns the module name for a given address.
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 {
135fn loadModuleDebugInfo(self: *SelfInfo, lookup: *const Module.Lookup, module: *Module) !void {
141136 if (builtin.target.os.tag.isDarwin()) {
142 return self.lookupModuleNameDyld(address);
137 @compileError("TODO");
143138 } else if (native_os == .windows) {
144 return self.lookupModuleNameWin32(address);
139 @compileError("TODO");
145140 } else if (native_os == .haiku) {
146 return null;
141 @compileError("TODO");
147142 } else if (builtin.target.cpu.arch.isWasm()) {
148 return null;
143 @compileError("TODO");
149144 } 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;
151173 }
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 };
152194}
153195
154196fn lookupModuleDyld(self: *SelfInfo, address: usize) !*Module {
......@@ -394,19 +436,24 @@ fn lookupModuleNameDl(self: *SelfInfo, address: usize) ?[]const u8 {
394436 return null;
395437}
396438
397fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {
439fn lookupModuleDl(self: *SelfInfo, address: usize) !Module.Lookup {
398440 var ctx: struct {
399441 // Input
400442 address: usize,
401443 // Output
402 base_address: usize = undefined,
403 name: []const u8 = undefined,
404 build_id: ?[]const u8 = null,
405 gnu_eh_frame: ?[]const u8 = null,
406 } = .{ .address = address };
444 lookup: Module.Lookup,
445 } = .{
446 .address = address,
447 .lookup = .{
448 .base_address = undefined,
449 .name = undefined,
450 .build_id = null,
451 .gnu_eh_frame = null,
452 },
453 };
407454 const CtxTy = @TypeOf(ctx);
408455
409 if (posix.dl_iterate_phdr(&ctx, error{Found}, struct {
456 posix.dl_iterate_phdr(&ctx, error{Found}, struct {
410457 fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void {
411458 _ = size;
412459 // The base address is too high
......@@ -423,8 +470,8 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {
423470 if (context.address >= seg_start and context.address < seg_end) {
424471 // Android libc uses NULL instead of an empty string to mark the
425472 // main program
426 context.name = mem.sliceTo(info.name, 0) orelse "";
427 context.base_address = info.addr;
473 context.lookup.name = mem.sliceTo(info.name, 0) orelse "";
474 context.lookup.base_address = info.addr;
428475 break;
429476 }
430477 } else return;
......@@ -440,10 +487,10 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {
440487 const note_type = mem.readInt(u32, note_bytes[8..12], native_endian);
441488 if (note_type != elf.NT_GNU_BUILD_ID) continue;
442489 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];
444491 },
445492 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];
447494 },
448495 else => {},
449496 }
......@@ -452,38 +499,36 @@ fn lookupModuleDl(self: *SelfInfo, address: usize) !*Module {
452499 // Stop the iteration
453500 return error.Found;
454501 }
455 }.callback)) {
456 return error.MissingDebugInfo;
457 } else |err| switch (err) {
458 error.Found => {},
459 }
502 }.callback) catch |err| switch (err) {
503 error.Found => return ctx.lookup,
504 };
505 if (true) return error.MissingDebugInfo;
460506
461 if (self.address_map.get(ctx.base_address)) |obj_di| {
507 if (self.address_map.get(ctx.lookup.base_address)) |obj_di| {
462508 return obj_di;
463509 }
464510
465 const obj_di = try self.allocator.create(Module);
466 errdefer self.allocator.destroy(obj_di);
467
468 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
469 if (ctx.gnu_eh_frame) |eh_frame_hdr| {
511 var sections: Dwarf.SectionArray = @splat(null);
512 if (ctx.lookup.gnu_eh_frame) |eh_frame_hdr| {
470513 // This is a special case - pointer offsets inside .eh_frame_hdr
471514 // are encoded relative to its base address, so we must use the
472515 // version that is already memory mapped, and not the one that
473516 // 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)] = .{
475518 .data = eh_frame_hdr,
476519 .owned = false,
477520 };
478521 }
479522
480 obj_di.* = try readElfDebugInfo(self.allocator, if (ctx.name.len > 0) ctx.name else null, ctx.build_id, null, &sections, null);
481 obj_di.base_address = ctx.base_address;
523 const obj_di = try self.allocator.create(Module);
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
483528 // 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
488533 return obj_di;
489534}
......@@ -625,49 +670,47 @@ pub const Module = switch (native_os) {
625670 }
626671
627672 pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, address: usize) !std.debug.Symbol {
628 nosuspend {
629 const result = try self.getOFileInfoForAddress(allocator, address);
630 if (result.symbol == null) return .{};
631
632 // Take the symbol name from the N_FUN STAB entry, we're going to
633 // use it if we fail to find the DWARF infos
634 const stab_symbol = mem.sliceTo(self.strings[result.symbol.?.strx..], 0);
635 if (result.o_file_info == null) return .{ .name = stab_symbol };
636
637 // Translate again the address, this time into an address inside the
638 // .o file
639 const relocated_address_o = result.o_file_info.?.addr_table.get(stab_symbol) orelse return .{
640 .name = "???",
641 };
673 const result = try self.getOFileInfoForAddress(allocator, address);
674 if (result.symbol == null) return .{};
675
676 // Take the symbol name from the N_FUN STAB entry, we're going to
677 // use it if we fail to find the DWARF infos
678 const stab_symbol = mem.sliceTo(self.strings[result.symbol.?.strx..], 0);
679 if (result.o_file_info == null) return .{ .name = stab_symbol };
680
681 // Translate again the address, this time into an address inside the
682 // .o file
683 const relocated_address_o = result.o_file_info.?.addr_table.get(stab_symbol) orelse return .{
684 .name = "???",
685 };
642686
643 const addr_off = result.relocated_address - result.symbol.?.addr;
644 const o_file_di = &result.o_file_info.?.di;
645 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {
646 return .{
647 .name = o_file_di.getSymbolName(relocated_address_o) orelse "???",
648 .compile_unit_name = compile_unit.die.getAttrString(
649 o_file_di,
650 std.dwarf.AT.name,
651 o_file_di.section(.debug_str),
652 compile_unit.*,
653 ) catch |err| switch (err) {
654 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 };
687 const addr_off = result.relocated_address - result.symbol.?.addr;
688 const o_file_di = &result.o_file_info.?.di;
689 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {
690 return .{
691 .name = o_file_di.getSymbolName(relocated_address_o) orelse "???",
692 .compile_unit_name = compile_unit.die.getAttrString(
693 o_file_di,
694 std.dwarf.AT.name,
695 o_file_di.section(.debug_str),
696 compile_unit.*,
697 ) catch |err| switch (err) {
698 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
668699 },
669 else => return err,
670 }
700 .source_location = o_file_di.getLineNumberInfo(
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,
671714 }
672715 }
673716
......@@ -676,35 +719,33 @@ pub const Module = switch (native_os) {
676719 symbol: ?*const MachoSymbol = null,
677720 o_file_info: ?*OFileInfo = null,
678721 } {
679 nosuspend {
680 // Translate the VA into an address into this object
681 const relocated_address = address - self.vmaddr_slide;
722 // Translate the VA into an address into this object
723 const relocated_address = address - self.vmaddr_slide;
682724
683 // Find the .o file where this symbol is defined
684 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{
685 .relocated_address = relocated_address,
686 };
725 // Find the .o file where this symbol is defined
726 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{
727 .relocated_address = relocated_address,
728 };
687729
688 // Check if its debug infos are already in the cache
689 const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0);
690 const o_file_info = self.ofiles.getPtr(o_file_path) orelse
691 (self.loadOFile(allocator, o_file_path) catch |err| switch (err) {
692 error.FileNotFound,
693 error.MissingDebugInfo,
694 error.InvalidDebugInfo,
695 => return .{
696 .relocated_address = relocated_address,
697 .symbol = symbol,
698 },
699 else => return err,
700 });
730 // Check if its debug infos are already in the cache
731 const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0);
732 const o_file_info = self.ofiles.getPtr(o_file_path) orelse
733 (self.loadOFile(allocator, o_file_path) catch |err| switch (err) {
734 error.FileNotFound,
735 error.MissingDebugInfo,
736 error.InvalidDebugInfo,
737 => return .{
738 .relocated_address = relocated_address,
739 .symbol = symbol,
740 },
741 else => return err,
742 });
701743
702 return .{
703 .relocated_address = relocated_address,
704 .symbol = symbol,
705 .o_file_info = o_file_info,
706 };
707 }
744 return .{
745 .relocated_address = relocated_address,
746 .symbol = symbol,
747 .o_file_info = o_file_info,
748 };
708749 }
709750
710751 pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {
......@@ -974,83 +1015,68 @@ fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module {
9741015 };
9751016}
9761017
977fn readCoffDebugInfo(gpa: Allocator, coff_obj: *coff.Coff) !Module {
978 nosuspend {
979 var di: Module = .{
980 .base_address = undefined,
981 .coff_image_base = coff_obj.getImageBase(),
982 .coff_section_headers = undefined,
983 .pdb = null,
984 .dwarf = null,
985 };
1018fn readCoffDebugInfo(allocator: Allocator, coff_obj: *coff.Coff) !Module {
1019 var di: Module = .{
1020 .base_address = undefined,
1021 .coff_image_base = coff_obj.getImageBase(),
1022 .coff_section_headers = undefined,
1023 };
9861024
987 if (coff_obj.getSectionByName(".debug_info")) |_| {
988 // This coff file has embedded DWARF debug info
989 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
990 errdefer for (sections) |section| if (section) |s| if (s.owned) gpa.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 }
1025 if (coff_obj.getSectionByName(".debug_info")) |_| {
1026 // This coff file has embedded DWARF debug info
1027 var sections: Dwarf.SectionArray = Dwarf.null_section_array;
1028 errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
10011029
1002 var dwarf: Dwarf = .{
1003 .endian = native_endian,
1004 .sections = sections,
1005 .is_macho = false,
1006 };
1007
1008 try Dwarf.open(&dwarf, gpa);
1009 di.dwarf = dwarf;
1030 inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |section, i| {
1031 sections[i] = if (coff_obj.getSectionByName("." ++ section.name)) |section_header| blk: {
1032 break :blk .{
1033 .data = try coff_obj.getSectionDataAlloc(section_header, allocator),
1034 .virtual_address = section_header.virtual_address,
1035 .owned = true,
1036 };
1037 } else null;
10101038 }
10111039
1012 const raw_path = try coff_obj.getPdbPath() orelse return di;
1013 const path = blk: {
1014 if (fs.path.isAbsolute(raw_path)) {
1015 break :blk raw_path;
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,
1040 var dwarf: Dwarf = .{
1041 .endian = native_endian,
1042 .sections = sections,
1043 .is_macho = false,
10301044 };
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);
1037 errdefer gpa.destroy(pdb_file_reader);
1046 try Dwarf.open(&dwarf, allocator);
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);
1042 try di.pdb.?.parseInfoStream();
1043 try di.pdb.?.parseDbiStream();
1062 di.pdb = Pdb.init(allocator, path) catch |err| switch (err) {
1063 error.FileNotFound, error.IsDir => {
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)
1046 return error.InvalidDebugInfo;
1072 if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age)
1073 return error.InvalidDebugInfo;
10471074
1048 // Only used by the pdb path
1049 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(gpa);
1050 errdefer gpa.free(di.coff_section_headers);
1075 // Only used by the pdb path
1076 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator);
1077 errdefer allocator.free(di.coff_section_headers);
10511078
1052 return di;
1053 }
1079 return di;
10541080}
10551081
10561082/// 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 {
10581084/// then this this function will recurse to attempt to load the debug sections from
10591085/// an external file.
10601086pub fn readElfDebugInfo(
1087 em: *Dwarf.ElfModule,
10611088 allocator: Allocator,
10621089 elf_filename: ?[]const u8,
10631090 build_id: ?[]const u8,
1064 expected_crc: ?u32,
10651091 parent_sections: *Dwarf.SectionArray,
1066 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
1067) !Dwarf.ElfModule {
1068 nosuspend {
1069 const elf_file = (if (elf_filename) |filename| blk: {
1070 break :blk fs.cwd().openFile(filename, .{});
1071 } else fs.openSelfExe(.{})) catch |err| switch (err) {
1072 error.FileNotFound => return error.MissingDebugInfo,
1073 else => return err,
1074 };
1092) !void {
1093 const elf_file = (if (elf_filename) |filename| blk: {
1094 break :blk fs.cwd().openFile(filename, .{});
1095 } else fs.openSelfExe(.{})) catch |err| switch (err) {
1096 error.FileNotFound => return error.MissingDebugInfo,
1097 else => return err,
1098 };
10751099
1076 const mapped_mem = try mapWholeFile(elf_file);
1077 return Dwarf.ElfModule.load(
1078 allocator,
1079 mapped_mem,
1080 build_id,
1081 expected_crc,
1082 parent_sections,
1083 parent_mapped_mem,
1084 elf_filename,
1085 );
1086 }
1100 const mapped_mem = try mapWholeFile(elf_file);
1101 return em.load(
1102 allocator,
1103 mapped_mem,
1104 build_id,
1105 null,
1106 parent_sections,
1107 null,
1108 elf_filename,
1109 );
10871110}
10881111
10891112const MachoSymbol = struct {
......@@ -1106,22 +1129,20 @@ const MachoSymbol = struct {
11061129/// Takes ownership of file, even on error.
11071130/// TODO it's weird to take ownership even on error, rework this code.
11081131fn mapWholeFile(file: File) ![]align(std.heap.page_size_min) const u8 {
1109 nosuspend {
1110 defer file.close();
1111
1112 const file_len = math.cast(usize, try file.getEndPos()) orelse math.maxInt(usize);
1113 const mapped_mem = try posix.mmap(
1114 null,
1115 file_len,
1116 posix.PROT.READ,
1117 .{ .TYPE = .SHARED },
1118 file.handle,
1119 0,
1120 );
1121 errdefer posix.munmap(mapped_mem);
1132 defer file.close();
1133
1134 const file_len = math.cast(usize, try file.getEndPos()) orelse math.maxInt(usize);
1135 const mapped_mem = try posix.mmap(
1136 null,
1137 file_len,
1138 posix.PROT.READ,
1139 .{ .TYPE = .SHARED },
1140 file.handle,
1141 0,
1142 );
1143 errdefer posix.munmap(mapped_mem);
11221144
1123 return mapped_mem;
1124 }
1145 return mapped_mem;
11251146}
11261147
11271148fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
......@@ -1172,7 +1193,7 @@ test machoSearchSymbols {
11721193/// Unwind a frame using MachO compact unwind info (from __unwind_info).
11731194/// If the compact encoding can't encode a way to unwind a frame, it will
11741195/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
1175pub fn unwindFrameMachO(
1196fn unwindFrameMachO(
11761197 allocator: Allocator,
11771198 base_address: usize,
11781199 context: *UnwindContext,
......@@ -1562,9 +1583,9 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
15621583///
15631584/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
15641585/// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
1565pub fn unwindFrameDwarf(
1586fn unwindFrameDwarf(
15661587 allocator: Allocator,
1567 di: *Dwarf,
1588 unwind: *Dwarf.Unwind,
15681589 base_address: usize,
15691590 context: *UnwindContext,
15701591 explicit_fde_offset: ?usize,
......@@ -1572,37 +1593,34 @@ pub fn unwindFrameDwarf(
15721593 if (!supports_unwinding) return error.UnsupportedCpuArchitecture;
15731594 if (context.pc == 0) return 0;
15741595
1575 const endian = di.endian;
1576
15771596 // Find the FDE and CIE
15781597 const cie, const fde = if (explicit_fde_offset) |fde_offset| blk: {
1579 const dwarf_section: Dwarf.Section.Id = .eh_frame;
1580 const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
1598 const frame_section = unwind.section(.eh_frame) orelse return error.MissingFDE;
15811599 if (fde_offset >= frame_section.len) return error.MissingFDE;
15821600
15831601 var fbr: std.Io.Reader = .fixed(frame_section);
15841602 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);
15871605 if (fde_entry_header.type != .fde) return error.MissingFDE;
15881606
15891607 const cie_offset = fde_entry_header.type.fde;
15901608 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);
15931611 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(
15961614 cie_entry_header.entry_bytes,
15971615 0,
15981616 true,
15991617 cie_entry_header.format,
1600 dwarf_section,
1618 .eh_frame,
16011619 cie_entry_header.length_offset,
16021620 @sizeOf(usize),
16031621 native_endian,
16041622 );
1605 const fde = try Dwarf.FrameDescriptionEntry.parse(
1623 const fde = try Dwarf.Unwind.FrameDescriptionEntry.parse(
16061624 fde_entry_header.entry_bytes,
16071625 0,
16081626 true,
......@@ -1616,33 +1634,33 @@ pub fn unwindFrameDwarf(
16161634 // `.eh_frame_hdr` may be incomplete. We'll try it first, but if the lookup fails, we fall
16171635 // back to loading `.eh_frame`/`.debug_frame` and using those from that point on.
16181636
1619 if (di.eh_frame_hdr) |header| hdr: {
1620 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else {
1621 try di.scanCieFdeInfo(allocator, base_address);
1622 di.eh_frame_hdr = null;
1637 if (unwind.eh_frame_hdr) |header| hdr: {
1638 const eh_frame_len = if (unwind.section(.eh_frame)) |eh_frame| eh_frame.len else {
1639 try unwind.scanCieFdeInfo(allocator, native_endian, base_address);
1640 unwind.eh_frame_hdr = null;
16231641 break :hdr;
16241642 };
16251643
1626 var cie: Dwarf.CommonInformationEntry = undefined;
1627 var fde: Dwarf.FrameDescriptionEntry = undefined;
1644 var cie: Dwarf.Unwind.CommonInformationEntry = undefined;
1645 var fde: Dwarf.Unwind.FrameDescriptionEntry = undefined;
16281646
16291647 header.findEntry(
16301648 eh_frame_len,
1631 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),
1649 @intFromPtr(unwind.section(.eh_frame_hdr).?.ptr),
16321650 context.pc,
16331651 &cie,
16341652 &fde,
1635 endian,
1653 native_endian,
16361654 ) catch |err| switch (err) {
16371655 error.MissingDebugInfo => {
16381656 // `.eh_frame_hdr` appears to be incomplete, so go ahead and populate `cie_map`
16391657 // 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
16421660 // Since `.eh_frame_hdr` is incomplete, we're very likely to get more lookup
16431661 // failures using it, and we've just built a complete, sorted list of FDEs
16441662 // anyway, so just stop using `.eh_frame_hdr` altogether.
1645 di.eh_frame_hdr = null;
1663 unwind.eh_frame_hdr = null;
16461664
16471665 break :hdr;
16481666 },
......@@ -1652,8 +1670,8 @@ pub fn unwindFrameDwarf(
16521670 break :blk .{ cie, fde };
16531671 }
16541672
1655 const index = std.sort.binarySearch(Dwarf.FrameDescriptionEntry, di.fde_list.items, context.pc, struct {
1656 pub fn compareFn(pc: usize, item: Dwarf.FrameDescriptionEntry) std.math.Order {
1673 const index = std.sort.binarySearch(Dwarf.Unwind.FrameDescriptionEntry, unwind.fde_list.items, context.pc, struct {
1674 pub fn compareFn(pc: usize, item: Dwarf.Unwind.FrameDescriptionEntry) std.math.Order {
16571675 if (pc < item.pc_begin) return .lt;
16581676
16591677 const range_end = item.pc_begin + item.pc_range;
......@@ -1663,15 +1681,16 @@ pub fn unwindFrameDwarf(
16631681 }
16641682 }.compareFn);
16651683
1666 const fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE;
1667 const cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
1684 const fde = if (index) |i| unwind.fde_list.items[i] else return error.MissingFDE;
1685 const cie = unwind.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
16681686
16691687 break :blk .{ cie, fde };
16701688 };
16711689
1690 // Do not set `compile_unit` because the spec states that CFIs
1691 // may not reference other debug sections anyway.
16721692 var expression_context: Dwarf.expression.Context = .{
16731693 .format = cie.format,
1674 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
16751694 .thread_context = context.thread_context,
16761695 .reg_context = context.reg_context,
16771696 .cfa = context.cfa,
......@@ -1679,7 +1698,7 @@ pub fn unwindFrameDwarf(
16791698
16801699 context.vm.reset();
16811700 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
16841703 const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde);
16851704 context.cfa = switch (row.cfa.rule) {
......@@ -2007,8 +2026,8 @@ pub const VirtualMachine = struct {
20072026 self: *VirtualMachine,
20082027 allocator: std.mem.Allocator,
20092028 pc: u64,
2010 cie: std.debug.Dwarf.CommonInformationEntry,
2011 fde: std.debug.Dwarf.FrameDescriptionEntry,
2029 cie: std.debug.Dwarf.Unwind.CommonInformationEntry,
2030 fde: std.debug.Dwarf.Unwind.FrameDescriptionEntry,
20122031 addr_size_bytes: u8,
20132032 endian: std.builtin.Endian,
20142033 ) !Row {
......@@ -2036,8 +2055,8 @@ pub const VirtualMachine = struct {
20362055 self: *VirtualMachine,
20372056 allocator: std.mem.Allocator,
20382057 pc: u64,
2039 cie: std.debug.Dwarf.CommonInformationEntry,
2040 fde: std.debug.Dwarf.FrameDescriptionEntry,
2058 cie: std.debug.Dwarf.Unwind.CommonInformationEntry,
2059 fde: std.debug.Dwarf.Unwind.FrameDescriptionEntry,
20412060 ) !Row {
20422061 return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), native_endian);
20432062 }
......@@ -2059,7 +2078,7 @@ pub const VirtualMachine = struct {
20592078 pub fn step(
20602079 self: *VirtualMachine,
20612080 allocator: std.mem.Allocator,
2062 cie: std.debug.Dwarf.CommonInformationEntry,
2081 cie: std.debug.Dwarf.Unwind.CommonInformationEntry,
20632082 is_initial: bool,
20642083 instruction: Dwarf.call_frame.Instruction,
20652084 ) !Row {