authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-06-16 21:24:05+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-06-16 21:24:05+02:00
logb64ce08c993f0871b2f9be7df561da69d9a9037e
tree72cfb05568b47cb540d2e405e4ffcc6d5333d5e7
parentb7a544c527f0d54cf9d044f26daf272e9297a786
parent038698738f3087faa7f581d42624b9b8564ec1e5

Merge pull request '`std.debug.Dwarf`: fix unwinding when address size is smaller than register size' (#35807) from alexrp/zig:std-debug-ilp32 into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35807 Reviewed-by: mlugg <mlugg@noreply.codeberg.org>

4 files changed, 114 insertions(+), 98 deletions(-)

lib/std/debug/Dwarf/SelfUnwinder.zig+8-13
......@@ -168,7 +168,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
168168 .none => return error.InvalidDebugInfo,
169169 .reg_off => |ro| cfa: {
170170 const ptr = try regNative(&unwinder.cpu_state, ro.register);
171 break :cfa try applyOffset(ptr.*, ro.offset);
171 break :cfa try applyOffset(@intCast(ptr.*), ro.offset);
172172 },
173173 .expression => |expr| cfa: {
174174 // On most implemented architectures, the CFA is defined to be the previous frame's SP.
......@@ -181,7 +181,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
181181 const value = try unwinder.expr_vm.run(expr, gpa, .{
182182 .format = format,
183183 .cpu_context = &unwinder.cpu_state,
184 }, prev_cfa_val) orelse return error.InvalidDebugInfo;
184 }, @intCast(prev_cfa_val)) orelse return error.InvalidDebugInfo;
185185 switch (value) {
186186 .generic => |g| break :cfa g,
187187 else => return error.InvalidDebugInfo,
......@@ -203,7 +203,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
203203 const new_val: union(enum) {
204204 same,
205205 undefined,
206 val: usize,
206 val: std.debug.cpu_context.Native.Gpr,
207207 bytes: []const u8,
208208 } = switch (rule) {
209209 .default => val: {
......@@ -219,7 +219,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
219219 .undefined => .undefined,
220220 .same_value => .same,
221221 .offset => |offset| val: {
222 const ptr: *const usize = @ptrFromInt(try applyOffset(cfa, offset));
222 const ptr: *const std.debug.cpu_context.Native.Gpr = @ptrFromInt(try applyOffset(cfa, offset));
223223 break :val .{ .val = ptr.* };
224224 },
225225 .val_offset => |offset| .{ .val = try applyOffset(cfa, offset) },
......@@ -260,12 +260,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
260260 has_return_address = false;
261261 }
262262 },
263 .val => |val| {
264 const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register));
265 if (dest.len != @sizeOf(usize)) return error.InvalidDebugInfo;
266 const dest_ptr: *align(1) usize = @ptrCast(dest);
267 dest_ptr.* = val;
268 },
263 .val => |val| (try regNative(&new_cpu_state, register)).* = val,
269264 .bytes => |src| {
270265 const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register));
271266 if (dest.len != src.len) return error.InvalidDebugInfo;
......@@ -275,7 +270,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
275270 }
276271
277272 const return_address = if (has_return_address)
278 stripInstructionPtrAuthCode((try regNative(&new_cpu_state, return_address_register)).*)
273 stripInstructionPtrAuthCode(@intCast((try regNative(&new_cpu_state, return_address_register)).*))
279274 else
280275 0;
281276
......@@ -303,9 +298,9 @@ pub fn regNative(ctx: *std.debug.cpu_context.Native, num: u16) error{
303298 InvalidRegister,
304299 UnsupportedRegister,
305300 IncompatibleRegisterSize,
306}!*align(1) usize {
301}!*align(1) std.debug.cpu_context.Native.Gpr {
307302 const bytes = try ctx.dwarfRegisterBytes(num);
308 if (bytes.len != @sizeOf(usize)) return error.IncompatibleRegisterSize;
303 if (bytes.len != @sizeOf(std.debug.cpu_context.Native.Gpr)) return error.IncompatibleRegisterSize;
309304 return @ptrCast(bytes);
310305}
311306
lib/std/debug/Dwarf/expression.zig+2-2
......@@ -387,7 +387,7 @@ pub fn StackMachine(comptime options: Options) type {
387387 .regval_type = .{
388388 .type_offset = rt.type_offset,
389389 .type_size = @sizeOf(addr_type),
390 .value = (try regNative(cpu_context, rt.register)).*,
390 .value = @intCast((try regNative(cpu_context, rt.register)).*),
391391 },
392392 });
393393 },
......@@ -738,7 +738,7 @@ pub fn StackMachine(comptime options: Options) type {
738738 var block_stream: std.Io.Reader = .fixed(block);
739739 const register = (try readOperand(&block_stream, block[0], context)).?.register;
740740 const value = (try regNative(cpu_context, register)).*;
741 try self.stack.append(allocator, .{ .generic = value });
741 try self.stack.append(allocator, .{ .generic = @intCast(value) });
742742 } else {
743743 var stack_machine: Self = .{};
744744 defer stack_machine.deinit(allocator);
lib/std/debug/SelfInfo/Elf.zig-10
......@@ -92,16 +92,6 @@ pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
9292}
9393
9494pub const can_unwind: bool = s: {
95 // The DWARF code can't deal with ILP32 ABIs yet: https://github.com/ziglang/zig/issues/25447
96 switch (builtin.target.abi) {
97 .gnuabin32,
98 .muslabin32,
99 .gnux32,
100 .muslx32,
101 => break :s false,
102 else => {},
103 }
104
10595 // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through
10696 // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format.
10797 const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {
lib/std/debug/cpu_context.zig+104-73
......@@ -240,9 +240,11 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native {
240240/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
241241const Aarch64 = extern struct {
242242 /// The numbered general-purpose registers X0 - X30.
243 x: [31]u64,
244 sp: u64,
245 pc: u64,
243 x: [31]Gpr,
244 sp: Gpr,
245 pc: Gpr,
246
247 pub const Gpr = u64;
246248
247249 pub inline fn current() Aarch64 {
248250 var ctx: Aarch64 = undefined;
......@@ -273,10 +275,10 @@ const Aarch64 = extern struct {
273275 return ctx;
274276 }
275277
276 pub fn getFp(ctx: *const Aarch64) u64 {
278 pub fn getFp(ctx: *const Aarch64) usize {
277279 return ctx.x[29];
278280 }
279 pub fn getPc(ctx: *const Aarch64) u64 {
281 pub fn getPc(ctx: *const Aarch64) usize {
280282 return ctx.pc;
281283 }
282284
......@@ -308,8 +310,10 @@ const Aarch64 = extern struct {
308310
309311const Alpha = extern struct {
310312 /// The numbered general-purpose registers R0 - R31.
311 r: [32]u64,
312 pc: u64,
313 r: [32]Gpr,
314 pc: Gpr,
315
316 pub const Gpr = u64;
313317
314318 pub inline fn current() Alpha {
315319 var ctx: Alpha = undefined;
......@@ -355,10 +359,10 @@ const Alpha = extern struct {
355359 return ctx;
356360 }
357361
358 pub fn getFp(ctx: *const Alpha) u64 {
362 pub fn getFp(ctx: *const Alpha) usize {
359363 return ctx.r[15];
360364 }
361 pub fn getPc(ctx: *const Alpha) u64 {
365 pub fn getPc(ctx: *const Alpha) usize {
362366 return ctx.pc;
363367 }
364368
......@@ -378,8 +382,10 @@ const Alpha = extern struct {
378382/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
379383const Arc = extern struct {
380384 /// The numbered general-purpose registers r0 - r31.
381 r: [32]u32,
382 pcl: u32,
385 r: [32]Gpr,
386 pcl: Gpr,
387
388 pub const Gpr = u32;
383389
384390 pub inline fn current() Arc {
385391 var ctx: Arc = undefined;
......@@ -423,10 +429,10 @@ const Arc = extern struct {
423429 return ctx;
424430 }
425431
426 pub fn getFp(ctx: *const Arc) u32 {
432 pub fn getFp(ctx: *const Arc) usize {
427433 return ctx.r[27];
428434 }
429 pub fn getPc(ctx: *const Arc) u32 {
435 pub fn getPc(ctx: *const Arc) usize {
430436 return ctx.pcl;
431437 }
432438
......@@ -446,7 +452,9 @@ const Arc = extern struct {
446452
447453const Arm = struct {
448454 /// The numbered general-purpose registers R0 - R15.
449 r: [16]u32,
455 r: [16]Gpr,
456
457 pub const Gpr = u32;
450458
451459 pub inline fn current() Arm {
452460 var ctx: Arm = undefined;
......@@ -462,10 +470,10 @@ const Arm = struct {
462470 return ctx;
463471 }
464472
465 pub fn getFp(ctx: *const Arm) u32 {
473 pub fn getFp(ctx: *const Arm) usize {
466474 return ctx.r[11];
467475 }
468 pub fn getPc(ctx: *const Arm) u32 {
476 pub fn getPc(ctx: *const Arm) usize {
469477 return ctx.r[15];
470478 }
471479
......@@ -512,8 +520,10 @@ const Arm = struct {
512520/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
513521const Csky = extern struct {
514522 /// The numbered general-purpose registers r0 - r31.
515 r: [32]u32,
516 pc: u32,
523 r: [32]Gpr,
524 pc: Gpr,
525
526 pub const Gpr = u32;
517527
518528 pub inline fn current() Csky {
519529 var ctx: Csky = undefined;
......@@ -528,10 +538,10 @@ const Csky = extern struct {
528538 return ctx;
529539 }
530540
531 pub fn getFp(ctx: *const Csky) u32 {
541 pub fn getFp(ctx: *const Csky) usize {
532542 return ctx.r[14];
533543 }
534 pub fn getPc(ctx: *const Csky) u32 {
544 pub fn getPc(ctx: *const Csky) usize {
535545 return ctx.pc;
536546 }
537547
......@@ -550,8 +560,10 @@ const Csky = extern struct {
550560/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
551561const Hexagon = extern struct {
552562 /// The numbered general-purpose registers r0 - r31.
553 r: [32]u32,
554 pc: u32,
563 r: [32]Gpr,
564 pc: Gpr,
565
566 pub const Gpr = u32;
555567
556568 pub inline fn current() Hexagon {
557569 var ctx: Hexagon = undefined;
......@@ -596,10 +608,10 @@ const Hexagon = extern struct {
596608 return ctx;
597609 }
598610
599 pub fn getFp(ctx: *const Hexagon) u32 {
611 pub fn getFp(ctx: *const Hexagon) usize {
600612 return ctx.r[30];
601613 }
602 pub fn getPc(ctx: *const Hexagon) u32 {
614 pub fn getPc(ctx: *const Hexagon) usize {
603615 return ctx.pc;
604616 }
605617
......@@ -623,9 +635,11 @@ const Hexagon = extern struct {
623635
624636/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
625637const Kvx = extern struct {
626 r: [64]u64,
627 ra: u64,
628 pc: u64,
638 r: [64]Gpr,
639 ra: Gpr,
640 pc: Gpr,
641
642 pub const Gpr = u64;
629643
630644 pub inline fn current() Kvx {
631645 var ctx: Kvx = undefined;
......@@ -671,10 +685,10 @@ const Kvx = extern struct {
671685 return ctx;
672686 }
673687
674 pub fn getFp(ctx: *const Kvx) u64 {
688 pub fn getFp(ctx: *const Kvx) usize {
675689 return ctx.r[14];
676690 }
677 pub fn getPc(ctx: *const Kvx) u64 {
691 pub fn getPc(ctx: *const Kvx) usize {
678692 return ctx.pc;
679693 }
680694
......@@ -695,7 +709,9 @@ const Kvx = extern struct {
695709
696710/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
697711const Lanai = extern struct {
698 r: [32]u32,
712 r: [32]Gpr,
713
714 pub const Gpr = u32;
699715
700716 pub inline fn current() Lanai {
701717 var ctx: Lanai = undefined;
......@@ -738,10 +754,10 @@ const Lanai = extern struct {
738754 return ctx;
739755 }
740756
741 pub fn getFp(ctx: *const Lanai) u32 {
757 pub fn getFp(ctx: *const Lanai) usize {
742758 return ctx.r[5];
743759 }
744 pub fn getPc(ctx: *const Lanai) u32 {
760 pub fn getPc(ctx: *const Lanai) usize {
745761 return ctx.r[2];
746762 }
747763
......@@ -842,10 +858,10 @@ const LoongArch = extern struct {
842858 return ctx;
843859 }
844860
845 pub fn getFp(ctx: *const LoongArch) Gpr {
861 pub fn getFp(ctx: *const LoongArch) usize {
846862 return ctx.r[22];
847863 }
848 pub fn getPc(ctx: *const LoongArch) Gpr {
864 pub fn getPc(ctx: *const LoongArch) usize {
849865 return ctx.pc;
850866 }
851867
......@@ -864,10 +880,12 @@ const LoongArch = extern struct {
864880/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
865881const M68k = extern struct {
866882 /// The numbered data registers d0 - d7.
867 d: [8]u32,
883 d: [8]Gpr,
868884 /// The numbered address registers a0 - a7.
869 a: [8]u32,
870 pc: u32,
885 a: [8]Gpr,
886 pc: Gpr,
887
888 pub const Gpr = u32;
871889
872890 pub inline fn current() M68k {
873891 var ctx: M68k = undefined;
......@@ -881,10 +899,10 @@ const M68k = extern struct {
881899 return ctx;
882900 }
883901
884 pub fn getFp(ctx: *const M68k) u32 {
902 pub fn getFp(ctx: *const M68k) usize {
885903 return ctx.a[6];
886904 }
887 pub fn getPc(ctx: *const M68k) u32 {
905 pub fn getPc(ctx: *const M68k) usize {
888906 return ctx.pc;
889907 }
890908
......@@ -905,8 +923,10 @@ const M68k = extern struct {
905923/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
906924const M88k = extern struct {
907925 /// The numbered general-purpose registers r0 - r31.
908 r: [32]u32,
909 xip: u32,
926 r: [32]Gpr,
927 xip: Gpr,
928
929 pub const Gpr = u32;
910930
911931 pub inline fn current() M88k {
912932 var ctx: M88k = undefined;
......@@ -952,10 +972,10 @@ const M88k = extern struct {
952972 return ctx;
953973 }
954974
955 pub fn getFp(ctx: *const M88k) u32 {
975 pub fn getFp(ctx: *const M88k) usize {
956976 return ctx.r[30];
957977 }
958 pub fn getPc(ctx: *const M88k) u32 {
978 pub fn getPc(ctx: *const M88k) usize {
959979 return ctx.xip;
960980 }
961981
......@@ -1103,8 +1123,10 @@ const Mips = extern struct {
11031123/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
11041124const Or1k = extern struct {
11051125 /// The numbered general-purpose registers r0 - r31.
1106 r: [32]u32,
1107 pc: u32,
1126 r: [32]Gpr,
1127 pc: Gpr,
1128
1129 pub const Gpr = u32;
11081130
11091131 pub inline fn current() Or1k {
11101132 var ctx: Or1k = undefined;
......@@ -1150,10 +1172,10 @@ const Or1k = extern struct {
11501172 return ctx;
11511173 }
11521174
1153 pub fn getFp(ctx: *const Or1k) u32 {
1175 pub fn getFp(ctx: *const Or1k) usize {
11541176 return ctx.r[2];
11551177 }
1156 pub fn getPc(ctx: *const Or1k) u32 {
1178 pub fn getPc(ctx: *const Or1k) usize {
11571179 return ctx.pc;
11581180 }
11591181
......@@ -1262,10 +1284,10 @@ const Powerpc = extern struct {
12621284 return ctx;
12631285 }
12641286
1265 pub fn getFp(ctx: *const Powerpc) Gpr {
1287 pub fn getFp(ctx: *const Powerpc) usize {
12661288 return ctx.r[1];
12671289 }
1268 pub fn getPc(ctx: *const Powerpc) Gpr {
1290 pub fn getPc(ctx: *const Powerpc) usize {
12691291 return ctx.pc;
12701292 }
12711293
......@@ -1415,10 +1437,10 @@ const Riscv = extern struct {
14151437 return ctx;
14161438 }
14171439
1418 pub fn getFp(ctx: *const Riscv) Gpr {
1440 pub fn getFp(ctx: *const Riscv) usize {
14191441 return ctx.x[8];
14201442 }
1421 pub fn getPc(ctx: *const Riscv) Gpr {
1443 pub fn getPc(ctx: *const Riscv) usize {
14221444 return ctx.pc;
14231445 }
14241446
......@@ -1441,13 +1463,15 @@ const Riscv = extern struct {
14411463/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
14421464const S390x = extern struct {
14431465 /// The numbered general-purpose registers r0 - r15.
1444 r: [16]u64,
1466 r: [16]Gpr,
14451467 /// The program counter.
14461468 psw: extern struct {
1447 mask: u64,
1448 addr: u64,
1469 mask: Gpr,
1470 addr: Gpr,
14491471 },
14501472
1473 pub const Gpr = u64;
1474
14511475 pub inline fn current() S390x {
14521476 var ctx: S390x = undefined;
14531477 asm volatile (
......@@ -1462,10 +1486,10 @@ const S390x = extern struct {
14621486 return ctx;
14631487 }
14641488
1465 pub fn getFp(ctx: *const S390x) u64 {
1489 pub fn getFp(ctx: *const S390x) usize {
14661490 return ctx.r[11];
14671491 }
1468 pub fn getPc(ctx: *const S390x) u64 {
1492 pub fn getPc(ctx: *const S390x) usize {
14691493 return ctx.psw.addr;
14701494 }
14711495
......@@ -1571,10 +1595,10 @@ const Sparc = extern struct {
15711595 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
15721596 }
15731597
1574 pub fn getFp(ctx: *const Sparc) Gpr {
1598 pub fn getFp(ctx: *const Sparc) usize {
15751599 return ctx.i[6];
15761600 }
1577 pub fn getPc(ctx: *const Sparc) Gpr {
1601 pub fn getPc(ctx: *const Sparc) usize {
15781602 return ctx.pc;
15791603 }
15801604
......@@ -1593,8 +1617,10 @@ const Sparc = extern struct {
15931617
15941618/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
15951619const Ve = extern struct {
1596 s: [64]u64,
1597 ic: u64,
1620 s: [64]Gpr,
1621 ic: Gpr,
1622
1623 pub const Gpr = u64;
15981624
15991625 pub inline fn current() Ve {
16001626 var ctx: Ve = undefined;
......@@ -1672,10 +1698,10 @@ const Ve = extern struct {
16721698 return ctx;
16731699 }
16741700
1675 pub fn getFp(ctx: *const Ve) u64 {
1701 pub fn getFp(ctx: *const Ve) usize {
16761702 return ctx.s[9];
16771703 }
1678 pub fn getPc(ctx: *const Ve) u64 {
1704 pub fn getPc(ctx: *const Ve) usize {
16791705 return ctx.ic;
16801706 }
16811707
......@@ -1693,14 +1719,15 @@ const Ve = extern struct {
16931719};
16941720
16951721const X86_16 = struct {
1696 pub const Register = enum {
1722 regs: std.enums.EnumArray(GprName, Gpr),
1723
1724 pub const GprName = enum {
16971725 // zig fmt: off
16981726 sp, bp, ss,
16991727 ip, cs,
17001728 // zig fmt: on
17011729 };
1702
1703 regs: std.enums.EnumArray(Register, u16),
1730 pub const Gpr = u16;
17041731
17051732 pub inline fn current() X86_16 {
17061733 var ctx: X86_16 = undefined;
......@@ -1719,10 +1746,10 @@ const X86_16 = struct {
17191746 return ctx;
17201747 }
17211748
1722 pub fn getFp(ctx: *const X86_16) u16 {
1749 pub fn getFp(ctx: *const X86_16) usize {
17231750 return ctx.regs.get(.bp);
17241751 }
1725 pub fn getPc(ctx: *const X86_16) u16 {
1752 pub fn getPc(ctx: *const X86_16) usize {
17261753 return ctx.regs.get(.ip);
17271754 }
17281755
......@@ -1740,17 +1767,19 @@ const X86_16 = struct {
17401767};
17411768
17421769const X86 = struct {
1770 gprs: std.enums.EnumArray(GprName, Gpr),
1771
17431772 /// The first 8 registers here intentionally match the order of registers in the x86 instruction
17441773 /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings,
17451774 /// among other things.
1746 pub const Gpr = enum {
1775 pub const GprName = enum {
17471776 // zig fmt: off
17481777 eax, ecx, edx, ebx,
17491778 esp, ebp, esi, edi,
17501779 eip,
17511780 // zig fmt: on
17521781 };
1753 gprs: std.enums.EnumArray(Gpr, u32),
1782 pub const Gpr = u32;
17541783
17551784 pub inline fn current() X86 {
17561785 var ctx: X86 = undefined;
......@@ -1772,10 +1801,10 @@ const X86 = struct {
17721801 return ctx;
17731802 }
17741803
1775 pub fn getFp(ctx: *const X86) u32 {
1804 pub fn getFp(ctx: *const X86) usize {
17761805 return ctx.gprs.get(.ebp);
17771806 }
1778 pub fn getPc(ctx: *const X86) u32 {
1807 pub fn getPc(ctx: *const X86) usize {
17791808 return ctx.gprs.get(.eip);
17801809 }
17811810
......@@ -1806,10 +1835,12 @@ const X86 = struct {
18061835};
18071836
18081837const X86_64 = struct {
1838 gprs: std.enums.EnumArray(GprName, Gpr),
1839
18091840 /// The order here intentionally matches the order of the DWARF register mappings. It's unclear
18101841 /// where those mappings actually originated from---the ordering of the first 4 registers seems
18111842 /// quite unusual---but it is currently convenient for us to match DWARF.
1812 pub const Gpr = enum {
1843 pub const GprName = enum {
18131844 // zig fmt: off
18141845 rax, rdx, rcx, rbx,
18151846 rsi, rdi, rbp, rsp,
......@@ -1818,7 +1849,7 @@ const X86_64 = struct {
18181849 rip,
18191850 // zig fmt: on
18201851 };
1821 gprs: std.enums.EnumArray(Gpr, u64),
1852 pub const Gpr = u64;
18221853
18231854 pub inline fn current() X86_64 {
18241855 var ctx: X86_64 = undefined;