Skip to content

Commit a931bfa

Browse files
authored
Merge pull request #20908 from ziglang/reorg-std.debug-again
std.debug: reorg and clarify API goals
2 parents 9e2668c + 6d606cc commit a931bfa

File tree

11 files changed

+3512
-3428
lines changed

11 files changed

+3512
-3428
lines changed

lib/std/debug.zig

Lines changed: 154 additions & 1515 deletions
Large diffs are not rendered by default.

lib/std/debug/Dwarf.zig

Lines changed: 119 additions & 803 deletions
Large diffs are not rendered by default.

lib/std/debug/Dwarf/abi.zig

Lines changed: 55 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,50 @@
11
const builtin = @import("builtin");
2+
23
const std = @import("../../std.zig");
34
const mem = std.mem;
4-
const native_os = builtin.os.tag;
55
const posix = std.posix;
6+
const Arch = std.Target.Cpu.Arch;
67

8+
/// Tells whether unwinding for this target is supported by the Dwarf standard.
9+
///
10+
/// See also `std.debug.SelfInfo.supportsUnwinding` which tells whether the Zig
11+
/// standard library has a working implementation of unwinding for this target.
712
pub fn supportsUnwinding(target: std.Target) bool {
813
return switch (target.cpu.arch) {
9-
.x86 => switch (target.os.tag) {
10-
.linux, .netbsd, .solaris, .illumos => true,
11-
else => false,
12-
},
13-
.x86_64 => switch (target.os.tag) {
14-
.linux, .netbsd, .freebsd, .openbsd, .macos, .ios, .solaris, .illumos => true,
15-
else => false,
16-
},
17-
.arm => switch (target.os.tag) {
18-
.linux => true,
19-
else => false,
20-
},
21-
.aarch64 => switch (target.os.tag) {
22-
.linux, .netbsd, .freebsd, .macos, .ios => true,
23-
else => false,
24-
},
25-
else => false,
14+
.amdgcn,
15+
.nvptx,
16+
.nvptx64,
17+
.spirv,
18+
.spirv32,
19+
.spirv64,
20+
.spu_2,
21+
=> false,
22+
23+
// Enabling this causes relocation errors such as:
24+
// error: invalid relocation type R_RISCV_SUB32 at offset 0x20
25+
.riscv64, .riscv32 => false,
26+
27+
// Conservative guess. Feel free to update this logic with any targets
28+
// that are known to not support Dwarf unwinding.
29+
else => true,
2630
};
2731
}
2832

