diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index eb9f702405..826fd3fca3 100644 --- a/lib/std/dwarf.zig +++ b/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 diff --git a/lib/std/dwarf/EH.zig b/lib/std/dwarf/EH.zig index 94d306fc08..3ee7e0be0f 100644 --- a/lib/std/dwarf/EH.zig +++ b/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; diff --git a/lib/std/dwarf/call_frame.zig b/lib/std/dwarf/call_frame.zig index 0f10b7d456..90f9458e43 100644 --- a/lib/std/dwarf/call_frame.zig +++ b/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;