Commit 69399fbb82

kcbanner <kcbanner@gmail.com>
2023-05-11 07:52:45
- add default register rule - fixup eh pointer decoding
1 parent a0a40c2
Changed files (3)
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