mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 13:58:27 +00:00
dwarf: don't dupe function names, as they are backed by the memory mapped sections
dwarf: const-correctness fixups dwarf: implement the remaining register rules dwarf: start implmenting the DWARF expression stack machine
This commit is contained in:
parent
62598c2187
commit
b85f84061a
@ -483,16 +483,14 @@ pub const StackIterator = struct {
|
||||
pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator {
|
||||
var iterator = init(first_address, null);
|
||||
iterator.debug_info = debug_info;
|
||||
iterator.dwarf_context = try DW.UnwindContext.init(context, &isValidMemory);
|
||||
iterator.dwarf_context = try DW.UnwindContext.init(debug_info.allocator, context, &isValidMemory);
|
||||
iterator.last_error = null;
|
||||
return iterator;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *StackIterator) void {
|
||||
if (have_ucontext) {
|
||||
if (self.debug_info) |debug_info| {
|
||||
self.dwarf_context.deinit(debug_info.allocator);
|
||||
}
|
||||
if (have_ucontext and self.debug_info != null) {
|
||||
self.dwarf_context.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,7 +597,7 @@ pub const StackIterator = struct {
|
||||
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;
|
||||
return di.unwindFrame(self.debug_info.?.allocator, &self.dwarf_context, module.base_address);
|
||||
return di.unwindFrame(&self.dwarf_context, module.base_address);
|
||||
} else return error.MissingDebugInfo;
|
||||
}
|
||||
|
||||
|
||||
@ -163,15 +163,9 @@ const PcRange = struct {
|
||||
const Func = struct {
|
||||
pc_range: ?PcRange,
|
||||
name: ?[]const u8,
|
||||
|
||||
fn deinit(func: *Func, allocator: mem.Allocator) void {
|
||||
if (func.name) |name| {
|
||||
allocator.free(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CompileUnit = struct {
|
||||
pub const CompileUnit = struct {
|
||||
version: u16,
|
||||
is_64: bool,
|
||||
die: *Die,
|
||||
@ -181,6 +175,7 @@ const CompileUnit = struct {
|
||||
addr_base: usize,
|
||||
rnglists_base: usize,
|
||||
loclists_base: usize,
|
||||
frame_base: ?*const FormValue,
|
||||
};
|
||||
|
||||
const AbbrevTable = std.ArrayList(AbbrevTableEntry);
|
||||
@ -216,7 +211,7 @@ const AbbrevAttr = struct {
|
||||
payload: i64,
|
||||
};
|
||||
|
||||
const FormValue = union(enum) {
|
||||
pub const FormValue = union(enum) {
|
||||
Address: u64,
|
||||
AddrOffset: usize,
|
||||
Block: []u8,
|
||||
@ -298,7 +293,7 @@ const Die = struct {
|
||||
|
||||
fn getAttrAddr(
|
||||
self: *const Die,
|
||||
di: *DwarfInfo,
|
||||
di: *const DwarfInfo,
|
||||
id: u64,
|
||||
compile_unit: CompileUnit,
|
||||
) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {
|
||||
@ -708,9 +703,6 @@ pub const DwarfInfo = struct {
|
||||
allocator.destroy(cu.die);
|
||||
}
|
||||
di.compile_unit_list.deinit(allocator);
|
||||
for (di.func_list.items) |*func| {
|
||||
func.deinit(allocator);
|
||||
}
|
||||
di.func_list.deinit(allocator);
|
||||
di.cie_map.deinit(allocator);
|
||||
di.fde_list.deinit(allocator);
|
||||
@ -793,6 +785,7 @@ pub const DwarfInfo = struct {
|
||||
.addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0,
|
||||
.rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0,
|
||||
.loclists_base = if (die_obj.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0,
|
||||
.frame_base = die_obj.getAttr(AT.frame_base),
|
||||
};
|
||||
},
|
||||
TAG.subprogram, TAG.inlined_subroutine, TAG.subroutine, TAG.entry_point => {
|
||||
@ -802,8 +795,7 @@ pub const DwarfInfo = struct {
|
||||
// Prevent endless loops
|
||||
while (depth > 0) : (depth -= 1) {
|
||||
if (this_die_obj.getAttr(AT.name)) |_| {
|
||||
const name = try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit);
|
||||
break :x try allocator.dupe(u8, name);
|
||||
break :x try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit);
|
||||
} else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
|
||||
// Follow the DIE it points to and repeat
|
||||
const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
|
||||
@ -834,7 +826,7 @@ pub const DwarfInfo = struct {
|
||||
break :x null;
|
||||
};
|
||||
|
||||
var found_range = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {
|
||||
var range_added = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: {
|
||||
if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
|
||||
const pc_end = switch (high_pc_value.*) {
|
||||
FormValue.Address => |value| value,
|
||||
@ -852,9 +844,11 @@ pub const DwarfInfo = struct {
|
||||
.end = pc_end,
|
||||
},
|
||||
});
|
||||
|
||||
break :blk true;
|
||||
}
|
||||
|
||||
break :blk true;
|
||||
break :blk false;
|
||||
} else |err| blk: {
|
||||
if (err != error.MissingDebugInfo) return err;
|
||||
break :blk false;
|
||||
@ -867,7 +861,7 @@ pub const DwarfInfo = struct {
|
||||
};
|
||||
|
||||
while (try iter.next()) |range| {
|
||||
found_range = true;
|
||||
range_added = true;
|
||||
try di.func_list.append(allocator, Func{
|
||||
.name = fn_name,
|
||||
.pc_range = .{
|
||||
@ -878,7 +872,7 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_range) {
|
||||
if (fn_name != null and !range_added) {
|
||||
try di.func_list.append(allocator, Func{
|
||||
.name = fn_name,
|
||||
.pc_range = null,
|
||||
@ -952,6 +946,7 @@ pub const DwarfInfo = struct {
|
||||
.addr_base = if (compile_unit_die.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0,
|
||||
.rnglists_base = if (compile_unit_die.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0,
|
||||
.loclists_base = if (compile_unit_die.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0,
|
||||
.frame_base = compile_unit_die.getAttr(AT.frame_base),
|
||||
};
|
||||
|
||||
compile_unit.pc_range = x: {
|
||||
@ -987,11 +982,11 @@ pub const DwarfInfo = struct {
|
||||
const DebugRangeIterator = struct {
|
||||
base_address: u64,
|
||||
section_type: DwarfSection,
|
||||
di: *DwarfInfo,
|
||||
di: *const DwarfInfo,
|
||||
compile_unit: *const CompileUnit,
|
||||
stream: io.FixedBufferStream([]const u8),
|
||||
|
||||
pub fn init(ranges_value: *const FormValue, di: *DwarfInfo, compile_unit: *const CompileUnit) !@This() {
|
||||
pub fn init(ranges_value: *const FormValue, di: *const DwarfInfo, compile_unit: *const CompileUnit) !@This() {
|
||||
const section_type = if (compile_unit.version >= 5) DwarfSection.debug_rnglists else DwarfSection.debug_ranges;
|
||||
const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo;
|
||||
|
||||
@ -1129,7 +1124,7 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
|
||||
pub fn findCompileUnit(di: *const DwarfInfo, target_address: u64) !*const CompileUnit {
|
||||
for (di.compile_unit_list.items) |*compile_unit| {
|
||||
if (compile_unit.pc_range) |range| {
|
||||
if (target_address >= range.start and target_address < range.end) return compile_unit;
|
||||
@ -1630,7 +1625,7 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwindFrame(di: *const DwarfInfo, allocator: mem.Allocator, context: *UnwindContext, module_base_address: usize) !usize {
|
||||
pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, module_base_address: usize) !usize {
|
||||
if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;
|
||||
if (context.pc == 0) return 0;
|
||||
|
||||
@ -1678,10 +1673,11 @@ pub const DwarfInfo = struct {
|
||||
cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
|
||||
}
|
||||
|
||||
const compile_unit: ?*const CompileUnit = di.findCompileUnit(fde.pc_begin) catch null;
|
||||
context.vm.reset();
|
||||
context.reg_ctx.eh_frame = cie.version != 4;
|
||||
|
||||
_ = try context.vm.runToNative(allocator, mapped_pc, cie, fde);
|
||||
_ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);
|
||||
const row = &context.vm.current_row;
|
||||
|
||||
context.cfa = switch (row.cfa.rule) {
|
||||
@ -1690,12 +1686,19 @@ pub const DwarfInfo = struct {
|
||||
const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx));
|
||||
break :blk try call_frame.applyOffset(value, offset);
|
||||
},
|
||||
.expression => |expression| {
|
||||
.expression => |expression| blk: {
|
||||
context.stack_machine.reset();
|
||||
const value = try context.stack_machine.run(
|
||||
expression,
|
||||
context.allocator,
|
||||
compile_unit,
|
||||
&context.ucontext,
|
||||
context.reg_ctx,
|
||||
context.cfa orelse 0,
|
||||
);
|
||||
|
||||
// TODO: Evaluate expression
|
||||
_ = expression;
|
||||
|
||||
return error.UnimplementedTODO;
|
||||
if (value != .generic) return error.InvalidExpressionValue;
|
||||
break :blk value.generic;
|
||||
},
|
||||
else => return error.InvalidCFARule,
|
||||
};
|
||||
@ -1713,7 +1716,13 @@ pub const DwarfInfo = struct {
|
||||
has_next_ip = column.rule != .undefined;
|
||||
}
|
||||
|
||||
try column.resolveValue(context.*, dest);
|
||||
try column.resolveValue(
|
||||
context,
|
||||
compile_unit,
|
||||
&context.ucontext,
|
||||
context.reg_ctx,
|
||||
dest,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1738,16 +1747,19 @@ pub const DwarfInfo = struct {
|
||||
};
|
||||
|
||||
pub const UnwindContext = struct {
|
||||
allocator: mem.Allocator,
|
||||
cfa: ?usize,
|
||||
pc: usize,
|
||||
ucontext: os.ucontext_t,
|
||||
reg_ctx: abi.RegisterContext,
|
||||
isValidMemory: *const fn (address: usize) bool,
|
||||
vm: call_frame.VirtualMachine = .{},
|
||||
stack_machine: expressions.StackMachine(.{ .call_frame_mode = true }) = .{},
|
||||
|
||||
pub fn init(ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
|
||||
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));
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.cfa = null,
|
||||
.pc = pc,
|
||||
.ucontext = ucontext.*,
|
||||
@ -1756,8 +1768,9 @@ pub const UnwindContext = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *UnwindContext, allocator: mem.Allocator) void {
|
||||
self.vm.deinit(allocator);
|
||||
pub fn deinit(self: *UnwindContext) void {
|
||||
self.vm.deinit(self.allocator);
|
||||
self.stack_machine.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn getFp(self: *const UnwindContext) !usize {
|
||||
|
||||
@ -183,9 +183,9 @@ pub const Instruction = union(Opcode) {
|
||||
offset_extended_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
|
||||
def_cfa_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
|
||||
def_cfa_offset_sf: InstructionType(.{ .offset = .sleb128_offset }),
|
||||
val_offset: InstructionType(.{ .a = .uleb128_offset, .b = .uleb128_offset }),
|
||||
val_offset_sf: InstructionType(.{ .a = .uleb128_offset, .b = .sleb128_offset }),
|
||||
val_expression: InstructionType(.{ .a = .uleb128_offset, .block = .block }),
|
||||
val_offset: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),
|
||||
val_offset_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
|
||||
val_expression: InstructionType(.{ .register = .uleb128_register, .block = .block }),
|
||||
|
||||
fn readOperands(
|
||||
self: *Instruction,
|
||||
@ -292,7 +292,14 @@ pub const VirtualMachine = struct {
|
||||
rule: RegisterRule = .{ .default = {} },
|
||||
|
||||
/// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes)
|
||||
pub fn resolveValue(self: Column, context: dwarf.UnwindContext, out: []u8) !void {
|
||||
pub fn resolveValue(
|
||||
self: Column,
|
||||
context: *dwarf.UnwindContext,
|
||||
compile_unit: ?*const dwarf.CompileUnit,
|
||||
ucontext: *const std.os.ucontext_t,
|
||||
reg_ctx: abi.RegisterContext,
|
||||
out: []u8,
|
||||
) !void {
|
||||
switch (self.rule) {
|
||||
.default => {
|
||||
const register = self.register orelse return error.InvalidRegister;
|
||||
@ -321,14 +328,21 @@ pub const VirtualMachine = struct {
|
||||
@memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx));
|
||||
},
|
||||
.expression => |expression| {
|
||||
// TODO
|
||||
_ = expression;
|
||||
unreachable;
|
||||
context.stack_machine.reset();
|
||||
const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?);
|
||||
|
||||
if (value != .generic) return error.InvalidExpressionValue;
|
||||
if (!context.isValidMemory(value.generic)) return error.InvalidExpressionAddress;
|
||||
|
||||
const ptr: *usize = @ptrFromInt(value.generic);
|
||||
mem.writeIntSliceNative(usize, out, ptr.*);
|
||||
},
|
||||
.val_expression => |expression| {
|
||||
// TODO
|
||||
_ = expression;
|
||||
unreachable;
|
||||
context.stack_machine.reset();
|
||||
const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?);
|
||||
|
||||
if (value != .generic) return error.InvalidExpressionValue;
|
||||
mem.writeIntSliceNative(usize, out, value.generic);
|
||||
},
|
||||
.architectural => return error.UnimplementedRule,
|
||||
}
|
||||
@ -546,12 +560,16 @@ pub const VirtualMachine = struct {
|
||||
.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.operands.offset) };
|
||||
self.current_row.cfa.rule = .{
|
||||
.val_offset = @intCast(i.operands.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.operands.offset * cie.data_alignment_factor };
|
||||
self.current_row.cfa.rule = .{
|
||||
.val_offset = i.operands.offset * cie.data_alignment_factor,
|
||||
};
|
||||
},
|
||||
.def_cfa_expression => |i| {
|
||||
try self.resolveCopyOnWrite(allocator);
|
||||
@ -567,17 +585,26 @@ pub const VirtualMachine = struct {
|
||||
.expression = i.operands.block,
|
||||
};
|
||||
},
|
||||
.val_offset => {
|
||||
// TODO: Implement
|
||||
unreachable;
|
||||
.val_offset => |i| {
|
||||
try self.resolveCopyOnWrite(allocator);
|
||||
const column = try self.getOrAddColumn(allocator, i.operands.register);
|
||||
column.rule = .{
|
||||
.val_offset = @as(i64, @intCast(i.operands.offset)) * cie.data_alignment_factor,
|
||||
};
|
||||
},
|
||||
.val_offset_sf => {
|
||||
// TODO: Implement
|
||||
unreachable;
|
||||
.val_offset_sf => |i| {
|
||||
try self.resolveCopyOnWrite(allocator);
|
||||
const column = try self.getOrAddColumn(allocator, i.operands.register);
|
||||
column.rule = .{
|
||||
.val_offset = i.operands.offset * cie.data_alignment_factor,
|
||||
};
|
||||
},
|
||||
.val_expression => {
|
||||
// TODO: Implement
|
||||
unreachable;
|
||||
.val_expression => |i| {
|
||||
try self.resolveCopyOnWrite(allocator);
|
||||
const column = try self.getOrAddColumn(allocator, i.operands.register);
|
||||
column.rule = .{
|
||||
.val_expression = i.operands.block,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,9 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const OP = @import("OP.zig");
|
||||
const leb = @import("../leb128.zig");
|
||||
const dwarf = @import("../dwarf.zig");
|
||||
const abi = dwarf.abi;
|
||||
const mem = std.mem;
|
||||
|
||||
pub const StackMachineOptions = struct {
|
||||
/// The address size of the target architecture
|
||||
@ -33,9 +36,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
|
||||
};
|
||||
|
||||
return struct {
|
||||
const Value = union(enum) {
|
||||
const Self = @This();
|
||||
|
||||
const Operand = union(enum) {
|
||||
generic: addr_type,
|
||||
const_type: []const u8,
|
||||
register: u8,
|
||||
base_register: struct {
|
||||
base_register: u8,
|
||||
@ -46,7 +50,11 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
|
||||
offset: i64,
|
||||
},
|
||||
block: []const u8,
|
||||
base_type: struct {
|
||||
register_type: struct {
|
||||
register: u8,
|
||||
type_offset: u64,
|
||||
},
|
||||
const_type: struct {
|
||||
type_offset: u64,
|
||||
value_bytes: []const u8,
|
||||
},
|
||||
@ -56,9 +64,31 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
|
||||
},
|
||||
};
|
||||
|
||||
const Value = union(enum) {
|
||||
generic: addr_type,
|
||||
regval_type: struct {
|
||||
// Offset of DW_TAG_base_type DIE
|
||||
type_offset: u64,
|
||||
value: addr_type,
|
||||
},
|
||||
const_type: struct {
|
||||
// Offset of DW_TAG_base_type DIE
|
||||
type_offset: u64,
|
||||
value_bytes: []const u8,
|
||||
},
|
||||
};
|
||||
|
||||
stack: std.ArrayListUnmanaged(Value) = .{},
|
||||
|
||||
fn generic(value: anytype) Value {
|
||||
pub fn reset(self: *Self) void {
|
||||
self.stack.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
self.stack.deinit(allocator);
|
||||
}
|
||||
|
||||
fn generic(value: anytype) Operand {
|
||||
const int_info = @typeInfo(@TypeOf(value)).Int;
|
||||
if (@sizeOf(@TypeOf(value)) > options.addr_size) {
|
||||
return .{ .generic = switch (int_info.signedness) {
|
||||
@ -73,7 +103,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Value {
|
||||
pub fn readOperand(stream: *std.io.FixedBufferStream([]const u8), opcode: u8) !?Operand {
|
||||
const reader = stream.reader();
|
||||
return switch (opcode) {
|
||||
OP.addr,
|
||||
@ -87,8 +117,8 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
|
||||
OP.const1s => generic(try reader.readByteSigned()),
|
||||
OP.const2u,
|
||||
OP.call2,
|
||||
OP.call4,
|
||||
=> generic(try reader.readInt(u16, options.endian)),
|
||||
OP.call4 => generic(try reader.readInt(u32, options.endian)),
|
||||
OP.const2s,
|
||||
OP.bra,
|
||||
OP.skip,
|
||||
@ -114,21 +144,35 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
|
||||
.offset = try leb.readILEB128(i64, reader),
|
||||
} },
|
||||
OP.regx => .{ .register = try leb.readULEB128(u8, reader) },
|
||||
OP.bregx, OP.regval_type => .{ .base_register = .{
|
||||
.base_register = try leb.readULEB128(u8, reader),
|
||||
.offset = try leb.readILEB128(i64, reader),
|
||||
} },
|
||||
OP.bregx => blk: {
|
||||
const base_register = try leb.readULEB128(u8, reader);
|
||||
const offset = try leb.readILEB128(i64, reader);
|
||||
break :blk .{ .base_register = .{
|
||||
.base_register = base_register,
|
||||
.offset = offset,
|
||||
} };
|
||||
},
|
||||
OP.regval_type => blk: {
|
||||
const register = try leb.readULEB128(u8, reader);
|
||||
const type_offset = try leb.readULEB128(u64, reader);
|
||||
break :blk .{ .register_type = .{
|
||||
.register = register,
|
||||
.type_offset = type_offset,
|
||||
} };
|
||||
},
|
||||
OP.piece => .{
|
||||
.composite_location = .{
|
||||
.size = try leb.readULEB128(u8, reader),
|
||||
.offset = 0,
|
||||
},
|
||||
},
|
||||
OP.bit_piece => .{
|
||||
.composite_location = .{
|
||||
.size = try leb.readULEB128(u8, reader),
|
||||
.offset = try leb.readILEB128(i64, reader),
|
||||
},
|
||||
OP.bit_piece => blk: {
|
||||
const size = try leb.readULEB128(u8, reader);
|
||||
const offset = try leb.readILEB128(i64, reader);
|
||||
break :blk .{ .composite_location = .{
|
||||
.size = size,
|
||||
.offset = offset,
|
||||
} };
|
||||
},
|
||||
OP.implicit_value, OP.entry_value => blk: {
|
||||
const size = try leb.readULEB128(u8, reader);
|
||||
@ -145,7 +189,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
|
||||
if (stream.pos + size > stream.buffer.len) return error.InvalidExpression;
|
||||
const value_bytes = stream.buffer[stream.pos..][0..size];
|
||||
stream.pos += size;
|
||||
break :blk .{ .base_type = .{
|
||||
break :blk .{ .const_type = .{
|
||||
.type_offset = type_offset,
|
||||
.value_bytes = value_bytes,
|
||||
} };
|
||||
@ -163,22 +207,144 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn step(
|
||||
self: *StackMachine,
|
||||
stream: std.io.FixedBufferStream([]const u8),
|
||||
pub fn run(
|
||||
self: *Self,
|
||||
expression: []const u8,
|
||||
allocator: std.mem.Allocator,
|
||||
) !void {
|
||||
if (@sizeOf(usize) != addr_type or options.endian != builtin.target.cpu.arch.endian())
|
||||
compile_unit: ?*const dwarf.CompileUnit,
|
||||
ucontext: *const std.os.ucontext_t,
|
||||
reg_ctx: abi.RegisterContext,
|
||||
initial_value: usize,
|
||||
) !Value {
|
||||
try self.stack.append(allocator, .{ .generic = initial_value });
|
||||
var stream = std.io.fixedBufferStream(expression);
|
||||
while (try self.step(&stream, allocator, compile_unit, ucontext, reg_ctx)) {}
|
||||
if (self.stack.items.len == 0) return error.InvalidExpression;
|
||||
return self.stack.items[self.stack.items.len - 1];
|
||||
}
|
||||
|
||||
/// Reads an opcode and its operands from the stream and executes it
|
||||
pub fn step(
|
||||
self: *Self,
|
||||
stream: *std.io.FixedBufferStream([]const u8),
|
||||
allocator: std.mem.Allocator,
|
||||
compile_unit: ?*const dwarf.CompileUnit,
|
||||
ucontext: *const std.os.ucontext_t,
|
||||
reg_ctx: dwarf.abi.RegisterContext,
|
||||
) !bool {
|
||||
if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
|
||||
@compileError("Execution of non-native address sizees / endianness is not supported");
|
||||
|
||||
const opcode = try stream.reader.readByte();
|
||||
_ = opcode;
|
||||
_ = self;
|
||||
_ = allocator;
|
||||
const opcode = try stream.reader().readByte();
|
||||
if (options.call_frame_mode) {
|
||||
// Certain opcodes are not allowed in a CFA context, see 6.4.2
|
||||
switch (opcode) {
|
||||
OP.addrx,
|
||||
OP.call2,
|
||||
OP.call4,
|
||||
OP.call_ref,
|
||||
OP.const_type,
|
||||
OP.constx,
|
||||
OP.convert,
|
||||
OP.deref_type,
|
||||
OP.regval_type,
|
||||
OP.reinterpret,
|
||||
OP.push_object_address,
|
||||
OP.call_frame_cfa,
|
||||
=> return error.InvalidCFAExpression,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// switch (opcode) {
|
||||
// OP.addr => try self.stack.append(allocator, try readOperand(stream, opcode)),
|
||||
// }
|
||||
switch (opcode) {
|
||||
|
||||
// 2.5.1.1: Literal Encodings
|
||||
OP.lit0...OP.lit31,
|
||||
OP.addr,
|
||||
OP.const1u,
|
||||
OP.const2u,
|
||||
OP.const4u,
|
||||
OP.const8u,
|
||||
OP.const1s,
|
||||
OP.const2s,
|
||||
OP.const4s,
|
||||
OP.const8s,
|
||||
OP.constu,
|
||||
OP.consts,
|
||||
=> try self.stack.append(allocator, .{ .generic = (try readOperand(stream, opcode)).?.generic }),
|
||||
|
||||
OP.const_type => {
|
||||
const const_type = (try readOperand(stream, opcode)).?.const_type;
|
||||
try self.stack.append(allocator, .{ .const_type = .{
|
||||
.type_offset = const_type.type_offset,
|
||||
.value_bytes = const_type.value_bytes,
|
||||
} });
|
||||
},
|
||||
|
||||
OP.addrx, OP.constx => {
|
||||
const debug_addr_index = (try readOperand(stream, opcode)).?.generic;
|
||||
|
||||
// TODO: Read item from .debug_addr, this requires need DW_AT_addr_base of the compile unit, push onto stack as generic
|
||||
|
||||
_ = debug_addr_index;
|
||||
unreachable;
|
||||
},
|
||||
|
||||
// 2.5.1.2: Register Values
|
||||
OP.fbreg => {
|
||||
if (compile_unit == null) return error.ExpressionRequiresCompileUnit;
|
||||
if (compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;
|
||||
|
||||
const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic);
|
||||
_ = offset;
|
||||
|
||||
switch (compile_unit.?.frame_base.?.*) {
|
||||
.ExprLoc => {
|
||||
// TODO: Run this expression in a nested stack machine
|
||||
return error.UnimplementedOpcode;
|
||||
},
|
||||
.LocListOffset => {
|
||||
// TODO: Read value from .debug_loclists
|
||||
return error.UnimplementedOpcode;
|
||||
},
|
||||
.SecOffset => {
|
||||
// TODO: Read value from .debug_loclists
|
||||
return error.UnimplementedOpcode;
|
||||
},
|
||||
else => return error.InvalidFrameBase,
|
||||
}
|
||||
},
|
||||
OP.breg0...OP.breg31, OP.bregx => {
|
||||
const base_register = (try readOperand(stream, opcode)).?.base_register;
|
||||
var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(ucontext, base_register.base_register, reg_ctx)));
|
||||
value += base_register.offset;
|
||||
try self.stack.append(allocator, .{ .generic = @intCast(value) });
|
||||
},
|
||||
OP.regval_type => {
|
||||
const register_type = (try readOperand(stream, opcode)).?.register_type;
|
||||
const value = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, register_type.register, reg_ctx));
|
||||
try self.stack.append(allocator, .{
|
||||
.regval_type = .{
|
||||
.value = value,
|
||||
.type_offset = register_type.type_offset,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 2.5.1.3: Stack Operations
|
||||
|
||||
OP.dup => {},
|
||||
|
||||
else => {
|
||||
std.debug.print("Unimplemented DWARF expression opcode: {x}\n", .{opcode});
|
||||
unreachable;
|
||||
},
|
||||
|
||||
// These have already been handled by readOperand
|
||||
OP.lo_user...OP.hi_user => unreachable,
|
||||
}
|
||||
|
||||
return stream.pos < stream.buffer.len;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user