authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-05-07 13:36:23-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:13-04:00
log6c1d1aa45c7aa955c63c63b2bfbeb895dd694076
treee042b64c4d56d76cfb4773c0d736e6b93da19b35
parentf6148f123e35dcbec4b9ab7458967fa93277e443

begin working on parsing unwind info


2 files changed, 265 insertions(+), 75 deletions(-)

lib/std/dwarf.zig+263-75
...@@ -662,13 +662,19 @@ pub const DwarfSection = enum {...@@ -662,13 +662,19 @@ pub const DwarfSection = enum {
662662
663pub const DwarfInfo = struct {663pub const DwarfInfo = struct {
664 endian: std.builtin.Endian,664 endian: std.builtin.Endian,
665 // No memory is owned by the DwarfInfo665
666 // No section memory is owned by the DwarfInfo
666 sections: [std.enums.directEnumArrayLen(DwarfSection, 0)]?[]const u8,667 sections: [std.enums.directEnumArrayLen(DwarfSection, 0)]?[]const u8,
668
667 // Filled later by the initializer669 // Filled later by the initializer
668 abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{},670 abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{},
669 compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},671 compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},
670 func_list: std.ArrayListUnmanaged(Func) = .{},672 func_list: std.ArrayListUnmanaged(Func) = .{},
671673
674 cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{},
675 // Sorted by start_pc
676 fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{},
677
672 pub fn section(di: DwarfInfo, dwarf_section: DwarfSection) ?[]const u8 {678 pub fn section(di: DwarfInfo, dwarf_section: DwarfSection) ?[]const u8 {
673 return di.sections[@enumToInt(dwarf_section)];679 return di.sections[@enumToInt(dwarf_section)];
674 }680 }
...@@ -1434,6 +1440,7 @@ pub const DwarfInfo = struct {...@@ -1434,6 +1440,7 @@ pub const DwarfInfo = struct {
1434 return getStringGeneric(di.section(.debug_line_str), offset);1440 return getStringGeneric(di.section(.debug_line_str), offset);
1435 }1441 }
14361442
1443
1437 fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 {1444 fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 {
1438 const debug_addr = di.section(.debug_addr) orelse return badDwarf();1445 const debug_addr = di.section(.debug_addr) orelse return badDwarf();
14391446
...@@ -1459,6 +1466,56 @@ pub const DwarfInfo = struct {...@@ -1459,6 +1466,56 @@ pub const DwarfInfo = struct {
1459 else => badDwarf(),1466 else => badDwarf(),
1460 };1467 };
1461 }1468 }
1469
1470 pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
1471 var has_eh_frame_hdr = false;
1472 if (di.section(.eh_frame)) |eh_frame_hdr| {
1473 has_eh_frame_hdr = true;
1474
1475 // TODO: Parse this section
1476 _ = eh_frame_hdr;
1477 }
1478
1479 if (di.section(.eh_frame)) |eh_frame| {
1480 var stream = io.fixedBufferStream(eh_frame);
1481 const reader = stream.reader();
1482
1483 while (stream.pos < stream.buffer.len) {
1484 const length_offset = stream.pos;
1485 var length: u64 = try reader.readInt(u32, di.endian);
1486 if (length == 0) break;
1487
1488 var is_64 = length == math.maxInt(u32);
1489 if (is_64) {
1490 length = try reader.readInt(u64, di.endian);
1491 }
1492
1493 const entry_bytes = eh_frame[stream.pos..][0..length];
1494 const id = try reader.readInt(u32, di.endian);
1495
1496 // TODO: Get section_offset here (pass in from headers)
1497
1498 if (id == 0) {
1499 const cie = try CommonInformationEntry.parse(entry_bytes, @ptrToInt(eh_frame.ptr), 0, length_offset, @sizeOf(usize), di.endian);
1500 try di.cie_map.put(allocator, length_offset, cie);
1501 } else {
1502 const cie_offset = stream.pos - 4 - id;
1503 const cie = di.cie_map.get(cie_offset) orelse return badDwarf();
1504 const fde = try FrameDescriptionEntry.parse(entry_bytes, @ptrToInt(eh_frame.ptr), 0, cie, @sizeOf(usize), di.endian);
1505 try di.fde_list.append(allocator, fde);
1506 }
1507 }
1508
1509 // TODO: Avoiding sorting if has_eh_frame_hdr exists
1510 std.sort.sort(FrameDescriptionEntry, di.fde_list.items, {}, struct {
1511 fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool {
1512 _ = ctx;
1513 return a.pc_begin < b.pc_begin;
1514 }
1515 }.lessThan);
1516 }
1517 }
1518
1462};1519};
14631520
1464/// Initialize DWARF info. The caller has the responsibility to initialize most1521/// Initialize DWARF info. The caller has the responsibility to initialize most
...@@ -1467,11 +1524,8 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {...@@ -1467,11 +1524,8 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
1467 try di.scanAllFunctions(allocator);1524 try di.scanAllFunctions(allocator);
1468 try di.scanAllCompileUnits(allocator);1525 try di.scanAllCompileUnits(allocator);
14691526
1470 // DEBUG1527 // Unwind info is not required
1471 if (di.section(.eh_frame)) |eh_frame| {1528 di.scanAllUnwindInfo(allocator) catch {};
1472 _ = try CommonInformationEntry.parse(eh_frame, 8, .Little);
1473 }
1474
1475}1529}
14761530
1477/// This function is to make it handy to comment out the return and make it1531/// This function is to make it handy to comment out the return and make it
...@@ -1495,74 +1549,131 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {...@@ -1495,74 +1549,131 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
1495 return str[casted_offset..last :0];1549 return str[casted_offset..last :0];
1496}1550}
14971551
1498const EhPointer = struct {1552const EhPointerContext = struct {
1499 value: union(enum) {1553 // The address of the pointer field itself
1500 signed: i64,1554 pc_rel_base: u64,
1501 unsigned: u64,
1502 },
1503 relative_to: u8,
15041555
1505 // address of the encoded value1556 // These relative addressing modes are only used in specific cases, and
1506 pc: u64,1557 // might not be available / required in all parsing contexts
15071558 data_rel_base: ?u64 = null,
1508 // TODO: Function to resolve the value given input state (.text start, .eh_frame_hdr start, functions start)1559 text_rel_base: ?u64 = null,
1560 function_rel_base: ?u64 = null,
1509};1561};
15101562
1511fn readEhPointer(enc: u8, pc: usize, addr_size_bytes: u8, endian: std.builtin.Endian, reader: anytype) !?EhPointer {1563fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext, endian: std.builtin.Endian) !?u64 {
1512 if (enc == EH.PE.omit) return null;1564 if (enc == EH.PE.omit) return null;
1513 return EhPointer{1565
1514 .value = switch (enc & 0x0f) {1566 const value: union(enum) {
1515 EH.PE.absptr => .{ .unsigned = switch (addr_size_bytes) {1567 signed: i64,
1568 unsigned: u64,
1569 } = switch (enc & 0x0f) {
1570 EH.PE.absptr => .{
1571 .unsigned = switch (addr_size_bytes) {
1516 2 => try reader.readInt(u16, endian),1572 2 => try reader.readInt(u16, endian),
1517 4 => try reader.readInt(u32, endian),1573 4 => try reader.readInt(u32, endian),
1518 8 => try reader.readInt(u64, endian),1574 8 => try reader.readInt(u64, endian),
1519 else => return error.InvalidAddrSize,1575 else => return error.InvalidAddrSize,
1520 } },1576 },
1521 EH.PE.uleb128 => .{ .unsigned = try leb.readULEB128(u64, reader) },
1522 EH.PE.udata2 => .{ .unsigned = try reader.readInt(u16, endian) },
1523 EH.PE.udata4 => .{ .unsigned = try reader.readInt(u32, endian) },
1524 EH.PE.udata8 => .{ .unsigned = try reader.readInt(u64, endian) },
1525 EH.PE.sleb128 => .{ .signed = try leb.readILEB128(i64, reader) },
1526 EH.PE.sdata2 => .{ .signed = try reader.readInt(i16, endian) },
1527 EH.PE.sdata4 => .{ .signed = try reader.readInt(i32, endian) },
1528 EH.PE.sdata8 => .{ .signed = try reader.readInt(i64, endian) },
1529 else => return badDwarf(),
1530 },1577 },
1531 .relative_to = enc & 0xf0,1578 EH.PE.uleb128 => .{ .unsigned = try leb.readULEB128(u64, reader) },
1532 .pc = pc1579 EH.PE.udata2 => .{ .unsigned = try reader.readInt(u16, endian) },
1580 EH.PE.udata4 => .{ .unsigned = try reader.readInt(u32, endian) },
1581 EH.PE.udata8 => .{ .unsigned = try reader.readInt(u64, endian) },
1582 EH.PE.sleb128 => .{ .signed = try leb.readILEB128(i64, reader) },
1583 EH.PE.sdata2 => .{ .signed = try reader.readInt(i16, endian) },
1584 EH.PE.sdata4 => .{ .signed = try reader.readInt(i32, endian) },
1585 EH.PE.sdata8 => .{ .signed = try reader.readInt(i64, endian) },
1586 else => return badDwarf(),
1533 };1587 };
1588
1589 const relative_to = enc & 0xf0;
1590 var base = switch (relative_to) {
1591 EH.PE.pcrel => ctx.pc_rel_base,
1592 EH.PE.textrel => ctx.text_rel_base orelse return error.PointerBaseNotSpecified,
1593 EH.PE.datarel => ctx.data_rel_base orelse return error.PointerBaseNotSpecified,
1594 EH.PE.funcrel => ctx.function_rel_base orelse return error.PointerBaseNotSpecified,
1595 EH.PE.indirect => {
1596 switch (addr_size_bytes) {
1597 2 => return @intToPtr(*const u16, value.unsigned).*,
1598 4 => return @intToPtr(*const u32, value.unsigned).*,
1599 8 => return @intToPtr(*const u64, value.unsigned).*,
1600 else => return error.UnsupportedAddrSize,
1601 }
1602 },
1603 else => null,
1604 };
1605
1606 if (base) |b| {
1607 return switch (value) {
1608 .signed => |s| @intCast(u64, s + @intCast(i64, b)),
1609 .unsigned => |u| u + b,
1610 };
1611 } else {
1612 return switch (value) {
1613 .signed => |s| @intCast(u64, s),
1614 .unsigned => |u| u,
1615 };
1616 }
1534}1617}
15351618
1536const CommonInformationEntry = struct {1619pub const CommonInformationEntry = struct {
1537 length: u32,1620 // Used in .eh_frame
1538 id: u32,1621 pub const eh_id = 0;
1622
1623 // Used in .debug_frame (DWARF32)
1624 pub const dwarf32_id = std.math.maxInt(u32);
1625
1626 // Used in .debug_frame (DWARF64)
1627 pub const dwarf64_id = std.math.maxInt(u64);
1628
1629 // Offset of the length field of this entry in the eh_frame section.
1630 // This is the key that FDEs use to reference CIEs.
1631 length_offset: u64,
1539 version: u8,1632 version: u8,
1540 code_alignment_factor: u64,
1541 data_alignment_factor: u64,
1542 return_address_register: u64,
15431633
1544 // Augmented data1634 code_alignment_factor: u32,
1545 lsda_pointer_enc: ?u8,1635 data_alignment_factor: i32,
1546 personality_routine_pointer: ?EhPointer,1636 return_address_register: u8,
1547 fde_pointer_enc: ?u8,
15481637
1638 aug_str: []const u8,
1639 aug_data: []const u8,
1640 lsda_pointer_enc: u8,
1641 personality_enc: ?u8,
1642 personality_routine_pointer: ?u64,
1643 fde_pointer_enc: u8,
1549 initial_instructions: []const u8,1644 initial_instructions: []const u8,
15501645
1551 // The returned struct references memory in `bytes`.1646 pub fn isSignalFrame(self: CommonInformationEntry) bool {
1552 pub fn parse(bytes: []const u8, addr_size_bytes: u8, endian: std.builtin.Endian) !CommonInformationEntry {1647 for (self.aug_str) |c| if (c == 'S') return true;
1553 if (addr_size_bytes > 8) return error.InvalidAddrSize;1648 return false;
1554 if (bytes.len < 4) return badDwarf();1649 }
1555 const length = mem.readInt(u32, bytes[0..4], endian);1650
1556 const cie_bytes = bytes[4..][0..length];1651 pub fn addressesSignedWithBKey(self: CommonInformationEntry) bool {
1652 for (self.aug_str) |c| if (c == 'B') return true;
1653 return false;
1654 }
1655
1656 pub fn mteTaggedFrame(self: CommonInformationEntry) bool {
1657 for (self.aug_str) |c| if (c == 'G') return true;
1658 return false;
1659 }
1660
1661 // The returned struct references memory backed by cie_bytes
1662 pub fn parse(
1663 cie_bytes: []const u8,
1664 section_base: u64,
1665 section_offset: u64,
1666 length_offset: u64,
1667 addr_size_bytes: u8,
1668 endian: std.builtin.Endian,
1669 ) !CommonInformationEntry {
1670 if (addr_size_bytes > 8) return error.UnsupportedAddrSize;
15571671
1558 var stream = io.fixedBufferStream(cie_bytes);1672 var stream = io.fixedBufferStream(cie_bytes);
1559 const reader = stream.reader();1673 const reader = stream.reader();
15601674
1561 const id = try reader.readInt(u32, endian);
1562 if (id != 0) return badDwarf();
1563
1564 const version = try reader.readByte();1675 const version = try reader.readByte();
1565 if (version != 1) return badDwarf();1676 if (version != 1 and version != 3) return error.UnsupportedDwarfVersion;
15661677
1567 var has_eh_data = false;1678 var has_eh_data = false;
1568 var has_aug_data = false;1679 var has_aug_data = false;
...@@ -1575,76 +1686,153 @@ const CommonInformationEntry = struct {...@@ -1575,76 +1686,153 @@ const CommonInformationEntry = struct {
1575 'z' => {1686 'z' => {
1576 if (aug_str_len != 0) return badDwarf();1687 if (aug_str_len != 0) return badDwarf();
1577 has_aug_data = true;1688 has_aug_data = true;
1578 aug_str_start = stream.pos;
1579 },1689 },
1580 'e' => {1690 'e' => {
1581 if (has_aug_data or aug_str_len != 0) return badDwarf();1691 if (has_aug_data or aug_str_len != 0) return badDwarf();
1582 if (try reader.readByte() != 'h') return badDwarf();1692 if (try reader.readByte() != 'h') return badDwarf();
1583 has_eh_data = true;1693 has_eh_data = true;
1584 },1694 },
1585 else => {1695 else => if (has_eh_data) return badDwarf(),
1586 if (has_eh_data) return badDwarf();
1587 aug_str_len += 1;
1588 },
1589 }1696 }
1697
1698 aug_str_len += 1;
1590 }1699 }
15911700
1592 if (has_eh_data) {1701 if (has_eh_data) {
1593 // legacy data created by older versions of gcc - ignored here1702 // legacy data created by older versions of gcc - unsupported here
1594 for (0..addr_size_bytes) |_| _ = try reader.readByte();1703 for (0..addr_size_bytes) |_| _ = try reader.readByte();
1595 }1704 }
15961705
1597 const code_alignment_factor = try leb.readULEB128(u64, reader);1706 const code_alignment_factor = try leb.readULEB128(u32, reader);
1598 const data_alignment_factor = try leb.readULEB128(u64, reader);1707 const data_alignment_factor = try leb.readILEB128(i32, reader);
1599 const return_address_register = try leb.readULEB128(u64, reader);1708 const return_address_register = if (version == 1) try reader.readByte() else try leb.readULEB128(u8, reader);
16001709
1601 var lsda_pointer_enc: ?u8 = null;1710 var lsda_pointer_enc: u8 = EH.PE.omit;
1602 var personality_routine_pointer: ?EhPointer = null;1711 var personality_enc: ?u8 = null;
1603 var fde_pointer_enc: ?u8 = null;1712 var personality_routine_pointer: ?u64 = null;
1713 var fde_pointer_enc: u8 = EH.PE.absptr;
16041714
1605 if (has_aug_data) {1715 var aug_data: []const u8 = &[_]u8{};
1716 const aug_str = if (has_aug_data) blk: {
1606 const aug_data_len = try leb.readULEB128(usize, reader);1717 const aug_data_len = try leb.readULEB128(usize, reader);
1607 const aug_data_start = stream.pos;1718 const aug_data_start = stream.pos;
1719 aug_data = cie_bytes[aug_data_start..][0..aug_data_len];
16081720
1609 const aug_str = cie_bytes[aug_str_start..][0..aug_str_len];1721 const aug_str = cie_bytes[aug_str_start..][0..aug_str_len];
1610 for (aug_str) |byte| {1722 for (aug_str[1..]) |byte| {
1611 switch (byte) {1723 switch (byte) {
1612 'L' => {1724 'L' => {
1613 lsda_pointer_enc = try reader.readByte();1725 lsda_pointer_enc = try reader.readByte();
1614 },1726 },
1615 'P' => {1727 'P' => {
1616 const personality_enc = try reader.readByte();1728 personality_enc = try reader.readByte();
1617 personality_routine_pointer = try readEhPointer(1729 personality_routine_pointer = try readEhPointer(
1618 personality_enc,1730 reader,
1619 @ptrToInt(&cie_bytes[stream.pos]),1731 personality_enc.?,
1620 addr_size_bytes,1732 addr_size_bytes,
1733 .{ .pc_rel_base = @ptrToInt(&cie_bytes[stream.pos]) - section_base + section_offset },
1621 endian,1734 endian,
1622 reader,
1623 );1735 );
1624 },1736 },
1625 'R' => {1737 'R' => {
1626 fde_pointer_enc = try reader.readByte();1738 fde_pointer_enc = try reader.readByte();
1627 },1739 },
1740 'S', 'B', 'G' => {},
1628 else => return badDwarf(),1741 else => return badDwarf(),
1629 }1742 }
1630 }1743 }
16311744
1632 // verify length field1745 // aug_data_len can include padding so the CIE ends on an address boundary
1633 if (stream.pos != (aug_data_start + aug_data_len)) return badDwarf();1746 try stream.seekTo(aug_data_start + aug_data_len);
1634 }1747 break :blk aug_str;
1748 } else &[_]u8{};
16351749
1636 const initial_instructions = cie_bytes[stream.pos..];1750 const initial_instructions = cie_bytes[stream.pos..];
1637 return .{1751 return .{
1638 .length = length,1752 .length_offset = length_offset,
1639 .id = id,
1640 .version = version,1753 .version = version,
1641 .code_alignment_factor = code_alignment_factor,1754 .code_alignment_factor = code_alignment_factor,
1642 .data_alignment_factor = data_alignment_factor,1755 .data_alignment_factor = data_alignment_factor,
1643 .return_address_register = return_address_register,1756 .return_address_register = return_address_register,
1757 .aug_str = aug_str,
1758 .aug_data = aug_data,
1644 .lsda_pointer_enc = lsda_pointer_enc,1759 .lsda_pointer_enc = lsda_pointer_enc,
1760 .personality_enc = personality_enc,
1645 .personality_routine_pointer = personality_routine_pointer,1761 .personality_routine_pointer = personality_routine_pointer,
1646 .fde_pointer_enc = fde_pointer_enc,1762 .fde_pointer_enc = fde_pointer_enc,
1647 .initial_instructions = initial_instructions,1763 .initial_instructions = initial_instructions,
1648 };1764 };
1649 }1765 }
1650};1766};
1767
1768pub const FrameDescriptionEntry = struct {
1769 // Offset into eh_frame where the CIE for this FDE is stored
1770 cie_length_offset: u64,
1771
1772 pc_begin: u64,
1773 pc_range: u64,
1774 lsda_pointer: ?u64,
1775 aug_data: []const u8,
1776 instructions: []const u8,
1777
1778 pub fn parse(
1779 fde_bytes: []const u8,
1780 section_base: u64,
1781 section_offset: u64,
1782 cie: CommonInformationEntry,
1783 addr_size_bytes: u8,
1784 endian: std.builtin.Endian,
1785 ) !FrameDescriptionEntry {
1786 if (addr_size_bytes > 8) return error.InvalidAddrSize;
1787
1788 var stream = io.fixedBufferStream(fde_bytes);
1789 const reader = stream.reader();
1790
1791 const pc_begin = try readEhPointer(
1792 reader,
1793 cie.fde_pointer_enc,
1794 addr_size_bytes,
1795 .{ .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) - section_base + section_offset },
1796 endian,
1797 ) orelse return badDwarf();
1798
1799 const pc_range = try readEhPointer(
1800 reader,
1801 cie.fde_pointer_enc & 0x0f,
1802 addr_size_bytes,
1803 .{ .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) - section_base + section_offset },
1804 endian,
1805 ) orelse return badDwarf();
1806
1807 var aug_data: []const u8 = &[_]u8{};
1808 const lsda_pointer = if (cie.aug_str.len > 0) blk: {
1809 const aug_data_len = try leb.readULEB128(u64, reader);
1810 const aug_data_start = stream.pos;
1811 aug_data = fde_bytes[aug_data_start..][0..aug_data_len];
1812
1813 const lsda_pointer = if (cie.lsda_pointer_enc != EH.PE.omit)
1814 try readEhPointer(
1815 reader,
1816 cie.lsda_pointer_enc & 0x0f,
1817 addr_size_bytes,
1818 .{ .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) },
1819 endian,
1820 )
1821 else
1822 null;
1823
1824 try stream.seekTo(aug_data_start + aug_data_len);
1825 break :blk lsda_pointer;
1826 } else null;
1827
1828 const instructions = fde_bytes[stream.pos..];
1829 return .{
1830 .cie_length_offset = cie.length_offset,
1831 .pc_begin = pc_begin,
1832 .pc_range = pc_range,
1833 .lsda_pointer = lsda_pointer,
1834 .aug_data = aug_data,
1835 .instructions = instructions,
1836 };
1837 }
1838};
lib/std/dwarf/EH.zig+2
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1pub const PE = struct {1pub const PE = struct {
2 pub const absptr = 0x00;2 pub const absptr = 0x00;
3
3 pub const uleb128 = 0x01;4 pub const uleb128 = 0x01;
4 pub const udata2 = 0x02;5 pub const udata2 = 0x02;
5 pub const udata4 = 0x03;6 pub const udata4 = 0x03;
...@@ -14,6 +15,7 @@ pub const PE = struct {...@@ -14,6 +15,7 @@ pub const PE = struct {
14 pub const datarel = 0x30;15 pub const datarel = 0x30;
15 pub const funcrel = 0x40;16 pub const funcrel = 0x40;
16 pub const aligned = 0x50;17 pub const aligned = 0x50;
18 pub const indirect = 0x80;
1719
18 pub const omit = 0xff;20 pub const omit = 0xff;
19};21};