From 21d0154139fbbbb218dc7734ba0cd44b5baf2876 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Sat, 8 Jul 2023 01:16:11 -0400 Subject: [PATCH] dwarf: skip register tests on unimplemented arch / os, add tests for type convesions debug: dupeContext -> copyContext --- lib/std/debug.zig | 53 ++++++++------ lib/std/dwarf.zig | 3 +- lib/std/dwarf/expressions.zig | 125 ++++++++++++++++++++++++++++------ 3 files changed, 139 insertions(+), 42 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index c60b9f8776..1907fdc4a1 100644 --- a/lib/std/debug.zig +++ b/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, diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 44c9a90e1b..c820814031 100644 --- a/lib/std/dwarf.zig +++ b/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, diff --git a/lib/std/dwarf/expressions.zig b/lib/std/dwarf/expressions.zig index 5b0e97cf45..38a70e3dbd 100644 --- a/lib/std/dwarf/expressions.zig +++ b/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); + + + + + } + }