| ... | ... | @@ -175,7 +175,6 @@ pub fn next(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheEn |
| 175 | 175 | |
| 176 | 176 | fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheEntry) !usize { |
| 177 | 177 | const format = cache_entry.cie.format; |
| 178 | | const return_address_register = cache_entry.cie.return_address_register; |
| 179 | 178 | |
| 180 | 179 | const cfa = switch (cache_entry.cfa_rule) { |
| 181 | 180 | .none => return error.InvalidDebugInfo, |
| ... | ... | @@ -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 | 204 | // Create a copy of the CPU state, to which we will apply the new rules. |
| 217 | 205 | var new_cpu_state = unwinder.cpu_state; |
| 218 | 206 | |
| 219 | 207 | // On all implemented architectures, the CFA is defined to be the previous frame's SP |
| 220 | 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 | 213 | const rules_len = cache_entry.num_rules; |
| 223 | 214 | for (cache_entry.rules_regs[0..rules_len], cache_entry.rules[0..rules_len]) |register, rule| { |
| 224 | 215 | const new_val: union(enum) { |
| ... | ... | @@ -228,13 +219,14 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE |
| 228 | 219 | bytes: []const u8, |
| 229 | 220 | } = switch (rule) { |
| 230 | 221 | .default => val: { |
| 231 | | // The default rule is typically equivalent to `.undefined`, but ABIs may override it. |
| 232 | | switch (builtin.target.cpu.arch) { |
| 233 | | .aarch64, .aarch64_be => if (register >= 19 and register <= 28) break :val .same, |
| 234 | | .s390x => if (register >= 6 and register <= 15) break :val .same, |
| 235 | | else => {}, |
| 236 | | } |
| 237 | | break :val .undefined; |
| 222 | // The way things are supposed to work is that `.undefined` is the default rule |
| 223 | // unless an ABI says otherwise (e.g. aarch64, s390x). |
| 224 | // |
| 225 | // Unfortunately, at some point, a decision was made to have libgcc's unwinder |
| 226 | // assume `.same` as the default for all registers. Compilers then started depending |
| 227 | // on this, and the practice was carried forward to LLVM's libunwind and some of its |
| 228 | // backends. |
| 229 | break :val .same; |
| 238 | 230 | }, |
| 239 | 231 | .undefined => .undefined, |
| 240 | 232 | .same_value => .same, |
| ... | ... | @@ -273,6 +265,12 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE |
| 273 | 265 | .undefined => { |
| 274 | 266 | const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register)); |
| 275 | 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 | 275 | .val => |val| { |
| 278 | 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 | 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: { |
| 295 | | const raw_ptr = try regNative(&new_cpu_state, return_address_register); |
| 296 | | break :pc stripInstructionPtrAuthCode(raw_ptr.*); |
| 297 | | } else 0; |
| 289 | const return_address = if (has_return_address) |
| 290 | stripInstructionPtrAuthCode((try regNative(&new_cpu_state, return_address_register)).*) |
| 291 | else |
| 292 | 0; |
| 298 | 293 | |
| 299 | 294 | (try regNative(&new_cpu_state, ip_reg_num)).* = return_address; |
| 300 | 295 | |