aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug/Dwarf
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/debug/Dwarf')
-rw-r--r--lib/std/debug/Dwarf/abi.zig167
-rw-r--r--lib/std/debug/Dwarf/call_frame.zig388
-rw-r--r--lib/std/debug/Dwarf/expression.zig26
3 files changed, 69 insertions, 512 deletions
diff --git a/lib/std/debug/Dwarf/abi.zig b/lib/std/debug/Dwarf/abi.zig
index 1a47625ae7..b2c4cb9536 100644
--- a/lib/std/debug/Dwarf/abi.zig
+++ b/lib/std/debug/Dwarf/abi.zig
@@ -1,44 +1,50 @@
const builtin = @import("builtin");
+
const std = @import("../../std.zig");
const mem = std.mem;
-const native_os = builtin.os.tag;
const posix = std.posix;
+const Arch = std.Target.Cpu.Arch;
+/// Tells whether unwinding for this target is supported by the Dwarf standard.
+///
+/// See also `std.debug.SelfInfo.supportsUnwinding` which tells whether the Zig
+/// standard library has a working implementation of unwinding for this target.
pub fn supportsUnwinding(target: std.Target) bool {
return switch (target.cpu.arch) {
- .x86 => switch (target.os.tag) {
- .linux, .netbsd, .solaris, .illumos => true,
- else => false,
- },
- .x86_64 => switch (target.os.tag) {
- .linux, .netbsd, .freebsd, .openbsd, .macos, .ios, .solaris, .illumos => true,
- else => false,
- },
- .arm => switch (target.os.tag) {
- .linux => true,
- else => false,
- },
- .aarch64 => switch (target.os.tag) {
- .linux, .netbsd, .freebsd, .macos, .ios => true,
- else => false,
- },
- else => false,
+ .amdgcn,
+ .nvptx,
+ .nvptx64,
+ .spirv,
+ .spirv32,
+ .spirv64,
+ .spu_2,
+ => false,
+
+ // Enabling this causes relocation errors such as:
+ // error: invalid relocation type R_RISCV_SUB32 at offset 0x20
+ .riscv64, .riscv32 => false,
+
+ // Conservative guess. Feel free to update this logic with any targets
+ // that are known to not support Dwarf unwinding.
+ else => true,
};
}
-pub fn ipRegNum() u8 {
- return switch (builtin.cpu.arch) {
+/// Returns `null` for CPU architectures without an instruction pointer register.
+pub fn ipRegNum(arch: Arch) ?u8 {
+ return switch (arch) {
.x86 => 8,
.x86_64 => 16,
.arm => 15,
.aarch64 => 32,
- else => unreachable,
+ else => null,
};
}
-pub fn fpRegNum(reg_context: RegisterContext) u8 {
- return switch (builtin.cpu.arch) {
- // GCC on OS X historically did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
+pub fn fpRegNum(arch: Arch, reg_context: RegisterContext) u8 {
+ return switch (arch) {
+ // GCC on OS X historically did the opposite of ELF for these registers
+ // (only in .eh_frame), and that is now the convention for MachO
.x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
.x86_64 => 6,
.arm => 11,
@@ -47,8 +53,8 @@ pub fn fpRegNum(reg_context: RegisterContext) u8 {
};
}
-pub fn spRegNum(reg_context: RegisterContext) u8 {
- return switch (builtin.cpu.arch) {
+pub fn spRegNum(arch: Arch, reg_context: RegisterContext) u8 {
+ return switch (arch) {
.x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
.x86_64 => 7,
.arm => 13,
@@ -57,33 +63,12 @@ pub fn spRegNum(reg_context: RegisterContext) u8 {
};
}
-/// Some platforms use pointer authentication - the upper bits of instruction pointers contain a signature.
-/// This function clears these signature bits to make the pointer usable.
-pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
- if (builtin.cpu.arch == .aarch64) {
- // `hint 0x07` maps to `xpaclri` (or `nop` if the hardware doesn't support it)
- // The save / restore is because `xpaclri` operates on x30 (LR)
- return asm (
- \\mov x16, x30
- \\mov x30, x15
- \\hint 0x07
- \\mov x15, x30
- \\mov x30, x16
- : [ret] "={x15}" (-> usize),
- : [ptr] "{x15}" (ptr),
- : "x16"
- );
- }
-
- return ptr;
-}
-
pub const RegisterContext = struct {
eh_frame: bool,
is_macho: bool,
};
-pub const AbiError = error{
+pub const RegBytesError = error{
InvalidRegister,
UnimplementedArch,
UnimplementedOs,
@@ -91,55 +76,21 @@ pub const AbiError = error{
ThreadContextNotSupported,
};
-fn RegValueReturnType(comptime ContextPtrType: type, comptime T: type) type {
- const reg_bytes_type = comptime RegBytesReturnType(ContextPtrType);
- const info = @typeInfo(reg_bytes_type).Pointer;
- return @Type(.{
- .Pointer = .{
- .size = .One,
- .is_const = info.is_const,
- .is_volatile = info.is_volatile,
- .is_allowzero = info.is_allowzero,
- .alignment = info.alignment,
- .address_space = info.address_space,
- .child = T,
- .sentinel = null,
- },
- });
-}
-
-/// Returns a pointer to a register stored in a ThreadContext, preserving the pointer attributes of the context.
-pub fn regValueNative(
- comptime T: type,
- thread_context_ptr: anytype,
- reg_number: u8,
- reg_context: ?RegisterContext,
-) !RegValueReturnType(@TypeOf(thread_context_ptr), T) {
- const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
- if (@sizeOf(T) != reg_bytes.len) return error.IncompatibleRegisterSize;
- return mem.bytesAsValue(T, reg_bytes[0..@sizeOf(T)]);
-}
-
-fn RegBytesReturnType(comptime ContextPtrType: type) type {
- const info = @typeInfo(ContextPtrType);
- if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
- @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
- }
-
- return if (info.Pointer.is_const) return []const u8 else []u8;
-}
-
/// Returns a slice containing the backing storage for `reg_number`.
///
+/// This function assumes the Dwarf information corresponds not necessarily to
+/// the current executable, but at least with a matching CPU architecture and
+/// OS. It is planned to lift this limitation with a future enhancement.
+///
/// `reg_context` describes in what context the register number is used, as it can have different
/// meanings depending on the DWARF container. It is only required when getting the stack or
/// frame pointer register on some architectures.
pub fn regBytes(
- thread_context_ptr: anytype,
+ thread_context_ptr: *std.debug.ThreadContext,
reg_number: u8,
reg_context: ?RegisterContext,
-) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
- if (native_os == .windows) {
+) RegBytesError![]u8 {
+ if (builtin.os.tag == .windows) {
return switch (builtin.cpu.arch) {
.x86 => switch (reg_number) {
0 => mem.asBytes(&thread_context_ptr.Eax),
@@ -194,7 +145,7 @@ pub fn regBytes(
const ucontext_ptr = thread_context_ptr;
return switch (builtin.cpu.arch) {
- .x86 => switch (native_os) {
+ .x86 => switch (builtin.os.tag) {
.linux, .netbsd, .solaris, .illumos => switch (reg_number) {
0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
@@ -229,7 +180,7 @@ pub fn regBytes(
},
else => error.UnimplementedOs,
},
- .x86_64 => switch (native_os) {
+ .x86_64 => switch (builtin.os.tag) {
.linux, .solaris, .illumos => switch (reg_number) {
0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
@@ -248,7 +199,7 @@ pub fn regBytes(
14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),
- 17...32 => |i| if (native_os.isSolarish())
+ 17...32 => |i| if (builtin.os.tag.isSolarish())
mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
else
mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
@@ -318,7 +269,7 @@ pub fn regBytes(
},
else => error.UnimplementedOs,
},
- .arm => switch (native_os) {
+ .arm => switch (builtin.os.tag) {
.linux => switch (reg_number) {
0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
@@ -341,7 +292,7 @@ pub fn regBytes(
},
else => error.UnimplementedOs,
},
- .aarch64 => switch (native_os) {
+ .aarch64 => switch (builtin.os.tag) {
.macos, .ios => switch (reg_number) {
0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
@@ -389,22 +340,14 @@ pub fn regBytes(
};
}
-/// Returns the ABI-defined default value this register has in the unwinding table
-/// before running any of the CIE instructions. The DWARF spec defines these as having
-/// the .undefined rule by default, but allows ABI authors to override that.
-pub fn getRegDefaultValue(reg_number: u8, context: *std.debug.Dwarf.UnwindContext, out: []u8) !void {
- switch (builtin.cpu.arch) {
- .aarch64 => {
- // Callee-saved registers are initialized as if they had the .same_value rule
- if (reg_number >= 19 and reg_number <= 28) {
- const src = try regBytes(context.thread_context, reg_number, context.reg_context);
- if (src.len != out.len) return error.RegisterSizeMismatch;
- @memcpy(out, src);
- return;
- }
- },
- else => {},
- }
-
- @memset(out, undefined);
+/// Returns a pointer to a register stored in a ThreadContext, preserving the
+/// pointer attributes of the context.
+pub fn regValueNative(
+ thread_context_ptr: *std.debug.ThreadContext,
+ reg_number: u8,
+ reg_context: ?RegisterContext,
+) !*align(1) usize {
+ const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
+ if (@sizeOf(usize) != reg_bytes.len) return error.IncompatibleRegisterSize;
+ return mem.bytesAsValue(usize, reg_bytes[0..@sizeOf(usize)]);
}
diff --git a/lib/std/debug/Dwarf/call_frame.zig b/lib/std/debug/Dwarf/call_frame.zig
index 73e00d3099..3e3d2585db 100644
--- a/lib/std/debug/Dwarf/call_frame.zig
+++ b/lib/std/debug/Dwarf/call_frame.zig
@@ -297,391 +297,3 @@ pub const Instruction = union(Opcode) {
}
}
};
-
-/// Since register rules are applied (usually) during a panic,
-/// checked addition / subtraction is used so that we can return
-/// an error and fall back to FP-based unwinding.
-pub fn applyOffset(base: usize, offset: i64) !usize {
- return if (offset >= 0)
- try std.math.add(usize, base, @as(usize, @intCast(offset)))
- else
- try std.math.sub(usize, base, @as(usize, @intCast(-offset)));
-}
-
-/// This is a virtual machine that runs DWARF call frame instructions.
-pub const VirtualMachine = struct {
- /// See section 6.4.1 of the DWARF5 specification for details on each
- const RegisterRule = union(enum) {
- // The spec says that the default rule for each column is the undefined rule.
- // However, it also allows ABI / compiler authors to specify alternate defaults, so
- // there is a distinction made here.
- default: void,
-
- undefined: void,
- same_value: void,
-
- // offset(N)
- offset: i64,
-
- // val_offset(N)
- val_offset: i64,
-
- // register(R)
- register: u8,
-
- // expression(E)
- expression: []const u8,
-
- // val_expression(E)
- val_expression: []const u8,
-
- // Augmenter-defined rule
- architectural: void,
- };
-
- /// Each row contains unwinding rules for a set of registers.
- pub const Row = struct {
- /// Offset from `FrameDescriptionEntry.pc_begin`
- offset: u64 = 0,
-
- /// Special-case column that defines the CFA (Canonical Frame Address) rule.
- /// The register field of this column defines the register that CFA is derived from.
- cfa: Column = .{},
-
- /// The register fields in these columns define the register the rule applies to.
- columns: ColumnRange = .{},
-
- /// Indicates that the next write to any column in this row needs to copy
- /// the backing column storage first, as it may be referenced by previous rows.
- copy_on_write: bool = false,
- };
-
- pub const Column = struct {
- register: ?u8 = null,
- rule: RegisterRule = .{ .default = {} },
-
- /// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes)
- pub fn resolveValue(
- self: Column,
- context: *std.debug.Dwarf.UnwindContext,
- expression_context: std.debug.Dwarf.expression.Context,
- ma: *debug.StackIterator.MemoryAccessor,
- out: []u8,
- ) !void {
- switch (self.rule) {
- .default => {
- const register = self.register orelse return error.InvalidRegister;
- try abi.getRegDefaultValue(register, context, out);
- },
- .undefined => {
- @memset(out, undefined);
- },
- .same_value => {
- // TODO: This copy could be eliminated if callers always copy the state then call this function to update it
- const register = self.register orelse return error.InvalidRegister;
- const src = try abi.regBytes(context.thread_context, register, context.reg_context);
- if (src.len != out.len) return error.RegisterSizeMismatch;
- @memcpy(out, src);
- },
- .offset => |offset| {
- if (context.cfa) |cfa| {
- const addr = try applyOffset(cfa, offset);
- if (ma.load(usize, addr) == null) return error.InvalidAddress;
- const ptr: *const usize = @ptrFromInt(addr);
- mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
- } else return error.InvalidCFA;
- },
- .val_offset => |offset| {
- if (context.cfa) |cfa| {
- mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian);
- } else return error.InvalidCFA;
- },
- .register => |register| {
- const src = try abi.regBytes(context.thread_context, register, context.reg_context);
- if (src.len != out.len) return error.RegisterSizeMismatch;
- @memcpy(out, try abi.regBytes(context.thread_context, register, context.reg_context));
- },
- .expression => |expression| {
- context.stack_machine.reset();
- const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
- const addr = if (value) |v| blk: {
- if (v != .generic) return error.InvalidExpressionValue;
- break :blk v.generic;
- } else return error.NoExpressionValue;
-
- if (ma.load(usize, addr) == null) return error.InvalidExpressionAddress;
- const ptr: *usize = @ptrFromInt(addr);
- mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
- },
- .val_expression => |expression| {
- context.stack_machine.reset();
- const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
- if (value) |v| {
- if (v != .generic) return error.InvalidExpressionValue;
- mem.writeInt(usize, out[0..@sizeOf(usize)], v.generic, native_endian);
- } else return error.NoExpressionValue;
- },
- .architectural => return error.UnimplementedRegisterRule,
- }
- }
- };
-
- const ColumnRange = struct {
- /// Index into `columns` of the first column in this row.
- start: usize = undefined,
- len: u8 = 0,
- };
-
- columns: std.ArrayListUnmanaged(Column) = .{},
- stack: std.ArrayListUnmanaged(ColumnRange) = .{},
- current_row: Row = .{},
-
- /// The result of executing the CIE's initial_instructions
- cie_row: ?Row = null,
-
- pub fn deinit(self: *VirtualMachine, allocator: std.mem.Allocator) void {
- self.stack.deinit(allocator);
- self.columns.deinit(allocator);
- self.* = undefined;
- }
-
- pub fn reset(self: *VirtualMachine) void {
- self.stack.clearRetainingCapacity();
- self.columns.clearRetainingCapacity();
- self.current_row = .{};
- self.cie_row = null;
- }
-
- /// Return a slice backed by the row's non-CFA columns
- pub fn rowColumns(self: VirtualMachine, row: Row) []Column {
- if (row.columns.len == 0) return &.{};
- return self.columns.items[row.columns.start..][0..row.columns.len];
- }
-
- /// Either retrieves or adds a column for `register` (non-CFA) in the current row.
- fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {
- for (self.rowColumns(self.current_row)) |*c| {
- if (c.register == register) return c;
- }
-
- if (self.current_row.columns.len == 0) {
- self.current_row.columns.start = self.columns.items.len;
- }
- self.current_row.columns.len += 1;
-
- const column = try self.columns.addOne(allocator);
- column.* = .{
- .register = register,
- };
-
- return column;
- }
-
- /// Runs the CIE instructions, then the FDE instructions. Execution halts
- /// once the row that corresponds to `pc` is known, and the row is returned.
- pub fn runTo(
- self: *VirtualMachine,
- allocator: std.mem.Allocator,
- pc: u64,
- cie: std.debug.Dwarf.CommonInformationEntry,
- fde: std.debug.Dwarf.FrameDescriptionEntry,
- addr_size_bytes: u8,
- endian: std.builtin.Endian,
- ) !Row {
- assert(self.cie_row == null);
- if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;
-
- var prev_row: Row = self.current_row;
-
- var cie_stream = std.io.fixedBufferStream(cie.initial_instructions);
- var fde_stream = std.io.fixedBufferStream(fde.instructions);
- var streams = [_]*std.io.FixedBufferStream([]const u8){
- &cie_stream,
- &fde_stream,
- };
-
- for (&streams, 0..) |stream, i| {
- while (stream.pos < stream.buffer.len) {
- const instruction = try std.debug.Dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
- prev_row = try self.step(allocator, cie, i == 0, instruction);
- if (pc < fde.pc_begin + self.current_row.offset) return prev_row;
- }
- }
-
- return self.current_row;
- }
-
- pub fn runToNative(
- self: *VirtualMachine,
- allocator: std.mem.Allocator,
- pc: u64,
- cie: std.debug.Dwarf.CommonInformationEntry,
- fde: std.debug.Dwarf.FrameDescriptionEntry,
- ) !Row {
- return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), builtin.target.cpu.arch.endian());
- }
-
- fn resolveCopyOnWrite(self: *VirtualMachine, allocator: std.mem.Allocator) !void {
- if (!self.current_row.copy_on_write) return;
-
- const new_start = self.columns.items.len;
- if (self.current_row.columns.len > 0) {
- try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len);
- self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row));
- self.current_row.columns.start = new_start;
- }
- }
-
- /// Executes a single instruction.
- /// If this instruction is from the CIE, `is_initial` should be set.
- /// Returns the value of `current_row` before executing this instruction.
- pub fn step(
- self: *VirtualMachine,
- allocator: std.mem.Allocator,
- cie: std.debug.Dwarf.CommonInformationEntry,
- is_initial: bool,
- instruction: Instruction,
- ) !Row {
- // CIE instructions must be run before FDE instructions
- assert(!is_initial or self.cie_row == null);
- if (!is_initial and self.cie_row == null) {
- self.cie_row = self.current_row;
- self.current_row.copy_on_write = true;
- }
-
- const prev_row = self.current_row;
- switch (instruction) {
- .set_loc => |i| {
- if (i.address <= self.current_row.offset) return error.InvalidOperation;
- // TODO: Check cie.segment_selector_size != 0 for DWARFV4
- self.current_row.offset = i.address;
- },
- inline .advance_loc,
- .advance_loc1,
- .advance_loc2,
- .advance_loc4,
- => |i| {
- self.current_row.offset += i.delta * cie.code_alignment_factor;
- self.current_row.copy_on_write = true;
- },
- inline .offset,
- .offset_extended,
- .offset_extended_sf,
- => |i| {
- try self.resolveCopyOnWrite(allocator);
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = .{ .offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor };
- },
- inline .restore,
- .restore_extended,
- => |i| {
- try self.resolveCopyOnWrite(allocator);
- if (self.cie_row) |cie_row| {
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = for (self.rowColumns(cie_row)) |cie_column| {
- if (cie_column.register == i.register) break cie_column.rule;
- } else .{ .default = {} };
- } else return error.InvalidOperation;
- },
- .nop => {},
- .undefined => |i| {
- try self.resolveCopyOnWrite(allocator);
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = .{ .undefined = {} };
- },
- .same_value => |i| {
- try self.resolveCopyOnWrite(allocator);
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = .{ .same_value = {} };
- },
- .register => |i| {
- try self.resolveCopyOnWrite(allocator);
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = .{ .register = i.target_register };
- },
- .remember_state => {
- try self.stack.append(allocator, self.current_row.columns);
- self.current_row.copy_on_write = true;
- },
- .restore_state => {
- const restored_columns = self.stack.popOrNull() orelse return error.InvalidOperation;
- self.columns.shrinkRetainingCapacity(self.columns.items.len - self.current_row.columns.len);
- try self.columns.ensureUnusedCapacity(allocator, restored_columns.len);
-
- self.current_row.columns.start = self.columns.items.len;
- self.current_row.columns.len = restored_columns.len;
- self.columns.appendSliceAssumeCapacity(self.columns.items[restored_columns.start..][0..restored_columns.len]);
- },
- .def_cfa => |i| {
- try self.resolveCopyOnWrite(allocator);
- self.current_row.cfa = .{
- .register = i.register,
- .rule = .{ .val_offset = @intCast(i.offset) },
- };
- },
- .def_cfa_sf => |i| {
- try self.resolveCopyOnWrite(allocator);
- self.current_row.cfa = .{
- .register = i.register,
- .rule = .{ .val_offset = i.offset * cie.data_alignment_factor },
- };
- },
- .def_cfa_register => |i| {
- try self.resolveCopyOnWrite(allocator);
- if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
- self.current_row.cfa.register = i.register;
- },
- .def_cfa_offset => |i| {
- try self.resolveCopyOnWrite(allocator);
- if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
- self.current_row.cfa.rule = .{
- .val_offset = @intCast(i.offset),
- };
- },
- .def_cfa_offset_sf => |i| {
- try self.resolveCopyOnWrite(allocator);
- if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
- self.current_row.cfa.rule = .{
- .val_offset = i.offset * cie.data_alignment_factor,
- };
- },
- .def_cfa_expression => |i| {
- try self.resolveCopyOnWrite(allocator);
- self.current_row.cfa.register = undefined;
- self.current_row.cfa.rule = .{
- .expression = i.block,
- };
- },
- .expression => |i| {
- try self.resolveCopyOnWrite(allocator);
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = .{
- .expression = i.block,
- };
- },
- .val_offset => |i| {
- try self.resolveCopyOnWrite(allocator);
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = .{
- .val_offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor,
- };
- },
- .val_offset_sf => |i| {
- try self.resolveCopyOnWrite(allocator);
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = .{
- .val_offset = i.offset * cie.data_alignment_factor,
- };
- },
- .val_expression => |i| {
- try self.resolveCopyOnWrite(allocator);
- const column = try self.getOrAddColumn(allocator, i.register);
- column.rule = .{
- .val_expression = i.block,
- };
- },
- }
-
- return prev_row;
- }
-};
diff --git a/lib/std/debug/Dwarf/expression.zig b/lib/std/debug/Dwarf/expression.zig
index 6243ea9717..5fab56de6e 100644
--- a/lib/std/debug/Dwarf/expression.zig
+++ b/lib/std/debug/Dwarf/expression.zig
@@ -1,11 +1,13 @@
-const std = @import("std");
const builtin = @import("builtin");
+const native_arch = builtin.cpu.arch;
+const native_endian = native_arch.endian();
+
+const std = @import("std");
const leb = std.leb;
const OP = std.dwarf.OP;
const abi = std.debug.Dwarf.abi;
const mem = std.mem;
const assert = std.debug.assert;
-const native_endian = builtin.cpu.arch.endian();
/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
/// Callers should specify all the fields relevant to their context. If a field is required
@@ -14,7 +16,7 @@ pub const Context = struct {
/// The dwarf format of the section this expression is in
format: std.dwarf.Format = .@"32",
/// If specified, any addresses will pass through before being accessed
- memory_accessor: ?*std.debug.StackIterator.MemoryAccessor = null,
+ memory_accessor: ?*std.debug.MemoryAccessor = null,
/// The compilation unit this expression relates to, if any
compile_unit: ?*const std.debug.Dwarf.CompileUnit = null,
/// When evaluating a user-presented expression, this is the address of the object being evaluated
@@ -34,7 +36,7 @@ pub const Options = struct {
/// The address size of the target architecture
addr_size: u8 = @sizeOf(usize),
/// Endianness of the target architecture
- endian: std.builtin.Endian = builtin.target.cpu.arch.endian(),
+ endian: std.builtin.Endian = native_endian,
/// Restrict the stack machine to a subset of opcodes used in call frame instructions
call_frame_context: bool = false,
};
@@ -60,7 +62,7 @@ pub const Error = error{
InvalidTypeLength,
TruncatedIntegralType,
-} || abi.AbiError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
+} || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
/// A stack machine that can decode and run DWARF expressions.
/// Expressions can be decoded for non-native address size and endianness,
@@ -304,7 +306,7 @@ pub fn StackMachine(comptime options: Options) type {
allocator: std.mem.Allocator,
context: Context,
) Error!bool {
- if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
+ if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != native_endian)
@compileError("Execution of non-native address sizes / endianness is not supported");
const opcode = try stream.reader().readByte();
@@ -1186,13 +1188,13 @@ test "DWARF expressions" {
// TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
- (try abi.regValueNative(usize, &thread_context, abi.fpRegNum(reg_context), reg_context)).* = 1;
- (try abi.regValueNative(usize, &thread_context, abi.spRegNum(reg_context), reg_context)).* = 2;
- (try abi.regValueNative(usize, &thread_context, abi.ipRegNum(), reg_context)).* = 3;
+ (try abi.regValueNative(&thread_context, abi.fpRegNum(native_arch, reg_context), reg_context)).* = 1;
+ (try abi.regValueNative(&thread_context, abi.spRegNum(native_arch, reg_context), reg_context)).* = 2;
+ (try abi.regValueNative(&thread_context, abi.ipRegNum(native_arch).?, reg_context)).* = 3;
- try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
- try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
- try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));
+ try b.writeBreg(writer, abi.fpRegNum(native_arch, reg_context), @as(usize, 100));
+ try b.writeBreg(writer, abi.spRegNum(native_arch, reg_context), @as(usize, 200));
+ try b.writeBregx(writer, abi.ipRegNum(native_arch).?, @as(usize, 300));
try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
_ = try stack_machine.run(program.items, allocator, context, 0);