Commit 21d0154139

kcbanner <kcbanner@gmail.com>
2023-07-08 07:16:11
dwarf: skip register tests on unimplemented arch / os, add tests for type convesions debug: dupeContext -> copyContext
1 parent 021f537
Changed files (3)
lib/std/dwarf/expressions.zig
@@ -17,7 +17,10 @@ pub const ExpressionContext = struct {
     /// The compilation unit this expression relates to, if any
     compile_unit: ?*const dwarf.CompileUnit = null,
 
-    // .debug_addr section
+    /// When evaluating a user-presented expression, this is the address of the object being evaluated
+    object_address: ?*const anyopaque = null,
+
+    /// .debug_addr section
     debug_addr: ?[]const u8 = null,
 
     /// Thread context
@@ -465,9 +468,11 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                         },
                     }
                 },
-                OP.push_object_address,
-                OP.form_tls_address,
-                => {
+                OP.push_object_address => {
+                    if (context.object_address == null) return error.IncompleteExpressionContext;
+                    try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) });
+                },
+                OP.form_tls_address => {
                     return error.UnimplementedExpressionOpcode;
                 },
                 OP.call_frame_cfa => {
@@ -1106,28 +1111,34 @@ test "DWARF expressions" {
             .reg_context = reg_context,
         };
 
-        // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
+        // Only test register operations on arch / os that have them implemented
+        if (abi.regBytes(&thread_context, 0, reg_context)) |_| {
 
-        mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, 0, reg_context), 0xee);
-        mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.fpRegNum(reg_context), reg_context), 1);
-        mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.spRegNum(reg_context), reg_context), 2);
-        mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.ipRegNum(), reg_context), 3);
+            // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
 
-        try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
-        try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
-        try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));
-        try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
+            mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, 0, reg_context), 0xee);
+            mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.fpRegNum(reg_context), reg_context), 1);
+            mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.spRegNum(reg_context), reg_context), 2);
+            mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.ipRegNum(), reg_context), 3);
 
-        _ = try stack_machine.run(program.items, allocator, context, 0);
+            try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
+            try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
+            try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));
+            try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
 
-        const regval_type = stack_machine.stack.popOrNull().?.regval_type;
-        try testing.expectEqual(@as(usize, 400), regval_type.type_offset);
-        try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
-        try testing.expectEqual(@as(usize, 0xee), regval_type.value);
+            _ = try stack_machine.run(program.items, allocator, context, 0);
 
-        try testing.expectEqual(@as(usize, 303), stack_machine.stack.popOrNull().?.generic);
-        try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
-        try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
+            const regval_type = stack_machine.stack.popOrNull().?.regval_type;
+            try testing.expectEqual(@as(usize, 400), regval_type.type_offset);
+            try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
+            try testing.expectEqual(@as(usize, 0xee), regval_type.value);
+
+            try testing.expectEqual(@as(usize, 303), stack_machine.stack.popOrNull().?.generic);
+            try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
+            try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
+        } else |err| {
+            if (err != error.UnimplementedArch and err != error.UnimplementedOs) return err;
+        }
     }
 
     // Stack operations
@@ -1242,7 +1253,14 @@ test "DWARF expressions" {
         try testing.expectEqual(@as(u8, 1), xderef_type.type_size);
         try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), xderef_type.value);
 
-        // TODO: Test OP.push_object_address
+        context.object_address = &deref_target;
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeOpcode(writer, OP.push_object_address);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(@as(usize, @intFromPtr(context.object_address.?)), stack_machine.stack.popOrNull().?.generic);
+
         // TODO: Test OP.form_tls_address
 
         context.cfa = @truncate(0xccddccdd_ccddccdd);
@@ -1437,6 +1455,69 @@ test "DWARF expressions" {
 
     }
 
+    // Type conversions
+    {
+        var context = ExpressionContext{};
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+
+        // TODO: Test typed OP.convert once implemented
+
+        const value: usize = @truncate(0xffeeffee_ffeeffee);
+        var value_bytes: [options.addr_size]u8 = undefined;
+        mem.writeIntSliceNative(usize, &value_bytes, value);
+
+        // Convert to generic type
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);
+        try b.writeConvert(writer, @as(usize, 0));
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
+
+        // Reinterpret to generic type
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);
+        try b.writeReinterpret(writer, @as(usize, 0));
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic);
+
+        // Reinterpret to new type
+        const die_offset: usize = 0xffee;
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes);
+        try b.writeReinterpret(writer, die_offset);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        const const_type = stack_machine.stack.popOrNull().?.const_type;
+        try testing.expectEqual(die_offset, const_type.type_offset);
+
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+        try b.writeLiteral(writer, 0);
+        try b.writeReinterpret(writer, die_offset);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        const regval_type = stack_machine.stack.popOrNull().?.regval_type;
+        try testing.expectEqual(die_offset, regval_type.type_offset);
+    }
+
+    // Special operations
+    {
+        var context = ExpressionContext{};
+        stack_machine.reset();
+        program.clearRetainingCapacity();
+
+        try b.writeOpcode(writer, OP.nop);
+        _ = try stack_machine.run(program.items, allocator, context, null);
+        try testing.expect(stack_machine.stack.popOrNull() == null);
+
+
+
+
+    }
+
 
 }
 
lib/std/debug.zig
@@ -135,7 +135,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
 
 /// Platform-specific thread state. This contains register state, and on some platforms
 /// information about the stack. This is not safe to trivially copy, because some platforms
