| author | |
| committer | |
| log | b85f84061aebb6c61ab9ca42d8147e8b76154818 |
| tree | e979dc3519fb83ed23680888605e1057217527b9 |
| parent | 62598c2187a63a7eb2d8c9f3dca0664ec5db270e |
dwarf: const-correctness fixups
dwarf: implement the remaining register rules
dwarf: start implmenting the DWARF expression stack machine4 files changed, 291 insertions(+), 87 deletions(-)
lib/std/debug.zig+4-6| ... | ... | @@ -483,16 +483,14 @@ pub const StackIterator = struct { |
| 483 | 483 | pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator { |
| 484 | 484 | var iterator = init(first_address, null); |
| 485 | 485 | iterator.debug_info = debug_info; |
| 486 | iterator.dwarf_context = try DW.UnwindContext.init(context, &isValidMemory); | |
| 486 | iterator.dwarf_context = try DW.UnwindContext.init(debug_info.allocator, context, &isValidMemory); | |
| 487 | 487 | iterator.last_error = null; |
| 488 | 488 | return iterator; |
| 489 | 489 | } |
| 490 | 490 | |
| 491 | 491 | pub fn deinit(self: *StackIterator) void { |
| 492 | if (have_ucontext) { | |
| 493 | if (self.debug_info) |debug_info| { | |
| 494 | self.dwarf_context.deinit(debug_info.allocator); | |
| 495 | } | |
| 492 | if (have_ucontext and self.debug_info != null) { | |
| 493 | self.dwarf_context.deinit(); | |
| 496 | 494 | } |
| 497 | 495 | } |
| 498 | 496 | |
| ... | ... | @@ -599,7 +597,7 @@ pub const StackIterator = struct { |
| 599 | 597 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { |
| 600 | 598 | self.dwarf_context.reg_ctx.eh_frame = true; |
| 601 | 599 | self.dwarf_context.reg_ctx.is_macho = di.is_macho; |
| 602 | return di.unwindFrame(self.debug_info.?.allocator, &self.dwarf_context, module.base_address); | |
| 600 | return di.unwindFrame(&self.dwarf_context, module.base_address); | |
| 603 | 601 | } else return error.MissingDebugInfo; |
| 604 | 602 | } |
| 605 | 603 |
lib/std/dwarf.zig+46-33| ... | ... | @@ -163,15 +163,9 @@ const PcRange = struct { |
| 163 | 163 | const Func = struct { |
| 164 | 164 | pc_range: ?PcRange, |
| 165 | 165 | name: ?[]const u8, |
| 166 | ||
| 167 | fn deinit(func: *Func, allocator: mem.Allocator) void { | |
| 168 | if (func.name) |name| { | |
| 169 | allocator.free(name); | |
| 170 | } | |
| 171 | } | |
| 172 | 166 | }; |
| 173 | 167 | |
| 174 | const CompileUnit = struct { | |
| 168 | pub const CompileUnit = struct { | |
| 175 | 169 | version: u16, |
| 176 | 170 | is_64: bool, |
| 177 | 171 | die: *Die, |
| ... | ... | @@ -181,6 +175,7 @@ const CompileUnit = struct { |
| 181 | 175 | addr_base: usize, |
| 182 | 176 | rnglists_base: usize, |
| 183 | 177 | loclists_base: usize, |
| 178 | frame_base: ?*const FormValue, | |
| 184 | 179 | }; |
| 185 | 180 | |
| 186 | 181 | const AbbrevTable = std.ArrayList(AbbrevTableEntry); |
| ... | ... | @@ -216,7 +211,7 @@ const AbbrevAttr = struct { |
| 216 | 211 | payload: i64, |
| 217 | 212 | }; |
| 218 | 213 | |
| 219 | const FormValue = union(enum) { | |
| 214 | pub const FormValue = union(enum) { | |
| 220 | 215 | Address: u64, |
| 221 | 216 | AddrOffset: usize, |
| 222 | 217 | Block: []u8, |
| ... | ... | @@ -298,7 +293,7 @@ const Die = struct { |
| 298 | 293 | |
| 299 | 294 | fn getAttrAddr( |
| 300 | 295 | self: *const Die, |
| 301 | di: *DwarfInfo, | |
| 296 | di: *const DwarfInfo, | |
| 302 | 297 | id: u64, |
| 303 | 298 | compile_unit: CompileUnit, |
| 304 | 299 | ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 { |
| ... | ... | @@ -708,9 +703,6 @@ pub const DwarfInfo = struct { |
| 708 | 703 | allocator.destroy(cu.die); |
| 709 | 704 | } |
| 710 | 705 | di.compile_unit_list.deinit(allocator); |
| 711 | for (di.func_list.items) |*func| { | |
| 712 | func.deinit(allocator); | |
| 713 | } | |
| 714 | 706 | di.func_list.deinit(allocator); |
| 715 | 707 | di.cie_map.deinit(allocator); |
| 716 | 708 | di.fde_list.deinit(allocator); |
| ... | ... | @@ -793,6 +785,7 @@ pub const DwarfInfo = struct { |
| 793 | 785 | .addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0, |
| 794 | 786 | .rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0, |
| 795 | 787 | .loclists_base = if (die_obj.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0, |
| 788 | .frame_base = die_obj.getAttr(AT.frame_base), | |
| 796 | 789 | }; |
| 797 | 790 | }, |
| 798 | 791 | TAG.subprogram, TAG.inlined_subroutine, TAG.subroutine, TAG.entry_point => { |
| ... | ... | @@ -802,8 +795,7 @@ pub const DwarfInfo = struct { |
| 802 | 795 | // Prevent endless loops |
| 803 | 796 | while (depth > 0) : (depth -= 1) { |
| 804 | 797 | if (this_die_obj.getAttr(AT.name)) |_| { |
| 805 | const name = try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit); | |
| 806 | break :x try allocator.dupe(u8, name); | |
| 798 | break :x try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit); | |
| 807 | 799 | } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { |
| 808 | 800 | // Follow the DIE it points to and repeat |
| 809 | 801 | const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin); |
| ... | ... | @@ -834,7 +826,7 @@ pub const DwarfInfo = struct { |
| 834 | 826 | break :x null; |
| 835 | 827 | }; |
| 836 | 828 | |
| 837 | var found_range = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: { | |
| 829 | var range_added = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: { | |
| 838 | 830 | if (die_obj.getAttr(AT.high_pc)) |high_pc_value| { |
| 839 | 831 | const pc_end = switch (high_pc_value.*) { |
| 840 | 832 | FormValue.Address => |value| value, |
| ... | ... | @@ -852,9 +844,11 @@ pub const DwarfInfo = struct { |
| 852 | 844 | .end = pc_end, |
| 853 | 845 | }, |
| 854 | 846 | }); |
| 847 | ||
| 848 | break :blk true; | |
| 855 | 849 | } |
| 856 | 850 | |
| 857 | break :blk true; | |
| 851 | break :blk false; | |
| 858 | 852 | } else |err| blk: { |
| 859 | 853 | if (err != error.MissingDebugInfo) return err; |
| 860 | 854 | break :blk false; |
| ... | ... | @@ -867,7 +861,7 @@ pub const DwarfInfo = struct { |
| 867 | 861 | }; |
| 868 | 862 | |
| 869 | 863 | while (try iter.next()) |range| { |
| 870 | found_range = true; | |
| 864 | range_added = true; | |
| 871 | 865 | try di.func_list.append(allocator, Func{ |
| 872 | 866 | .name = fn_name, |
| 873 | 867 | .pc_range = .{ |
| ... | ... | @@ -878,7 +872,7 @@ pub const DwarfInfo = struct { |
| 878 | 872 | } |
| 879 | 873 | } |
| 880 | 874 | |
| 881 | if (!found_range) { | |
| 875 | if (fn_name != null and !range_added) { | |
| 882 | 876 | try di.func_list.append(allocator, Func{ |
| 883 | 877 | .name = fn_name, |
| 884 | 878 | .pc_range = null, |
| ... | ... | @@ -952,6 +946,7 @@ pub const DwarfInfo = struct { |
| 952 | 946 | .addr_base = if (compile_unit_die.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0, |
| 953 | 947 | .rnglists_base = if (compile_unit_die.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0, |
| 954 | 948 | .loclists_base = if (compile_unit_die.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0, |
| 949 | .frame_base = compile_unit_die.getAttr(AT.frame_base), | |
| 955 | 950 | }; |
| 956 | 951 | |
| 957 | 952 | compile_unit.pc_range = x: { |
| ... | ... | @@ -987,11 +982,11 @@ pub const DwarfInfo = struct { |
| 987 | 982 | const DebugRangeIterator = struct { |
| 988 | 983 | base_address: u64, |
| 989 | 984 | section_type: DwarfSection, |
| 990 | di: *DwarfInfo, | |
| 985 | di: *const DwarfInfo, | |
| 991 | 986 | compile_unit: *const CompileUnit, |
| 992 | 987 | stream: io.FixedBufferStream([]const u8), |
| 993 | 988 | |
| 994 | pub fn init(ranges_value: *const FormValue, di: *DwarfInfo, compile_unit: *const CompileUnit) !@This() { | |
| 989 | pub fn init(ranges_value: *const FormValue, di: *const DwarfInfo, compile_unit: *const CompileUnit) !@This() { | |
| 995 | 990 | const section_type = if (compile_unit.version >= 5) DwarfSection.debug_rnglists else DwarfSection.debug_ranges; |
| 996 | 991 | const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo; |
| 997 | 992 | |
| ... | ... | @@ -1129,7 +1124,7 @@ pub const DwarfInfo = struct { |
| 1129 | 1124 | } |
| 1130 | 1125 | }; |
| 1131 | 1126 | |
| 1132 | pub fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { | |
| 1127 | pub fn findCompileUnit(di: *const DwarfInfo, target_address: u64) !*const CompileUnit { | |
| 1133 | 1128 | for (di.compile_unit_list.items) |*compile_unit| { |
| 1134 | 1129 | if (compile_unit.pc_range) |range| { |
| 1135 | 1130 | if (target_address >= range.start and target_address < range.end) return compile_unit; |
| ... | ... | @@ -1630,7 +1625,7 @@ pub const DwarfInfo = struct { |
| 1630 | 1625 | } |
| 1631 | 1626 | } |
| 1632 | 1627 | |
| 1633 | pub fn unwindFrame(di: *const DwarfInfo, allocator: mem.Allocator, context: *UnwindContext, module_base_address: usize) !usize { | |
| 1628 | pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, module_base_address: usize) !usize { | |
| 1634 | 1629 | if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture; |
| 1635 | 1630 | if (context.pc == 0) return 0; |
| 1636 | 1631 | |
| ... | ... | @@ -1678,10 +1673,11 @@ pub const DwarfInfo = struct { |
| 1678 | 1673 | cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE; |
| 1679 | 1674 | } |
| 1680 | 1675 | |
| 1676 | const compile_unit: ?*const CompileUnit = di.findCompileUnit(fde.pc_begin) catch null; | |
| 1681 | 1677 | context.vm.reset(); |
| 1682 | 1678 | context.reg_ctx.eh_frame = cie.version != 4; |
| 1683 | 1679 | |
| 1684 | _ = try context.vm.runToNative(allocator, mapped_pc, cie, fde); | |
| 1680 | _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde); | |
| 1685 | 1681 | const row = &context.vm.current_row; |
| 1686 | 1682 | |
| 1687 | 1683 | context.cfa = switch (row.cfa.rule) { |
| ... | ... | @@ -1690,12 +1686,19 @@ pub const DwarfInfo = struct { |
| 1690 | 1686 | const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); |
| 1691 | 1687 | break :blk try call_frame.applyOffset(value, offset); |
| 1692 | 1688 | }, |
| 1693 | .expression => |expression| { | |
| 1694 | ||
| 1695 | // TODO: Evaluate expression | |
| 1696 | _ = expression; | |
| 1697 | ||
| 1698 | return error.UnimplementedTODO; | |
| 1689 | .expression => |expression| blk: { | |
| 1690 | context.stack_machine.reset(); | |
| 1691 | const value = try context.stack_machine.run( | |
| 1692 | expression, | |
| 1693 | context.allocator, | |
| 1694 | compile_unit, | |
| 1695 | &context.ucontext, | |
| 1696 | context.reg_ctx, | |
| 1697 | context.cfa orelse 0, | |
| 1698 | ); | |
| 1699 | ||
| 1700 | if (value != .generic) return error.InvalidExpressionValue; | |
| 1701 | break :blk value.generic; | |
| 1699 | 1702 | }, |
| 1700 | 1703 | else => return error.InvalidCFARule, |
| 1701 | 1704 | }; |
| ... | ... | @@ -1713,7 +1716,13 @@ pub const DwarfInfo = struct { |
| 1713 | 1716 | has_next_ip = column.rule != .undefined; |
| 1714 | 1717 | } |
| 1715 | 1718 | |
| 1716 | try column.resolveValue(context.*, dest); | |
| 1719 | try column.resolveValue( | |
| 1720 | context, | |
| 1721 | compile_unit, | |
| 1722 | &context.ucontext, | |
| 1723 | context.reg_ctx, | |
| 1724 | dest, | |
| 1725 | ); | |
| 1717 | 1726 | } |
| 1718 | 1727 | } |
| 1719 | 1728 | |
| ... | ... | @@ -1738,16 +1747,19 @@ pub const DwarfInfo = struct { |
| 1738 | 1747 | }; |
| 1739 | 1748 | |
| 1740 | 1749 | pub const UnwindContext = struct { |
| 1750 | allocator: mem.Allocator, | |
| 1741 | 1751 | cfa: ?usize, |
| 1742 | 1752 | pc: usize, |
| 1743 | 1753 | ucontext: os.ucontext_t, |
| 1744 | 1754 | reg_ctx: abi.RegisterContext, |
| 1745 | 1755 | isValidMemory: *const fn (address: usize) bool, |
| 1746 | 1756 | vm: call_frame.VirtualMachine = .{}, |
| 1757 | stack_machine: expressions.StackMachine(.{ .call_frame_mode = true }) = .{}, | |
| 1747 | 1758 | |
| 1748 | pub fn init(ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext { | |
| 1759 | pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext { | |
| 1749 | 1760 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); |
| 1750 | 1761 | return .{ |
| 1762 | .allocator = allocator, | |
| 1751 | 1763 | .cfa = null, |
| 1752 | 1764 | .pc = pc, |
| 1753 | 1765 | .ucontext = ucontext.*, |
| ... | ... | @@ -1756,8 +1768,9 @@ pub const UnwindContext = struct { |
| 1756 | 1768 | }; |
| 1757 | 1769 | } |
| 1758 | 1770 | |
| 1759 | pub fn deinit(self: *UnwindContext, allocator: mem.Allocator) void { | |
| 1760 | self.vm.deinit(allocator); | |
| 1771 | pub fn deinit(self: *UnwindContext) void { | |
| 1772 | self.vm.deinit(self.allocator); | |
| 1773 | self.stack_machine.deinit(self.allocator); | |
| 1761 | 1774 | } |
| 1762 | 1775 | |
| 1763 | 1776 | pub fn getFp(self: *const UnwindContext) !usize { |
lib/std/dwarf/call_frame.zig+48-21| ... | ... | @@ -183,9 +183,9 @@ pub const Instruction = union(Opcode) { |
| 183 | 183 | offset_extended_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), |
| 184 | 184 | def_cfa_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), |
| 185 | 185 | def_cfa_offset_sf: InstructionType(.{ .offset = .sleb128_offset }), |
| 186 | val_offset: InstructionType(.{ .a = .uleb128_offset, .b = .uleb128_offset }), | |
| 187 | val_offset_sf: InstructionType(.{ .a = .uleb128_offset, .b = .sleb128_offset }), | |
| 188 | val_expression: InstructionType(.{ .a = .uleb128_offset, .block = .block }), | |
| 186 | val_offset: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }), | |
| 187 | val_offset_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), | |
| 188 | val_expression: InstructionType(.{ .register = .uleb128_register, .block = .block }), | |
| 189 | 189 | |
| 190 | 190 | fn readOperands( |
| 191 | 191 | self: *Instruction, |
| ... | ... | @@ -292,7 +292,14 @@ pub const VirtualMachine = struct { |
| 292 | 292 | rule: RegisterRule = .{ .default = {} }, |
| 293 | 293 | |
| 294 | 294 | /// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes) |
| 295 | pub fn resolveValue(self: Column, context: dwarf.UnwindContext, out: []u8) !void { | |
| 295 | pub fn resolveValue( | |
| 296 | self: Column, | |
| 297 | context: *dwarf.UnwindContext, | |
| 298 | compile_unit: ?*const dwarf.CompileUnit, | |
| 299 | ucontext: *const std.os.ucontext_t, | |
| 300 | reg_ctx: abi.RegisterContext, | |
| 301 | out: []u8, | |
| 302 | ) !void { | |
| 296 | 303 | switch (self.rule) { |
| 297 | 304 | .default => { |
| 298 | 305 | const register = self.register orelse return error.InvalidRegister; |
| ... | ... | @@ -321,14 +328,21 @@ pub const VirtualMachine = struct { |
| 321 | 328 | @memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); |
| 322 | 329 | }, |
| 323 | 330 | .expression => |expression| { |
| 324 | // TODO | |
| 325 | _ = expression; | |
| 326 | unreachable; | |
| 331 | context.stack_machine.reset(); | |
| 332 | const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?); | |
| 333 | ||
| 334 | if (value != .generic) return error.InvalidExpressionValue; | |
| 335 | if (!context.isValidMemory(value.generic)) return error.InvalidExpressionAddress; | |
| 336 | ||
| 337 | const ptr: *usize = @ptrFromInt(value.generic); | |
| 338 | mem.writeIntSliceNative(usize, out, ptr.*); | |
| 327 | 339 | }, |
| 328 | 340 | .val_expression => |expression| { |
| 329 | // TODO | |
| 330 | _ = expression; | |
| 331 | unreachable; | |
| 341 | context.stack_machine.reset(); | |
| 342 | const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?); | |
| 343 | ||
| 344 | if (value != .generic) return error.InvalidExpressionValue; | |
| 345 | mem.writeIntSliceNative(usize, out, value.generic); | |
| 332 | 346 | }, |
| 333 | 347 | .architectural => return error.UnimplementedRule, |
| 334 | 348 | } |
| ... | ... | @@ -546,12 +560,16 @@ pub const VirtualMachine = struct { |
| 546 | 560 | .def_cfa_offset => |i| { |
| 547 | 561 | try self.resolveCopyOnWrite(allocator); |
| 548 | 562 | if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation; |
| 549 | self.current_row.cfa.rule = .{ .val_offset = @intCast(i.operands.offset) }; | |
| 563 | self.current_row.cfa.rule = .{ | |
| 564 | .val_offset = @intCast(i.operands.offset), | |
| 565 | }; | |
| 550 | 566 | }, |
| 551 | 567 | .def_cfa_offset_sf => |i| { |
| 552 | 568 | try self.resolveCopyOnWrite(allocator); |
| 553 | 569 | if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation; |
| 554 | self.current_row.cfa.rule = .{ .val_offset = i.operands.offset * cie.data_alignment_factor }; | |
| 570 | self.current_row.cfa.rule = .{ | |
| 571 | .val_offset = i.operands.offset * cie.data_alignment_factor, | |
| 572 | }; | |
| 555 | 573 | }, |
| 556 | 574 | .def_cfa_expression => |i| { |
| 557 | 575 | try self.resolveCopyOnWrite(allocator); |
| ... | ... | @@ -567,17 +585,26 @@ pub const VirtualMachine = struct { |
| 567 | 585 | .expression = i.operands.block, |
| 568 | 586 | }; |
| 569 | 587 | }, |
| 570 | .val_offset => { | |
| 571 | // TODO: Implement | |
| 572 | unreachable; | |
| 588 | .val_offset => |i| { | |
| 589 | try self.resolveCopyOnWrite(allocator); | |
| 590 | const column = try self.getOrAddColumn(allocator, i.operands.register); | |
| 591 | column.rule = .{ | |
| 592 | .val_offset = @as(i64, @intCast(i.operands.offset)) * cie.data_alignment_factor, | |
| 593 | }; | |
| 573 | 594 | }, |
| 574 | .val_offset_sf => { | |
| 575 | // TODO: Implement | |
| 576 | unreachable; | |
| 595 | .val_offset_sf => |i| { | |
| 596 | try self.resolveCopyOnWrite(allocator); | |
| 597 | const column = try self.getOrAddColumn(allocator, i.operands.register); | |
| 598 | column.rule = .{ | |
| 599 | .val_offset = i.operands.offset * cie.data_alignment_factor, | |
| 600 | }; | |
| 577 | 601 | }, |
| 578 | .val_expression => { | |
| 579 | // TODO: Implement | |
| 580 | unreachable; | |
| 602 | .val_expression => |i| { | |
| 603 | try self.resolveCopyOnWrite(allocator); | |
| 604 | const column = try self.getOrAddColumn(allocator, i.operands.register); | |
| 605 | column.rule = .{ | |
| 606 | .val_expression = i.operands.block, | |
| 607 | }; | |
| 581 | 608 | }, |
| 582 | 609 | } |
| 583 | 610 |
lib/std/dwarf/expressions.zig+193-27| ... | ... | @@ -2,6 +2,9 @@ const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const OP = @import("OP.zig"); |
| 4 | 4 | const leb = @import("../leb128.zig"); |
| 5 | const dwarf = @import("../dwarf.zig"); | |
| 6 | const abi = dwarf.abi; | |
| 7 | const mem = std.mem; | |
| 5 | 8 | |
| 6 | 9 | pub const StackMachineOptions = struct { |
| 7 | 10 | /// The address size of the target architecture |
| ... | ... | @@ -33,9 +36,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 33 | 36 | }; |
| 34 | 37 | |
| 35 | 38 | return struct { |
| 36 | const Value = union(enum) { | |
| 39 | const Self = @This(); | |
| 40 | ||
| 41 | const Operand = union(enum) { | |
| 37 | 42 | generic: addr_type, |
| 38 | const_type: []const u8, | |
| 39 | 43 | register: u8, |
| 40 | 44 | base_register: struct { |
| 41 | 45 | base_register: u8, |
| ... | ... | @@ -46,7 +50,11 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 46 | 50 | offset: i64, |
| 47 | 51 | }, |
| 48 | 52 | block: []const u8, |
| 49 | base_type: struct { | |
| 53 | register_type: struct { | |
| 54 | register: u8, | |
| 55 | type_offset: u64, | |
| 56 | }, | |
| 57 | const_type: struct { | |
| 50 | 58 | type_offset: u64, |
| 51 | 59 | value_bytes: []const u8, |
| 52 | 60 | }, |
| ... | ... | @@ -56,9 +64,31 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 56 | 64 | }, |
| 57 | 65 | }; |
| 58 | 66 | |
| 67 | const Value = union(enum) { | |
| 68 | generic: addr_type, | |
| 69 | regval_type: struct { | |
| 70 | // Offset of DW_TAG_base_type DIE | |
| 71 | type_offset: u64, | |
| 72 | value: addr_type, | |
| 73 | }, | |
| 74 | const_type: struct { | |
| 75 | // Offset of DW_TAG_base_type DIE | |
| 76 | type_offset: u64, | |
| 77 | value_bytes: []const u8, | |
| 78 | }, | |
| 79 | }; | |
| 80 | ||
| 59 | 81 | stack: std.ArrayListUnmanaged(Value) = .{}, |
| 60 | 82 | |
| 61 | fn generic(value: anytype) Value { | |
| 83 | pub fn reset(self: *Self) void { | |
| 84 | self.stack.clearRetainingCapacity(); | |
| 85 | } | |
| 86 | ||
| 87 | pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { | |
| 88 | self.stack.deinit(allocator); | |
| 89 | } | |
| 90 | ||
| 91 | fn generic(value: anytype) Operand { | |
| 62 | 92 | const int_info = @typeInfo(@TypeOf(value)).Int; |
| 63 | 93 | if (@sizeOf(@TypeOf(value)) > options.addr_size) { |
| 64 | 94 | return .{ .generic = switch (int_info.signedness) { |
| ... | ... | @@ -73,7 +103,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 73 | 103 | } |
| 74 | 104 | } |
| 75 | 105 | |
| 76 | pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Value { | |
| 106 | pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Operand { | |
| 77 | 107 | const reader = stream.reader(); |
| 78 | 108 | return switch (opcode) { |
| 79 | 109 | OP.addr, |
| ... | ... | @@ -87,8 +117,8 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 87 | 117 | OP.const1s => generic(try reader.readByteSigned()), |
| 88 | 118 | OP.const2u, |
| 89 | 119 | OP.call2, |
| 90 | OP.call4, | |
| 91 | 120 | => generic(try reader.readInt(u16, options.endian)), |
| 121 | OP.call4 => generic(try reader.readInt(u32, options.endian)), | |
| 92 | 122 | OP.const2s, |
| 93 | 123 | OP.bra, |
| 94 | 124 | OP.skip, |
| ... | ... | @@ -114,21 +144,35 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 114 | 144 | .offset = try leb.readILEB128(i64, reader), |
| 115 | 145 | } }, |
| 116 | 146 | OP.regx => .{ .register = try leb.readULEB128(u8, reader) }, |
| 117 | OP.bregx, OP.regval_type => .{ .base_register = .{ | |
| 118 | .base_register = try leb.readULEB128(u8, reader), | |
| 119 | .offset = try leb.readILEB128(i64, reader), | |
| 120 | } }, | |
| 147 | OP.bregx => blk: { | |
| 148 | const base_register = try leb.readULEB128(u8, reader); | |
| 149 | const offset = try leb.readILEB128(i64, reader); | |
| 150 | break :blk .{ .base_register = .{ | |
| 151 | .base_register = base_register, | |
| 152 | .offset = offset, | |
| 153 | } }; | |
| 154 | }, | |
| 155 | OP.regval_type => blk: { | |
| 156 | const register = try leb.readULEB128(u8, reader); | |
| 157 | const type_offset = try leb.readULEB128(u64, reader); | |
| 158 | break :blk .{ .register_type = .{ | |
| 159 | .register = register, | |
| 160 | .type_offset = type_offset, | |
| 161 | } }; | |
| 162 | }, | |
| 121 | 163 | OP.piece => .{ |
| 122 | 164 | .composite_location = .{ |
| 123 | 165 | .size = try leb.readULEB128(u8, reader), |
| 124 | 166 | .offset = 0, |
| 125 | 167 | }, |
| 126 | 168 | }, |
| 127 | OP.bit_piece => .{ | |
| 128 | .composite_location = .{ | |
| 129 | .size = try leb.readULEB128(u8, reader), | |
| 130 | .offset = try leb.readILEB128(i64, reader), | |
| 131 | }, | |
| 169 | OP.bit_piece => blk: { | |
| 170 | const size = try leb.readULEB128(u8, reader); | |
| 171 | const offset = try leb.readILEB128(i64, reader); | |
| 172 | break :blk .{ .composite_location = .{ | |
| 173 | .size = size, | |
| 174 | .offset = offset, | |
| 175 | } }; | |
| 132 | 176 | }, |
| 133 | 177 | OP.implicit_value, OP.entry_value => blk: { |
| 134 | 178 | const size = try leb.readULEB128(u8, reader); |
| ... | ... | @@ -145,7 +189,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 145 | 189 | if (stream.pos + size > stream.buffer.len) return error.InvalidExpression; |
| 146 | 190 | const value_bytes = stream.buffer[stream.pos..][0..size]; |
| 147 | 191 | stream.pos += size; |
| 148 | break :blk .{ .base_type = .{ | |
| 192 | break :blk .{ .const_type = .{ | |
| 149 | 193 | .type_offset = type_offset, |
| 150 | 194 | .value_bytes = value_bytes, |
| 151 | 195 | } }; |
| ... | ... | @@ -163,22 +207,144 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 163 | 207 | }; |
| 164 | 208 | } |
| 165 | 209 | |
| 210 | pub fn run( | |
| 211 | self: *Self, | |
| 212 | expression: []const u8, | |
| 213 | allocator: std.mem.Allocator, | |
| 214 | compile_unit: ?*const dwarf.CompileUnit, | |
| 215 | ucontext: *const std.os.ucontext_t, | |
| 216 | reg_ctx: abi.RegisterContext, | |
| 217 | initial_value: usize, | |
| 218 | ) !Value { | |
| 219 | try self.stack.append(allocator, .{ .generic = initial_value }); | |
| 220 | var stream = std.io.fixedBufferStream(expression); | |
| 221 | while (try self.step(&stream, allocator, compile_unit, ucontext, reg_ctx)) {} | |
| 222 | if (self.stack.items.len == 0) return error.InvalidExpression; | |
| 223 | return self.stack.items[self.stack.items.len - 1]; | |
| 224 | } | |
| 225 | ||
| 226 | /// Reads an opcode and its operands from the stream and executes it | |
| 166 | 227 | pub fn step( |
| 167 | self: *StackMachine, | |
| 168 | stream: std.io.FixedBufferStream([]const u8), | |
| 228 | self: *Self, | |
| 229 | stream: *std.io.FixedBufferStream([]const u8), | |
| 169 | 230 | allocator: std.mem.Allocator, |
| 170 | ) !void { | |
| 171 | if (@sizeOf(usize) != addr_type or options.endian != builtin.target.cpu.arch.endian()) | |
| 231 | compile_unit: ?*const dwarf.CompileUnit, | |
| 232 | ucontext: *const std.os.ucontext_t, | |
| 233 | reg_ctx: dwarf.abi.RegisterContext, | |
| 234 | ) !bool { | |
| 235 | if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian()) | |
| 172 | 236 | @compileError("Execution of non-native address sizees / endianness is not supported"); |
| 173 | 237 | |
| 174 | const opcode = try stream.reader.readByte(); | |
| 175 | _ = opcode; | |
| 176 | _ = self; | |
| 177 | _ = allocator; | |
| 238 | const opcode = try stream.reader().readByte(); | |
| 239 | if (options.call_frame_mode) { | |
| 240 | // Certain opcodes are not allowed in a CFA context, see 6.4.2 | |
| 241 | switch (opcode) { | |
| 242 | OP.addrx, | |
| 243 | OP.call2, | |
| 244 | OP.call4, | |
| 245 | OP.call_ref, | |
| 246 | OP.const_type, | |
| 247 | OP.constx, | |
| 248 | OP.convert, | |
| 249 | OP.deref_type, | |
| 250 | OP.regval_type, | |
| 251 | OP.reinterpret, | |
| 252 | OP.push_object_address, | |
| 253 | OP.call_frame_cfa, | |
| 254 | => return error.InvalidCFAExpression, | |
| 255 | else => {}, | |
| 256 | } | |
| 257 | } | |
| 258 | ||
| 259 | switch (opcode) { | |
| 260 | ||
| 261 | // 2.5.1.1: Literal Encodings | |
| 262 | OP.lit0...OP.lit31, | |
| 263 | OP.addr, | |
| 264 | OP.const1u, | |
| 265 | OP.const2u, | |
| 266 | OP.const4u, | |
| 267 | OP.const8u, | |
| 268 | OP.const1s, | |
| 269 | OP.const2s, | |
| 270 | OP.const4s, | |
| 271 | OP.const8s, | |
| 272 | OP.constu, | |
| 273 | OP.consts, | |
| 274 | => try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode)).?.generic }), | |
| 275 | ||
| 276 | OP.const_type => { | |
| 277 | const const_type = (try readOperand(stream, opcode)).?.const_type; | |
| 278 | try self.stack.append(allocator, .{ .const_type = .{ | |
| 279 | .type_offset = const_type.type_offset, | |
| 280 | .value_bytes = const_type.value_bytes, | |
| 281 | } }); | |
| 282 | }, | |
| 283 | ||
| 284 | OP.addrx, OP.constx => { | |
| 285 | const debug_addr_index = (try readOperand(stream, opcode)).?.generic; | |
| 286 | ||
| 287 | // TODO: Read item from .debug_addr, this requires need DW_AT_addr_base of the compile unit, push onto stack as generic | |
| 288 | ||
| 289 | _ = debug_addr_index; | |
| 290 | unreachable; | |
| 291 | }, | |
| 292 | ||
| 293 | // 2.5.1.2: Register Values | |
| 294 | OP.fbreg => { | |
| 295 | if (compile_unit == null) return error.ExpressionRequiresCompileUnit; | |
| 296 | if (compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase; | |
| 297 | ||
| 298 | const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic); | |
| 299 | _ = offset; | |
| 300 | ||
| 301 | switch (compile_unit.?.frame_base.?.*) { | |
| 302 | .ExprLoc => { | |
| 303 | // TODO: Run this expression in a nested stack machine | |
| 304 | return error.UnimplementedOpcode; | |
| 305 | }, | |
| 306 | .LocListOffset => { | |
| 307 | // TODO: Read value from .debug_loclists | |
| 308 | return error.UnimplementedOpcode; | |
| 309 | }, | |
| 310 | .SecOffset => { | |
| 311 | // TODO: Read value from .debug_loclists | |
| 312 | return error.UnimplementedOpcode; | |
| 313 | }, | |
| 314 | else => return error.InvalidFrameBase, | |
| 315 | } | |
| 316 | }, | |
| 317 | OP.breg0...OP.breg31, OP.bregx => { | |
| 318 | const base_register = (try readOperand(stream, opcode)).?.base_register; | |
| 319 | var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(ucontext, base_register.base_register, reg_ctx))); | |
| 320 | value += base_register.offset; | |
| 321 | try self.stack.append(allocator, .{ .generic = @intCast(value) }); | |
| 322 | }, | |
| 323 | OP.regval_type => { | |
| 324 | const register_type = (try readOperand(stream, opcode)).?.register_type; | |
| 325 | const value = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, register_type.register, reg_ctx)); | |
| 326 | try self.stack.append(allocator, .{ | |
| 327 | .regval_type = .{ | |
| 328 | .value = value, | |
| 329 | .type_offset = register_type.type_offset, | |
| 330 | }, | |
| 331 | }); | |
| 332 | }, | |
| 333 | ||
| 334 | // 2.5.1.3: Stack Operations | |
| 335 | ||
| 336 | OP.dup => {}, | |
| 337 | ||
| 338 | else => { | |
| 339 | std.debug.print("Unimplemented DWARF expression opcode: {x}\n", .{opcode}); | |
| 340 | unreachable; | |
| 341 | }, | |
| 342 | ||
| 343 | // These have already been handled by readOperand | |
| 344 | OP.lo_user...OP.hi_user => unreachable, | |
| 345 | } | |
| 178 | 346 | |
| 179 | // switch (opcode) { | |
| 180 | // OP.addr => try self.stack.append(allocator, try readOperand(stream, opcode)), | |
| 181 | // } | |
| 347 | return stream.pos < stream.buffer.len; | |
| 182 | 348 | } |
| 183 | 349 | }; |
| 184 | 350 | } |