11const  builtin  =  @import ("builtin" );
2+ 
23const  std  =  @import ("../../std.zig" );
34const  mem  =  std .mem ;
4- const  native_os  =  builtin .os .tag ;
55const  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. 
712pub  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- 
8166pub  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. 
13788pub  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