| author | |
| committer | |
| log | 1f083e9ed78f5c3c2d848d0abc58612c4ce88804 |
| tree | 7eb2c831d2eecdd768c847c9ffe7948de7f2d24f |
| parent | bc4da9a90743c11c7c0b3e485f46d365d57d87b7 |
| parent | 95bdb0c1c65c128923ffac3f4be6b4619eb4a54b |
| signature |
`std.debug`: add `s390x-linux` unwind support8 files changed, 124 insertions(+), 10 deletions(-)
lib/std/debug.zig+3| ... | @@ -952,6 +952,9 @@ const StackIterator = union(enum) { | ... | @@ -952,6 +952,9 @@ const StackIterator = union(enum) { |
| 952 | /// Offset of the saved return address wrt the frame pointer. | 952 | /// Offset of the saved return address wrt the frame pointer. |
| 953 | const ra_offset = off: { | 953 | const ra_offset = off: { |
| 954 | if (native_arch == .powerpc64le) break :off 2 * @sizeOf(usize); | 954 | 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); | ||
| 955 | break :off @sizeOf(usize); | 958 | break :off @sizeOf(usize); |
| 956 | }; | 959 | }; |
| 957 | 960 |
lib/std/debug/Dwarf.zig+3| ... | @@ -1433,6 +1433,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { | ... | @@ -1433,6 +1433,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { |
| 1433 | .arm, .armeb, .thumb, .thumbeb => 15, | 1433 | .arm, .armeb, .thumb, .thumbeb => 15, |
| 1434 | .loongarch32, .loongarch64 => 32, | 1434 | .loongarch32, .loongarch64 => 32, |
| 1435 | .riscv32, .riscv32be, .riscv64, .riscv64be => 32, | 1435 | .riscv32, .riscv32be, .riscv64, .riscv64be => 32, |
| 1436 | .s390x => 65, | ||
| 1436 | .x86 => 8, | 1437 | .x86 => 8, |
| 1437 | .x86_64 => 16, | 1438 | .x86_64 => 16, |
| 1438 | else => null, | 1439 | else => null, |
| ... | @@ -1445,6 +1446,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { | ... | @@ -1445,6 +1446,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { |
| 1445 | .arm, .armeb, .thumb, .thumbeb => 11, | 1446 | .arm, .armeb, .thumb, .thumbeb => 11, |
| 1446 | .loongarch32, .loongarch64 => 22, | 1447 | .loongarch32, .loongarch64 => 22, |
| 1447 | .riscv32, .riscv32be, .riscv64, .riscv64be => 8, | 1448 | .riscv32, .riscv32be, .riscv64, .riscv64be => 8, |
| 1449 | .s390x => 11, | ||
| 1448 | .x86 => 5, | 1450 | .x86 => 5, |
| 1449 | .x86_64 => 6, | 1451 | .x86_64 => 6, |
| 1450 | else => unreachable, | 1452 | else => unreachable, |
| ... | @@ -1457,6 +1459,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { | ... | @@ -1457,6 +1459,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { |
| 1457 | .arm, .armeb, .thumb, .thumbeb => 13, | 1459 | .arm, .armeb, .thumb, .thumbeb => 13, |
| 1458 | .loongarch32, .loongarch64 => 3, | 1460 | .loongarch32, .loongarch64 => 3, |
| 1459 | .riscv32, .riscv32be, .riscv64, .riscv64be => 2, | 1461 | .riscv32, .riscv32be, .riscv64, .riscv64be => 2, |
| 1462 | .s390x => 15, | ||
| 1460 | .x86 => 4, | 1463 | .x86 => 4, |
| 1461 | .x86_64 => 7, | 1464 | .x86_64 => 7, |
| 1462 | else => unreachable, | 1465 | else => unreachable, |
lib/std/debug/Dwarf/SelfUnwinder.zig+12-4| ... | @@ -176,7 +176,11 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE | ... | @@ -176,7 +176,11 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE |
| 176 | break :cfa try applyOffset(ptr.*, ro.offset); | 176 | break :cfa try applyOffset(ptr.*, ro.offset); |
| 177 | }, | 177 | }, |
| 178 | .expression => |expr| cfa: { | 178 | .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. | ||
| 180 | const prev_cfa_val = (try regNative(&unwinder.cpu_state, sp_reg_num)).*; | 184 | const prev_cfa_val = (try regNative(&unwinder.cpu_state, sp_reg_num)).*; |
| 181 | unwinder.expr_vm.reset(); | 185 | unwinder.expr_vm.reset(); |
| 182 | const value = try unwinder.expr_vm.run(expr, gpa, .{ | 186 | const value = try unwinder.expr_vm.run(expr, gpa, .{ |
| ... | @@ -193,9 +197,13 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE | ... | @@ -193,9 +197,13 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE |
| 193 | // If unspecified, we'll use the default rule for the return address register, which is | 197 | // If unspecified, we'll use the default rule for the return address register, which is |
| 194 | // typically equivalent to `.undefined` (meaning there is no return address), but may be | 198 | // typically equivalent to `.undefined` (meaning there is no return address), but may be |
| 195 | // overriden by ABIs. | 199 | // overriden by ABIs. |
| 196 | var has_return_address: bool = builtin.cpu.arch.isAARCH64() and | 200 | var has_return_address: bool = switch (builtin.cpu.arch) { |
| 197 | return_address_register >= 19 and | 201 | // DWARF for the Arm 64-bit Architecture (AArch64) §4.3, p1 |
| 198 | return_address_register <= 28; | 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 | }; | ||
| 199 | 207 | ||
| 200 | // Create a copy of the CPU state, to which we will apply the new rules. | 208 | // Create a copy of the CPU state, to which we will apply the new rules. |
| 201 | var new_cpu_state = unwinder.cpu_state; | 209 | var new_cpu_state = unwinder.cpu_state; |
lib/std/debug/SelfInfo/Elf.zig+1| ... | @@ -90,6 +90,7 @@ pub const can_unwind: bool = s: { | ... | @@ -90,6 +90,7 @@ pub const can_unwind: bool = s: { |
| 90 | .loongarch64, | 90 | .loongarch64, |
| 91 | .riscv32, | 91 | .riscv32, |
| 92 | .riscv64, | 92 | .riscv64, |
| 93 | .s390x, | ||
| 93 | .x86, | 94 | .x86, |
| 94 | .x86_64, | 95 | .x86_64, |
| 95 | }, | 96 | }, |
lib/std/debug/cpu_context.zig+87| ... | @@ -8,6 +8,7 @@ else switch (native_arch) { | ... | @@ -8,6 +8,7 @@ else switch (native_arch) { |
| 8 | .arm, .armeb, .thumb, .thumbeb => Arm, | 8 | .arm, .armeb, .thumb, .thumbeb => Arm, |
| 9 | .loongarch32, .loongarch64 => LoongArch, | 9 | .loongarch32, .loongarch64 => LoongArch, |
| 10 | .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv, | 10 | .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv, |
| 11 | .s390x => S390x, | ||
| 11 | .x86 => X86, | 12 | .x86 => X86, |
| 12 | .x86_64 => X86_64, | 13 | .x86_64 => X86_64, |
| 13 | else => noreturn, | 14 | else => noreturn, |
| ... | @@ -189,6 +190,17 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { | ... | @@ -189,6 +190,17 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { |
| 189 | }, | 190 | }, |
| 190 | else => null, | 191 | else => null, |
| 191 | }, | 192 | }, |
| 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 | }, | ||
| 192 | else => null, | 204 | else => null, |
| 193 | }; | 205 | }; |
| 194 | } | 206 | } |
| ... | @@ -677,6 +689,81 @@ pub const Riscv = extern struct { | ... | @@ -677,6 +689,81 @@ pub const Riscv = extern struct { |
| 677 | } | 689 | } |
| 678 | }; | 690 | }; |
| 679 | 691 | ||
| 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 | |||
| 680 | const signal_ucontext_t = switch (native_os) { | 767 | const signal_ucontext_t = switch (native_os) { |
| 681 | .linux => std.os.linux.ucontext_t, | 768 | .linux => std.os.linux.ucontext_t, |
| 682 | .emscripten => std.os.emscripten.ucontext_t, | 769 | .emscripten => std.os.emscripten.ucontext_t, |
lib/std/os/linux/s390x.zig+8-3| ... | @@ -269,7 +269,12 @@ pub const ucontext_t = extern struct { | ... | @@ -269,7 +269,12 @@ pub const ucontext_t = extern struct { |
| 269 | }; | 269 | }; |
| 270 | 270 | ||
| 271 | pub const mcontext_t = extern struct { | 271 | pub const mcontext_t = extern struct { |
| 272 | __regs1: [18]u64, | 272 | psw: extern struct { |
| 273 | __regs2: [18]u32, | 273 | mask: u64, |
| 274 | __regs3: [16]f64, | 274 | addr: u64, |
| 275 | }, | ||
| 276 | gregs: [16]u64, | ||
| 277 | aregs: [16]u32, | ||
| 278 | fpc: u32, | ||
| 279 | fregs: [16]f64, | ||
| 275 | }; | 280 | }; |
src/Compilation.zig+6-2| ... | @@ -7193,6 +7193,9 @@ pub fn addCCArgs( | ... | @@ -7193,6 +7193,9 @@ pub fn addCCArgs( |
| 7193 | } | 7193 | } |
| 7194 | 7194 | ||
| 7195 | try argv.append(if (mod.omit_frame_pointer) "-fomit-frame-pointer" else "-fno-omit-frame-pointer"); | 7195 | 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 | } | ||
| 7196 | 7199 | ||
| 7197 | const ssp_buf_size = mod.stack_protector; | 7200 | const ssp_buf_size = mod.stack_protector; |
| 7198 | if (ssp_buf_size != 0) { | 7201 | if (ssp_buf_size != 0) { |
| ... | @@ -7258,9 +7261,10 @@ pub fn addCCArgs( | ... | @@ -7258,9 +7261,10 @@ pub fn addCCArgs( |
| 7258 | const is_enabled = target.cpu.features.isEnabled(index); | 7261 | const is_enabled = target.cpu.features.isEnabled(index); |
| 7259 | 7262 | ||
| 7260 | if (feature.llvm_name) |llvm_name| { | 7263 | 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. |
| 7262 | if (std.mem.startsWith(u8, llvm_name, "soft-float") or | 7265 | 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"))) | ||
| 7264 | continue; | 7268 | continue; |
| 7265 | 7269 | ||
| 7266 | // Ignore these until we figure out how to handle the concept of omitting features. | 7270 | // Ignore these until we figure out how to handle the concept of omitting features. |
src/Package/Module.zig+4-1| ... | @@ -343,7 +343,10 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { | ... | @@ -343,7 +343,10 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { |
| 343 | // See https://github.com/ziglang/zig/issues/23539 | 343 | // See https://github.com/ziglang/zig/issues/23539 |
| 344 | if (target_util.isDynamicAMDGCNFeature(target, feature)) continue; | 344 | if (target_util.isDynamicAMDGCNFeature(target, feature)) continue; |
| 345 | 345 | ||
| 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 | } | ||
| 347 | 350 | ||
| 348 | if (is_enabled) { | 351 | if (is_enabled) { |
| 349 | try buf.ensureUnusedCapacity(2 + llvm_name.len); | 352 | try buf.ensureUnusedCapacity(2 + llvm_name.len); |