| ... | @@ -243,6 +243,15 @@ pub const DwarfUnwindContext = struct { | ... | @@ -243,6 +243,15 @@ pub const DwarfUnwindContext = struct { |
| 243 | return ptr.*; | 243 | return ptr.*; |
| 244 | } | 244 | } |
| 245 | | 245 | |
| | 246 | /// The default rule is typically equivalent to `.undefined`, but ABIs may define it differently. |
| | 247 | fn defaultRuleBehavior(register: u8) enum { undefined, same_value } { |
| | 248 | if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 28) { |
| | 249 | // The default rule for callee-saved registers on AArch64 acts like the `.same_value` rule |
| | 250 | return .same_value; |
| | 251 | } |
| | 252 | return .undefined; |
| | 253 | } |
| | 254 | |
| 246 | /// Resolves the register rule and places the result into `out` (see regBytes). Returns `true` | 255 | /// Resolves the register rule and places the result into `out` (see regBytes). Returns `true` |
| 247 | /// iff the rule was undefined. This is *not* the same as `col.rule == .undefined`, because the | 256 | /// iff the rule was undefined. This is *not* the same as `col.rule == .undefined`, because the |
| 248 | /// default rule may be undefined. | 257 | /// default rule may be undefined. |
| ... | @@ -256,17 +265,18 @@ pub const DwarfUnwindContext = struct { | ... | @@ -256,17 +265,18 @@ pub const DwarfUnwindContext = struct { |
| 256 | switch (col.rule) { | 265 | switch (col.rule) { |
| 257 | .default => { | 266 | .default => { |
| 258 | const register = col.register orelse return error.InvalidRegister; | 267 | const register = col.register orelse return error.InvalidRegister; |
| 259 | // The default type is usually undefined, but can be overriden by ABI authors. | 268 | switch (defaultRuleBehavior(register)) { |
| 260 | // See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`. | 269 | .undefined => { |
| 261 | if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 28) { | 270 | @memset(out, undefined); |
| 262 | // Callee-saved registers are initialized as if they had the .same_value rule | 271 | return true; |
| 263 | const src = try context.cpu_context.dwarfRegisterBytes(register); | 272 | }, |
| 264 | if (src.len != out.len) return error.RegisterSizeMismatch; | 273 | .same_value => { |
| 265 | @memcpy(out, src); | 274 | const src = try context.cpu_context.dwarfRegisterBytes(register); |
| 266 | return false; | 275 | if (src.len != out.len) return error.RegisterSizeMismatch; |
| | 276 | @memcpy(out, src); |
| | 277 | return false; |
| | 278 | }, |
| 267 | } | 279 | } |
| 268 | @memset(out, undefined); | | |
| 269 | return true; | | |
| 270 | }, | 280 | }, |
| 271 | .undefined => { | 281 | .undefined => { |
| 272 | @memset(out, undefined); | 282 | @memset(out, undefined); |
| ... | @@ -449,7 +459,9 @@ pub const DwarfUnwindContext = struct { | ... | @@ -449,7 +459,9 @@ pub const DwarfUnwindContext = struct { |
| 449 | | 459 | |
| 450 | expression_context.cfa = context.cfa; | 460 | expression_context.cfa = context.cfa; |
| 451 | | 461 | |
| 452 | var has_return_address = true; | 462 | // If the rule for the return address register is 'undefined', that indicates there is no |
| | 463 | // return address, i.e. this is the end of the stack. |
| | 464 | var explicit_has_return_address: ?bool = null; |
| 453 | | 465 | |
| 454 | // Create a copy of the CPU context, to which we will apply the new rules. | 466 | // Create a copy of the CPU context, to which we will apply the new rules. |
| 455 | var new_cpu_context = context.cpu_context; | 467 | var new_cpu_context = context.cpu_context; |
| ... | @@ -462,11 +474,18 @@ pub const DwarfUnwindContext = struct { | ... | @@ -462,11 +474,18 @@ pub const DwarfUnwindContext = struct { |
| 462 | const dest = try new_cpu_context.dwarfRegisterBytes(register); | 474 | const dest = try new_cpu_context.dwarfRegisterBytes(register); |
| 463 | const rule_undef = try context.resolveRegisterRule(gpa, column, expression_context, dest); | 475 | const rule_undef = try context.resolveRegisterRule(gpa, column, expression_context, dest); |
| 464 | if (register == cie.return_address_register) { | 476 | if (register == cie.return_address_register) { |
| 465 | has_return_address = !rule_undef; | 477 | explicit_has_return_address = !rule_undef; |
| 466 | } | 478 | } |
| 467 | } | 479 | } |
| 468 | } | 480 | } |
| 469 | | 481 | |
| | 482 | // If the return address register did not have an explicitly specified rules then it uses |
| | 483 | // the default rule, which is usually equivalent to '.undefined', i.e. end-of-stack. |
| | 484 | const has_return_address = explicit_has_return_address orelse switch (defaultRuleBehavior(cie.return_address_register)) { |
| | 485 | .undefined => false, |
| | 486 | .same_value => return error.InvalidDebugInfo, // this doesn't make sense, we would get stuck in an infinite loop |
| | 487 | }; |
| | 488 | |
| 470 | const return_address: usize = if (has_return_address) pc: { | 489 | const return_address: usize = if (has_return_address) pc: { |
| 471 | const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register); | 490 | const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register); |
| 472 | break :pc stripInstructionPtrAuthCode(raw_ptr.*); | 491 | break :pc stripInstructionPtrAuthCode(raw_ptr.*); |