| ... | @@ -175,7 +175,6 @@ pub fn next(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheEn | ... | @@ -175,7 +175,6 @@ pub fn next(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheEn |
| 175 | | 175 | |
| 176 | fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheEntry) !usize { | 176 | fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheEntry) !usize { |
| 177 | const format = cache_entry.cie.format; | 177 | const format = cache_entry.cie.format; |
| 178 | const return_address_register = cache_entry.cie.return_address_register; | | |
| 179 | | 178 | |
| 180 | const cfa = switch (cache_entry.cfa_rule) { | 179 | const cfa = switch (cache_entry.cfa_rule) { |
| 181 | .none => return error.InvalidDebugInfo, | 180 | .none => return error.InvalidDebugInfo, |
| ... | @@ -202,23 +201,15 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE | ... | @@ -202,23 +201,15 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE |
| 202 | }, | 201 | }, |
| 203 | }; | 202 | }; |
| 204 | | 203 | |
| 205 | // If unspecified, we'll use the default rule for the return address register, which is | | |
| 206 | // typically equivalent to `.undefined` (meaning there is no return address), but may be | | |
| 207 | // overriden by ABIs. | | |
| 208 | var has_return_address: bool = switch (builtin.cpu.arch) { | | |
| 209 | // DWARF for the Arm 64-bit Architecture (AArch64) §4.3, p1 | | |
| 210 | .aarch64, .aarch64_be => return_address_register >= 19 and return_address_register <= 28, | | |
| 211 | // ELF ABI s390x Supplement §1.6.4 | | |
| 212 | .s390x => return_address_register >= 6 and return_address_register <= 15, | | |
| 213 | else => false, | | |
| 214 | }; | | |
| 215 | | | |
| 216 | // Create a copy of the CPU state, to which we will apply the new rules. | 204 | // Create a copy of the CPU state, to which we will apply the new rules. |
| 217 | var new_cpu_state = unwinder.cpu_state; | 205 | var new_cpu_state = unwinder.cpu_state; |
| 218 | | 206 | |
| 219 | // On all implemented architectures, the CFA is defined to be the previous frame's SP | 207 | // On all implemented architectures, the CFA is defined to be the previous frame's SP |
| 220 | (try regNative(&new_cpu_state, sp_reg_num)).* = cfa; | 208 | (try regNative(&new_cpu_state, sp_reg_num)).* = cfa; |
| 221 | | 209 | |
| | 210 | const return_address_register = cache_entry.cie.return_address_register; |
| | 211 | var has_return_address = true; |
| | 212 | |
| 222 | const rules_len = cache_entry.num_rules; | 213 | const rules_len = cache_entry.num_rules; |
| 223 | for (cache_entry.rules_regs[0..rules_len], cache_entry.rules[0..rules_len]) |register, rule| { | 214 | for (cache_entry.rules_regs[0..rules_len], cache_entry.rules[0..rules_len]) |register, rule| { |
| 224 | const new_val: union(enum) { | 215 | const new_val: union(enum) { |
| ... | @@ -228,13 +219,14 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE | ... | @@ -228,13 +219,14 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE |
| 228 | bytes: []const u8, | 219 | bytes: []const u8, |
| 229 | } = switch (rule) { | 220 | } = switch (rule) { |
| 230 | .default => val: { | 221 | .default => val: { |
| 231 | // The default rule is typically equivalent to `.undefined`, but ABIs may override it. | 222 | // The way things are supposed to work is that `.undefined` is the default rule |
| 232 | switch (builtin.target.cpu.arch) { | 223 | // unless an ABI says otherwise (e.g. aarch64, s390x). |
| 233 | .aarch64, .aarch64_be => if (register >= 19 and register <= 28) break :val .same, | 224 | // |
| 234 | .s390x => if (register >= 6 and register <= 15) break :val .same, | 225 | // Unfortunately, at some point, a decision was made to have libgcc's unwinder |
| 235 | else => {}, | 226 | // assume `.same` as the default for all registers. Compilers then started depending |
| 236 | } | 227 | // on this, and the practice was carried forward to LLVM's libunwind and some of its |
| 237 | break :val .undefined; | 228 | // backends. |
| | 229 | break :val .same; |
| 238 | }, | 230 | }, |
| 239 | .undefined => .undefined, | 231 | .undefined => .undefined, |
| 240 | .same_value => .same, | 232 | .same_value => .same, |
| ... | @@ -273,6 +265,12 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE | ... | @@ -273,6 +265,12 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE |
| 273 | .undefined => { | 265 | .undefined => { |
| 274 | const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register)); | 266 | const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register)); |
| 275 | @memset(dest, undefined); | 267 | @memset(dest, undefined); |
| | 268 | |
| | 269 | // If the return address register is explicitly set to `.undefined`, it means that |
| | 270 | // there are no more frames to unwind. |
| | 271 | if (register == return_address_register) { |
| | 272 | has_return_address = false; |
| | 273 | } |
| 276 | }, | 274 | }, |
| 277 | .val => |val| { | 275 | .val => |val| { |
| 278 | const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register)); | 276 | const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register)); |
| ... | @@ -286,15 +284,12 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE | ... | @@ -286,15 +284,12 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE |
| 286 | @memcpy(dest, src); | 284 | @memcpy(dest, src); |
| 287 | }, | 285 | }, |
| 288 | } | 286 | } |
| 289 | if (register == return_address_register) { | | |
| 290 | has_return_address = new_val != .undefined; | | |
| 291 | } | | |
| 292 | } | 287 | } |
| 293 | | 288 | |
| 294 | const return_address: usize = if (has_return_address) pc: { | 289 | const return_address = if (has_return_address) |
| 295 | const raw_ptr = try regNative(&new_cpu_state, return_address_register); | 290 | stripInstructionPtrAuthCode((try regNative(&new_cpu_state, return_address_register)).*) |
| 296 | break :pc stripInstructionPtrAuthCode(raw_ptr.*); | 291 | else |
| 297 | } else 0; | 292 | 0; |
| 298 | | 293 | |
| 299 | (try regNative(&new_cpu_state, ip_reg_num)).* = return_address; | 294 | (try regNative(&new_cpu_state, ip_reg_num)).* = return_address; |
| 300 | | 295 | |