commit b64ce08c993f0871b2f9be7df561da69d9a9037e (tree)
parent b7a544c527f0d54cf9d044f26daf272e9297a786
Author: Alex Rønne Petersen <alex@alexrp.com>
Date: Tue, 16 Jun 2026 21:24:05 +0200
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>
Diffstat:
4 files changed, 114 insertions(+), 98 deletions(-)
diff --git a/lib/std/debug/Dwarf/SelfUnwinder.zig b/lib/std/debug/Dwarf/SelfUnwinder.zig
@@ -168,7 +168,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
.none => return error.InvalidDebugInfo,
.reg_off => |ro| cfa: {
const ptr = try regNative(&unwinder.cpu_state, ro.register);
- break :cfa try applyOffset(ptr.*, ro.offset);
+ break :cfa try applyOffset(@intCast(ptr.*), ro.offset);
},
.expression => |expr| cfa: {
// 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
const value = try unwinder.expr_vm.run(expr, gpa, .{
.format = format,
.cpu_context = &unwinder.cpu_state,
- }, prev_cfa_val) orelse return error.InvalidDebugInfo;
+ }, @intCast(prev_cfa_val)) orelse return error.InvalidDebugInfo;
switch (value) {
.generic => |g| break :cfa g,
else => return error.InvalidDebugInfo,
@@ -203,7 +203,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
const new_val: union(enum) {
same,
undefined,
- val: usize,
+ val: std.debug.cpu_context.Native.Gpr,
bytes: []const u8,
} = switch (rule) {
.default => val: {
@@ -219,7 +219,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
.undefined => .undefined,
.same_value => .same,
.offset => |offset| val: {
- const ptr: *const usize = @ptrFromInt(try applyOffset(cfa, offset));
+ const ptr: *const std.debug.cpu_context.Native.Gpr = @ptrFromInt(try applyOffset(cfa, offset));
break :val .{ .val = ptr.* };
},
.val_offset => |offset| .{ .val = try applyOffset(cfa, offset) },
@@ -260,12 +260,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
has_return_address = false;
}
},
- .val => |val| {
- const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register));
- if (dest.len != @sizeOf(usize)) return error.InvalidDebugInfo;
- const dest_ptr: *align(1) usize = @ptrCast(dest);
- dest_ptr.* = val;
- },
+ .val => |val| (try regNative(&new_cpu_state, register)).* = val,
.bytes => |src| {
const dest = try new_cpu_state.dwarfRegisterBytes(@intCast(register));
if (dest.len != src.len) return error.InvalidDebugInfo;
@@ -275,7 +270,7 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
}
const return_address = if (has_return_address)
- stripInstructionPtrAuthCode((try regNative(&new_cpu_state, return_address_register)).*)
+ stripInstructionPtrAuthCode(@intCast((try regNative(&new_cpu_state, return_address_register)).*))
else
0;
@@ -303,9 +298,9 @@ pub fn regNative(ctx: *std.debug.cpu_context.Native, num: u16) error{
InvalidRegister,
UnsupportedRegister,
IncompatibleRegisterSize,
-}!*align(1) usize {
+}!*align(1) std.debug.cpu_context.Native.Gpr {
const bytes = try ctx.dwarfRegisterBytes(num);
- if (bytes.len != @sizeOf(usize)) return error.IncompatibleRegisterSize;
+ if (bytes.len != @sizeOf(std.debug.cpu_context.Native.Gpr)) return error.IncompatibleRegisterSize;
return @ptrCast(bytes);
}
diff --git a/lib/std/debug/Dwarf/expression.zig b/lib/std/debug/Dwarf/expression.zig
@@ -387,7 +387,7 @@ pub fn StackMachine(comptime options: Options) type {
.regval_type = .{
.type_offset = rt.type_offset,
.type_size = @sizeOf(addr_type),
- .value = (try regNative(cpu_context, rt.register)).*,
+ .value = @intCast((try regNative(cpu_context, rt.register)).*),
},
});
},
@@ -738,7 +738,7 @@ pub fn StackMachine(comptime options: Options) type {
var block_stream: std.Io.Reader = .fixed(block);
const register = (try readOperand(&block_stream, block[0], context)).?.register;
const value = (try regNative(cpu_context, register)).*;
- try self.stack.append(allocator, .{ .generic = value });
+ try self.stack.append(allocator, .{ .generic = @intCast(value) });
} else {
var stack_machine: Self = .{};
defer stack_machine.deinit(allocator);
diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig
@@ -92,16 +92,6 @@ pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
}
pub const can_unwind: bool = s: {
- // The DWARF code can't deal with ILP32 ABIs yet: https://github.com/ziglang/zig/issues/25447
- switch (builtin.target.abi) {
- .gnuabin32,
- .muslabin32,
- .gnux32,
- .muslx32,
- => break :s false,
- else => {},
- }
-
// Notably, we are yet to support unwinding on ARM. There, unwinding is not done through
// `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format.
const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {
diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig
@@ -240,9 +240,11 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Aarch64 = extern struct {
/// The numbered general-purpose registers X0 - X30.
- x: [31]u64,
- sp: u64,
- pc: u64,
+ x: [31]Gpr,
+ sp: Gpr,
+ pc: Gpr,
+
+ pub const Gpr = u64;
pub inline fn current() Aarch64 {
var ctx: Aarch64 = undefined;
@@ -273,10 +275,10 @@ const Aarch64 = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Aarch64) u64 {
+ pub fn getFp(ctx: *const Aarch64) usize {
return ctx.x[29];
}
- pub fn getPc(ctx: *const Aarch64) u64 {
+ pub fn getPc(ctx: *const Aarch64) usize {
return ctx.pc;
}
@@ -308,8 +310,10 @@ const Aarch64 = extern struct {
const Alpha = extern struct {
/// The numbered general-purpose registers R0 - R31.
- r: [32]u64,
- pc: u64,
+ r: [32]Gpr,
+ pc: Gpr,
+
+ pub const Gpr = u64;
pub inline fn current() Alpha {
var ctx: Alpha = undefined;
@@ -355,10 +359,10 @@ const Alpha = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Alpha) u64 {
+ pub fn getFp(ctx: *const Alpha) usize {
return ctx.r[15];
}
- pub fn getPc(ctx: *const Alpha) u64 {
+ pub fn getPc(ctx: *const Alpha) usize {
return ctx.pc;
}
@@ -378,8 +382,10 @@ const Alpha = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Arc = extern struct {
/// The numbered general-purpose registers r0 - r31.
- r: [32]u32,
- pcl: u32,
+ r: [32]Gpr,
+ pcl: Gpr,
+
+ pub const Gpr = u32;
pub inline fn current() Arc {
var ctx: Arc = undefined;
@@ -423,10 +429,10 @@ const Arc = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Arc) u32 {
+ pub fn getFp(ctx: *const Arc) usize {
return ctx.r[27];
}
- pub fn getPc(ctx: *const Arc) u32 {
+ pub fn getPc(ctx: *const Arc) usize {
return ctx.pcl;
}
@@ -446,7 +452,9 @@ const Arc = extern struct {
const Arm = struct {
/// The numbered general-purpose registers R0 - R15.
- r: [16]u32,
+ r: [16]Gpr,
+
+ pub const Gpr = u32;
pub inline fn current() Arm {
var ctx: Arm = undefined;
@@ -462,10 +470,10 @@ const Arm = struct {
return ctx;
}
- pub fn getFp(ctx: *const Arm) u32 {
+ pub fn getFp(ctx: *const Arm) usize {
return ctx.r[11];
}
- pub fn getPc(ctx: *const Arm) u32 {
+ pub fn getPc(ctx: *const Arm) usize {
return ctx.r[15];
}
@@ -512,8 +520,10 @@ const Arm = struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Csky = extern struct {
/// The numbered general-purpose registers r0 - r31.
- r: [32]u32,
- pc: u32,
+ r: [32]Gpr,
+ pc: Gpr,
+
+ pub const Gpr = u32;
pub inline fn current() Csky {
var ctx: Csky = undefined;
@@ -528,10 +538,10 @@ const Csky = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Csky) u32 {
+ pub fn getFp(ctx: *const Csky) usize {
return ctx.r[14];
}
- pub fn getPc(ctx: *const Csky) u32 {
+ pub fn getPc(ctx: *const Csky) usize {
return ctx.pc;
}
@@ -550,8 +560,10 @@ const Csky = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Hexagon = extern struct {
/// The numbered general-purpose registers r0 - r31.
- r: [32]u32,
- pc: u32,
+ r: [32]Gpr,
+ pc: Gpr,
+
+ pub const Gpr = u32;
pub inline fn current() Hexagon {
var ctx: Hexagon = undefined;
@@ -596,10 +608,10 @@ const Hexagon = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Hexagon) u32 {
+ pub fn getFp(ctx: *const Hexagon) usize {
return ctx.r[30];
}
- pub fn getPc(ctx: *const Hexagon) u32 {
+ pub fn getPc(ctx: *const Hexagon) usize {
return ctx.pc;
}
@@ -623,9 +635,11 @@ const Hexagon = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Kvx = extern struct {
- r: [64]u64,
- ra: u64,
- pc: u64,
+ r: [64]Gpr,
+ ra: Gpr,
+ pc: Gpr,
+
+ pub const Gpr = u64;
pub inline fn current() Kvx {
var ctx: Kvx = undefined;
@@ -671,10 +685,10 @@ const Kvx = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Kvx) u64 {
+ pub fn getFp(ctx: *const Kvx) usize {
return ctx.r[14];
}
- pub fn getPc(ctx: *const Kvx) u64 {
+ pub fn getPc(ctx: *const Kvx) usize {
return ctx.pc;
}
@@ -695,7 +709,9 @@ const Kvx = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Lanai = extern struct {
- r: [32]u32,
+ r: [32]Gpr,
+
+ pub const Gpr = u32;
pub inline fn current() Lanai {
var ctx: Lanai = undefined;
@@ -738,10 +754,10 @@ const Lanai = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Lanai) u32 {
+ pub fn getFp(ctx: *const Lanai) usize {
return ctx.r[5];
}
- pub fn getPc(ctx: *const Lanai) u32 {
+ pub fn getPc(ctx: *const Lanai) usize {
return ctx.r[2];
}
@@ -842,10 +858,10 @@ const LoongArch = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const LoongArch) Gpr {
+ pub fn getFp(ctx: *const LoongArch) usize {
return ctx.r[22];
}
- pub fn getPc(ctx: *const LoongArch) Gpr {
+ pub fn getPc(ctx: *const LoongArch) usize {
return ctx.pc;
}
@@ -864,10 +880,12 @@ const LoongArch = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const M68k = extern struct {
/// The numbered data registers d0 - d7.
- d: [8]u32,
+ d: [8]Gpr,
/// The numbered address registers a0 - a7.
- a: [8]u32,
- pc: u32,
+ a: [8]Gpr,
+ pc: Gpr,
+
+ pub const Gpr = u32;
pub inline fn current() M68k {
var ctx: M68k = undefined;
@@ -881,10 +899,10 @@ const M68k = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const M68k) u32 {
+ pub fn getFp(ctx: *const M68k) usize {
return ctx.a[6];
}
- pub fn getPc(ctx: *const M68k) u32 {
+ pub fn getPc(ctx: *const M68k) usize {
return ctx.pc;
}
@@ -905,8 +923,10 @@ const M68k = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const M88k = extern struct {
/// The numbered general-purpose registers r0 - r31.
- r: [32]u32,
- xip: u32,
+ r: [32]Gpr,
+ xip: Gpr,
+
+ pub const Gpr = u32;
pub inline fn current() M88k {
var ctx: M88k = undefined;
@@ -952,10 +972,10 @@ const M88k = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const M88k) u32 {
+ pub fn getFp(ctx: *const M88k) usize {
return ctx.r[30];
}
- pub fn getPc(ctx: *const M88k) u32 {
+ pub fn getPc(ctx: *const M88k) usize {
return ctx.xip;
}
@@ -1103,8 +1123,10 @@ const Mips = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Or1k = extern struct {
/// The numbered general-purpose registers r0 - r31.
- r: [32]u32,
- pc: u32,
+ r: [32]Gpr,
+ pc: Gpr,
+
+ pub const Gpr = u32;
pub inline fn current() Or1k {
var ctx: Or1k = undefined;
@@ -1150,10 +1172,10 @@ const Or1k = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Or1k) u32 {
+ pub fn getFp(ctx: *const Or1k) usize {
return ctx.r[2];
}
- pub fn getPc(ctx: *const Or1k) u32 {
+ pub fn getPc(ctx: *const Or1k) usize {
return ctx.pc;
}
@@ -1262,10 +1284,10 @@ const Powerpc = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Powerpc) Gpr {
+ pub fn getFp(ctx: *const Powerpc) usize {
return ctx.r[1];
}
- pub fn getPc(ctx: *const Powerpc) Gpr {
+ pub fn getPc(ctx: *const Powerpc) usize {
return ctx.pc;
}
@@ -1415,10 +1437,10 @@ const Riscv = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Riscv) Gpr {
+ pub fn getFp(ctx: *const Riscv) usize {
return ctx.x[8];
}
- pub fn getPc(ctx: *const Riscv) Gpr {
+ pub fn getPc(ctx: *const Riscv) usize {
return ctx.pc;
}
@@ -1441,13 +1463,15 @@ const Riscv = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const S390x = extern struct {
/// The numbered general-purpose registers r0 - r15.
- r: [16]u64,
+ r: [16]Gpr,
/// The program counter.
psw: extern struct {
- mask: u64,
- addr: u64,
+ mask: Gpr,
+ addr: Gpr,
},
+ pub const Gpr = u64;
+
pub inline fn current() S390x {
var ctx: S390x = undefined;
asm volatile (
@@ -1462,10 +1486,10 @@ const S390x = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const S390x) u64 {
+ pub fn getFp(ctx: *const S390x) usize {
return ctx.r[11];
}
- pub fn getPc(ctx: *const S390x) u64 {
+ pub fn getPc(ctx: *const S390x) usize {
return ctx.psw.addr;
}
@@ -1571,10 +1595,10 @@ const Sparc = extern struct {
asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
}
- pub fn getFp(ctx: *const Sparc) Gpr {
+ pub fn getFp(ctx: *const Sparc) usize {
return ctx.i[6];
}
- pub fn getPc(ctx: *const Sparc) Gpr {
+ pub fn getPc(ctx: *const Sparc) usize {
return ctx.pc;
}
@@ -1593,8 +1617,10 @@ const Sparc = extern struct {
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Ve = extern struct {
- s: [64]u64,
- ic: u64,
+ s: [64]Gpr,
+ ic: Gpr,
+
+ pub const Gpr = u64;
pub inline fn current() Ve {
var ctx: Ve = undefined;
@@ -1672,10 +1698,10 @@ const Ve = extern struct {
return ctx;
}
- pub fn getFp(ctx: *const Ve) u64 {
+ pub fn getFp(ctx: *const Ve) usize {
return ctx.s[9];
}
- pub fn getPc(ctx: *const Ve) u64 {
+ pub fn getPc(ctx: *const Ve) usize {
return ctx.ic;
}
@@ -1693,14 +1719,15 @@ const Ve = extern struct {
};
const X86_16 = struct {
- pub const Register = enum {
+ regs: std.enums.EnumArray(GprName, Gpr),
+
+ pub const GprName = enum {
// zig fmt: off
sp, bp, ss,
ip, cs,
// zig fmt: on
};
-
- regs: std.enums.EnumArray(Register, u16),
+ pub const Gpr = u16;
pub inline fn current() X86_16 {
var ctx: X86_16 = undefined;
@@ -1719,10 +1746,10 @@ const X86_16 = struct {
return ctx;
}
- pub fn getFp(ctx: *const X86_16) u16 {
+ pub fn getFp(ctx: *const X86_16) usize {
return ctx.regs.get(.bp);
}
- pub fn getPc(ctx: *const X86_16) u16 {
+ pub fn getPc(ctx: *const X86_16) usize {
return ctx.regs.get(.ip);
}
@@ -1740,17 +1767,19 @@ const X86_16 = struct {
};
const X86 = struct {
+ gprs: std.enums.EnumArray(GprName, Gpr),
+
/// The first 8 registers here intentionally match the order of registers in the x86 instruction
/// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings,
/// among other things.
- pub const Gpr = enum {
+ pub const GprName = enum {
// zig fmt: off
eax, ecx, edx, ebx,
esp, ebp, esi, edi,
eip,
// zig fmt: on
};
- gprs: std.enums.EnumArray(Gpr, u32),
+ pub const Gpr = u32;
pub inline fn current() X86 {
var ctx: X86 = undefined;
@@ -1772,10 +1801,10 @@ const X86 = struct {
return ctx;
}
- pub fn getFp(ctx: *const X86) u32 {
+ pub fn getFp(ctx: *const X86) usize {
return ctx.gprs.get(.ebp);
}
- pub fn getPc(ctx: *const X86) u32 {
+ pub fn getPc(ctx: *const X86) usize {
return ctx.gprs.get(.eip);
}
@@ -1806,10 +1835,12 @@ const X86 = struct {
};
const X86_64 = struct {
+ gprs: std.enums.EnumArray(GprName, Gpr),
+
/// The order here intentionally matches the order of the DWARF register mappings. It's unclear
/// where those mappings actually originated from---the ordering of the first 4 registers seems
/// quite unusual---but it is currently convenient for us to match DWARF.
- pub const Gpr = enum {
+ pub const GprName = enum {
// zig fmt: off
rax, rdx, rcx, rbx,
rsi, rdi, rbp, rsp,
@@ -1818,7 +1849,7 @@ const X86_64 = struct {
rip,
// zig fmt: on
};
- gprs: std.enums.EnumArray(Gpr, u64),
+ pub const Gpr = u64;
pub inline fn current() X86_64 {
var ctx: X86_64 = undefined;