mirror of
https://github.com/ziglang/zig.git
synced 2026-02-17 06:49:23 +00:00
debug: rename StackTraceContext to ThreadContext
dwarf: use ThreadContext instead of os.ucontext_t dwarf: add regBytes impl for windows dwarf: fixup expression types for non-native
This commit is contained in:
parent
8547c42ba5
commit
5f72c6508d
@ -133,7 +133,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub const StackTraceContext = blk: {
|
||||
pub const ThreadContext = blk: {
|
||||
if (native_os == .windows) {
|
||||
break :blk std.os.windows.CONTEXT;
|
||||
} else if (have_ucontext) {
|
||||
@ -146,7 +146,7 @@ pub const StackTraceContext = blk: {
|
||||
/// Tries to print the stack trace starting from the supplied base pointer to stderr,
|
||||
/// unbuffered, and ignores any error returned.
|
||||
/// TODO multithreaded awareness
|
||||
pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void {
|
||||
pub fn dumpStackTraceFromBase(context: *const ThreadContext) void {
|
||||
nosuspend {
|
||||
if (comptime builtin.target.isWasm()) {
|
||||
if (native_os == .wasi) {
|
||||
@ -437,7 +437,7 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
|
||||
else => true,
|
||||
});
|
||||
|
||||
pub inline fn getContext(context: *StackTraceContext) bool {
|
||||
pub inline fn getContext(context: *ThreadContext) bool {
|
||||
if (native_os == .windows) {
|
||||
context.* = std.mem.zeroes(windows.CONTEXT);
|
||||
windows.ntdll.RtlCaptureContext(context);
|
||||
@ -606,8 +606,8 @@ pub const StackIterator = struct {
|
||||
fn next_dwarf(self: *StackIterator) !usize {
|
||||
const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
|
||||
if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {
|
||||
self.dwarf_context.reg_ctx.eh_frame = true;
|
||||
self.dwarf_context.reg_ctx.is_macho = di.is_macho;
|
||||
self.dwarf_context.reg_context.eh_frame = true;
|
||||
self.dwarf_context.reg_context.is_macho = di.is_macho;
|
||||
return di.unwindFrame(&self.dwarf_context, module.base_address);
|
||||
} else return error.MissingDebugInfo;
|
||||
}
|
||||
@ -663,7 +663,7 @@ pub fn writeCurrentStackTrace(
|
||||
tty_config: io.tty.Config,
|
||||
start_addr: ?usize,
|
||||
) !void {
|
||||
var context: StackTraceContext = undefined;
|
||||
var context: ThreadContext = undefined;
|
||||
const has_context = getContext(&context);
|
||||
if (native_os == .windows) {
|
||||
return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr);
|
||||
|
||||
@ -146,11 +146,11 @@ pub const CC = enum(u8) {
|
||||
pass_by_reference = 0x4,
|
||||
pass_by_value = 0x5,
|
||||
|
||||
lo_user = 0x40,
|
||||
hi_user = 0xff,
|
||||
|
||||
GNU_renesas_sh = 0x40,
|
||||
GNU_borland_fastcall_i386 = 0x41,
|
||||
|
||||
pub const lo_user = 0x40;
|
||||
pub const hi_user = 0xff;
|
||||
};
|
||||
|
||||
pub const Format = enum { @"32", @"64" };
|
||||
@ -1676,13 +1676,13 @@ pub const DwarfInfo = struct {
|
||||
var expression_context = .{
|
||||
.isValidMemory = context.isValidMemory,
|
||||
.compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
|
||||
.ucontext = &context.ucontext,
|
||||
.reg_ctx = context.reg_ctx,
|
||||
.thread_context = &context.thread_context,
|
||||
.reg_context = context.reg_context,
|
||||
.cfa = context.cfa,
|
||||
};
|
||||
|
||||
context.vm.reset();
|
||||
context.reg_ctx.eh_frame = cie.version != 4;
|
||||
context.reg_context.eh_frame = cie.version != 4;
|
||||
|
||||
_ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);
|
||||
const row = &context.vm.current_row;
|
||||
@ -1690,7 +1690,7 @@ pub const DwarfInfo = struct {
|
||||
context.cfa = switch (row.cfa.rule) {
|
||||
.val_offset => |offset| blk: {
|
||||
const register = row.cfa.register orelse return error.InvalidCFARule;
|
||||
const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx));
|
||||
const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, register, context.reg_context));
|
||||
break :blk try call_frame.applyOffset(value, offset);
|
||||
},
|
||||
.expression => |expression| blk: {
|
||||
@ -1711,14 +1711,14 @@ pub const DwarfInfo = struct {
|
||||
if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;
|
||||
expression_context.cfa = context.cfa;
|
||||
|
||||
// Buffering the modifications is done because copying the ucontext is not portable,
|
||||
// Buffering the modifications is done because copying the thread context is not portable,
|
||||
// some implementations (ie. darwin) use internal pointers to the mcontext.
|
||||
var arena = std.heap.ArenaAllocator.init(context.allocator);
|
||||
defer arena.deinit();
|
||||
const update_allocator = arena.allocator();
|
||||
|
||||
const RegisterUpdate = struct {
|
||||
// Backed by ucontext
|
||||
// Backed by thread_context
|
||||
old_value: []u8,
|
||||
// Backed by arena
|
||||
new_value: []const u8,
|
||||
@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct {
|
||||
has_next_ip = column.rule != .undefined;
|
||||
}
|
||||
|
||||
const old_value = try abi.regBytes(&context.ucontext, register, context.reg_ctx);
|
||||
const old_value = try abi.regBytes(&context.thread_context, register, context.reg_context);
|
||||
const new_value = try update_allocator.alloc(u8, old_value.len);
|
||||
|
||||
const prev = update_tail;
|
||||
@ -1758,12 +1758,12 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
|
||||
if (has_next_ip) {
|
||||
context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, comptime abi.ipRegNum(), context.reg_ctx));
|
||||
context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, comptime abi.ipRegNum(), context.reg_context));
|
||||
} else {
|
||||
context.pc = 0;
|
||||
}
|
||||
|
||||
mem.writeIntSliceNative(usize, try abi.regBytes(&context.ucontext, abi.spRegNum(context.reg_ctx), context.reg_ctx), context.cfa.?);
|
||||
mem.writeIntSliceNative(usize, try abi.regBytes(&context.thread_context, abi.spRegNum(context.reg_context), context.reg_context), context.cfa.?);
|
||||
|
||||
// The call instruction will have pushed the address of the instruction that follows the call as the return address
|
||||
// However, this return address may be past the end of the function if the caller was `noreturn`.
|
||||
@ -1779,20 +1779,24 @@ pub const UnwindContext = struct {
|
||||
allocator: mem.Allocator,
|
||||
cfa: ?usize,
|
||||
pc: usize,
|
||||
ucontext: os.ucontext_t,
|
||||
reg_ctx: abi.RegisterContext,
|
||||
thread_context: debug.ThreadContext,
|
||||
reg_context: abi.RegisterContext,
|
||||
isValidMemory: *const fn (address: usize) bool,
|
||||
vm: call_frame.VirtualMachine = .{},
|
||||
stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{},
|
||||
|
||||
pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
|
||||
const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null));
|
||||
pub fn init(allocator: mem.Allocator, thread_context: *const debug.ThreadContext, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
|
||||
const pc = mem.readIntSliceNative(usize, try abi.regBytes(thread_context, abi.ipRegNum(), null));
|
||||
|
||||
if (builtin.os.tag == .macos) @compileError("Fix below TODO");
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.cfa = null,
|
||||
.pc = pc,
|
||||
.ucontext = ucontext.*,
|
||||
.reg_ctx = undefined,
|
||||
// TODO: This is broken on macos, need a function that knows how to copy the OSs mcontext properly
|
||||
.thread_context = thread_context.*,
|
||||
.reg_context = undefined,
|
||||
.isValidMemory = isValidMemory,
|
||||
};
|
||||
}
|
||||
@ -1803,7 +1807,7 @@ pub const UnwindContext = struct {
|
||||
}
|
||||
|
||||
pub fn getFp(self: *const UnwindContext) !usize {
|
||||
return mem.readIntSliceNative(usize, try abi.regBytes(&self.ucontext, abi.fpRegNum(self.reg_ctx), self.reg_ctx));
|
||||
return mem.readIntSliceNative(usize, try abi.regBytes(&self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context));
|
||||
}
|
||||
};
|
||||
|
||||
@ -2388,3 +2392,7 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
|
||||
return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
||||
|
||||
@ -3,11 +3,6 @@ const std = @import("../std.zig");
|
||||
const os = std.os;
|
||||
const mem = std.mem;
|
||||
|
||||
pub const RegisterContext = struct {
|
||||
eh_frame: bool,
|
||||
is_macho: bool,
|
||||
};
|
||||
|
||||
pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool {
|
||||
return switch (arch) {
|
||||
.x86,
|
||||
@ -29,10 +24,10 @@ pub fn ipRegNum() u8 {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fpRegNum(reg_ctx: RegisterContext) u8 {
|
||||
pub fn fpRegNum(reg_context: RegisterContext) u8 {
|
||||
return switch (builtin.cpu.arch) {
|
||||
// GCC on OS X did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
|
||||
.x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 4 else 5,
|
||||
.x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
|
||||
.x86_64 => 6,
|
||||
.arm => 11,
|
||||
.aarch64 => 29,
|
||||
@ -40,9 +35,9 @@ pub fn fpRegNum(reg_ctx: RegisterContext) u8 {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn spRegNum(reg_ctx: RegisterContext) u8 {
|
||||
pub fn spRegNum(reg_context: RegisterContext) u8 {
|
||||
return switch (builtin.cpu.arch) {
|
||||
.x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 5 else 4,
|
||||
.x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
|
||||
.x86_64 => 7,
|
||||
.arm => 13,
|
||||
.aarch64 => 31,
|
||||
@ -52,21 +47,76 @@ pub fn spRegNum(reg_ctx: RegisterContext) u8 {
|
||||
|
||||
fn RegBytesReturnType(comptime ContextPtrType: type) type {
|
||||
const info = @typeInfo(ContextPtrType);
|
||||
if (info != .Pointer or info.Pointer.child != os.ucontext_t) {
|
||||
@compileError("Expected a pointer to ucontext_t, got " ++ @typeName(@TypeOf(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;
|
||||
}
|
||||
|
||||
pub const RegisterContext = struct {
|
||||
eh_frame: bool,
|
||||
is_macho: bool,
|
||||
};
|
||||
|
||||
/// Returns a slice containing the backing storage for `reg_number`.
|
||||
///
|
||||
/// `reg_ctx` describes in what context the register number is used, as it can have different
|
||||
/// `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(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext) !RegBytesReturnType(@TypeOf(ucontext_ptr)) {
|
||||
var m = &ucontext_ptr.mcontext;
|
||||
pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?RegisterContext) !RegBytesReturnType(@TypeOf(thread_context_ptr)) {
|
||||
if (builtin.os.tag == .windows) {
|
||||
return switch (builtin.cpu.arch) {
|
||||
.x86 => switch (reg_number) {
|
||||
0 => mem.asBytes(&thread_context_ptr.Eax),
|
||||
1 => mem.asBytes(&thread_context_ptr.Ecx),
|
||||
2 => mem.asBytes(&thread_context_ptr.Edx),
|
||||
3 => mem.asBytes(&thread_context_ptr.Ebx),
|
||||
4 => mem.asBytes(&thread_context_ptr.Esp),
|
||||
5 => mem.asBytes(&thread_context_ptr.Ebp),
|
||||
6 => mem.asBytes(&thread_context_ptr.Esi),
|
||||
7 => mem.asBytes(&thread_context_ptr.Edi),
|
||||
8 => mem.asBytes(&thread_context_ptr.Eip),
|
||||
9 => mem.asBytes(&thread_context_ptr.EFlags),
|
||||
10 => mem.asBytes(&thread_context_ptr.SegCs),
|
||||
11 => mem.asBytes(&thread_context_ptr.SegSs),
|
||||
12 => mem.asBytes(&thread_context_ptr.SegDs),
|
||||
13 => mem.asBytes(&thread_context_ptr.SegEs),
|
||||
14 => mem.asBytes(&thread_context_ptr.SegFs),
|
||||
15 => mem.asBytes(&thread_context_ptr.SegGs),
|
||||
else => error.InvalidRegister,
|
||||
},
|
||||
.x86_64 => switch (reg_number) {
|
||||
0 => mem.asBytes(&thread_context_ptr.Rax),
|
||||
1 => mem.asBytes(&thread_context_ptr.Rdx),
|
||||
2 => mem.asBytes(&thread_context_ptr.Rcx),
|
||||
3 => mem.asBytes(&thread_context_ptr.Rbx),
|
||||
4 => mem.asBytes(&thread_context_ptr.Rsi),
|
||||
5 => mem.asBytes(&thread_context_ptr.Rdi),
|
||||
6 => mem.asBytes(&thread_context_ptr.Rbp),
|
||||
7 => mem.asBytes(&thread_context_ptr.Rsp),
|
||||
8 => mem.asBytes(&thread_context_ptr.R8),
|
||||
9 => mem.asBytes(&thread_context_ptr.R9),
|
||||
10 => mem.asBytes(&thread_context_ptr.R10),
|
||||
11 => mem.asBytes(&thread_context_ptr.R11),
|
||||
12 => mem.asBytes(&thread_context_ptr.R12),
|
||||
13 => mem.asBytes(&thread_context_ptr.R13),
|
||||
14 => mem.asBytes(&thread_context_ptr.R14),
|
||||
15 => mem.asBytes(&thread_context_ptr.R15),
|
||||
16 => mem.asBytes(&thread_context_ptr.Rip),
|
||||
else => error.InvalidRegister,
|
||||
},
|
||||
.aarch64 => switch (reg_number) {
|
||||
0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
|
||||
31 => mem.asBytes(&thread_context_ptr.Sp),
|
||||
32 => mem.asBytes(&thread_context_ptr.Pc),
|
||||
},
|
||||
else => error.UnimplementedArch,
|
||||
};
|
||||
}
|
||||
|
||||
const ucontext_ptr = thread_context_ptr;
|
||||
var m = &ucontext_ptr.mcontext;
|
||||
return switch (builtin.cpu.arch) {
|
||||
.x86 => switch (builtin.os.tag) {
|
||||
.linux, .netbsd, .solaris => switch (reg_number) {
|
||||
@ -74,7 +124,7 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
|
||||
1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]),
|
||||
2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]),
|
||||
3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]),
|
||||
4...5 => if (reg_ctx) |r| bytes: {
|
||||
4...5 => if (reg_context) |r| bytes: {
|
||||
if (reg_number == 4) {
|
||||
break :bytes if (r.eh_frame and r.is_macho)
|
||||
mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP])
|
||||
@ -98,7 +148,6 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
|
||||
14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]),
|
||||
15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]),
|
||||
16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
|
||||
// TODO: Map TRAPNO, ERR, UESP
|
||||
32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
|
||||
else => error.InvalidRegister,
|
||||
},
|
||||
|
||||
@ -2,10 +2,10 @@ const builtin = @import("builtin");
|
||||
const std = @import("../std.zig");
|
||||
const mem = std.mem;
|
||||
const debug = std.debug;
|
||||
const leb = @import("../leb128.zig");
|
||||
const abi = @import("abi.zig");
|
||||
const dwarf = @import("../dwarf.zig");
|
||||
const expressions = @import("expressions.zig");
|
||||
const leb = std.leb;
|
||||
const dwarf = std.dwarf;
|
||||
const abi = dwarf.abi;
|
||||
const expressions = dwarf.expressions;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Opcode = enum(u8) {
|
||||
@ -315,9 +315,9 @@ pub const VirtualMachine = struct {
|
||||
} else return error.InvalidCFA;
|
||||
},
|
||||
.register => |register| {
|
||||
const src = try abi.regBytes(&context.ucontext, register, context.reg_ctx);
|
||||
const src = try abi.regBytes(&context.thread_context, register, context.reg_context);
|
||||
if (src.len != out.len) return error.RegisterTypeMismatch;
|
||||
@memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx));
|
||||
@memcpy(out, try abi.regBytes(&context.thread_context, register, context.reg_context));
|
||||
},
|
||||
.expression => |expression| {
|
||||
context.stack_machine.reset();
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const OP = @import("OP.zig");
|
||||
const leb = @import("../leb128.zig");
|
||||
const dwarf = @import("../dwarf.zig");
|
||||
const leb = std.leb;
|
||||
const dwarf = std.dwarf;
|
||||
const abi = dwarf.abi;
|
||||
const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
@ -17,12 +17,12 @@ pub const ExpressionContext = struct {
|
||||
/// The compilation unit this expression relates to, if any
|
||||
compile_unit: ?*const dwarf.CompileUnit = null,
|
||||
|
||||
/// Register context
|
||||
ucontext: ?*std.os.ucontext_t,
|
||||
reg_ctx: ?abi.RegisterContext,
|
||||
/// Thread context
|
||||
thread_context: ?*std.debug.ThreadContext = null,
|
||||
reg_context: ?abi.RegisterContext = null,
|
||||
|
||||
/// Call frame address, if in a CFI context
|
||||
cfa: ?usize,
|
||||
cfa: ?usize = null,
|
||||
};
|
||||
|
||||
pub const ExpressionOptions = struct {
|
||||
@ -344,13 +344,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
OP.breg0...OP.breg31,
|
||||
OP.bregx,
|
||||
=> {
|
||||
if (context.ucontext == null) return error.IncompleteExpressionContext;
|
||||
if (context.thread_context == null) return error.IncompleteExpressionContext;
|
||||
|
||||
const base_register = (try readOperand(stream, opcode)).?.base_register;
|
||||
var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(
|
||||
context.ucontext.?,
|
||||
context.thread_context.?,
|
||||
base_register.base_register,
|
||||
context.reg_ctx,
|
||||
context.reg_context,
|
||||
)));
|
||||
value += base_register.offset;
|
||||
try self.stack.append(allocator, .{ .generic = @intCast(value) });
|
||||
@ -358,9 +358,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
OP.regval_type => {
|
||||
const register_type = (try readOperand(stream, opcode)).?.register_type;
|
||||
const value = mem.readIntSliceNative(usize, try abi.regBytes(
|
||||
context.ucontext.?,
|
||||
context.thread_context.?,
|
||||
register_type.register,
|
||||
context.reg_ctx,
|
||||
context.reg_context,
|
||||
));
|
||||
try self.stack.append(allocator, .{
|
||||
.regval_type = .{
|
||||
@ -464,7 +464,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
// 2.5.1.4: Arithmetic and Logical Operations
|
||||
OP.abs => {
|
||||
if (self.stack.items.len == 0) return error.InvalidExpression;
|
||||
const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
const value: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
self.stack.items[self.stack.items.len - 1] = .{
|
||||
.generic = std.math.absCast(value),
|
||||
};
|
||||
@ -478,10 +478,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
},
|
||||
OP.div => {
|
||||
if (self.stack.items.len < 2) return error.InvalidExpression;
|
||||
const a: isize = @bitCast(try self.stack.pop().asIntegral());
|
||||
const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
|
||||
const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
self.stack.items[self.stack.items.len - 1] = .{
|
||||
.generic = @bitCast(try std.math.divTrunc(isize, b, a)),
|
||||
.generic = @bitCast(try std.math.divTrunc(addr_type_signed, b, a)),
|
||||
};
|
||||
},
|
||||
OP.minus => {
|
||||
@ -493,16 +493,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
},
|
||||
OP.mod => {
|
||||
if (self.stack.items.len < 2) return error.InvalidExpression;
|
||||
const a: isize = @bitCast(try self.stack.pop().asIntegral());
|
||||
const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
|
||||
const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
self.stack.items[self.stack.items.len - 1] = .{
|
||||
.generic = @bitCast(@mod(b, a)),
|
||||
};
|
||||
},
|
||||
OP.mul => {
|
||||
if (self.stack.items.len < 2) return error.InvalidExpression;
|
||||
const a: isize = @bitCast(try self.stack.pop().asIntegral());
|
||||
const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
|
||||
const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
self.stack.items[self.stack.items.len - 1] = .{
|
||||
.generic = @bitCast(@mulWithOverflow(a, b)[0]),
|
||||
};
|
||||
@ -512,7 +512,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
self.stack.items[self.stack.items.len - 1] = .{
|
||||
.generic = @bitCast(
|
||||
try std.math.negate(
|
||||
@as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
|
||||
@as(addr_type_signed, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
|
||||
),
|
||||
),
|
||||
};
|
||||
@ -563,9 +563,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
OP.shra => {
|
||||
if (self.stack.items.len < 2) return error.InvalidExpression;
|
||||
const a = try self.stack.pop().asIntegral();
|
||||
const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
|
||||
self.stack.items[self.stack.items.len - 1] = .{
|
||||
.generic = @bitCast(std.math.shr(isize, b, a)),
|
||||
.generic = @bitCast(std.math.shr(addr_type_signed, b, a)),
|
||||
};
|
||||
},
|
||||
OP.xor => {
|
||||
@ -589,8 +589,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
const b = self.stack.items[self.stack.items.len - 1];
|
||||
|
||||
if (a == .generic and b == .generic) {
|
||||
const a_int: isize = @bitCast(a.asIntegral() catch unreachable);
|
||||
const b_int: isize = @bitCast(b.asIntegral() catch unreachable);
|
||||
const a_int: addr_type_signed = @bitCast(a.asIntegral() catch unreachable);
|
||||
const b_int: addr_type_signed = @bitCast(b.asIntegral() catch unreachable);
|
||||
const result = @intFromBool(switch (opcode) {
|
||||
OP.le => b_int < a_int,
|
||||
OP.ge => b_int >= a_int,
|
||||
@ -617,7 +617,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
if (condition) {
|
||||
const new_pos = std.math.cast(
|
||||
usize,
|
||||
try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset),
|
||||
try std.math.add(addr_type_signed, @as(addr_type_signed, @intCast(stream.pos)), branch_offset),
|
||||
) orelse return error.InvalidExpression;
|
||||
|
||||
if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression;
|
||||
@ -710,7 +710,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Writer(options: ExpressionOptions) type {
|
||||
pub fn Builder(comptime options: ExpressionOptions) type {
|
||||
const addr_type = switch (options.addr_size) {
|
||||
2 => u16,
|
||||
4 => u32,
|
||||
@ -959,10 +959,33 @@ fn opcodeValidInCFA(opcode: u8) bool {
|
||||
};
|
||||
}
|
||||
|
||||
const testing = std.testing;
|
||||
test "DWARF expressions" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
const options = ExpressionOptions{};
|
||||
const stack_machine = StackMachine(options){};
|
||||
var stack_machine = StackMachine(options){};
|
||||
defer stack_machine.deinit(allocator);
|
||||
|
||||
const b = Builder(options);
|
||||
|
||||
var program = std.ArrayList(u8).init(allocator);
|
||||
defer program.deinit();
|
||||
|
||||
const writer = program.writer();
|
||||
|
||||
// Literals
|
||||
{
|
||||
const context = ExpressionContext{};
|
||||
for (0..32) |i| {
|
||||
try b.writeLiteral(writer, @intCast(i));
|
||||
}
|
||||
|
||||
_ = try stack_machine.run(program.items, allocator, context, 0);
|
||||
|
||||
for (0..32) |i| {
|
||||
const expected = 31 - i;
|
||||
try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,7 +271,7 @@ const StackContext = union(enum) {
|
||||
current: struct {
|
||||
ret_addr: ?usize,
|
||||
},
|
||||
exception: *const debug.StackTraceContext,
|
||||
exception: *const debug.ThreadContext,
|
||||
not_supported: void,
|
||||
|
||||
pub fn dumpStackTrace(ctx: @This()) void {
|
||||
|
||||
@ -5,7 +5,7 @@ const testing = std.testing;
|
||||
noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void {
|
||||
expected[0] = @returnAddress();
|
||||
|
||||
var context: debug.StackTraceContext = undefined;
|
||||
var context: debug.ThreadContext = undefined;
|
||||
testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
|
||||
|
||||
var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
|
||||
|
||||
@ -5,7 +5,7 @@ const testing = std.testing;
|
||||
noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
|
||||
expected[0] = @returnAddress();
|
||||
|
||||
var context: debug.StackTraceContext = undefined;
|
||||
var context: debug.ThreadContext = undefined;
|
||||
testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
|
||||
|
||||
var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user