authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-10-09 23:48:47+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-10-10 15:12:27+02:00
log9aeabad519159a89220e475a6a544cd7d3a5db37
tree7ee4c18a00f8adfdc3a6d6666308e99e5e990c8a
parentf010a3131976b2e85aadccaaeda5c03d2288849f

std.debug.Dwarf.SelfUnwinder: assume same-value rule by default for all columns

This fixes leaf function unwinding, presumably among other things.

1 files changed, 21 insertions(+), 26 deletions(-)

lib/std/debug/Dwarf/SelfUnwinder.zig+21-26
...@@ -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
175175
176fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheEntry) !usize {176fn 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;
179178
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 };
204203
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;
218206
219 // On all implemented architectures, the CFA is defined to be the previous frame's SP207 // 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;
221209
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 }
293288
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;
298293
299 (try regNative(&new_cpu_state, ip_reg_num)).* = return_address;294 (try regNative(&new_cpu_state, ip_reg_num)).* = return_address;
300295