| ... | @@ -682,6 +682,8 @@ pub const DwarfInfo = struct { | ... | @@ -682,6 +682,8 @@ pub const DwarfInfo = struct { |
| 682 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, | 682 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, |
| 683 | func_list: std.ArrayListUnmanaged(Func) = .{}, | 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 | cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{}, | 687 | cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{}, |
| 686 | // Sorted by start_pc | 688 | // Sorted by start_pc |
| 687 | fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{}, | 689 | fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{}, |
| ... | @@ -1489,60 +1491,79 @@ pub const DwarfInfo = struct { | ... | @@ -1489,60 +1491,79 @@ pub const DwarfInfo = struct { |
| 1489 | } | 1491 | } |
| 1490 | | 1492 | |
| 1491 | pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void { | 1493 | pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void { |
| 1492 | var has_eh_frame_hdr = false; | 1494 | if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: { |
| 1493 | if (di.section(.eh_frame_hdr)) |eh_frame_hdr| { | 1495 | var stream = io.fixedBufferStream(eh_frame_hdr); |
| 1494 | has_eh_frame_hdr = true; | 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 | if (di.section(.eh_frame)) |eh_frame| { | 1533 | if (di.section(.eh_frame)) |eh_frame| { |
| 1502 | var stream = io.fixedBufferStream(eh_frame); | 1534 | var stream = io.fixedBufferStream(eh_frame); |
| 1503 | const reader = stream.reader(); | | |
| 1504 | | | |
| 1505 | while (stream.pos < stream.buffer.len) { | 1535 | while (stream.pos < stream.buffer.len) { |
| 1506 | const length_offset = stream.pos; | 1536 | const entry_header = try EntryHeader.read(&stream, di.endian); |
| 1507 | var length: usize = try reader.readInt(u32, di.endian); | 1537 | switch (entry_header.type) { |
| 1508 | if (length == 0) break; | 1538 | .cie => { |
| 1509 | | 1539 | const cie = try CommonInformationEntry.parse( |
| 1510 | var is_64 = length == math.maxInt(u32); | 1540 | entry_header.entry_bytes, |
| 1511 | if (is_64) { | 1541 | -@intCast(isize, @ptrToInt(binary_mem.ptr)), |
| 1512 | length = std.math.cast(usize, try reader.readInt(u64, di.endian)) orelse return error.LengthOverflow; | 1542 | //@ptrToInt(eh_frame.ptr), |
| 1513 | } | 1543 | //@ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr), |
| 1514 | | 1544 | true, |
| 1515 | const id_len = @as(u8, if (is_64) 8 else 4); | 1545 | entry_header.length_offset, |
| 1516 | const id = if (is_64) try reader.readInt(u64, di.endian) else try reader.readInt(u32, di.endian); | 1546 | @sizeOf(usize), |
| 1517 | const entry_bytes = eh_frame[stream.pos..][0 .. length - id_len]; | 1547 | di.endian, |
| 1518 | | 1548 | ); |
| 1519 | if (id == 0) { | 1549 | try di.cie_map.put(allocator, entry_header.length_offset, cie); |
| 1520 | const cie = try CommonInformationEntry.parse( | 1550 | }, |
| 1521 | entry_bytes, | 1551 | .fde => |cie_offset| { |
| 1522 | @ptrToInt(eh_frame.ptr), | 1552 | const cie = di.cie_map.get(cie_offset) orelse return badDwarf(); |
| 1523 | @ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr), | 1553 | const fde = try FrameDescriptionEntry.parse( |
| 1524 | true, | 1554 | entry_header.entry_bytes, |
| 1525 | length_offset, | 1555 | -@intCast(isize, @ptrToInt(binary_mem.ptr)), |
| 1526 | @sizeOf(usize), | 1556 | //@ptrToInt(eh_frame.ptr), |
| 1527 | di.endian, | 1557 | //@ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr), |
| 1528 | ); | 1558 | true, |
| 1529 | try di.cie_map.put(allocator, length_offset, cie); | 1559 | cie, |
| 1530 | } else { | 1560 | @sizeOf(usize), |
| 1531 | const cie_offset = stream.pos - id_len - id; | 1561 | di.endian, |
| 1532 | const cie = di.cie_map.get(cie_offset) orelse return badDwarf(); | 1562 | ); |
| 1533 | const fde = try FrameDescriptionEntry.parse( | 1563 | try di.fde_list.append(allocator, fde); |
| 1534 | entry_bytes, | 1564 | }, |
| 1535 | @ptrToInt(eh_frame.ptr), | 1565 | .terminator => break, |
| 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); | | |
| 1543 | } | 1566 | } |
| 1544 | | | |
| 1545 | stream.pos += entry_bytes.len; | | |
| 1546 | } | 1567 | } |
| 1547 | | 1568 | |
| 1548 | // TODO: Avoiding sorting if has_eh_frame_hdr exists | 1569 | // TODO: Avoiding sorting if has_eh_frame_hdr exists |
| ... | @@ -1560,59 +1581,67 @@ pub const DwarfInfo = struct { | ... | @@ -1560,59 +1581,67 @@ pub const DwarfInfo = struct { |
| 1560 | if (context.pc == 0) return; | 1581 | if (context.pc == 0) return; |
| 1561 | | 1582 | |
| 1562 | // TODO: Handle signal frame (ie. use_prev_instr in libunwind) | 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; | 1585 | // Find the FDE and CIE |
| 1579 | } | 1586 | var cie: CommonInformationEntry = undefined; |
| 1580 | } | 1587 | var fde: FrameDescriptionEntry = undefined; |
| 1581 | }.compareFn); | | |
| 1582 | | 1588 | |
| 1583 | const fde = if (index) |i| &di.fde_list.items[i] else return error.MissingFDE; | 1589 | // In order to support reading .eh_frame from the ELF file (vs using the already-mapped section), |
| 1584 | const cie = di.cie_map.getPtr(fde.cie_length_offset) orelse return error.MissingCIE; | 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; | 1595 | if (di.eh_frame_hdr) |header| { |
| 1587 | // const prev_pc = context.pc; | 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? | 1610 | return .gt; |
| 1590 | var vm = call_frame.VirtualMachine{}; | 1611 | } |
| 1591 | defer vm.deinit(allocator); | 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 | context.cfa = switch (row.cfa.rule) { | 1622 | context.cfa = switch (row.cfa.rule) { |
| 1595 | .val_offset => |offset| blk: { | 1623 | .val_offset => |offset| blk: { |
| 1596 | const register = row.cfa.register orelse return error.InvalidCFARule; | 1624 | const register = row.cfa.register orelse return error.InvalidCFARule; |
| 1597 | const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); | 1625 | const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); |
| 1598 | | | |
| 1599 | // TODO: Check isValidMemory? | | |
| 1600 | break :blk try call_frame.applyOffset(value, offset); | 1626 | break :blk try call_frame.applyOffset(value, offset); |
| 1601 | }, | 1627 | }, |
| 1602 | .expression => |expression| { | 1628 | .expression => |expression| { |
| 1603 | | 1629 | |
| 1604 | // TODO: Evaluate expression | 1630 | // TODO: Evaluate expression |
| 1605 | _ = expression; | 1631 | _ = expression; |
| | 1632 | |
| 1606 | return error.UnimplementedTODO; | 1633 | return error.UnimplementedTODO; |
| 1607 | }, | 1634 | }, |
| 1608 | else => return error.InvalidCFARule, | 1635 | else => return error.InvalidCFARule, |
| 1609 | }; | 1636 | }; |
| 1610 | | 1637 | |
| | 1638 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; |
| | 1639 | |
| 1611 | // Update the context with the previous frame's values | 1640 | // Update the context with the previous frame's values |
| 1612 | var next_ucontext = context.ucontext; | 1641 | var next_ucontext = context.ucontext; |
| 1613 | | 1642 | |
| 1614 | var has_next_ip = false; | 1643 | var has_next_ip = false; |
| 1615 | for (vm.rowColumns(row)) |column| { | 1644 | for (context.vm.rowColumns(row)) |column| { |
| 1616 | if (column.register) |register| { | 1645 | if (column.register) |register| { |
| 1617 | const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx); | 1646 | const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx); |
| 1618 | if (register == cie.return_address_register) { | 1647 | if (register == cie.return_address_register) { |
| ... | @@ -1640,17 +1669,24 @@ pub const UnwindContext = struct { | ... | @@ -1640,17 +1669,24 @@ pub const UnwindContext = struct { |
| 1640 | pc: usize, | 1669 | pc: usize, |
| 1641 | ucontext: os.ucontext_t, | 1670 | ucontext: os.ucontext_t, |
| 1642 | reg_ctx: abi.RegisterContext, | 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 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); | 1676 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); |
| 1646 | return .{ | 1677 | return .{ |
| 1647 | .cfa = null, | 1678 | .cfa = null, |
| 1648 | .pc = pc, | 1679 | .pc = pc, |
| 1649 | .ucontext = ucontext.*, | 1680 | .ucontext = ucontext.*, |
| 1650 | .reg_ctx = undefined, | 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 | pub fn getFp(self: *const UnwindContext) !usize { | 1690 | pub fn getFp(self: *const UnwindContext) !usize { |
| 1655 | return mem.readIntSliceNative(usize, try abi.regBytes(&self.ucontext, abi.fpRegNum(self.reg_ctx), self.reg_ctx)); | 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,7 +1730,7 @@ const EhPointerContext = struct { |
| 1694 | | 1730 | |
| 1695 | // Whether or not to follow indirect pointers. This should only be | 1731 | // Whether or not to follow indirect pointers. This should only be |
| 1696 | // used when decoding pointers at runtime using the current process's | 1732 | // used when decoding pointers at runtime using the current process's |
| 1697 | // debug info. | 1733 | // debug info |
| 1698 | follow_indirect: bool, | 1734 | follow_indirect: bool, |
| 1699 | | 1735 | |
| 1700 | // These relative addressing modes are only used in specific cases, and | 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,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 | pub const CommonInformationEntry = struct { | 1964 | pub const CommonInformationEntry = struct { |
| 1766 | // Used in .eh_frame | 1965 | // Used in .eh_frame |
| 1767 | pub const eh_id = 0; | 1966 | pub const eh_id = 0; |
| 1768 | | 1967 | |
| 1769 | // Used in .debug_frame (DWARF32) | 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 | // Used in .debug_frame (DWARF64) | 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 | // Offset of the length field of this entry in the eh_frame section. | 1974 | // Offset of the length field of this entry in the eh_frame section. |
| 1776 | // This is the key that FDEs use to reference CIEs. | 1975 | // This is the key that FDEs use to reference CIEs. |
| ... | @@ -1804,12 +2003,17 @@ pub const CommonInformationEntry = struct { | ... | @@ -1804,12 +2003,17 @@ pub const CommonInformationEntry = struct { |
| 1804 | return false; | 2003 | return false; |
| 1805 | } | 2004 | } |
| 1806 | | 2005 | |
| 1807 | // This function expects to read the CIE starting with the version field. | 2006 | /// This function expects to read the CIE starting with the version field. |
| 1808 | // The returned struct references memory backed by cie_bytes. | 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 | pub fn parse( | 2014 | pub fn parse( |
| 1810 | cie_bytes: []const u8, | 2015 | cie_bytes: []const u8, |
| 1811 | section_base: u64, | 2016 | pc_rel_offset: i64, |
| 1812 | section_offset: u64, | | |
| 1813 | is_runtime: bool, | 2017 | is_runtime: bool, |
| 1814 | length_offset: u64, | 2018 | length_offset: u64, |
| 1815 | addr_size_bytes: u8, | 2019 | addr_size_bytes: u8, |
| ... | @@ -1879,7 +2083,7 @@ pub const CommonInformationEntry = struct { | ... | @@ -1879,7 +2083,7 @@ pub const CommonInformationEntry = struct { |
| 1879 | personality_enc.?, | 2083 | personality_enc.?, |
| 1880 | addr_size_bytes, | 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 | .follow_indirect = is_runtime, | 2087 | .follow_indirect = is_runtime, |
| 1884 | }, | 2088 | }, |
| 1885 | endian, | 2089 | endian, |
| ... | @@ -1926,11 +2130,22 @@ pub const FrameDescriptionEntry = struct { | ... | @@ -1926,11 +2130,22 @@ pub const FrameDescriptionEntry = struct { |
| 1926 | aug_data: []const u8, | 2130 | aug_data: []const u8, |
| 1927 | instructions: []const u8, | 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 | pub fn parse( | 2146 | pub fn parse( |
| 1931 | fde_bytes: []const u8, | 2147 | fde_bytes: []const u8, |
| 1932 | section_base: u64, | 2148 | pc_rel_offset: i64, |
| 1933 | section_offset: u64, | | |
| 1934 | is_runtime: bool, | 2149 | is_runtime: bool, |
| 1935 | cie: CommonInformationEntry, | 2150 | cie: CommonInformationEntry, |
| 1936 | addr_size_bytes: u8, | 2151 | addr_size_bytes: u8, |
| ... | @@ -1946,7 +2161,7 @@ pub const FrameDescriptionEntry = struct { | ... | @@ -1946,7 +2161,7 @@ pub const FrameDescriptionEntry = struct { |
| 1946 | cie.fde_pointer_enc, | 2161 | cie.fde_pointer_enc, |
| 1947 | addr_size_bytes, | 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 | .follow_indirect = is_runtime, | 2165 | .follow_indirect = is_runtime, |
| 1951 | }, | 2166 | }, |
| 1952 | endian, | 2167 | endian, |
| ... | @@ -1975,7 +2190,7 @@ pub const FrameDescriptionEntry = struct { | ... | @@ -1975,7 +2190,7 @@ pub const FrameDescriptionEntry = struct { |
| 1975 | cie.lsda_pointer_enc, | 2190 | cie.lsda_pointer_enc, |
| 1976 | addr_size_bytes, | 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 | .follow_indirect = is_runtime, | 2194 | .follow_indirect = is_runtime, |
| 1980 | }, | 2195 | }, |
| 1981 | endian, | 2196 | endian, |
| ... | @@ -1998,3 +2213,11 @@ pub const FrameDescriptionEntry = struct { | ... | @@ -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 | } |