| ... | @@ -13,6 +13,7 @@ pub const OP = @import("dwarf/OP.zig"); | ... | @@ -13,6 +13,7 @@ pub const OP = @import("dwarf/OP.zig"); |
| 13 | pub const LANG = @import("dwarf/LANG.zig"); | 13 | pub const LANG = @import("dwarf/LANG.zig"); |
| 14 | pub const FORM = @import("dwarf/FORM.zig"); | 14 | pub const FORM = @import("dwarf/FORM.zig"); |
| 15 | pub const ATE = @import("dwarf/ATE.zig"); | 15 | pub const ATE = @import("dwarf/ATE.zig"); |
| | 16 | pub const EH = @import("dwarf/EH.zig"); |
| 16 | | 17 | |
| 17 | pub const LLE = struct { | 18 | pub const LLE = struct { |
| 18 | pub const end_of_list = 0x00; | 19 | pub const end_of_list = 0x00; |
| ... | @@ -337,7 +338,7 @@ const Die = struct { | ... | @@ -337,7 +338,7 @@ const Die = struct { |
| 337 | FormValue.String => |value| return value, | 338 | FormValue.String => |value| return value, |
| 338 | FormValue.StrPtr => |offset| return di.getString(offset), | 339 | FormValue.StrPtr => |offset| return di.getString(offset), |
| 339 | FormValue.StrOffset => |index| { | 340 | FormValue.StrOffset => |index| { |
| 340 | const debug_str_offsets = di.debug_str_offsets orelse return badDwarf(); | 341 | const debug_str_offsets = di.section(.debug_str_offsets) orelse return badDwarf(); |
| 341 | if (compile_unit.str_offsets_base == 0) return badDwarf(); | 342 | if (compile_unit.str_offsets_base == 0) return badDwarf(); |
| 342 | if (compile_unit.is_64) { | 343 | if (compile_unit.is_64) { |
| 343 | const byte_offset = compile_unit.str_offsets_base + 8 * index; | 344 | const byte_offset = compile_unit.str_offsets_base + 8 * index; |
| ... | @@ -642,26 +643,36 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con | ... | @@ -642,26 +643,36 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 642 | return null; | 643 | return null; |
| 643 | } | 644 | } |
| 644 | | 645 | |
| | 646 | pub const DwarfSection = enum { |
| | 647 | debug_info, |
| | 648 | debug_abbrev, |
| | 649 | debug_str, |
| | 650 | debug_str_offsets, |
| | 651 | debug_line, |
| | 652 | debug_line_str, |
| | 653 | debug_ranges, |
| | 654 | debug_loclists, |
| | 655 | debug_rnglists, |
| | 656 | debug_addr, |
| | 657 | debug_names, |
| | 658 | debug_frame, |
| | 659 | eh_frame, |
| | 660 | eh_frame_hdr, |
| | 661 | }; |
| | 662 | |
| 645 | pub const DwarfInfo = struct { | 663 | pub const DwarfInfo = struct { |
| 646 | endian: std.builtin.Endian, | 664 | endian: std.builtin.Endian, |
| 647 | // No memory is owned by the DwarfInfo | 665 | // No memory is owned by the DwarfInfo |
| 648 | debug_info: []const u8, | 666 | sections: [std.enums.directEnumArrayLen(DwarfSection, 0)]?[]const u8, |
| 649 | debug_abbrev: []const u8, | | |
| 650 | debug_str: []const u8, | | |
| 651 | debug_str_offsets: ?[]const u8, | | |
| 652 | debug_line: []const u8, | | |
| 653 | debug_line_str: ?[]const u8, | | |
| 654 | debug_ranges: ?[]const u8, | | |
| 655 | debug_loclists: ?[]const u8, | | |
| 656 | debug_rnglists: ?[]const u8, | | |
| 657 | debug_addr: ?[]const u8, | | |
| 658 | debug_names: ?[]const u8, | | |
| 659 | debug_frame: ?[]const u8, | | |
| 660 | // Filled later by the initializer | 667 | // Filled later by the initializer |
| 661 | abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{}, | 668 | abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{}, |
| 662 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, | 669 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, |
| 663 | func_list: std.ArrayListUnmanaged(Func) = .{}, | 670 | func_list: std.ArrayListUnmanaged(Func) = .{}, |
| 664 | | 671 | |
| | 672 | pub fn section(di: DwarfInfo, dwarf_section: DwarfSection) ?[]const u8 { |
| | 673 | return di.sections[@enumToInt(dwarf_section)]; |
| | 674 | } |
| | 675 | |
| 665 | pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void { | 676 | pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void { |
| 666 | for (di.abbrev_table_list.items) |*abbrev| { | 677 | for (di.abbrev_table_list.items) |*abbrev| { |
| 667 | abbrev.deinit(); | 678 | abbrev.deinit(); |
| ... | @@ -691,7 +702,7 @@ pub const DwarfInfo = struct { | ... | @@ -691,7 +702,7 @@ pub const DwarfInfo = struct { |
| 691 | } | 702 | } |
| 692 | | 703 | |
| 693 | fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void { | 704 | fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 694 | var stream = io.fixedBufferStream(di.debug_info); | 705 | var stream = io.fixedBufferStream(di.section(.debug_info).?); |
| 695 | const in = stream.reader(); | 706 | const in = stream.reader(); |
| 696 | const seekable = &stream.seekableStream(); | 707 | const seekable = &stream.seekableStream(); |
| 697 | var this_unit_offset: u64 = 0; | 708 | var this_unit_offset: u64 = 0; |
| ... | @@ -764,7 +775,7 @@ pub const DwarfInfo = struct { | ... | @@ -764,7 +775,7 @@ pub const DwarfInfo = struct { |
| 764 | // Prevent endless loops | 775 | // Prevent endless loops |
| 765 | while (depth > 0) : (depth -= 1) { | 776 | while (depth > 0) : (depth -= 1) { |
| 766 | if (this_die_obj.getAttr(AT.name)) |_| { | 777 | if (this_die_obj.getAttr(AT.name)) |_| { |
| 767 | const name = try this_die_obj.getAttrString(di, AT.name, di.debug_str, compile_unit); | 778 | const name = try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit); |
| 768 | break :x try allocator.dupe(u8, name); | 779 | break :x try allocator.dupe(u8, name); |
| 769 | } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { | 780 | } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { |
| 770 | // Follow the DIE it points to and repeat | 781 | // Follow the DIE it points to and repeat |
| ... | @@ -836,7 +847,7 @@ pub const DwarfInfo = struct { | ... | @@ -836,7 +847,7 @@ pub const DwarfInfo = struct { |
| 836 | } | 847 | } |
| 837 | | 848 | |
| 838 | fn scanAllCompileUnits(di: *DwarfInfo, allocator: mem.Allocator) !void { | 849 | fn scanAllCompileUnits(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 839 | var stream = io.fixedBufferStream(di.debug_info); | 850 | var stream = io.fixedBufferStream(di.section(.debug_info).?); |
| 840 | const in = &stream.reader(); | 851 | const in = &stream.reader(); |
| 841 | const seekable = &stream.seekableStream(); | 852 | const seekable = &stream.seekableStream(); |
| 842 | var this_unit_offset: u64 = 0; | 853 | var this_unit_offset: u64 = 0; |
| ... | @@ -930,7 +941,7 @@ pub const DwarfInfo = struct { | ... | @@ -930,7 +941,7 @@ pub const DwarfInfo = struct { |
| 930 | if (target_address >= range.start and target_address < range.end) return compile_unit; | 941 | if (target_address >= range.start and target_address < range.end) return compile_unit; |
| 931 | } | 942 | } |
| 932 | | 943 | |
| 933 | const opt_debug_ranges = if (compile_unit.version >= 5) di.debug_rnglists else di.debug_ranges; | 944 | const opt_debug_ranges = if (compile_unit.version >= 5) di.section(.debug_rnglists) else di.section(.debug_ranges); |
| 934 | const debug_ranges = opt_debug_ranges orelse continue; | 945 | const debug_ranges = opt_debug_ranges orelse continue; |
| 935 | | 946 | |
| 936 | const ranges_val = compile_unit.die.getAttr(AT.ranges) orelse continue; | 947 | const ranges_val = compile_unit.die.getAttr(AT.ranges) orelse continue; |
| ... | @@ -1065,7 +1076,7 @@ pub const DwarfInfo = struct { | ... | @@ -1065,7 +1076,7 @@ pub const DwarfInfo = struct { |
| 1065 | } | 1076 | } |
| 1066 | | 1077 | |
| 1067 | fn parseAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, offset: u64) !AbbrevTable { | 1078 | fn parseAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, offset: u64) !AbbrevTable { |
| 1068 | var stream = io.fixedBufferStream(di.debug_abbrev); | 1079 | var stream = io.fixedBufferStream(di.section(.debug_abbrev).?); |
| 1069 | const in = &stream.reader(); | 1080 | const in = &stream.reader(); |
| 1070 | const seekable = &stream.seekableStream(); | 1081 | const seekable = &stream.seekableStream(); |
| 1071 | | 1082 | |
| ... | @@ -1146,11 +1157,11 @@ pub const DwarfInfo = struct { | ... | @@ -1146,11 +1157,11 @@ pub const DwarfInfo = struct { |
| 1146 | compile_unit: CompileUnit, | 1157 | compile_unit: CompileUnit, |
| 1147 | target_address: u64, | 1158 | target_address: u64, |
| 1148 | ) !debug.LineInfo { | 1159 | ) !debug.LineInfo { |
| 1149 | var stream = io.fixedBufferStream(di.debug_line); | 1160 | var stream = io.fixedBufferStream(di.section(.debug_line).?); |
| 1150 | const in = &stream.reader(); | 1161 | const in = &stream.reader(); |
| 1151 | const seekable = &stream.seekableStream(); | 1162 | const seekable = &stream.seekableStream(); |
| 1152 | | 1163 | |
| 1153 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.debug_line_str, compile_unit); | 1164 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.section(.debug_line_str), compile_unit); |
| 1154 | const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list); | 1165 | const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list); |
| 1155 | | 1166 | |
| 1156 | try seekable.seekTo(line_info_offset); | 1167 | try seekable.seekTo(line_info_offset); |
| ... | @@ -1416,15 +1427,15 @@ pub const DwarfInfo = struct { | ... | @@ -1416,15 +1427,15 @@ pub const DwarfInfo = struct { |
| 1416 | } | 1427 | } |
| 1417 | | 1428 | |
| 1418 | fn getString(di: DwarfInfo, offset: u64) ![]const u8 { | 1429 | fn getString(di: DwarfInfo, offset: u64) ![]const u8 { |
| 1419 | return getStringGeneric(di.debug_str, offset); | 1430 | return getStringGeneric(di.section(.debug_str), offset); |
| 1420 | } | 1431 | } |
| 1421 | | 1432 | |
| 1422 | fn getLineString(di: DwarfInfo, offset: u64) ![]const u8 { | 1433 | fn getLineString(di: DwarfInfo, offset: u64) ![]const u8 { |
| 1423 | return getStringGeneric(di.debug_line_str, offset); | 1434 | return getStringGeneric(di.section(.debug_line_str), offset); |
| 1424 | } | 1435 | } |
| 1425 | | 1436 | |
| 1426 | fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 { | 1437 | fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 { |
| 1427 | const debug_addr = di.debug_addr orelse return badDwarf(); | 1438 | const debug_addr = di.section(.debug_addr) orelse return badDwarf(); |
| 1428 | | 1439 | |
| 1429 | // addr_base points to the first item after the header, however we | 1440 | // addr_base points to the first item after the header, however we |
| 1430 | // need to read the header to know the size of each item. Empirically, | 1441 | // need to read the header to know the size of each item. Empirically, |
| ... | @@ -1455,6 +1466,12 @@ pub const DwarfInfo = struct { | ... | @@ -1455,6 +1466,12 @@ pub const DwarfInfo = struct { |
| 1455 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void { | 1466 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 1456 | try di.scanAllFunctions(allocator); | 1467 | try di.scanAllFunctions(allocator); |
| 1457 | try di.scanAllCompileUnits(allocator); | 1468 | try di.scanAllCompileUnits(allocator); |
| | 1469 | |
| | 1470 | // DEBUG |
| | 1471 | if (di.section(.eh_frame)) |eh_frame| { |
| | 1472 | _ = try CommonInformationEntry.parse(eh_frame, 8, .Little); |
| | 1473 | } |
| | 1474 | |
| 1458 | } | 1475 | } |
| 1459 | | 1476 | |
| 1460 | /// This function is to make it handy to comment out the return and make it | 1477 | /// This function is to make it handy to comment out the return and make it |
| ... | @@ -1477,3 +1494,157 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 { | ... | @@ -1477,3 +1494,157 @@ fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 { |
| 1477 | const last = mem.indexOfScalarPos(u8, str, casted_offset, 0) orelse return badDwarf(); | 1494 | const last = mem.indexOfScalarPos(u8, str, casted_offset, 0) orelse return badDwarf(); |
| 1478 | return str[casted_offset..last :0]; | 1495 | return str[casted_offset..last :0]; |
| 1479 | } | 1496 | } |
| | 1497 | |
| | 1498 | const EhPointer = struct { |
| | 1499 | value: union(enum) { |
| | 1500 | signed: i64, |
| | 1501 | unsigned: u64, |
| | 1502 | }, |
| | 1503 | relative_to: u8, |
| | 1504 | |
| | 1505 | // address of the encoded value |
| | 1506 | pc: u64, |
| | 1507 | |
| | 1508 | // TODO: Function to resolve the value given input state (.text start, .eh_frame_hdr start, functions start) |
| | 1509 | }; |
| | 1510 | |
| | 1511 | fn readEhPointer(enc: u8, pc: usize, addr_size_bytes: u8, endian: std.builtin.Endian, reader: anytype) !?EhPointer { |
| | 1512 | if (enc == EH.PE.omit) return null; |
| | 1513 | return EhPointer{ |
| | 1514 | .value = switch (enc & 0x0f) { |
| | 1515 | EH.PE.absptr => .{ .unsigned = switch (addr_size_bytes) { |
| | 1516 | 2 => try reader.readInt(u16, endian), |
| | 1517 | 4 => try reader.readInt(u32, endian), |
| | 1518 | 8 => try reader.readInt(u64, endian), |
| | 1519 | else => return error.InvalidAddrSize, |
| | 1520 | } }, |
| | 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 | }, |
| | 1531 | .relative_to = enc & 0xf0, |
| | 1532 | .pc = pc |
| | 1533 | }; |
| | 1534 | } |
| | 1535 | |
| | 1536 | const CommonInformationEntry = struct { |
| | 1537 | length: u32, |
| | 1538 | id: u32, |
| | 1539 | version: u8, |
| | 1540 | code_alignment_factor: u64, |
| | 1541 | data_alignment_factor: u64, |
| | 1542 | return_address_register: u64, |
| | 1543 | |
| | 1544 | // Augmented data |
| | 1545 | lsda_pointer_enc: ?u8, |
| | 1546 | personality_routine_pointer: ?EhPointer, |
| | 1547 | fde_pointer_enc: ?u8, |
| | 1548 | |
| | 1549 | initial_instructions: []const u8, |
| | 1550 | |
| | 1551 | // The returned struct references memory in `bytes`. |
| | 1552 | pub fn parse(bytes: []const u8, addr_size_bytes: u8, endian: std.builtin.Endian) !CommonInformationEntry { |
| | 1553 | if (addr_size_bytes > 8) return error.InvalidAddrSize; |
| | 1554 | if (bytes.len < 4) return badDwarf(); |
| | 1555 | const length = mem.readInt(u32, bytes[0..4], endian); |
| | 1556 | const cie_bytes = bytes[4..][0..length]; |
| | 1557 | |
| | 1558 | var stream = io.fixedBufferStream(cie_bytes); |
| | 1559 | const reader = stream.reader(); |
| | 1560 | |
| | 1561 | const id = try reader.readInt(u32, endian); |
| | 1562 | if (id != 0) return badDwarf(); |
| | 1563 | |
| | 1564 | const version = try reader.readByte(); |
| | 1565 | if (version != 1) return badDwarf(); |
| | 1566 | |
| | 1567 | var has_eh_data = false; |
| | 1568 | var has_aug_data = false; |
| | 1569 | |
| | 1570 | var aug_str_len: usize = 0; |
| | 1571 | var aug_str_start = stream.pos; |
| | 1572 | var aug_byte = try reader.readByte(); |
| | 1573 | while (aug_byte != 0) : (aug_byte = try reader.readByte()) { |
| | 1574 | switch (aug_byte) { |
| | 1575 | 'z' => { |
| | 1576 | if (aug_str_len != 0) return badDwarf(); |
| | 1577 | has_aug_data = true; |
| | 1578 | aug_str_start = stream.pos; |
| | 1579 | }, |
| | 1580 | 'e' => { |
| | 1581 | if (has_aug_data or aug_str_len != 0) return badDwarf(); |
| | 1582 | if (try reader.readByte() != 'h') return badDwarf(); |
| | 1583 | has_eh_data = true; |
| | 1584 | }, |
| | 1585 | else => { |
| | 1586 | if (has_eh_data) return badDwarf(); |
| | 1587 | aug_str_len += 1; |
| | 1588 | }, |
| | 1589 | } |
| | 1590 | } |
| | 1591 | |
| | 1592 | if (has_eh_data) { |
| | 1593 | // legacy data created by older versions of gcc - ignored here |
| | 1594 | for (0..addr_size_bytes) |_| _ = try reader.readByte(); |
| | 1595 | } |
| | 1596 | |
| | 1597 | const code_alignment_factor = try leb.readULEB128(u64, reader); |
| | 1598 | const data_alignment_factor = try leb.readULEB128(u64, reader); |
| | 1599 | const return_address_register = try leb.readULEB128(u64, reader); |
| | 1600 | |
| | 1601 | var lsda_pointer_enc: ?u8 = null; |
| | 1602 | var personality_routine_pointer: ?EhPointer = null; |
| | 1603 | var fde_pointer_enc: ?u8 = null; |
| | 1604 | |
| | 1605 | if (has_aug_data) { |
| | 1606 | const aug_data_len = try leb.readULEB128(usize, reader); |
| | 1607 | const aug_data_start = stream.pos; |
| | 1608 | |
| | 1609 | const aug_str = cie_bytes[aug_str_start..][0..aug_str_len]; |
| | 1610 | for (aug_str) |byte| { |
| | 1611 | switch (byte) { |
| | 1612 | 'L' => { |
| | 1613 | lsda_pointer_enc = try reader.readByte(); |
| | 1614 | }, |
| | 1615 | 'P' => { |
| | 1616 | const personality_enc = try reader.readByte(); |
| | 1617 | personality_routine_pointer = try readEhPointer( |
| | 1618 | personality_enc, |
| | 1619 | @ptrToInt(&cie_bytes[stream.pos]), |
| | 1620 | addr_size_bytes, |
| | 1621 | endian, |
| | 1622 | reader, |
| | 1623 | ); |
| | 1624 | }, |
| | 1625 | 'R' => { |
| | 1626 | fde_pointer_enc = try reader.readByte(); |
| | 1627 | }, |
| | 1628 | else => return badDwarf(), |
| | 1629 | } |
| | 1630 | } |
| | 1631 | |
| | 1632 | // verify length field |
| | 1633 | if (stream.pos != (aug_data_start + aug_data_len)) return badDwarf(); |
| | 1634 | } |
| | 1635 | |
| | 1636 | const initial_instructions = cie_bytes[stream.pos..]; |
| | 1637 | return .{ |
| | 1638 | .length = length, |
| | 1639 | .id = id, |
| | 1640 | .version = version, |
| | 1641 | .code_alignment_factor = code_alignment_factor, |
| | 1642 | .data_alignment_factor = data_alignment_factor, |
| | 1643 | .return_address_register = return_address_register, |
| | 1644 | .lsda_pointer_enc = lsda_pointer_enc, |
| | 1645 | .personality_routine_pointer = personality_routine_pointer, |
| | 1646 | .fde_pointer_enc = fde_pointer_enc, |
| | 1647 | .initial_instructions = initial_instructions, |
| | 1648 | }; |
| | 1649 | } |
| | 1650 | }; |