| ... | ... | @@ -682,6 +682,8 @@ pub const DwarfInfo = struct { |
| 682 | 682 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, |
| 683 | 683 | func_list: std.ArrayListUnmanaged(Func) = .{}, |
| 684 | 684 | |
| 685 | eh_frame_hdr: ?ExceptionFrameHeader = null, |
| 686 | // These lookup tables are only used if `eh_frame_hdr` is null |
| 685 | 687 | cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{}, |
| 686 | 688 | // Sorted by start_pc |
| 687 | 689 | fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{}, |
| ... | ... | @@ -1489,60 +1491,79 @@ pub const DwarfInfo = struct { |
| 1489 | 1491 | } |
| 1490 | 1492 | |
| 1491 | 1493 | pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void { |
| 1492 | | var has_eh_frame_hdr = false; |
| 1493 | | if (di.section(.eh_frame_hdr)) |eh_frame_hdr| { |
| 1494 | | has_eh_frame_hdr = true; |
| 1494 | if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: { |
| 1495 | var stream = io.fixedBufferStream(eh_frame_hdr); |
| 1496 | const reader = stream.reader(); |
| 1495 | 1497 | |
| 1496 | | // TODO: Parse this section to get the lookup table, and skip loading the entire section |
| 1498 | const version = try reader.readByte(); |
| 1499 | if (version != 1) break :blk; |
| 1500 | |
| 1501 | const eh_frame_ptr_enc = try reader.readByte(); |
| 1502 | if (eh_frame_ptr_enc == EH.PE.omit) break :blk; |
| 1503 | const fde_count_enc = try reader.readByte(); |
| 1504 | if (fde_count_enc == EH.PE.omit) break :blk; |
| 1505 | const table_enc = try reader.readByte(); |
| 1506 | if (table_enc == EH.PE.omit) break :blk; |
| 1507 | |
| 1508 | const eh_frame_ptr = std.math.cast(usize, try readEhPointer(reader, eh_frame_ptr_enc, @sizeOf(usize), .{ |
| 1509 | .pc_rel_base = @ptrToInt(&eh_frame_hdr[stream.pos]), |
| 1510 | .follow_indirect = true, |
| 1511 | }, builtin.cpu.arch.endian()) orelse return badDwarf()) orelse return badDwarf(); |
| 1512 | |
| 1513 | const fde_count = std.math.cast(usize, try readEhPointer(reader, fde_count_enc, @sizeOf(usize), .{ |
| 1514 | .pc_rel_base = @ptrToInt(&eh_frame_hdr[stream.pos]), |
| 1515 | .follow_indirect = true, |
| 1516 | }, builtin.cpu.arch.endian()) orelse return badDwarf()) orelse return badDwarf(); |
| 1517 | |
| 1518 | const entry_size = try ExceptionFrameHeader.entrySize(table_enc); |
| 1519 | const entries_len = fde_count * entry_size; |
| 1520 | if (entries_len > eh_frame_hdr.len - stream.pos) return badDwarf(); |
| 1521 | |
| 1522 | di.eh_frame_hdr = .{ |
| 1523 | .eh_frame_ptr = eh_frame_ptr, |
| 1524 | .table_enc = table_enc, |
| 1525 | .fde_count = fde_count, |
| 1526 | .entries = eh_frame_hdr[stream.pos..][0..entries_len], |
| 1527 | }; |
| 1497 | 1528 | |
| 1498 | | _ = eh_frame_hdr; |
| 1529 | // No need to scan .eh_frame, we have a binary search table already |
| 1530 | return; |
| 1499 | 1531 | } |
| 1500 | 1532 | |
| 1501 | 1533 | if (di.section(.eh_frame)) |eh_frame| { |
| 1502 | 1534 | var stream = io.fixedBufferStream(eh_frame); |
| 1503 | | const reader = stream.reader(); |
| 1504 | | |
| 1505 | 1535 | while (stream.pos < stream.buffer.len) { |
| 1506 | | const length_offset = stream.pos; |
| 1507 | | var length: usize = try reader.readInt(u32, di.endian); |
| 1508 | | if (length == 0) break; |
| 1509 | | |
| 1510 | | var is_64 = length == math.maxInt(u32); |
| 1511 | | if (is_64) { |
| 1512 | | length = std.math.cast(usize, try reader.readInt(u64, di.endian)) orelse return error.LengthOverflow; |
| 1513 | | } |
| 1514 | | |
| 1515 | | const id_len = @as(u8, if (is_64) 8 else 4); |
| 1516 | | const id = if (is_64) try reader.readInt(u64, di.endian) else try reader.readInt(u32, di.endian); |
| 1517 | | const entry_bytes = eh_frame[stream.pos..][0 .. length - id_len]; |
| 1518 | | |
| 1519 | | if (id == 0) { |
| 1520 | | const cie = try CommonInformationEntry.parse( |
| 1521 | | entry_bytes, |
| 1522 | | @ptrToInt(eh_frame.ptr), |
| 1523 | | @ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr), |
| 1524 | | true, |
| 1525 | | length_offset, |
| 1526 | | @sizeOf(usize), |
| 1527 | | di.endian, |
| 1528 | | ); |
| 1529 | | try di.cie_map.put(allocator, length_offset, cie); |
| 1530 | | } else { |
| 1531 | | const cie_offset = stream.pos - id_len - id; |
| 1532 | | const cie = di.cie_map.get(cie_offset) orelse return badDwarf(); |
| 1533 | | const fde = try FrameDescriptionEntry.parse( |
| 1534 | | entry_bytes, |
| 1535 | | @ptrToInt(eh_frame.ptr), |
| 1536 | | @ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr), |
| 1537 | | true, |
| 1538 | | cie, |
| 1539 | | @sizeOf(usize), |
| 1540 | | di.endian, |
| 1541 | | ); |
| 1542 | | try di.fde_list.append(allocator, fde); |
| 1536 | const entry_header = try EntryHeader.read(&stream, di.endian); |
| 1537 | switch (entry_header.type) { |
| 1538 | .cie => { |
| 1539 | const cie = try CommonInformationEntry.parse( |
| 1540 | entry_header.entry_bytes, |
| 1541 | -@intCast(isize, @ptrToInt(binary_mem.ptr)), |
| 1542 | //@ptrToInt(eh_frame.ptr), |
| 1543 | //@ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr), |
| 1544 | true, |
| 1545 | entry_header.length_offset, |
| 1546 | @sizeOf(usize), |
| 1547 | di.endian, |
| 1548 | ); |
| 1549 | try di.cie_map.put(allocator, entry_header.length_offset, cie); |
| 1550 | }, |
| 1551 | .fde => |cie_offset| { |
| 1552 | const cie = di.cie_map.get(cie_offset) orelse return badDwarf(); |
| 1553 | const fde = try FrameDescriptionEntry.parse( |
| 1554 | entry_header.entry_bytes, |
| 1555 | -@intCast(isize, @ptrToInt(binary_mem.ptr)), |
| 1556 | //@ptrToInt(eh_frame.ptr), |
| 1557 | //@ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr), |
| 1558 | true, |
| 1559 | cie, |
| 1560 | @sizeOf(usize), |
| 1561 | di.endian, |
| 1562 | ); |
| 1563 | try di.fde_list.append(allocator, fde); |
| 1564 | }, |
| 1565 | .terminator => break, |
| 1543 | 1566 | } |
| 1544 | | |
| 1545 | | stream.pos += entry_bytes.len; |
| 1546 | 1567 | } |
| 1547 | 1568 | |
| 1548 | 1569 | // TODO: Avoiding sorting if has_eh_frame_hdr exists |
| ... | ... | @@ -1560,59 +1581,67 @@ pub const DwarfInfo = struct { |
| 1560 | 1581 | if (context.pc == 0) return; |
| 1561 | 1582 | |
| 1562 | 1583 | // TODO: Handle signal frame (ie. use_prev_instr in libunwind) |
| 1563 | | // TOOD: Use eh_frame_hdr to accelerate the search if available |
| 1564 | | //const eh_frame_hdr = di.section(.eh_frame_hdr) orelse return error.MissingDebugInfo; |
| 1565 | | |
| 1566 | | // Find the FDE |
| 1567 | | const unmapped_pc = context.pc - module_base_address; |
| 1568 | | const index = std.sort.binarySearch(FrameDescriptionEntry, unmapped_pc, di.fde_list.items, {}, struct { |
| 1569 | | pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order { |
| 1570 | | if (pc < mid_item.pc_begin) { |
| 1571 | | return .lt; |
| 1572 | | } else { |
| 1573 | | const range_end = mid_item.pc_begin + mid_item.pc_range; |
| 1574 | | if (pc < range_end) { |
| 1575 | | return .eq; |
| 1576 | | } |
| 1577 | 1584 | |
| 1578 | | return .gt; |
| 1579 | | } |
| 1580 | | } |
| 1581 | | }.compareFn); |
| 1585 | // Find the FDE and CIE |
| 1586 | var cie: CommonInformationEntry = undefined; |
| 1587 | var fde: FrameDescriptionEntry = undefined; |
| 1582 | 1588 | |
| 1583 | | const fde = if (index) |i| &di.fde_list.items[i] else return error.MissingFDE; |
| 1584 | | const cie = di.cie_map.getPtr(fde.cie_length_offset) orelse return error.MissingCIE; |
| 1589 | // In order to support reading .eh_frame from the ELF file (vs using the already-mapped section), |
| 1590 | // scanAllUnwindInfo has already mapped any pc-relative offsets such that they we be relative to zero |
| 1591 | // instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly |
| 1592 | // as pointers will be decoded relative to the alreayd-mapped .eh_frame. |
| 1593 | var mapped_pc: usize = undefined; |
| 1585 | 1594 | |
| 1586 | | // const prev_cfa = context.cfa; |
| 1587 | | // const prev_pc = context.pc; |
| 1595 | if (di.eh_frame_hdr) |header| { |
| 1596 | mapped_pc = context.pc; |
| 1597 | try header.findEntry(context.isValidMemory, @ptrToInt(di.section(.eh_frame_hdr).?.ptr), mapped_pc, &cie, &fde); |
| 1598 | } else { |
| 1599 | mapped_pc = context.pc - module_base_address; |
| 1600 | const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct { |
| 1601 | pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order { |
| 1602 | if (pc < mid_item.pc_begin) { |
| 1603 | return .lt; |
| 1604 | } else { |
| 1605 | const range_end = mid_item.pc_begin + mid_item.pc_range; |
| 1606 | if (pc < range_end) { |
| 1607 | return .eq; |
| 1608 | } |
| 1588 | 1609 | |
| 1589 | | // TODO: Cache this on self so we can re-use the allocations? |
| 1590 | | var vm = call_frame.VirtualMachine{}; |
| 1591 | | defer vm.deinit(allocator); |
| 1610 | return .gt; |
| 1611 | } |
| 1612 | } |
| 1613 | }.compareFn); |
| 1592 | 1614 | |
| 1593 | | const row = try vm.runToNative(allocator, unmapped_pc, cie.*, fde.*); |
| 1615 | fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE; |
| 1616 | cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE; |
| 1617 | } |
| 1618 | |
| 1619 | context.vm.reset(); |
| 1620 | |
| 1621 | const row = try context.vm.runToNative(allocator, mapped_pc, cie, fde); |
| 1594 | 1622 | context.cfa = switch (row.cfa.rule) { |
| 1595 | 1623 | .val_offset => |offset| blk: { |
| 1596 | 1624 | const register = row.cfa.register orelse return error.InvalidCFARule; |
| 1597 | 1625 | const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); |
| 1598 | | |
| 1599 | | // TODO: Check isValidMemory? |
| 1600 | 1626 | break :blk try call_frame.applyOffset(value, offset); |
| 1601 | 1627 | }, |
| 1602 | 1628 | .expression => |expression| { |
| 1603 | 1629 | |
| 1604 | 1630 | // TODO: Evaluate expression |
| 1605 | 1631 | _ = expression; |
| 1632 | |
| 1606 | 1633 | return error.UnimplementedTODO; |
| 1607 | 1634 | }, |
| 1608 | 1635 | else => return error.InvalidCFARule, |
| 1609 | 1636 | }; |
| 1610 | 1637 | |
| 1638 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; |
| 1639 | |
| 1611 | 1640 | // Update the context with the previous frame's values |
| 1612 | 1641 | var next_ucontext = context.ucontext; |
| 1613 | 1642 | |
| 1614 | 1643 | var has_next_ip = false; |
| 1615 | | for (vm.rowColumns(row)) |column| { |
| 1644 | for (context.vm.rowColumns(row)) |column| { |
| 1616 | 1645 | if (column.register) |register| { |
| 1617 | 1646 | const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx); |
| 1618 | 1647 | if (register == cie.return_address_register) { |
| ... | ... | @@ -1640,17 +1669,24 @@ pub const UnwindContext = struct { |
| 1640 | 1669 | pc: usize, |
| 1641 | 1670 | ucontext: os.ucontext_t, |
| 1642 | 1671 | reg_ctx: abi.RegisterContext, |
| 1672 | isValidMemory: *const fn (address: usize) bool, |
| 1673 | vm: call_frame.VirtualMachine = .{}, |
| 1643 | 1674 | |
| 1644 | | pub fn init(ucontext: *const os.ucontext_t) !UnwindContext { |
| 1675 | pub fn init(ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext { |
| 1645 | 1676 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); |
| 1646 | 1677 | return .{ |
| 1647 | 1678 | .cfa = null, |
| 1648 | 1679 | .pc = pc, |
| 1649 | 1680 | .ucontext = ucontext.*, |
| 1650 | 1681 | .reg_ctx = undefined, |
| 1682 | .isValidMemory = isValidMemory, |
| 1651 | 1683 | }; |
| 1652 | 1684 | } |
| 1653 | 1685 | |
| 1686 | pub fn deinit(self: *UnwindContext, allocator: mem.Allocator) void { |
| 1687 | self.vm.deinit(allocator); |
| 1688 | } |
| 1689 | |
| 1654 | 1690 | pub fn getFp(self: *const UnwindContext) !usize { |
| 1655 | 1691 | return mem.readIntSliceNative(usize, try abi.regBytes(&self.ucontext, abi.fpRegNum(self.reg_ctx), self.reg_ctx)); |
| 1656 | 1692 | } |
| ... | ... | @@ -1694,7 +1730,7 @@ const EhPointerContext = struct { |
| 1694 | 1730 | |
| 1695 | 1731 | // Whether or not to follow indirect pointers. This should only be |
| 1696 | 1732 | // used when decoding pointers at runtime using the current process's |
| 1697 | | // debug info. |
| 1733 | // debug info |
| 1698 | 1734 | follow_indirect: bool, |
| 1699 | 1735 | |
| 1700 | 1736 | // These relative addressing modes are only used in specific cases, and |
| ... | ... | @@ -1762,15 +1798,178 @@ fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerCo |
| 1762 | 1798 | } |
| 1763 | 1799 | } |
| 1764 | 1800 | |
| 1801 | /// This represents the decoded .eh_frame_hdr header |
| 1802 | pub const ExceptionFrameHeader = struct { |
| 1803 | eh_frame_ptr: usize, |
| 1804 | table_enc: u8, |
| 1805 | fde_count: usize, |
| 1806 | entries: []const u8, |
| 1807 | |
| 1808 | pub fn entrySize(table_enc: u8) !u8 { |
| 1809 | return switch (table_enc & EH.PE.type_mask) { |
| 1810 | EH.PE.udata2, |
| 1811 | EH.PE.sdata2, |
| 1812 | => 4, |
| 1813 | EH.PE.udata4, |
| 1814 | EH.PE.sdata4, |
| 1815 | => 8, |
| 1816 | EH.PE.udata8, |
| 1817 | EH.PE.sdata8, |
| 1818 | => 16, |
| 1819 | // This is a binary search table, so all entries must be the same length |
| 1820 | else => return badDwarf(), |
| 1821 | }; |
| 1822 | } |
| 1823 | |
| 1824 | pub fn findEntry( |
| 1825 | self: ExceptionFrameHeader, |
| 1826 | isValidMemory: *const fn (address: usize) bool, |
| 1827 | eh_frame_hdr_ptr: usize, |
| 1828 | pc: usize, |
| 1829 | cie: *CommonInformationEntry, |
| 1830 | fde: *FrameDescriptionEntry, |
| 1831 | ) !void { |
| 1832 | const entry_size = try entrySize(self.table_enc); |
| 1833 | |
| 1834 | var left: usize = 0; |
| 1835 | var len: usize = self.fde_count; |
| 1836 | |
| 1837 | var stream = io.fixedBufferStream(self.entries); |
| 1838 | const reader = stream.reader(); |
| 1839 | |
| 1840 | while (len > 1) { |
| 1841 | const mid = left + len / 2; |
| 1842 | |
| 1843 | try stream.seekTo(mid * entry_size); |
| 1844 | const pc_begin = try readEhPointer(reader, self.table_enc, @sizeOf(usize), .{ |
| 1845 | .pc_rel_base = @ptrToInt(&self.entries[stream.pos]), |
| 1846 | .follow_indirect = true, |
| 1847 | .data_rel_base = eh_frame_hdr_ptr, |
| 1848 | }, builtin.cpu.arch.endian()) orelse return badDwarf(); |
| 1849 | |
| 1850 | if (pc >= pc_begin) left = mid; |
| 1851 | if (pc == pc_begin) break; |
| 1852 | |
| 1853 | len /= 2; |
| 1854 | } |
| 1855 | |
| 1856 | try stream.seekTo(left * entry_size); |
| 1857 | |
| 1858 | // Read past pc_begin |
| 1859 | _ = try readEhPointer(reader, self.table_enc, @sizeOf(usize), .{ |
| 1860 | .pc_rel_base = @ptrToInt(&self.entries[stream.pos]), |
| 1861 | .follow_indirect = true, |
| 1862 | .data_rel_base = eh_frame_hdr_ptr, |
| 1863 | }, builtin.cpu.arch.endian()) orelse return badDwarf(); |
| 1864 | |
| 1865 | const fde_ptr = try readEhPointer(reader, self.table_enc, @sizeOf(usize), .{ |
| 1866 | .pc_rel_base = @ptrToInt(&self.entries[stream.pos]), |
| 1867 | .follow_indirect = true, |
| 1868 | .data_rel_base = eh_frame_hdr_ptr, |
| 1869 | }, builtin.cpu.arch.endian()) orelse return badDwarf(); |
| 1870 | |
| 1871 | // TODO: Should this also do isValidMemory(fde_ptr) + 11 (worst case header size)? |
| 1872 | |
| 1873 | // The length of the .eh_frame section is unknown at this point, since .eh_frame_hdr only provides the start |
| 1874 | if (!isValidMemory(fde_ptr) or fde_ptr < self.eh_frame_ptr) return badDwarf(); |
| 1875 | const eh_frame = @intToPtr([*]const u8, self.eh_frame_ptr)[0..math.maxInt(usize)]; |
| 1876 | const fde_offset = fde_ptr - self.eh_frame_ptr; |
| 1877 | |
| 1878 | var eh_frame_stream = io.fixedBufferStream(eh_frame); |
| 1879 | try eh_frame_stream.seekTo(fde_offset); |
| 1880 | |
| 1881 | const fde_entry_header = try EntryHeader.read(&eh_frame_stream, builtin.cpu.arch.endian()); |
| 1882 | if (!isValidMemory(@ptrToInt(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]))) return badDwarf(); |
| 1883 | if (fde_entry_header.type != .fde) return badDwarf(); |
| 1884 | |
| 1885 | const cie_offset = fde_entry_header.type.fde; |
| 1886 | try eh_frame_stream.seekTo(cie_offset); |
| 1887 | const cie_entry_header = try EntryHeader.read(&eh_frame_stream, builtin.cpu.arch.endian()); |
| 1888 | if (!isValidMemory(@ptrToInt(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]))) return badDwarf(); |
| 1889 | if (cie_entry_header.type != .cie) return badDwarf(); |
| 1890 | |
| 1891 | cie.* = try CommonInformationEntry.parse( |
| 1892 | cie_entry_header.entry_bytes, |
| 1893 | 0, |
| 1894 | true, |
| 1895 | cie_entry_header.length_offset, |
| 1896 | @sizeOf(usize), |
| 1897 | builtin.cpu.arch.endian(), |
| 1898 | ); |
| 1899 | |
| 1900 | fde.* = try FrameDescriptionEntry.parse( |
| 1901 | fde_entry_header.entry_bytes, |
| 1902 | 0, |
| 1903 | true, |
| 1904 | cie.*, |
| 1905 | @sizeOf(usize), |
| 1906 | builtin.cpu.arch.endian(), |
| 1907 | ); |
| 1908 | } |
| 1909 | }; |
| 1910 | |
| 1911 | pub const EntryHeader = struct { |
| 1912 | /// Offset of the length in the backing buffer |
| 1913 | length_offset: usize, |
| 1914 | is_64: bool, |
| 1915 | type: union(enum) { |
| 1916 | cie, |
| 1917 | /// Value is the offset of the corresponding CIE |
| 1918 | fde: u64, |
| 1919 | terminator: void, |
| 1920 | }, |
| 1921 | /// The entry's contents, not including the ID field |
| 1922 | entry_bytes: []const u8, |
| 1923 | |
| 1924 | /// Reads a header for either an FDE or a CIE, then advances the stream to the position after the trailing structure. |
| 1925 | /// `stream` must be a stream backed by the .eh_frame section. |
| 1926 | pub fn read(stream: *std.io.FixedBufferStream([]const u8), endian: std.builtin.Endian) !EntryHeader { |
| 1927 | const reader = stream.reader(); |
| 1928 | const length_offset = stream.pos; |
| 1929 | |
| 1930 | var is_64: bool = undefined; |
| 1931 | const length = math.cast(usize, try readUnitLength(reader, endian, &is_64)) orelse return badDwarf(); |
| 1932 | if (length == 0) return .{ |
| 1933 | .length_offset = length_offset, |
| 1934 | .is_64 = is_64, |
| 1935 | .type = .{ .terminator = {} }, |
| 1936 | .entry_bytes = &.{}, |
| 1937 | }; |
| 1938 | |
| 1939 | const id_len = @as(u8, if (is_64) 8 else 4); |
| 1940 | const id = if (is_64) try reader.readInt(u64, endian) else try reader.readInt(u32, endian); |
| 1941 | const entry_bytes = stream.buffer[stream.pos..][0 .. length - id_len]; |
| 1942 | |
| 1943 | const result = EntryHeader{ |
| 1944 | .length_offset = length_offset, |
| 1945 | .is_64 = is_64, |
| 1946 | .type = switch (id) { |
| 1947 | 0 => .{ .cie = {} }, |
| 1948 | // TODO: Support CommonInformationEntry.dwarf32_id, CommonInformationEntry.dwarf64_id |
| 1949 | else => .{ .fde = stream.pos - id_len - id }, |
| 1950 | }, |
| 1951 | .entry_bytes = entry_bytes, |
| 1952 | }; |
| 1953 | |
| 1954 | stream.pos += entry_bytes.len; |
| 1955 | return result; |
| 1956 | } |
| 1957 | |
| 1958 | /// The length of the entry including the ID field, but not the length field itself |
| 1959 | pub fn entryLength(self: EntryHeader) usize { |
| 1960 | return self.entry_bytes.len + @as(u8, if (self.is_64) 8 else 4); |
| 1961 | } |
| 1962 | }; |
| 1963 | |
| 1765 | 1964 | pub const CommonInformationEntry = struct { |
| 1766 | 1965 | // Used in .eh_frame |
| 1767 | 1966 | pub const eh_id = 0; |
| 1768 | 1967 | |
| 1769 | 1968 | // Used in .debug_frame (DWARF32) |
| 1770 | | pub const dwarf32_id = std.math.maxInt(u32); |
| 1969 | pub const dwarf32_id = math.maxInt(u32); |
| 1771 | 1970 | |
| 1772 | 1971 | // Used in .debug_frame (DWARF64) |
| 1773 | | pub const dwarf64_id = std.math.maxInt(u64); |
| 1972 | pub const dwarf64_id = math.maxInt(u64); |
| 1774 | 1973 | |
| 1775 | 1974 | // Offset of the length field of this entry in the eh_frame section. |
| 1776 | 1975 | // This is the key that FDEs use to reference CIEs. |
| ... | ... | @@ -1804,12 +2003,17 @@ pub const CommonInformationEntry = struct { |
| 1804 | 2003 | return false; |
| 1805 | 2004 | } |
| 1806 | 2005 | |
| 1807 | | // This function expects to read the CIE starting with the version field. |
| 1808 | | // The returned struct references memory backed by cie_bytes. |
| 2006 | /// This function expects to read the CIE starting with the version field. |
| 2007 | /// The returned struct references memory backed by cie_bytes. |
| 2008 | /// |
| 2009 | /// See the FrameDescriptionEntry.parse documentation for the description |
| 2010 | /// of `pc_rel_offset` and `is_runtime`. |
| 2011 | /// |
| 2012 | /// `length_offset` specifies the offset of this CIE's length field in the |
| 2013 | /// .eh_frame section. |
| 1809 | 2014 | pub fn parse( |
| 1810 | 2015 | cie_bytes: []const u8, |
| 1811 | | section_base: u64, |
| 1812 | | section_offset: u64, |
| 2016 | pc_rel_offset: i64, |
| 1813 | 2017 | is_runtime: bool, |
| 1814 | 2018 | length_offset: u64, |
| 1815 | 2019 | addr_size_bytes: u8, |
| ... | ... | @@ -1879,7 +2083,7 @@ pub const CommonInformationEntry = struct { |
| 1879 | 2083 | personality_enc.?, |
| 1880 | 2084 | addr_size_bytes, |
| 1881 | 2085 | .{ |
| 1882 | | .pc_rel_base = @ptrToInt(&cie_bytes[stream.pos]) - section_base + section_offset, |
| 2086 | .pc_rel_base = try pcRelBase(@ptrToInt(&cie_bytes[stream.pos]), pc_rel_offset), |
| 1883 | 2087 | .follow_indirect = is_runtime, |
| 1884 | 2088 | }, |
| 1885 | 2089 | endian, |
| ... | ... | @@ -1926,11 +2130,22 @@ pub const FrameDescriptionEntry = struct { |
| 1926 | 2130 | aug_data: []const u8, |
| 1927 | 2131 | instructions: []const u8, |
| 1928 | 2132 | |
| 1929 | | // This function expects to read the FDE starting with the PC Begin field |
| 2133 | /// This function expects to read the FDE starting at the PC Begin field. |
| 2134 | /// The returned struct references memory backed by fde_bytes. |
| 2135 | /// |
| 2136 | /// `pc_rel_offset` specifies an offset to be applied to pc_rel_base values |
| 2137 | /// used when decoding pointers. This should be set to zero if fde_bytes is |
| 2138 | /// backed by the memory of the .eh_frame section in the running executable. |
| 2139 | /// |
| 2140 | /// Otherwise, it should be the relative offset to translate addresses from |
| 2141 | /// where the section is currently stored in memory, to where it *would* be |
| 2142 | /// stored at runtime: section runtime offset - backing section data base ptr. |
| 2143 | /// |
| 2144 | /// Similarly, `is_runtime` specifies this function is being called on a runtime section, and so |
| 2145 | /// indirect pointers can be followed. |
| 1930 | 2146 | pub fn parse( |
| 1931 | 2147 | fde_bytes: []const u8, |
| 1932 | | section_base: u64, |
| 1933 | | section_offset: u64, |
| 2148 | pc_rel_offset: i64, |
| 1934 | 2149 | is_runtime: bool, |
| 1935 | 2150 | cie: CommonInformationEntry, |
| 1936 | 2151 | addr_size_bytes: u8, |
| ... | ... | @@ -1946,7 +2161,7 @@ pub const FrameDescriptionEntry = struct { |
| 1946 | 2161 | cie.fde_pointer_enc, |
| 1947 | 2162 | addr_size_bytes, |
| 1948 | 2163 | .{ |
| 1949 | | .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) - section_base + section_offset, |
| 2164 | .pc_rel_base = try pcRelBase(@ptrToInt(&fde_bytes[stream.pos]), pc_rel_offset), |
| 1950 | 2165 | .follow_indirect = is_runtime, |
| 1951 | 2166 | }, |
| 1952 | 2167 | endian, |
| ... | ... | @@ -1975,7 +2190,7 @@ pub const FrameDescriptionEntry = struct { |
| 1975 | 2190 | cie.lsda_pointer_enc, |
| 1976 | 2191 | addr_size_bytes, |
| 1977 | 2192 | .{ |
| 1978 | | .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) - section_base + section_offset, |
| 2193 | .pc_rel_base = try pcRelBase(@ptrToInt(&fde_bytes[stream.pos]), pc_rel_offset), |
| 1979 | 2194 | .follow_indirect = is_runtime, |
| 1980 | 2195 | }, |
| 1981 | 2196 | endian, |
| ... | ... | @@ -1998,3 +2213,11 @@ pub const FrameDescriptionEntry = struct { |
| 1998 | 2213 | }; |
| 1999 | 2214 | } |
| 2000 | 2215 | }; |
| 2216 | |
| 2217 | fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize { |
| 2218 | if (pc_rel_offset < 0) { |
| 2219 | return math.sub(usize, field_ptr, @intCast(usize, -pc_rel_offset)); |
| 2220 | } else { |
| 2221 | return math.add(usize, field_ptr, @intCast(usize, pc_rel_offset)); |
| 2222 | } |
| 2223 | } |