| ... | ... | @@ -663,7 +663,22 @@ pub const DwarfSection = enum { |
| 663 | 663 | pub const DwarfInfo = struct { |
| 664 | 664 | pub const Section = struct { |
| 665 | 665 | data: []const u8, |
| 666 | // Module-relative virtual address. |
| 667 | // Only set if the section data was loaded from disk. |
| 668 | virtual_address: ?usize = null, |
| 669 | // If `data` is owned by this DwarfInfo. |
| 666 | 670 | owned: bool, |
| 671 | |
| 672 | // For sections that are not memory mapped by the loader, this is an offset |
| 673 | // from `data.ptr` to where the section would have been mapped. Otherwise, |
| 674 | // `data` is directly backed by the section and the offset is zero. |
| 675 | pub fn virtualOffset(self: Section, base_address: usize) i64 { |
| 676 | return if (self.virtual_address) |va| |
| 677 | @as(i64, @intCast(base_address + va)) - |
| 678 | @as(i64, @intCast(@intFromPtr(self.data.ptr))) |
| 679 | else |
| 680 | 0; |
| 681 | } |
| 667 | 682 | }; |
| 668 | 683 | |
| 669 | 684 | const num_sections = std.enums.directEnumArrayLen(DwarfSection, 0); |
| ... | ... | @@ -690,6 +705,10 @@ pub const DwarfInfo = struct { |
| 690 | 705 | return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.data else null; |
| 691 | 706 | } |
| 692 | 707 | |
| 708 | pub fn sectionVirtualOffset(di: DwarfInfo, dwarf_section: DwarfSection, base_address: usize) ?i64 { |
| 709 | return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.virtualOffset(base_address) else null; |
| 710 | } |
| 711 | |
| 693 | 712 | pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void { |
| 694 | 713 | for (di.sections) |opt_section| { |
| 695 | 714 | if (opt_section) |s| if (s.owned) allocator.free(s.data); |
| ... | ... | @@ -1540,7 +1559,12 @@ pub const DwarfInfo = struct { |
| 1540 | 1559 | }; |
| 1541 | 1560 | } |
| 1542 | 1561 | |
| 1543 | | pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void { |
| 1562 | /// If .eh_frame_hdr is present, then only the header needs to be parsed. |
| 1563 | /// |
| 1564 | /// Otherwise, .eh_frame and .debug_frame are scanned and a sorted list |
| 1565 | /// of FDEs is built. In this case, the decoded PC ranges in the FDEs |
| 1566 | /// are all normalized to be relative to the module's base. |
| 1567 | pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, base_address: usize) !void { |
| 1544 | 1568 | if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: { |
| 1545 | 1569 | var stream = io.fixedBufferStream(eh_frame_hdr); |
| 1546 | 1570 | const reader = stream.reader(); |
| ... | ... | @@ -1582,15 +1606,15 @@ pub const DwarfInfo = struct { |
| 1582 | 1606 | |
| 1583 | 1607 | const frame_sections = [2]DwarfSection{ .eh_frame, .debug_frame }; |
| 1584 | 1608 | for (frame_sections) |frame_section| { |
| 1585 | | if (di.section(frame_section)) |eh_frame| { |
| 1586 | | var stream = io.fixedBufferStream(eh_frame); |
| 1609 | if (di.section(frame_section)) |section_data| { |
| 1610 | var stream = io.fixedBufferStream(section_data); |
| 1587 | 1611 | while (stream.pos < stream.buffer.len) { |
| 1588 | 1612 | const entry_header = try EntryHeader.read(&stream, frame_section, di.endian); |
| 1589 | 1613 | switch (entry_header.type) { |
| 1590 | 1614 | .cie => { |
| 1591 | 1615 | const cie = try CommonInformationEntry.parse( |
| 1592 | 1616 | entry_header.entry_bytes, |
| 1593 | | -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))), |
| 1617 | di.sectionVirtualOffset(frame_section, base_address).?, |
| 1594 | 1618 | true, |
| 1595 | 1619 | entry_header.is_64, |
| 1596 | 1620 | frame_section, |
| ... | ... | @@ -1604,7 +1628,7 @@ pub const DwarfInfo = struct { |
| 1604 | 1628 | const cie = di.cie_map.get(cie_offset) orelse return badDwarf(); |
| 1605 | 1629 | const fde = try FrameDescriptionEntry.parse( |
| 1606 | 1630 | entry_header.entry_bytes, |
| 1607 | | -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))), |
| 1631 | di.sectionVirtualOffset(frame_section, base_address).?, |
| 1608 | 1632 | true, |
| 1609 | 1633 | cie, |
| 1610 | 1634 | @sizeOf(usize), |
| ... | ... | @@ -1637,7 +1661,7 @@ pub const DwarfInfo = struct { |
| 1637 | 1661 | var fde: FrameDescriptionEntry = undefined; |
| 1638 | 1662 | |
| 1639 | 1663 | // In order to support reading .eh_frame from the ELF file (vs using the already-mapped section), |
| 1640 | | // scanAllUnwindInfo has already mapped any pc-relative offsets such that they we be relative to zero |
| 1664 | // scanAllUnwindInfo has already mapped any pc-relative offsets such that they will be relative to zero |
| 1641 | 1665 | // instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly |
| 1642 | 1666 | // as pointers will be decoded relative to the already-mapped .eh_frame. |
| 1643 | 1667 | var mapped_pc: usize = undefined; |
| ... | ... | @@ -1653,7 +1677,8 @@ pub const DwarfInfo = struct { |
| 1653 | 1677 | &fde, |
| 1654 | 1678 | ); |
| 1655 | 1679 | } else { |
| 1656 | | mapped_pc = context.pc - module_base_address; |
| 1680 | //mapped_pc = context.pc - module_base_address; |
| 1681 | mapped_pc = context.pc; |
| 1657 | 1682 | const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct { |
| 1658 | 1683 | pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order { |
| 1659 | 1684 | if (pc < mid_item.pc_begin) return .lt; |
| ... | ... | @@ -1819,12 +1844,9 @@ pub const UnwindContext = struct { |
| 1819 | 1844 | /// Initialize DWARF info. The caller has the responsibility to initialize most |
| 1820 | 1845 | /// the DwarfInfo fields before calling. `binary_mem` is the raw bytes of the |
| 1821 | 1846 | /// main binary file (not the secondary debug info file). |
| 1822 | | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void { |
| 1847 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 1823 | 1848 | try di.scanAllFunctions(allocator); |
| 1824 | 1849 | try di.scanAllCompileUnits(allocator); |
| 1825 | | |
| 1826 | | // Unwind info is not required |
| 1827 | | di.scanAllUnwindInfo(allocator, binary_mem) catch {}; |
| 1828 | 1850 | } |
| 1829 | 1851 | |
| 1830 | 1852 | /// This function is to make it handy to comment out the return and make it |
| ... | ... | @@ -1898,9 +1920,10 @@ fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerCo |
| 1898 | 1920 | else => null, |
| 1899 | 1921 | }; |
| 1900 | 1922 | |
| 1901 | | const ptr = if (base) |b| switch (value) { |
| 1902 | | .signed => |s| @as(u64, @intCast(s + @as(i64, @intCast(b)))), |
| 1903 | | .unsigned => |u| u + b, |
| 1923 | const ptr: u64 = if (base) |b| switch (value) { |
| 1924 | .signed => |s| @intCast(try math.add(i64, s, @as(i64, @intCast(b)))), |
| 1925 | // absptr can actually contain signed values in some cases (aarch64 MachO) |
| 1926 | .unsigned => |u| u +% b, |
| 1904 | 1927 | } else switch (value) { |
| 1905 | 1928 | .signed => |s| @as(u64, @intCast(s)), |
| 1906 | 1929 | .unsigned => |u| u, |
| ... | ... | @@ -2311,15 +2334,14 @@ pub const FrameDescriptionEntry = struct { |
| 2311 | 2334 | instructions: []const u8, |
| 2312 | 2335 | |
| 2313 | 2336 | /// This function expects to read the FDE starting at the PC Begin field. |
| 2314 | | /// The returned struct references memory backed by fde_bytes. |
| 2337 | /// The returned struct references memory backed by `fde_bytes`. |
| 2315 | 2338 | /// |
| 2316 | 2339 | /// `pc_rel_offset` specifies an offset to be applied to pc_rel_base values |
| 2317 | 2340 | /// used when decoding pointers. This should be set to zero if fde_bytes is |
| 2318 | | /// backed by the memory of the .eh_frame section in the running executable. |
| 2319 | | /// |
| 2341 | /// backed by the memory of a .eh_frame / .debug_frame section in the running executable. |
| 2320 | 2342 | /// Otherwise, it should be the relative offset to translate addresses from |
| 2321 | 2343 | /// where the section is currently stored in memory, to where it *would* be |
| 2322 | | /// stored at runtime: section runtime offset - backing section data base ptr. |
| 2344 | /// stored at runtime: section base addr - backing data base ptr. |
| 2323 | 2345 | /// |
| 2324 | 2346 | /// Similarly, `is_runtime` specifies this function is being called on a runtime |
| 2325 | 2347 | /// section, and so indirect pointers can be followed. |