Commit 253e6971ad

kcbanner <kcbanner@gmail.com>
2023-07-19 02:14:42
dwarf: implement aarch64 default register rules
1 parent 4421b14
Changed files (3)
lib/std/dwarf/abi.zig
@@ -367,12 +367,21 @@ pub fn regBytes(
 }
 
 /// Returns the ABI-defined default value this register has in the unwinding table
-/// before running any of the CIE instructions. The DWARF spec defines these values
-///  to be undefined, but allows ABI authors to override that default.
-pub fn getRegDefaultValue(reg_number: u8, out: []u8) void {
-
-    // Implement any ABI-specific rules here
+/// before running any of the CIE instructions. The DWARF spec defines these as having
+/// the .undefined rule by default, but allows ABI authors to override that.
+pub fn getRegDefaultValue(reg_number: u8, context: *std.dwarf.UnwindContext, out: []u8) !void {
+    switch (builtin.cpu.arch) {
+        .aarch64 => {
+            // Callee-saved registers are initialized as if they had the .same_value rule
+            if (reg_number >= 19 and reg_number <= 28) {
+                const src = try regBytes(context.thread_context, reg_number, context.reg_context);
+                if (src.len != out.len) return error.RegisterSizeMismatch;
+                @memcpy(out, src);
+                return;
+            }
+        },
+        else => {},
+    }
 
-    _ = reg_number;
     @memset(out, undefined);
 }
lib/std/dwarf/call_frame.zig
@@ -295,12 +295,18 @@ pub const VirtualMachine = struct {
             switch (self.rule) {
                 .default => {
                     const register = self.register orelse return error.InvalidRegister;
-                    abi.getRegDefaultValue(register, out);
+                    try abi.getRegDefaultValue(register, context, out);
                 },
                 .undefined => {
                     @memset(out, undefined);
                 },
-                .same_value => {},
+                .same_value => {
+                    // TODO: This copy could be eliminated if callers always copy the state then call this function to update it
+                    const register = self.register orelse return error.InvalidRegister;
+                    const src = try abi.regBytes(context.thread_context, register, context.reg_context);
+                    if (src.len != out.len) return error.RegisterSizeMismatch;
+                    @memcpy(out, src);
+                },
                 .offset => |offset| {
                     if (context.cfa) |cfa| {
                         const addr = try applyOffset(cfa, offset);
@@ -316,7 +322,7 @@ pub const VirtualMachine = struct {
                 },
                 .register => |register| {
                     const src = try abi.regBytes(context.thread_context, register, context.reg_context);
-                    if (src.len != out.len) return error.RegisterTypeMismatch;
+                    if (src.len != out.len) return error.RegisterSizeMismatch;
                     @memcpy(out, try abi.regBytes(context.thread_context, register, context.reg_context));
                 },
                 .expression => |expression| {
lib/std/dwarf.zig
@@ -1740,21 +1740,7 @@ pub const DwarfInfo = struct {
         context.reg_context.eh_frame = cie.version != 4;
         context.reg_context.is_macho = di.is_macho;
 
-        if (comptime builtin.target.isDarwin()) {
-            std.debug.print("  state before:\n", .{});
-            std.debug.print("    cfa {?x}:\n", .{context.cfa});
-            for (context.thread_context.mcontext.ss.regs, 0..) |reg, i| {
-                std.debug.print("    {}:0x{x}\n", .{i, reg});
-            }
-            std.debug.print("    fp:0x{x}\n", .{context.thread_context.mcontext.ss.fp});
-            std.debug.print("    lr:0x{x}\n", .{context.thread_context.mcontext.ss.lr});
-            std.debug.print("    sp:0x{x}\n", .{context.thread_context.mcontext.ss.sp});
-            std.debug.print("    pc:0x{x}\n", .{context.thread_context.mcontext.ss.pc});
-        }
-
         const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde);
-        std.debug.print("     ran to 0x{x}\n", .{row.offset + fde.pc_begin});
-
         context.cfa = switch (row.cfa.rule) {
             .val_offset => |offset| blk: {
                 const register = row.cfa.register orelse return error.InvalidCFARule;
@@ -1789,47 +1775,48 @@ pub const DwarfInfo = struct {
 
         const RegisterUpdate = struct {
             // Backed by thread_context
-            old_value: []u8,
+            dest: []u8,
             // Backed by arena
-            new_value: []const u8,
+            src: []const u8,
             prev: ?*@This(),
         };
 
         var update_tail: ?*RegisterUpdate = null;
-        var has_next_ip = true;
+        var has_return_address= true;
         for (context.vm.rowColumns(row)) |column| {
             if (column.register) |register| {
                 if (register == cie.return_address_register) {
-                    has_next_ip = column.rule != .undefined;
+                    has_return_address = column.rule != .undefined;
                 }
-                std.debug.print("      updated {}\n", .{register});
 
-                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 dest = try abi.regBytes(context.thread_context, register, context.reg_context);
+                const src = try update_allocator.alloc(u8, dest.len);
 
                 const prev = update_tail;
                 update_tail = try update_allocator.create(RegisterUpdate);
                 update_tail.?.* = .{
-                    .old_value = old_value,
-                    .new_value = new_value,
+                    .dest = dest,
+                    .src = src,
                     .prev = prev,
                 };
 
                 try column.resolveValue(
                     context,
                     expression_context,
-                    new_value,
+                    src,
                 );
             }
         }
 
+        // On all implemented architectures, the CFA is defined as being the previous frame's SP
         (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(context.reg_context), context.reg_context)).* = context.cfa.?;
+
         while (update_tail) |tail| {
-            @memcpy(tail.old_value, tail.new_value);
+            @memcpy(tail.dest, tail.src);
             update_tail = tail.prev;
         }
 
-        if (has_next_ip) {
+        if (has_return_address) {
             context.pc = abi.stripInstructionPtrAuthCode(mem.readIntSliceNative(usize, try abi.regBytes(
                 context.thread_context,
                 cie.return_address_register,
@@ -1838,10 +1825,8 @@ pub const DwarfInfo = struct {
         } else {
             context.pc = 0;
         }
-        (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc;
-        std.debug.print("     new context.pc: 0x{x}\n", .{context.pc});
 
-        (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(context.reg_context), context.reg_context)).* = context.cfa.?;
+        (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc;
 
         // 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`. By subtracting one,