-/// use internal pointers within this structure. To make a copy, use `dupeContext`.
+/// use internal pointers within this structure. To make a copy, use `copyContext`.
 pub const ThreadContext = blk: {
     if (native_os == .windows) {
         break :blk std.os.windows.CONTEXT;
@@ -460,7 +460,7 @@ pub inline fn getContext(context: *ThreadContext) bool {
     return result;
 }
 
-pub fn dupeContext(source: *const ThreadContext, dest: *ThreadContext) void {
+pub fn copyContext(source: *const ThreadContext, dest: *ThreadContext) void {
     if (native_os == .windows) dest.* = source.*;
     if (!have_ucontext) return {};
 
@@ -474,7 +474,7 @@ pub fn dupeContext(source: *const ThreadContext, dest: *ThreadContext) void {
 }
 
 pub const UnwindError = if (have_ucontext)
-    @typeInfo(@typeInfo(@TypeOf(StackIterator.next_dwarf)).Fn.return_type.?).ErrorUnion.error_set
+    @typeInfo(@typeInfo(@TypeOf(StackIterator.next_unwind)).Fn.return_type.?).ErrorUnion.error_set
 else
     void;
 
@@ -619,11 +619,21 @@ pub const StackIterator = struct {
         }
     }
 
-    fn next_dwarf(self: *StackIterator) !usize {
+    fn next_unwind(self: *StackIterator) !usize {
         const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
+        switch (native_os) {
+            .macos, .ios, .watchos, .tvos => {
+                const o_file_info = try module.getOFileInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc);
+                if (o_file_info.unwind_info == null) return error.MissingUnwindInfo;
+
+                // TODO: Unwind using __unwind_info,
+                unreachable;
+
+            },
+            else => {},
+        }
+
         if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {
-            self.dwarf_context.reg_context.eh_frame = true;
-            self.dwarf_context.reg_context.is_macho = di.is_macho;
             return di.unwindFrame(&self.dwarf_context, module.base_address);
         } else return error.MissingDebugInfo;
     }
@@ -631,7 +641,7 @@ pub const StackIterator = struct {
     fn next_internal(self: *StackIterator) ?usize {
         if (have_ucontext and self.debug_info != null) {
             if (self.dwarf_context.pc == 0) return null;
-            if (self.next_dwarf()) |return_address| {
+            if (self.next_unwind()) |return_address| {
                 return return_address;
             } else |err| {
                 self.last_error = err;
@@ -1882,6 +1892,7 @@ pub const ModuleDebugInfo = switch (native_os) {
         const OFileInfo = struct {
             di: DW.DwarfInfo,
             addr_table: std.StringHashMap(u64),
+            unwind_info: ?[]const u8,
         };
 
         fn deinit(self: *@This(), allocator: mem.Allocator) void {
@@ -1939,21 +1950,24 @@ pub const ModuleDebugInfo = switch (native_os) {
                 addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value);
             }
 
+            var unwind_info: ?[]const u8 = null;
             var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
             for (segcmd.?.getSections()) |sect| {
-                if (!std.mem.eql(u8, "__DWARF", sect.segName())) continue;
+                if (std.mem.eql(u8, "__TEXT", sect.segName()) and mem.eql(u8, "__unwind_info", sect.sectName())) {
+                    unwind_info = try chopSlice(mapped_mem, sect.offset, sect.size);
+                } else if (std.mem.eql(u8, "__DWARF", sect.segName())) {
+                    var section_index: ?usize = null;
+                    inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
+                        if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i;
+                    }
+                    if (section_index == null) continue;
 
-                var section_index: ?usize = null;
-                inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
-                    if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i;
+                    const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size);
+                    sections[section_index.?] = .{
+                        .data = section_bytes,
+                        .owned = false,
+                    };
                 }
-                if (section_index == null) continue;
-
-                const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size);
-                sections[section_index.?] = .{
-                    .data = section_bytes,
-                    .owned = false,
-                };
             }
 
             const missing_debug_info =
@@ -1973,6 +1987,7 @@ pub const ModuleDebugInfo = switch (native_os) {
             var info = OFileInfo{
                 .di = di,
                 .addr_table = addr_table,
+                .unwind_info = unwind_info,
             };
 
             // Add the debug info to the cache
@@ -2030,7 +2045,7 @@ pub const ModuleDebugInfo = switch (native_os) {
             }
         }
 
-        fn getOFileInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !struct {
+        pub fn getOFileInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !struct {
             relocated_address: usize,
             symbol: ?*const MachoSymbol = null,
             o_file_info: ?*OFileInfo = null,
lib/std/dwarf.zig
@@ -1683,6 +1683,7 @@ pub const DwarfInfo = struct {
 
         context.vm.reset();
         context.reg_context.eh_frame = cie.version != 4;
+        context.reg_context.is_macho = di.is_macho;
 
         _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);
         const row = &context.vm.current_row;
@@ -1791,7 +1792,7 @@ pub const UnwindContext = struct {
         const pc = mem.readIntSliceNative(usize, try abi.regBytes(thread_context, abi.ipRegNum(), null));
 
         const context_copy = try allocator.create(debug.ThreadContext);
-        debug.dupeContext(thread_context, context_copy);
+        debug.copyContext(thread_context, context_copy);
 
         return .{
             .allocator = allocator,