29-
pub fn ipRegNum() u8 {
30-
return switch (builtin.cpu.arch) {
33+
/// Returns `null` for CPU architectures without an instruction pointer register.
34+
pub fn ipRegNum(arch: Arch) ?u8 {
35+
return switch (arch) {
3136
.x86 => 8,
3237
.x86_64 => 16,
3338
.arm => 15,
3439
.aarch64 => 32,
35-
else => unreachable,
40+
else => null,
3641
};
3742
}
3843

39-
pub fn fpRegNum(reg_context: RegisterContext) u8 {
40-
return switch (builtin.cpu.arch) {
41-
// GCC on OS X historically did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
44+
pub fn fpRegNum(arch: Arch, reg_context: RegisterContext) u8 {
45+
return switch (arch) {
46+
// GCC on OS X historically did the opposite of ELF for these registers
47+
// (only in .eh_frame), and that is now the convention for MachO
4248
.x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
4349
.x86_64 => 6,
4450
.arm => 11,
@@ -47,8 +53,8 @@ pub fn fpRegNum(reg_context: RegisterContext) u8 {
4753
};
4854
}
4955

50-
pub fn spRegNum(reg_context: RegisterContext) u8 {
51-
return switch (builtin.cpu.arch) {
56+
pub fn spRegNum(arch: Arch, reg_context: RegisterContext) u8 {
57+
return switch (arch) {
5258
.x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
5359
.x86_64 => 7,
5460
.arm => 13,
@@ -57,89 +63,34 @@ pub fn spRegNum(reg_context: RegisterContext) u8 {
5763
};
5864
}
5965

60-
/// Some platforms use pointer authentication - the upper bits of instruction pointers contain a signature.
61-
/// This function clears these signature bits to make the pointer usable.
62-
pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
63-
if (builtin.cpu.arch == .aarch64) {
64-
// `hint 0x07` maps to `xpaclri` (or `nop` if the hardware doesn't support it)
65-
// The save / restore is because `xpaclri` operates on x30 (LR)
66-
return asm (
67-
\\mov x16, x30
68-
\\mov x30, x15
69-
\\hint 0x07
70-
\\mov x15, x30
71-
\\mov x30, x16
72-
: [ret] "={x15}" (-> usize),
73-
: [ptr] "{x15}" (ptr),
74-
: "x16"
75-
);
76-
}
77-
78-
return ptr;
79-
}
80-
8166
pub const RegisterContext = struct {
8267
eh_frame: bool,
8368
is_macho: bool,
8469
};
8570

86-
pub const AbiError = error{
71+
pub const RegBytesError = error{
8772
InvalidRegister,
8873
UnimplementedArch,
8974
UnimplementedOs,
9075
RegisterContextRequired,
9176
ThreadContextNotSupported,
9277
};
9378

94-
fn RegValueReturnType(comptime ContextPtrType: type, comptime T: type) type {
95-
const reg_bytes_type = comptime RegBytesReturnType(ContextPtrType);
96-
const info = @typeInfo(reg_bytes_type).Pointer;
97-
return @Type(.{
98-
.Pointer = .{
99-
.size = .One,
100-
.is_const = info.is_const,
101-
.is_volatile = info.is_volatile,
102-
.is_allowzero = info.is_allowzero,
103-
.alignment = info.alignment,
104-
.address_space = info.address_space,
105-
.child = T,
106-
.sentinel = null,
107-
},
108-
});
109-
}
110-
111-
/// Returns a pointer to a register stored in a ThreadContext, preserving the pointer attributes of the context.
112-
pub fn regValueNative(
113-
comptime T: type,
114-
thread_context_ptr: anytype,
115-
reg_number: u8,
116-
reg_context: ?RegisterContext,
117-
) !RegValueReturnType(@TypeOf(thread_context_ptr), T) {
118-
const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
119-
if (@sizeOf(T) != reg_bytes.len) return error.IncompatibleRegisterSize;
120-
return mem.bytesAsValue(T, reg_bytes[0..@sizeOf(T)]);
121-
}
122-
123-
fn RegBytesReturnType(comptime ContextPtrType: type) type {
124-
const info = @typeInfo(ContextPtrType);
125-
if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
126-
@compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
127-
}
128-
129-
return if (info.Pointer.is_const) return []const u8 else []u8;
130-
}
131-
13279
/// Returns a slice containing the backing storage for `reg_number`.
13380
///
81+
/// This function assumes the Dwarf information corresponds not necessarily to
82+
/// the current executable, but at least with a matching CPU architecture and
83+
/// OS. It is planned to lift this limitation with a future enhancement.
84+
///
13485
/// `reg_context` describes in what context the register number is used, as it can have different
13586
/// meanings depending on the DWARF container. It is only required when getting the stack or
13687
/// frame pointer register on some architectures.
13788
pub fn regBytes(
138-
thread_context_ptr: anytype,
89+
thread_context_ptr: *std.debug.ThreadContext,
13990
reg_number: u8,
14091
reg_context: ?RegisterContext,
141-
) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
142-
if (native_os == .windows) {
92+
) RegBytesError![]u8 {
93+
if (builtin.os.tag == .windows) {
14394
return switch (builtin.cpu.arch) {
14495
.x86 => switch (reg_number) {
14596
0 => mem.asBytes(&thread_context_ptr.Eax),
@@ -194,7 +145,7 @@ pub fn regBytes(
194145

195146
const ucontext_ptr = thread_context_ptr;
196147
return switch (builtin.cpu.arch) {
197-
.x86 => switch (native_os) {
148+
.x86 => switch (builtin.os.tag) {
198149
.linux, .netbsd, .solaris, .illumos => switch (reg_number) {
199150
0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
200151
1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
@@ -229,7 +180,7 @@ pub fn regBytes(
229180
},
230181
else => error.UnimplementedOs,
231182
},
232-
.x86_64 => switch (native_os) {
183+
.x86_64 => switch (builtin.os.tag) {
233184
.linux, .solaris, .illumos => switch (reg_number) {
234185
0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
235186
1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
@@ -248,7 +199,7 @@ pub fn regBytes(
248199
14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
249200
15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
250201
16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),
251-
17...32 => |i| if (native_os.isSolarish())
202+
17...32 => |i| if (builtin.os.tag.isSolarish())
252203
mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
253204
else
254205
mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
@@ -318,7 +269,7 @@ pub fn regBytes(
318269
},
319270
else => error.UnimplementedOs,
320271
},
321-
.arm => switch (native_os) {
272+
.arm => switch (builtin.os.tag) {
322273
.linux => switch (reg_number) {
323274
0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
324275
1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
@@ -341,7 +292,7 @@ pub fn regBytes(
341292
},
342293
else => error.UnimplementedOs,
343294
},
344-
.aarch64 => switch (native_os) {
295+
.aarch64 => switch (builtin.os.tag) {
345296
.macos, .ios => switch (reg_number) {
346297
0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
347298
29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
@@ -389,22 +340,14 @@ pub fn regBytes(
389340
};
390341
}
391342

392-
/// Returns the ABI-defined default value this register has in the unwinding table
393-
/// before running any of the CIE instructions. The DWARF spec defines these as having
394-
/// the .undefined rule by default, but allows ABI authors to override that.
395-
pub fn getRegDefaultValue(reg_number: u8, context: *std.debug.Dwarf.UnwindContext, out: []u8) !void {
396-
switch (builtin.cpu.arch) {
397-
.aarch64 => {
398-
// Callee-saved registers are initialized as if they had the .same_value rule
399-
if (reg_number >= 19 and reg_number <= 28) {
400-
const src = try regBytes(context.thread_context, reg_number, context.reg_context);
401-
if (src.len != out.len) return error.RegisterSizeMismatch;
402-
@memcpy(out, src);
403-
return;
404-
}
405-
},
406-
else => {},
407-
}
408-
409-
@memset(out, undefined);
343+
/// Returns a pointer to a register stored in a ThreadContext, preserving the
344+
/// pointer attributes of the context.
345+
pub fn regValueNative(
346+
thread_context_ptr: *std.debug.ThreadContext,
347+
reg_number: u8,
348+
reg_context: ?RegisterContext,
349+
) !*align(1) usize {
350+
const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
351+
if (@sizeOf(usize) != reg_bytes.len) return error.IncompatibleRegisterSize;
352+
return mem.bytesAsValue(usize, reg_bytes[0..@sizeOf(usize)]);
410353
}

0 commit comments

Comments
 (0)