Commit 69399fbb82
Changed files (3)
lib
std
lib/std/dwarf/call_frame.zig
@@ -216,16 +216,35 @@ pub const Instruction = union(Opcode) {
}
};
-/// See section 6.4.1 of the DWARF5 specification
+/// This is a virtual machine that runs DWARF call frame instructions.
+/// See section 6.4.1 of the DWARF5 specification.
pub const VirtualMachine = struct {
+
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,
};
@@ -248,7 +267,7 @@ pub const VirtualMachine = struct {
pub const Column = struct {
/// Register can only null in the case of the CFA column
register: ?u8 = null,
- rule: RegisterRule = .{ .undefined = {} },
+ rule: RegisterRule = .{ .default = {} },
};
const ColumnRange = struct {
@@ -264,13 +283,6 @@ pub const VirtualMachine = struct {
/// The result of executing the CIE's initial_instructions
cie_row: ?Row = null,
- pub fn reset(self: *VirtualMachine) void {
- self.stack.clearRetainingCapacity();
- self.columns.clearRetainingCapacity();
- self.current_row = .{};
- self.cie_row = null;
- }
-
pub fn deinit(self: *VirtualMachine, allocator: std.mem.Allocator) void {
self.stack.deinit(allocator);
self.columns.deinit(allocator);
@@ -357,7 +369,7 @@ pub const VirtualMachine = struct {
/// 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
+ /// Returns the value of `current_row` before executing this instruction.
pub fn step(
self: *VirtualMachine,
allocator: std.mem.Allocator,
@@ -367,13 +379,16 @@ pub const VirtualMachine = struct {
) !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;
+ 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.operands.address <= self.current_row.offset) return error.InvalidOperation;
- // TODO: Check cie.segment_selector_size != for DWARFV4
+ // TODO: Check cie.segment_selector_size != 0 for DWARFV4
self.current_row.offset = i.operands.address;
},
inline .advance_loc,
@@ -392,11 +407,6 @@ pub const VirtualMachine = struct {
const column = try self.getOrAddColumn(allocator, i.operands.register);
column.rule = .{ .offset = @intCast(i64, i.operands.offset) * cie.data_alignment_factor };
},
- // .offset_extended_sf => |i| {
- // try self.resolveCopyOnWrite(allocator);
- // const column = try self.getOrAddColumn(allocator, i.operands.register);
- // column.rule = .{ .offset = i.operands.offset * cie.data_alignment_factor };
- // },
inline .restore,
.restore_extended,
=> |i| {
@@ -405,7 +415,7 @@ pub const VirtualMachine = struct {
const column = try self.getOrAddColumn(allocator, i.operands.register);
column.rule = for (self.rowColumns(cie_row)) |cie_column| {
if (cie_column.register == i.operands.register) break cie_column.rule;
- } else .{ .undefined = {} };
+ } else .{ .default = {} };
} else return error.InvalidOperation;
},
.nop => {},
@@ -427,13 +437,6 @@ pub const VirtualMachine = struct {
.remember_state => {
try self.stack.append(allocator, self.current_row.columns);
self.current_row.copy_on_write = true;
-
- // 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;
- // }
},
.restore_state => {
const restored_columns = self.stack.popOrNull() orelse return error.InvalidOperation;
lib/std/dwarf/EH.zig
@@ -1,6 +1,10 @@
pub const PE = struct {
pub const absptr = 0x00;
+ pub const size_mask = 0x7;
+ pub const sign_mask = 0x8;
+ pub const type_mask = size_mask | sign_mask;
+
pub const uleb128 = 0x01;
pub const udata2 = 0x02;
pub const udata4 = 0x03;
@@ -10,11 +14,13 @@ pub const PE = struct {
pub const sdata4 = 0x0B;
pub const sdata8 = 0x0C;
+ pub const rel_mask = 0x70;
pub const pcrel = 0x10;
pub const textrel = 0x20;
pub const datarel = 0x30;
pub const funcrel = 0x40;
pub const aligned = 0x50;
+
pub const indirect = 0x80;
pub const omit = 0xff;
lib/std/dwarf.zig
@@ -1494,18 +1494,34 @@ pub const DwarfInfo = struct {
}
const id_len = @as(u8, if (is_64) 8 else 4);
- const entry_bytes = eh_frame[stream.pos..][0..length - id_len];
+ const entry_bytes = eh_frame[stream.pos..][0 .. length - id_len];
const id = try reader.readInt(u32, di.endian);
// TODO: Get section_offset here (pass in from headers)
if (id == 0) {
- const cie = try CommonInformationEntry.parse(entry_bytes, @ptrToInt(eh_frame.ptr), 0, length_offset, @sizeOf(usize), di.endian);
+ const cie = try CommonInformationEntry.parse(
+ entry_bytes,
+ @ptrToInt(eh_frame.ptr),
+ 0,
+ true,
+ length_offset,
+ @sizeOf(usize),
+ di.endian,
+ );
try di.cie_map.put(allocator, length_offset, cie);
} else {
const cie_offset = stream.pos - 4 - id;
const cie = di.cie_map.get(cie_offset) orelse return badDwarf();
- const fde = try FrameDescriptionEntry.parse(entry_bytes, @ptrToInt(eh_frame.ptr), 0, cie, @sizeOf(usize), di.endian);
+ const fde = try FrameDescriptionEntry.parse(
+ entry_bytes,
+ @ptrToInt(eh_frame.ptr),
+ 0,
+ true,
+ cie,
+ @sizeOf(usize),
+ di.endian,
+ );
try di.fde_list.append(allocator, fde);
}
}
@@ -1557,6 +1573,11 @@ const EhPointerContext = struct {
// The address of the pointer field itself
pc_rel_base: u64,
+ // Whether or not to follow indirect pointers. This should only be
+ // used when decoding pointers at runtime using the current process's
+ // debug info.
+ follow_indirect: bool,
+
// These relative addressing modes are only used in specific cases, and
// might not be available / required in all parsing contexts
data_rel_base: ?u64 = null,
@@ -1570,7 +1591,7 @@ fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerCo
const value: union(enum) {
signed: i64,
unsigned: u64,
- } = switch (enc & 0x0f) {
+ } = switch (enc & EH.PE.type_mask) {
EH.PE.absptr => .{
.unsigned = switch (addr_size_bytes) {
2 => try reader.readInt(u16, endian),
@@ -1590,33 +1611,31 @@ fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerCo
else => return badDwarf(),
};
- const relative_to = enc & 0xf0;
- var base = switch (relative_to) {
+ var base = switch (enc & EH.PE.rel_mask) {
EH.PE.pcrel => ctx.pc_rel_base,
EH.PE.textrel => ctx.text_rel_base orelse return error.PointerBaseNotSpecified,
EH.PE.datarel => ctx.data_rel_base orelse return error.PointerBaseNotSpecified,
EH.PE.funcrel => ctx.function_rel_base orelse return error.PointerBaseNotSpecified,
- EH.PE.indirect => {
- switch (addr_size_bytes) {
- 2 => return @intToPtr(*const u16, value.unsigned).*,
- 4 => return @intToPtr(*const u32, value.unsigned).*,
- 8 => return @intToPtr(*const u64, value.unsigned).*,
- else => return error.UnsupportedAddrSize,
- }
- },
else => null,
};
- if (base) |b| {
- return switch (value) {
- .signed => |s| @intCast(u64, s + @intCast(i64, b)),
- .unsigned => |u| u + b,
+ const ptr = if (base) |b| switch (value) {
+ .signed => |s| @intCast(u64, s + @intCast(i64, b)),
+ .unsigned => |u| u + b,
+ } else switch (value) {
+ .signed => |s| @intCast(u64, s),
+ .unsigned => |u| u,
+ };
+
+ if ((enc & EH.PE.indirect) > 0 and ctx.follow_indirect) {
+ return switch (addr_size_bytes) {
+ 2 => return @intToPtr(*const u16, ptr).*,
+ 4 => return @intToPtr(*const u32, ptr).*,
+ 8 => return @intToPtr(*const u64, ptr).*,
+ else => return error.UnsupportedAddrSize,
};
} else {
- return switch (value) {
- .signed => |s| @intCast(u64, s),
- .unsigned => |u| u,
- };
+ return ptr;
}
}
@@ -1668,6 +1687,7 @@ pub const CommonInformationEntry = struct {
cie_bytes: []const u8,
section_base: u64,
section_offset: u64,
+ is_runtime: bool,
length_offset: u64,
addr_size_bytes: u8,
endian: std.builtin.Endian,
@@ -1735,7 +1755,10 @@ pub const CommonInformationEntry = struct {
reader,
personality_enc.?,
addr_size_bytes,
- .{ .pc_rel_base = @ptrToInt(&cie_bytes[stream.pos]) - section_base + section_offset },
+ .{
+ .pc_rel_base = @ptrToInt(&cie_bytes[stream.pos]) - section_base + section_offset,
+ .follow_indirect = is_runtime,
+ },
endian,
);
},
@@ -1785,6 +1808,7 @@ pub const FrameDescriptionEntry = struct {
fde_bytes: []const u8,
section_base: u64,
section_offset: u64,
+ is_runtime: bool,
cie: CommonInformationEntry,
addr_size_bytes: u8,
endian: std.builtin.Endian,
@@ -1798,15 +1822,21 @@ pub const FrameDescriptionEntry = struct {
reader,
cie.fde_pointer_enc,
addr_size_bytes,
- .{ .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) - section_base + section_offset },
+ .{
+ .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) - section_base + section_offset,
+ .follow_indirect = is_runtime,
+ },
endian,
) orelse return badDwarf();
const pc_range = try readEhPointer(
reader,
- cie.fde_pointer_enc & 0x0f,
+ cie.fde_pointer_enc,
addr_size_bytes,
- .{ .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) - section_base + section_offset },
+ .{
+ .pc_rel_base = 0,
+ .follow_indirect = false,
+ },
endian,
) orelse return badDwarf();
@@ -1819,9 +1849,12 @@ pub const FrameDescriptionEntry = struct {
const lsda_pointer = if (cie.lsda_pointer_enc != EH.PE.omit)
try readEhPointer(
reader,
- cie.lsda_pointer_enc & 0x0f,
+ cie.lsda_pointer_enc,
addr_size_bytes,
- .{ .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) },
+ .{
+ .pc_rel_base = @ptrToInt(&fde_bytes[stream.pos]) - section_base + section_offset,
+ .follow_indirect = is_runtime,
+ },
endian,
)
else