| 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,16 +483,14 @@ pub const StackIterator = struct { |
| 483 | pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator { | 483 | pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator { |
| 484 | var iterator = init(first_address, null); | 484 | var iterator = init(first_address, null); |
| 485 | iterator.debug_info = debug_info; | 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 | iterator.last_error = null; | 487 | iterator.last_error = null; |
| 488 | return iterator; | 488 | return iterator; |
| 489 | } | 489 | } |
| 490 | 490 | ||
| 491 | pub fn deinit(self: *StackIterator) void { | 491 | pub fn deinit(self: *StackIterator) void { |
| 492 | if (have_ucontext) { | 492 | if (have_ucontext and self.debug_info != null) { |
| 493 | if (self.debug_info) |debug_info| { | 493 | self.dwarf_context.deinit(); |
| 494 | self.dwarf_context.deinit(debug_info.allocator); | ||
| 495 | } | ||
| 496 | } | 494 | } |
| 497 | } | 495 | } |
| 498 | 496 | ||
| ... | @@ -599,7 +597,7 @@ pub const StackIterator = struct { | ... | @@ -599,7 +597,7 @@ pub const StackIterator = struct { |
| 599 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { | 597 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { |
| 600 | self.dwarf_context.reg_ctx.eh_frame = true; | 598 | self.dwarf_context.reg_ctx.eh_frame = true; |
| 601 | self.dwarf_context.reg_ctx.is_macho = di.is_macho; | 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 | } else return error.MissingDebugInfo; | 601 | } else return error.MissingDebugInfo; |
| 604 | } | 602 | } |
| 605 | 603 |
lib/std/dwarf.zig+46-33| ... | @@ -163,15 +163,9 @@ const PcRange = struct { | ... | @@ -163,15 +163,9 @@ const PcRange = struct { |
| 163 | const Func = struct { | 163 | const Func = struct { |
| 164 | pc_range: ?PcRange, | 164 | pc_range: ?PcRange, |
| 165 | name: ?[]const u8, | 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 | version: u16, | 169 | version: u16, |
| 176 | is_64: bool, | 170 | is_64: bool, |
| 177 | die: *Die, | 171 | die: *Die, |
| ... | @@ -181,6 +175,7 @@ const CompileUnit = struct { | ... | @@ -181,6 +175,7 @@ const CompileUnit = struct { |
| 181 | addr_base: usize, | 175 | addr_base: usize, |
| 182 | rnglists_base: usize, | 176 | rnglists_base: usize, |
| 183 | loclists_base: usize, | 177 | loclists_base: usize, |
| 178 | frame_base: ?*const FormValue, | ||
| 184 | }; | 179 | }; |
| 185 | 180 | ||
| 186 | const AbbrevTable = std.ArrayList(AbbrevTableEntry); | 181 | const AbbrevTable = std.ArrayList(AbbrevTableEntry); |
| ... | @@ -216,7 +211,7 @@ const AbbrevAttr = struct { | ... | @@ -216,7 +211,7 @@ const AbbrevAttr = struct { |
| 216 | payload: i64, | 211 | payload: i64, |
| 217 | }; | 212 | }; |
| 218 | 213 | ||
| 219 | const FormValue = union(enum) { | 214 | pub const FormValue = union(enum) { |
| 220 | Address: u64, | 215 | Address: u64, |
| 221 | AddrOffset: usize, | 216 | AddrOffset: usize, |
| 222 | Block: []u8, | 217 | Block: []u8, |
| ... | @@ -298,7 +293,7 @@ const Die = struct { | ... | @@ -298,7 +293,7 @@ const Die = struct { |
| 298 | 293 | ||
| 299 | fn getAttrAddr( | 294 | fn getAttrAddr( |
| 300 | self: *const Die, | 295 | self: *const Die, |
| 301 | di: *DwarfInfo, | 296 | di: *const DwarfInfo, |
| 302 | id: u64, | 297 | id: u64, |
| 303 | compile_unit: CompileUnit, | 298 | compile_unit: CompileUnit, |
| 304 | ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 { | 299 | ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 { |
| ... | @@ -708,9 +703,6 @@ pub const DwarfInfo = struct { | ... | @@ -708,9 +703,6 @@ pub const DwarfInfo = struct { |
| 708 | allocator.destroy(cu.die); | 703 | allocator.destroy(cu.die); |
| 709 | } | 704 | } |
| 710 | di.compile_unit_list.deinit(allocator); | 705 | di.compile_unit_list.deinit(allocator); |
| 711 | for (di.func_list.items) |*func| { | ||
| 712 | func.deinit(allocator); | ||
| 713 | } | ||
| 714 | di.func_list.deinit(allocator); | 706 | di.func_list.deinit(allocator); |
| 715 | di.cie_map.deinit(allocator); | 707 | di.cie_map.deinit(allocator); |
| 716 | di.fde_list.deinit(allocator); | 708 | di.fde_list.deinit(allocator); |
| ... | @@ -793,6 +785,7 @@ pub const DwarfInfo = struct { | ... | @@ -793,6 +785,7 @@ pub const DwarfInfo = struct { |
| 793 | .addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0, | 785 | .addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0, |
| 794 | .rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0, | 786 | .rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0, |
| 795 | .loclists_base = if (die_obj.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0, | 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 | TAG.subprogram, TAG.inlined_subroutine, TAG.subroutine, TAG.entry_point => { | 791 | TAG.subprogram, TAG.inlined_subroutine, TAG.subroutine, TAG.entry_point => { |
| ... | @@ -802,8 +795,7 @@ pub const DwarfInfo = struct { | ... | @@ -802,8 +795,7 @@ pub const DwarfInfo = struct { |
| 802 | // Prevent endless loops | 795 | // Prevent endless loops |
| 803 | while (depth > 0) : (depth -= 1) { | 796 | while (depth > 0) : (depth -= 1) { |
| 804 | if (this_die_obj.getAttr(AT.name)) |_| { | 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); | 798 | break :x try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit); |
| 806 | break :x try allocator.dupe(u8, name); | ||
| 807 | } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { | 799 | } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { |
| 808 | // Follow the DIE it points to and repeat | 800 | // Follow the DIE it points to and repeat |
| 809 | const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin); | 801 | const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin); |
| ... | @@ -834,7 +826,7 @@ pub const DwarfInfo = struct { | ... | @@ -834,7 +826,7 @@ pub const DwarfInfo = struct { |
| 834 | break :x null; | 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 | if (die_obj.getAttr(AT.high_pc)) |high_pc_value| { | 830 | if (die_obj.getAttr(AT.high_pc)) |high_pc_value| { |
| 839 | const pc_end = switch (high_pc_value.*) { | 831 | const pc_end = switch (high_pc_value.*) { |
| 840 | FormValue.Address => |value| value, | 832 | FormValue.Address => |value| value, |
| ... | @@ -852,9 +844,11 @@ pub const DwarfInfo = struct { | ... | @@ -852,9 +844,11 @@ pub const DwarfInfo = struct { |
| 852 | .end = pc_end, | 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 | } else |err| blk: { | 852 | } else |err| blk: { |
| 859 | if (err != error.MissingDebugInfo) return err; | 853 | if (err != error.MissingDebugInfo) return err; |
| 860 | break :blk false; | 854 | break :blk false; |
| ... | @@ -867,7 +861,7 @@ pub const DwarfInfo = struct { | ... | @@ -867,7 +861,7 @@ pub const DwarfInfo = struct { |
| 867 | }; | 861 | }; |
| 868 | 862 | ||
| 869 | while (try iter.next()) |range| { | 863 | while (try iter.next()) |range| { |
| 870 | found_range = true; | 864 | range_added = true; |
| 871 | try di.func_list.append(allocator, Func{ | 865 | try di.func_list.append(allocator, Func{ |
| 872 | .name = fn_name, | 866 | .name = fn_name, |
| 873 | .pc_range = .{ | 867 | .pc_range = .{ |
| ... | @@ -878,7 +872,7 @@ pub const DwarfInfo = struct { | ... | @@ -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 | try di.func_list.append(allocator, Func{ | 876 | try di.func_list.append(allocator, Func{ |
| 883 | .name = fn_name, | 877 | .name = fn_name, |
| 884 | .pc_range = null, | 878 | .pc_range = null, |
| ... | @@ -952,6 +946,7 @@ pub const DwarfInfo = struct { | ... | @@ -952,6 +946,7 @@ pub const DwarfInfo = struct { |
| 952 | .addr_base = if (compile_unit_die.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0, | 946 | .addr_base = if (compile_unit_die.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0, |
| 953 | .rnglists_base = if (compile_unit_die.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0, | 947 | .rnglists_base = if (compile_unit_die.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0, |
| 954 | .loclists_base = if (compile_unit_die.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0, | 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 | compile_unit.pc_range = x: { | 952 | compile_unit.pc_range = x: { |
| ... | @@ -987,11 +982,11 @@ pub const DwarfInfo = struct { | ... | @@ -987,11 +982,11 @@ pub const DwarfInfo = struct { |
| 987 | const DebugRangeIterator = struct { | 982 | const DebugRangeIterator = struct { |
| 988 | base_address: u64, | 983 | base_address: u64, |
| 989 | section_type: DwarfSection, | 984 | section_type: DwarfSection, |
| 990 | di: *DwarfInfo, | 985 | di: *const DwarfInfo, |
| 991 | compile_unit: *const CompileUnit, | 986 | compile_unit: *const CompileUnit, |
| 992 | stream: io.FixedBufferStream([]const u8), | 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 | const section_type = if (compile_unit.version >= 5) DwarfSection.debug_rnglists else DwarfSection.debug_ranges; | 990 | const section_type = if (compile_unit.version >= 5) DwarfSection.debug_rnglists else DwarfSection.debug_ranges; |
| 996 | const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo; | 991 | const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo; |
| 997 | 992 | ||
| ... | @@ -1129,7 +1124,7 @@ pub const DwarfInfo = struct { | ... | @@ -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 | for (di.compile_unit_list.items) |*compile_unit| { | 1128 | for (di.compile_unit_list.items) |*compile_unit| { |
| 1134 | if (compile_unit.pc_range) |range| { | 1129 | if (compile_unit.pc_range) |range| { |
| 1135 | if (target_address >= range.start and target_address < range.end) return compile_unit; | 1130 | if (target_address >= range.start and target_address < range.end) return compile_unit; |
| ... | @@ -1630,7 +1625,7 @@ pub const DwarfInfo = struct { | ... | @@ -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 | if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture; | 1629 | if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture; |
| 1635 | if (context.pc == 0) return 0; | 1630 | if (context.pc == 0) return 0; |
| 1636 | 1631 | ||
| ... | @@ -1678,10 +1673,11 @@ pub const DwarfInfo = struct { | ... | @@ -1678,10 +1673,11 @@ pub const DwarfInfo = struct { |
| 1678 | cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE; | 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 | context.vm.reset(); | 1677 | context.vm.reset(); |
| 1682 | context.reg_ctx.eh_frame = cie.version != 4; | 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 | const row = &context.vm.current_row; | 1681 | const row = &context.vm.current_row; |
| 1686 | 1682 | ||
| 1687 | context.cfa = switch (row.cfa.rule) { | 1683 | context.cfa = switch (row.cfa.rule) { |
| ... | @@ -1690,12 +1686,19 @@ pub const DwarfInfo = struct { | ... | @@ -1690,12 +1686,19 @@ pub const DwarfInfo = struct { |
| 1690 | const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); | 1686 | const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); |
| 1691 | break :blk try call_frame.applyOffset(value, offset); | 1687 | break :blk try call_frame.applyOffset(value, offset); |
| 1692 | }, | 1688 | }, |
| 1693 | .expression => |expression| { | 1689 | .expression => |expression| blk: { |
| 1694 | 1690 | context.stack_machine.reset(); | |
| 1695 | // TODO: Evaluate expression | 1691 | const value = try context.stack_machine.run( |
| 1696 | _ = expression; | 1692 | expression, |
| 1697 | 1693 | context.allocator, | |
| 1698 | return error.UnimplementedTODO; | 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 | else => return error.InvalidCFARule, | 1703 | else => return error.InvalidCFARule, |
| 1701 | }; | 1704 | }; |
| ... | @@ -1713,7 +1716,13 @@ pub const DwarfInfo = struct { | ... | @@ -1713,7 +1716,13 @@ pub const DwarfInfo = struct { |
| 1713 | has_next_ip = column.rule != .undefined; | 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,16 +1747,19 @@ pub const DwarfInfo = struct { |
| 1738 | }; | 1747 | }; |
| 1739 | 1748 | ||
| 1740 | pub const UnwindContext = struct { | 1749 | pub const UnwindContext = struct { |
| 1750 | allocator: mem.Allocator, | ||
| 1741 | cfa: ?usize, | 1751 | cfa: ?usize, |
| 1742 | pc: usize, | 1752 | pc: usize, |
| 1743 | ucontext: os.ucontext_t, | 1753 | ucontext: os.ucontext_t, |
| 1744 | reg_ctx: abi.RegisterContext, | 1754 | reg_ctx: abi.RegisterContext, |
| 1745 | isValidMemory: *const fn (address: usize) bool, | 1755 | isValidMemory: *const fn (address: usize) bool, |
| 1746 | vm: call_frame.VirtualMachine = .{}, | 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 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); | 1760 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); |
| 1750 | return .{ | 1761 | return .{ |
| 1762 | .allocator = allocator, | ||
| 1751 | .cfa = null, | 1763 | .cfa = null, |
| 1752 | .pc = pc, | 1764 | .pc = pc, |
| 1753 | .ucontext = ucontext.*, | 1765 | .ucontext = ucontext.*, |
| ... | @@ -1756,8 +1768,9 @@ pub const UnwindContext = struct { | ... | @@ -1756,8 +1768,9 @@ pub const UnwindContext = struct { |
| 1756 | }; | 1768 | }; |
| 1757 | } | 1769 | } |
| 1758 | 1770 | ||
| 1759 | pub fn deinit(self: *UnwindContext, allocator: mem.Allocator) void { | 1771 | pub fn deinit(self: *UnwindContext) void { |
| 1760 | self.vm.deinit(allocator); | 1772 | self.vm.deinit(self.allocator); |
| 1773 | self.stack_machine.deinit(self.allocator); | ||
| 1761 | } | 1774 | } |
| 1762 | 1775 | ||
| 1763 | pub fn getFp(self: *const UnwindContext) !usize { | 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,9 +183,9 @@ pub const Instruction = union(Opcode) { |
| 183 | offset_extended_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), | 183 | offset_extended_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), |
| 184 | def_cfa_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), | 184 | def_cfa_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), |
| 185 | def_cfa_offset_sf: InstructionType(.{ .offset = .sleb128_offset }), | 185 | def_cfa_offset_sf: InstructionType(.{ .offset = .sleb128_offset }), |
| 186 | val_offset: InstructionType(.{ .a = .uleb128_offset, .b = .uleb128_offset }), | 186 | val_offset: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }), |
| 187 | val_offset_sf: InstructionType(.{ .a = .uleb128_offset, .b = .sleb128_offset }), | 187 | val_offset_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }), |
| 188 | val_expression: InstructionType(.{ .a = .uleb128_offset, .block = .block }), | 188 | val_expression: InstructionType(.{ .register = .uleb128_register, .block = .block }), |
| 189 | 189 | ||
| 190 | fn readOperands( | 190 | fn readOperands( |
| 191 | self: *Instruction, | 191 | self: *Instruction, |
| ... | @@ -292,7 +292,14 @@ pub const VirtualMachine = struct { | ... | @@ -292,7 +292,14 @@ pub const VirtualMachine = struct { |
| 292 | rule: RegisterRule = .{ .default = {} }, | 292 | rule: RegisterRule = .{ .default = {} }, |
| 293 | 293 | ||
| 294 | /// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes) | 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 | switch (self.rule) { | 303 | switch (self.rule) { |
| 297 | .default => { | 304 | .default => { |
| 298 | const register = self.register orelse return error.InvalidRegister; | 305 | const register = self.register orelse return error.InvalidRegister; |
| ... | @@ -321,14 +328,21 @@ pub const VirtualMachine = struct { | ... | @@ -321,14 +328,21 @@ pub const VirtualMachine = struct { |
| 321 | @memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); | 328 | @memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx)); |
| 322 | }, | 329 | }, |
| 323 | .expression => |expression| { | 330 | .expression => |expression| { |
| 324 | // TODO | 331 | context.stack_machine.reset(); |
| 325 | _ = expression; | 332 | const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?); |
| 326 | unreachable; | 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 | .val_expression => |expression| { | 340 | .val_expression => |expression| { |
| 329 | // TODO | 341 | context.stack_machine.reset(); |
| 330 | _ = expression; | 342 | const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?); |
| 331 | unreachable; | 343 | |
| 344 | if (value != .generic) return error.InvalidExpressionValue; | ||
| 345 | mem.writeIntSliceNative(usize, out, value.generic); | ||
| 332 | }, | 346 | }, |
| 333 | .architectural => return error.UnimplementedRule, | 347 | .architectural => return error.UnimplementedRule, |
| 334 | } | 348 | } |
| ... | @@ -546,12 +560,16 @@ pub const VirtualMachine = struct { | ... | @@ -546,12 +560,16 @@ pub const VirtualMachine = struct { |
| 546 | .def_cfa_offset => |i| { | 560 | .def_cfa_offset => |i| { |
| 547 | try self.resolveCopyOnWrite(allocator); | 561 | try self.resolveCopyOnWrite(allocator); |
| 548 | if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation; | 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 | .def_cfa_offset_sf => |i| { | 567 | .def_cfa_offset_sf => |i| { |
| 552 | try self.resolveCopyOnWrite(allocator); | 568 | try self.resolveCopyOnWrite(allocator); |
| 553 | if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation; | 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 | .def_cfa_expression => |i| { | 574 | .def_cfa_expression => |i| { |
| 557 | try self.resolveCopyOnWrite(allocator); | 575 | try self.resolveCopyOnWrite(allocator); |
| ... | @@ -567,17 +585,26 @@ pub const VirtualMachine = struct { | ... | @@ -567,17 +585,26 @@ pub const VirtualMachine = struct { |
| 567 | .expression = i.operands.block, | 585 | .expression = i.operands.block, |
| 568 | }; | 586 | }; |
| 569 | }, | 587 | }, |
| 570 | .val_offset => { | 588 | .val_offset => |i| { |
| 571 | // TODO: Implement | 589 | try self.resolveCopyOnWrite(allocator); |
| 572 | unreachable; | 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 => { | 595 | .val_offset_sf => |i| { |
| 575 | // TODO: Implement | 596 | try self.resolveCopyOnWrite(allocator); |
| 576 | unreachable; | 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 => { | 602 | .val_expression => |i| { |
| 579 | // TODO: Implement | 603 | try self.resolveCopyOnWrite(allocator); |
| 580 | unreachable; | 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,6 +2,9 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const OP = @import("OP.zig"); | 3 | const OP = @import("OP.zig"); |
| 4 | const leb = @import("../leb128.zig"); | 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 | pub const StackMachineOptions = struct { | 9 | pub const StackMachineOptions = struct { |
| 7 | /// The address size of the target architecture | 10 | /// The address size of the target architecture |
| ... | @@ -33,9 +36,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { | ... | @@ -33,9 +36,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 33 | }; | 36 | }; |
| 34 | 37 | ||
| 35 | return struct { | 38 | return struct { |
| 36 | const Value = union(enum) { | 39 | const Self = @This(); |
| 40 | |||
| 41 | const Operand = union(enum) { | ||
| 37 | generic: addr_type, | 42 | generic: addr_type, |
| 38 | const_type: []const u8, | ||
| 39 | register: u8, | 43 | register: u8, |
| 40 | base_register: struct { | 44 | base_register: struct { |
| 41 | base_register: u8, | 45 | base_register: u8, |
| ... | @@ -46,7 +50,11 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { | ... | @@ -46,7 +50,11 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 46 | offset: i64, | 50 | offset: i64, |
| 47 | }, | 51 | }, |
| 48 | block: []const u8, | 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 | type_offset: u64, | 58 | type_offset: u64, |
| 51 | value_bytes: []const u8, | 59 | value_bytes: []const u8, |
| 52 | }, | 60 | }, |
| ... | @@ -56,9 +64,31 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { | ... | @@ -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 | stack: std.ArrayListUnmanaged(Value) = .{}, | 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 | const int_info = @typeInfo(@TypeOf(value)).Int; | 92 | const int_info = @typeInfo(@TypeOf(value)).Int; |
| 63 | if (@sizeOf(@TypeOf(value)) > options.addr_size) { | 93 | if (@sizeOf(@TypeOf(value)) > options.addr_size) { |
| 64 | return .{ .generic = switch (int_info.signedness) { | 94 | return .{ .generic = switch (int_info.signedness) { |
| ... | @@ -73,7 +103,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { | ... | @@ -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 | const reader = stream.reader(); | 107 | const reader = stream.reader(); |
| 78 | return switch (opcode) { | 108 | return switch (opcode) { |
| 79 | OP.addr, | 109 | OP.addr, |
| ... | @@ -87,8 +117,8 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { | ... | @@ -87,8 +117,8 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 87 | OP.const1s => generic(try reader.readByteSigned()), | 117 | OP.const1s => generic(try reader.readByteSigned()), |
| 88 | OP.const2u, | 118 | OP.const2u, |
| 89 | OP.call2, | 119 | OP.call2, |
| 90 | OP.call4, | ||
| 91 | => generic(try reader.readInt(u16, options.endian)), | 120 | => generic(try reader.readInt(u16, options.endian)), |
| 121 | OP.call4 => generic(try reader.readInt(u32, options.endian)), | ||
| 92 | OP.const2s, | 122 | OP.const2s, |
| 93 | OP.bra, | 123 | OP.bra, |
| 94 | OP.skip, | 124 | OP.skip, |
| ... | @@ -114,21 +144,35 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { | ... | @@ -114,21 +144,35 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 114 | .offset = try leb.readILEB128(i64, reader), | 144 | .offset = try leb.readILEB128(i64, reader), |
| 115 | } }, | 145 | } }, |
| 116 | OP.regx => .{ .register = try leb.readULEB128(u8, reader) }, | 146 | OP.regx => .{ .register = try leb.readULEB128(u8, reader) }, |
| 117 | OP.bregx, OP.regval_type => .{ .base_register = .{ | 147 | OP.bregx => blk: { |
| 118 | .base_register = try leb.readULEB128(u8, reader), | 148 | const base_register = try leb.readULEB128(u8, reader); |
| 119 | .offset = try leb.readILEB128(i64, reader), | 149 | const offset = try leb.readILEB128(i64, reader); |
| 120 | } }, | 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 | OP.piece => .{ | 163 | OP.piece => .{ |
| 122 | .composite_location = .{ | 164 | .composite_location = .{ |
| 123 | .size = try leb.readULEB128(u8, reader), | 165 | .size = try leb.readULEB128(u8, reader), |
| 124 | .offset = 0, | 166 | .offset = 0, |
| 125 | }, | 167 | }, |
| 126 | }, | 168 | }, |
| 127 | OP.bit_piece => .{ | 169 | OP.bit_piece => blk: { |
| 128 | .composite_location = .{ | 170 | const size = try leb.readULEB128(u8, reader); |
| 129 | .size = try leb.readULEB128(u8, reader), | 171 | const offset = try leb.readILEB128(i64, reader); |
| 130 | .offset = try leb.readILEB128(i64, reader), | 172 | break :blk .{ .composite_location = .{ |
| 131 | }, | 173 | .size = size, |
| 174 | .offset = offset, | ||
| 175 | } }; | ||
| 132 | }, | 176 | }, |
| 133 | OP.implicit_value, OP.entry_value => blk: { | 177 | OP.implicit_value, OP.entry_value => blk: { |
| 134 | const size = try leb.readULEB128(u8, reader); | 178 | const size = try leb.readULEB128(u8, reader); |
| ... | @@ -145,7 +189,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { | ... | @@ -145,7 +189,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 145 | if (stream.pos + size > stream.buffer.len) return error.InvalidExpression; | 189 | if (stream.pos + size > stream.buffer.len) return error.InvalidExpression; |
| 146 | const value_bytes = stream.buffer[stream.pos..][0..size]; | 190 | const value_bytes = stream.buffer[stream.pos..][0..size]; |
| 147 | stream.pos += size; | 191 | stream.pos += size; |
| 148 | break :blk .{ .base_type = .{ | 192 | break :blk .{ .const_type = .{ |
| 149 | .type_offset = type_offset, | 193 | .type_offset = type_offset, |
| 150 | .value_bytes = value_bytes, | 194 | .value_bytes = value_bytes, |
| 151 | } }; | 195 | } }; |
| ... | @@ -163,22 +207,144 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { | ... | @@ -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 | pub fn step( | 227 | pub fn step( |
| 167 | self: *StackMachine, | 228 | self: *Self, |
| 168 | stream: std.io.FixedBufferStream([]const u8), | 229 | stream: *std.io.FixedBufferStream([]const u8), |
| 169 | allocator: std.mem.Allocator, | 230 | allocator: std.mem.Allocator, |
| 170 | ) !void { | 231 | compile_unit: ?*const dwarf.CompileUnit, |
| 171 | if (@sizeOf(usize) != addr_type or options.endian != builtin.target.cpu.arch.endian()) | 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 | @compileError("Execution of non-native address sizees / endianness is not supported"); | 236 | @compileError("Execution of non-native address sizees / endianness is not supported"); |
| 173 | 237 | ||
| 174 | const opcode = try stream.reader.readByte(); | 238 | const opcode = try stream.reader().readByte(); |
| 175 | _ = opcode; | 239 | if (options.call_frame_mode) { |
| 176 | _ = self; | 240 | // Certain opcodes are not allowed in a CFA context, see 6.4.2 |
| 177 | _ = allocator; | 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) { | 347 | return stream.pos < stream.buffer.len; |
| 180 | // OP.addr => try self.stack.append(allocator, try readOperand(stream, opcode)), | ||
| 181 | // } | ||
| 182 | } | 348 | } |
| 183 | }; | 349 | }; |
| 184 | } | 350 | } |