mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
std.debug: don't assume return address register is defined if not specified
This logic was causing some occasional infinite looping on ARM, where the `.debug_frame` section is often incomplete since the `.exidx` section is used for unwind information. But the information we're getting from the compiler is totally *valid*: it's leaving the rule as the default, which is (as with most architectures) equivalent to `.undefined`!
This commit is contained in:
parent
099a950410
commit
c41bf99684
@ -243,6 +243,15 @@ pub const DwarfUnwindContext = struct {
|
|||||||
return ptr.*;
|
return ptr.*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The default rule is typically equivalent to `.undefined`, but ABIs may define it differently.
|
||||||
|
fn defaultRuleBehavior(register: u8) enum { undefined, same_value } {
|
||||||
|
if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 28) {
|
||||||
|
// The default rule for callee-saved registers on AArch64 acts like the `.same_value` rule
|
||||||
|
return .same_value;
|
||||||
|
}
|
||||||
|
return .undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolves the register rule and places the result into `out` (see regBytes). Returns `true`
|
/// Resolves the register rule and places the result into `out` (see regBytes). Returns `true`
|
||||||
/// iff the rule was undefined. This is *not* the same as `col.rule == .undefined`, because the
|
/// iff the rule was undefined. This is *not* the same as `col.rule == .undefined`, because the
|
||||||
/// default rule may be undefined.
|
/// default rule may be undefined.
|
||||||
@ -256,17 +265,18 @@ pub const DwarfUnwindContext = struct {
|
|||||||
switch (col.rule) {
|
switch (col.rule) {
|
||||||
.default => {
|
.default => {
|
||||||
const register = col.register orelse return error.InvalidRegister;
|
const register = col.register orelse return error.InvalidRegister;
|
||||||
// The default type is usually undefined, but can be overriden by ABI authors.
|
switch (defaultRuleBehavior(register)) {
|
||||||
// See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`.
|
.undefined => {
|
||||||
if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 28) {
|
@memset(out, undefined);
|
||||||
// Callee-saved registers are initialized as if they had the .same_value rule
|
return true;
|
||||||
|
},
|
||||||
|
.same_value => {
|
||||||
const src = try context.cpu_context.dwarfRegisterBytes(register);
|
const src = try context.cpu_context.dwarfRegisterBytes(register);
|
||||||
if (src.len != out.len) return error.RegisterSizeMismatch;
|
if (src.len != out.len) return error.RegisterSizeMismatch;
|
||||||
@memcpy(out, src);
|
@memcpy(out, src);
|
||||||
return false;
|
return false;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@memset(out, undefined);
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
.undefined => {
|
.undefined => {
|
||||||
@memset(out, undefined);
|
@memset(out, undefined);
|
||||||
@ -449,7 +459,9 @@ pub const DwarfUnwindContext = struct {
|
|||||||
|
|
||||||
expression_context.cfa = context.cfa;
|
expression_context.cfa = context.cfa;
|
||||||
|
|
||||||
var has_return_address = true;
|
// If the rule for the return address register is 'undefined', that indicates there is no
|
||||||
|
// return address, i.e. this is the end of the stack.
|
||||||
|
var explicit_has_return_address: ?bool = null;
|
||||||
|
|
||||||
// Create a copy of the CPU context, to which we will apply the new rules.
|
// Create a copy of the CPU context, to which we will apply the new rules.
|
||||||
var new_cpu_context = context.cpu_context;
|
var new_cpu_context = context.cpu_context;
|
||||||
@ -462,11 +474,18 @@ pub const DwarfUnwindContext = struct {
|
|||||||
const dest = try new_cpu_context.dwarfRegisterBytes(register);
|
const dest = try new_cpu_context.dwarfRegisterBytes(register);
|
||||||
const rule_undef = try context.resolveRegisterRule(gpa, column, expression_context, dest);
|
const rule_undef = try context.resolveRegisterRule(gpa, column, expression_context, dest);
|
||||||
if (register == cie.return_address_register) {
|
if (register == cie.return_address_register) {
|
||||||
has_return_address = !rule_undef;
|
explicit_has_return_address = !rule_undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the return address register did not have an explicitly specified rules then it uses
|
||||||
|
// the default rule, which is usually equivalent to '.undefined', i.e. end-of-stack.
|
||||||
|
const has_return_address = explicit_has_return_address orelse switch (defaultRuleBehavior(cie.return_address_register)) {
|
||||||
|
.undefined => false,
|
||||||
|
.same_value => return error.InvalidDebugInfo, // this doesn't make sense, we would get stuck in an infinite loop
|
||||||
|
};
|
||||||
|
|
||||||
const return_address: usize = if (has_return_address) pc: {
|
const return_address: usize = if (has_return_address) pc: {
|
||||||
const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register);
|
const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register);
|
||||||
break :pc stripInstructionPtrAuthCode(raw_ptr.*);
|
break :pc stripInstructionPtrAuthCode(raw_ptr.*);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user