Skip to content

Commit 1f083e9

Browse files
authored
Merge pull request #25443 from alexrp/s390x-unwind
`std.debug`: add `s390x-linux` unwind support
2 parents bc4da9a + 95bdb0c commit 1f083e9

File tree

8 files changed

+124
-10
lines changed

8 files changed

+124
-10
lines changed

lib/std/debug.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,9 @@ const StackIterator = union(enum) {
952952
/// Offset of the saved return address wrt the frame pointer.
953953
const ra_offset = off: {
954954
if (native_arch == .powerpc64le) break :off 2 * @sizeOf(usize);
955+
// On s390x, r14 is the link register and we need to grab it from its customary slot in the
956+
// register save area (ELF ABI s390x Supplement §1.2.2.2).
957+
if (native_arch == .s390x) break :off 14 * @sizeOf(usize);
955958
break :off @sizeOf(usize);
956959
};
957960

lib/std/debug/Dwarf.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
14331433
.arm, .armeb, .thumb, .thumbeb => 15,
14341434
.loongarch32, .loongarch64 => 32,
14351435
.riscv32, .riscv32be, .riscv64, .riscv64be => 32,
1436+
.s390x => 65,
14361437
.x86 => 8,
14371438
.x86_64 => 16,
14381439
else => null,
@@ -1445,6 +1446,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
14451446
.arm, .armeb, .thumb, .thumbeb => 11,
14461447
.loongarch32, .loongarch64 => 22,
14471448
.riscv32, .riscv32be, .riscv64, .riscv64be => 8,
1449+
.s390x => 11,
14481450
.x86 => 5,
14491451
.x86_64 => 6,
14501452
else => unreachable,
@@ -1457,6 +1459,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {
14571459
.arm, .armeb, .thumb, .thumbeb => 13,
14581460
.loongarch32, .loongarch64 => 3,
14591461
.riscv32, .riscv32be, .riscv64, .riscv64be => 2,
1462+
.s390x => 15,
14601463
.x86 => 4,
14611464
.x86_64 => 7,
14621465
else => unreachable,

lib/std/debug/Dwarf/SelfUnwinder.zig

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,11 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
176176
break :cfa try applyOffset(ptr.*, ro.offset);
177177
},
178178
.expression => |expr| cfa: {
179-
// On all implemented architectures, the CFA is defined to be the previous frame's SP
179+
// On most implemented architectures, the CFA is defined to be the previous frame's SP.
180+
//
181+
// On s390x, it's defined to be SP + 160 (ELF ABI s390x Supplement §1.6.3); however,
182+
// what this actually means is that there will be a `def_cfa r15 + 160`, so nothing
183+
// special for us to do.
180184
const prev_cfa_val = (try regNative(&unwinder.cpu_state, sp_reg_num)).*;
181185
unwinder.expr_vm.reset();
182186
const value = try unwinder.expr_vm.run(expr, gpa, .{
@@ -193,9 +197,13 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
193197
// If unspecified, we'll use the default rule for the return address register, which is
194198
// typically equivalent to `.undefined` (meaning there is no return address), but may be
195199
// overriden by ABIs.
196-
var has_return_address: bool = builtin.cpu.arch.isAARCH64() and
197-
return_address_register >= 19 and
198-
return_address_register <= 28;
200+
var has_return_address: bool = switch (builtin.cpu.arch) {
201+
// DWARF for the Arm 64-bit Architecture (AArch64) §4.3, p1
202+
.aarch64, .aarch64_be => return_address_register >= 19 and return_address_register <= 28,
203+
// ELF ABI s390x Supplement §1.6.4
204+
.s390x => return_address_register >= 6 and return_address_register <= 15,
205+
else => false,
206+
};
199207

200208
// Create a copy of the CPU state, to which we will apply the new rules.
201209
var new_cpu_state = unwinder.cpu_state;

lib/std/debug/SelfInfo/Elf.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ pub const can_unwind: bool = s: {
9090
.loongarch64,
9191
.riscv32,
9292
.riscv64,
93+
.s390x,
9394
.x86,
9495
.x86_64,
9596
},

lib/std/debug/cpu_context.zig

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ else switch (native_arch) {
88
.arm, .armeb, .thumb, .thumbeb => Arm,
99
.loongarch32, .loongarch64 => LoongArch,
1010
.riscv32, .riscv32be, .riscv64, .riscv64be => Riscv,
11+
.s390x => S390x,
1112
.x86 => X86,
1213
.x86_64 => X86_64,
1314
else => noreturn,
@@ -189,6 +190,17 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {
189190
},
190191
else => null,
191192
},
193+
.s390x => switch (builtin.os.tag) {
194+
.linux => .{
195+
.r = uc.mcontext.gregs,
196+
.f = uc.mcontext.fregs,
197+
.psw = .{
198+
.mask = uc.mcontext.psw.mask,
199+
.addr = uc.mcontext.psw.addr,
200+
},
201+
},
202+
else => null,
203+
},
192204
else => null,
193205
};
194206
}
@@ -677,6 +689,81 @@ pub const Riscv = extern struct {
677689
}
678690
};
679691

692+
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
693+
pub const S390x = extern struct {
694+
/// The numbered general-purpose registers r0 - r15.
695+
r: [16]u64,
696+
/// The numbered floating-point registers f0 - f15. Yes, really - they can be used in DWARF CFI.
697+
f: [16]f64,
698+
/// The program counter.
699+
psw: extern struct {
700+
mask: u64,
701+
addr: u64,
702+
},
703+
704+
pub inline fn current() S390x {
705+
var ctx: S390x = undefined;
706+
asm volatile (
707+
\\ stmg %%r0, %%r15, 0(%%r2)
708+
\\ std %%f0, 128(%%r2)
709+
\\ std %%f1, 136(%%r2)
710+
\\ std %%f2, 144(%%r2)
711+
\\ std %%f3, 152(%%r2)
712+
\\ std %%f4, 160(%%r2)
713+
\\ std %%f5, 168(%%r2)
714+
\\ std %%f6, 176(%%r2)
715+
\\ std %%f7, 184(%%r2)
716+
\\ std %%f8, 192(%%r2)
717+
\\ std %%f9, 200(%%r2)
718+
\\ std %%f10, 208(%%r2)
719+
\\ std %%f11, 216(%%r2)
720+
\\ std %%f12, 224(%%r2)
721+
\\ std %%f13, 232(%%r2)
722+
\\ std %%f14, 240(%%r2)
723+
\\ std %%f15, 248(%%r2)
724+
\\ epsw %%r0, %%r1
725+
\\ stm %%r0, %%r1, 256(%%r2)
726+
\\ larl %%r0, .
727+
\\ stg %%r0, 264(%%r2)
728+
\\ lg %%r0, 0(%%r2)
729+
\\ lg %%r1, 8(%%r2)
730+
:
731+
: [gprs] "{r2}" (&ctx),
732+
: .{ .memory = true });
733+
return ctx;
734+
}
735+
736+
pub fn dwarfRegisterBytes(ctx: *S390x, register_num: u16) DwarfRegisterError![]u8 {
737+
switch (register_num) {
738+
0...15 => return @ptrCast(&ctx.r[register_num]),
739+
// Why???
740+
16 => return @ptrCast(&ctx.f[0]),
741+
17 => return @ptrCast(&ctx.f[2]),
742+
18 => return @ptrCast(&ctx.f[4]),
743+
19 => return @ptrCast(&ctx.f[6]),
744+
20 => return @ptrCast(&ctx.f[1]),
745+
21 => return @ptrCast(&ctx.f[3]),
746+
22 => return @ptrCast(&ctx.f[5]),
747+
23 => return @ptrCast(&ctx.f[7]),
748+
24 => return @ptrCast(&ctx.f[8]),
749+
25 => return @ptrCast(&ctx.f[10]),
750+
26 => return @ptrCast(&ctx.f[12]),
751+
27 => return @ptrCast(&ctx.f[14]),
752+
28 => return @ptrCast(&ctx.f[9]),
753+
29 => return @ptrCast(&ctx.f[11]),
754+
30 => return @ptrCast(&ctx.f[13]),
755+
31 => return @ptrCast(&ctx.f[15]),
756+
64 => return @ptrCast(&ctx.psw.mask),
757+
65 => return @ptrCast(&ctx.psw.addr),
758+
759+
48...63 => return error.UnsupportedRegister, // a0 - a15
760+
68...83 => return error.UnsupportedRegister, // v16 - v31
761+
762+
else => return error.InvalidRegister,
763+
}
764+
}
765+
};
766+
680767
const signal_ucontext_t = switch (native_os) {
681768
.linux => std.os.linux.ucontext_t,
682769
.emscripten => std.os.emscripten.ucontext_t,

lib/std/os/linux/s390x.zig

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,12 @@ pub const ucontext_t = extern struct {
269269
};
270270

271271
pub const mcontext_t = extern struct {
272-
__regs1: [18]u64,
273-
__regs2: [18]u32,
274-
__regs3: [16]f64,
272+
psw: extern struct {
273+
mask: u64,
274+
addr: u64,
275+
},
276+
gregs: [16]u64,
277+
aregs: [16]u32,
278+
fpc: u32,
279+
fregs: [16]f64,
275280
};

src/Compilation.zig

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7193,6 +7193,9 @@ pub fn addCCArgs(
71937193
}
71947194

71957195
try argv.append(if (mod.omit_frame_pointer) "-fomit-frame-pointer" else "-fno-omit-frame-pointer");
7196+
if (target.cpu.arch == .s390x) {
7197+
try argv.append(if (mod.omit_frame_pointer) "-mbackchain" else "-mno-backchain");
7198+
}
71967199

71977200
const ssp_buf_size = mod.stack_protector;
71987201
if (ssp_buf_size != 0) {
@@ -7258,9 +7261,10 @@ pub fn addCCArgs(
72587261
const is_enabled = target.cpu.features.isEnabled(index);
72597262

72607263
if (feature.llvm_name) |llvm_name| {
7261-
// We communicate float ABI to Clang through the dedicated options.
7264+
// We communicate these to Clang through the dedicated options.
72627265
if (std.mem.startsWith(u8, llvm_name, "soft-float") or
7263-
std.mem.startsWith(u8, llvm_name, "hard-float"))
7266+
std.mem.startsWith(u8, llvm_name, "hard-float") or
7267+
(target.cpu.arch == .s390x and std.mem.eql(u8, llvm_name, "backchain")))
72647268
continue;
72657269

72667270
// Ignore these until we figure out how to handle the concept of omitting features.

src/Package/Module.zig

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,10 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
343343
// See https://github.com/ziglang/zig/issues/23539
344344
if (target_util.isDynamicAMDGCNFeature(target, feature)) continue;
345345

346-
const is_enabled = target.cpu.features.isEnabled(feature.index);
346+
var is_enabled = target.cpu.features.isEnabled(feature.index);
347+
if (target.cpu.arch == .s390x and @as(std.Target.s390x.Feature, @enumFromInt(feature.index)) == .backchain) {
348+
is_enabled = !omit_frame_pointer;
349+
}
347350

348351
if (is_enabled) {
349352
try buf.ensureUnusedCapacity(2 + llvm_name.len);

0 commit comments

Comments
 (0)