1
1
const builtin = @import ("builtin" );
2
+
2
3
const std = @import ("../../std.zig" );
3
4
const mem = std .mem ;
4
- const native_os = builtin .os .tag ;
5
5
const posix = std .posix ;
6
+ const Arch = std .Target .Cpu .Arch ;
6
7
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.
7
12
pub fn supportsUnwinding (target : std.Target ) bool {
8
13
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 ,
26
30
};
27
31
}
28
32
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 ) {
31
36
.x86 = > 8 ,
32
37
.x86_64 = > 16 ,
33
38
.arm = > 15 ,
34
39
.aarch64 = > 32 ,
35
- else = > unreachable ,
40
+ else = > null ,
36
41
};
37
42
}
38
43
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
42
48
.x86 = > if (reg_context .eh_frame and reg_context .is_macho ) 4 else 5 ,
43
49
.x86_64 = > 6 ,
44
50
.arm = > 11 ,
@@ -47,8 +53,8 @@ pub fn fpRegNum(reg_context: RegisterContext) u8 {
47
53
};
48
54
}
49
55
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 ) {
52
58
.x86 = > if (reg_context .eh_frame and reg_context .is_macho ) 5 else 4 ,
53
59
.x86_64 = > 7 ,
54
60
.arm = > 13 ,
@@ -57,89 +63,34 @@ pub fn spRegNum(reg_context: RegisterContext) u8 {
57
63
};
58
64
}
59
65
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
-
81
66
pub const RegisterContext = struct {
82
67
eh_frame : bool ,
83
68
is_macho : bool ,
84
69
};
85
70
86
- pub const AbiError = error {
71
+ pub const RegBytesError = error {
87
72
InvalidRegister ,
88
73
UnimplementedArch ,
89
74
UnimplementedOs ,
90
75
RegisterContextRequired ,
91
76
ThreadContextNotSupported ,
92
77
};
93
78
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
-
132
79
/// Returns a slice containing the backing storage for `reg_number`.
133
80
///
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
+ ///
134
85
/// `reg_context` describes in what context the register number is used, as it can have different
135
86
/// meanings depending on the DWARF container. It is only required when getting the stack or
136
87
/// frame pointer register on some architectures.
137
88
pub fn regBytes (
138
- thread_context_ptr : anytype ,
89
+ thread_context_ptr : * std.debug.ThreadContext ,
139
90
reg_number : u8 ,
140
91
reg_context : ? RegisterContext ,
141
- ) AbiError ! RegBytesReturnType ( @TypeOf ( thread_context_ptr )) {
142
- if (native_os == .windows ) {
92
+ ) RegBytesError ! [] u8 {
93
+ if (builtin . os . tag == .windows ) {
143
94
return switch (builtin .cpu .arch ) {
144
95
.x86 = > switch (reg_number ) {
145
96
0 = > mem .asBytes (& thread_context_ptr .Eax ),
@@ -194,7 +145,7 @@ pub fn regBytes(
194
145
195
146
const ucontext_ptr = thread_context_ptr ;
196
147
return switch (builtin .cpu .arch ) {
197
- .x86 = > switch (native_os ) {
148
+ .x86 = > switch (builtin . os . tag ) {
198
149
.linux , .netbsd , .solaris , .illumos = > switch (reg_number ) {
199
150
0 = > mem .asBytes (& ucontext_ptr .mcontext .gregs [posix .REG .EAX ]),
200
151
1 = > mem .asBytes (& ucontext_ptr .mcontext .gregs [posix .REG .ECX ]),
@@ -229,7 +180,7 @@ pub fn regBytes(
229
180
},
230
181
else = > error .UnimplementedOs ,
231
182
},
232
- .x86_64 = > switch (native_os ) {
183
+ .x86_64 = > switch (builtin . os . tag ) {
233
184
.linux , .solaris , .illumos = > switch (reg_number ) {
234
185
0 = > mem .asBytes (& ucontext_ptr .mcontext .gregs [posix .REG .RAX ]),
235
186
1 = > mem .asBytes (& ucontext_ptr .mcontext .gregs [posix .REG .RDX ]),
@@ -248,7 +199,7 @@ pub fn regBytes(
248
199
14 = > mem .asBytes (& ucontext_ptr .mcontext .gregs [posix .REG .R14 ]),
249
200
15 = > mem .asBytes (& ucontext_ptr .mcontext .gregs [posix .REG .R15 ]),
250
201
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 ())
252
203
mem .asBytes (& ucontext_ptr .mcontext .fpregs .chip_state .xmm [i - 17 ])
253
204
else
254
205
mem .asBytes (& ucontext_ptr .mcontext .fpregs .xmm [i - 17 ]),
@@ -318,7 +269,7 @@ pub fn regBytes(
318
269
},
319
270
else = > error .UnimplementedOs ,
320
271
},
321
- .arm = > switch (native_os ) {
272
+ .arm = > switch (builtin . os . tag ) {
322
273
.linux = > switch (reg_number ) {
323
274
0 = > mem .asBytes (& ucontext_ptr .mcontext .arm_r0 ),
324
275
1 = > mem .asBytes (& ucontext_ptr .mcontext .arm_r1 ),
@@ -341,7 +292,7 @@ pub fn regBytes(
341
292
},
342
293
else = > error .UnimplementedOs ,
343
294
},
344
- .aarch64 = > switch (native_os ) {
295
+ .aarch64 = > switch (builtin . os . tag ) {
345
296
.macos , .ios = > switch (reg_number ) {
346
297
0... 28 = > mem .asBytes (& ucontext_ptr .mcontext .ss .regs [reg_number ]),
347
298
29 = > mem .asBytes (& ucontext_ptr .mcontext .ss .fp ),
@@ -389,22 +340,14 @@ pub fn regBytes(
389
340
};
390
341
}
391
342
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 )]);
410
353
}
0 commit comments