| 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 | 952 | /// Offset of the saved return address wrt the frame pointer. |
| 953 | 953 | const ra_offset = off: { |
| 954 | 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 | 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 | 1433 | .arm, .armeb, .thumb, .thumbeb => 15, |
| 1434 | 1434 | .loongarch32, .loongarch64 => 32, |
| 1435 | 1435 | .riscv32, .riscv32be, .riscv64, .riscv64be => 32, |
| 1436 | .s390x => 65, | |
| 1436 | 1437 | .x86 => 8, |
| 1437 | 1438 | .x86_64 => 16, |
| 1438 | 1439 | else => null, |
| ... | ... | @@ -1445,6 +1446,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { |
| 1445 | 1446 | .arm, .armeb, .thumb, .thumbeb => 11, |
| 1446 | 1447 | .loongarch32, .loongarch64 => 22, |
| 1447 | 1448 | .riscv32, .riscv32be, .riscv64, .riscv64be => 8, |
| 1449 | .s390x => 11, | |
| 1448 | 1450 | .x86 => 5, |
| 1449 | 1451 | .x86_64 => 6, |
| 1450 | 1452 | else => unreachable, |
| ... | ... | @@ -1457,6 +1459,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { |
| 1457 | 1459 | .arm, .armeb, .thumb, .thumbeb => 13, |
| 1458 | 1460 | .loongarch32, .loongarch64 => 3, |
| 1459 | 1461 | .riscv32, .riscv32be, .riscv64, .riscv64be => 2, |
| 1462 | .s390x => 15, | |
| 1460 | 1463 | .x86 => 4, |
| 1461 | 1464 | .x86_64 => 7, |
| 1462 | 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 | 176 | break :cfa try applyOffset(ptr.*, ro.offset); |
| 177 | 177 | }, |
| 178 | 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 | 184 | const prev_cfa_val = (try regNative(&unwinder.cpu_state, sp_reg_num)).*; |
| 181 | 185 | unwinder.expr_vm.reset(); |
| 182 | 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 | 197 | // If unspecified, we'll use the default rule for the return address register, which is |
| 194 | 198 | // typically equivalent to `.undefined` (meaning there is no return address), but may be |
| 195 | 199 | // 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 | }; | |
| 199 | 207 | |
| 200 | 208 | // Create a copy of the CPU state, to which we will apply the new rules. |
| 201 | 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 | 90 | .loongarch64, |
| 91 | 91 | .riscv32, |
| 92 | 92 | .riscv64, |
| 93 | .s390x, | |
| 93 | 94 | .x86, |
| 94 | 95 | .x86_64, |
| 95 | 96 | }, |
lib/std/debug/cpu_context.zig+87| ... | ... | @@ -8,6 +8,7 @@ else switch (native_arch) { |
| 8 | 8 | .arm, .armeb, .thumb, .thumbeb => Arm, |
| 9 | 9 | .loongarch32, .loongarch64 => LoongArch, |
| 10 | 10 | .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv, |
| 11 | .s390x => S390x, | |
| 11 | 12 | .x86 => X86, |
| 12 | 13 | .x86_64 => X86_64, |
| 13 | 14 | else => noreturn, |
| ... | ... | @@ -189,6 +190,17 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { |
| 189 | 190 | }, |
| 190 | 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 | 204 | else => null, |
| 193 | 205 | }; |
| 194 | 206 | } |
| ... | ... | @@ -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 | 767 | const signal_ucontext_t = switch (native_os) { |
| 681 | 768 | .linux => std.os.linux.ucontext_t, |
| 682 | 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 | 269 | }; |
| 270 | 270 | |
| 271 | 271 | 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, | |
| 275 | 280 | }; |
src/Compilation.zig+6-2| ... | ... | @@ -7193,6 +7193,9 @@ pub fn addCCArgs( |
| 7193 | 7193 | } |
| 7194 | 7194 | |
| 7195 | 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 | 7200 | const ssp_buf_size = mod.stack_protector; |
| 7198 | 7201 | if (ssp_buf_size != 0) { |
| ... | ... | @@ -7258,9 +7261,10 @@ pub fn addCCArgs( |
| 7258 | 7261 | const is_enabled = target.cpu.features.isEnabled(index); |
| 7259 | 7262 | |
| 7260 | 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 | 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 | 7268 | continue; |
| 7265 | 7269 | |
| 7266 | 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 | 343 | // See https://github.com/ziglang/zig/issues/23539 |
| 344 | 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 | 351 | if (is_enabled) { |
| 349 | 352 | try buf.ensureUnusedCapacity(2 + llvm_name.len); |