From 389d1177a57a442b7814d9fdede2a088c614b69d Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 4 May 2021 18:43:31 +0200 Subject: [PATCH 1/8] stage1: Fix LLVM error in inline asm invocation Pointer types need an extra indirection layer during the generation of the function prototype for inline asm blocks. Closes #3606 --- src/stage1/codegen.cpp | 4 +++- test/stage1/behavior/asm.zig | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index ede15e4394..015a64f68a 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -4880,6 +4880,9 @@ static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutableGen *executable, I type_ref = get_llvm_type(g, wider_type); value_ref = gen_widen_or_shorten(g, false, type, wider_type, value_ref); } + } else if (handle_is_ptr(g, type)) { + ZigType *gen_type = get_pointer_to_type(g, type, true); + type_ref = get_llvm_type(g, gen_type); } param_types[param_index] = type_ref; @@ -9296,7 +9299,6 @@ static void init(CodeGen *g) { char *layout_str = LLVMCopyStringRepOfTargetData(g->target_data_ref); LLVMSetDataLayout(g->module, layout_str); - assert(g->pointer_size_bytes == LLVMPointerSize(g->target_data_ref)); g->is_big_endian = (LLVMByteOrder(g->target_data_ref) == LLVMBigEndian); diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig index 170ad3325d..ade774910d 100644 --- a/test/stage1/behavior/asm.zig +++ b/test/stage1/behavior/asm.zig @@ -87,6 +87,21 @@ test "sized integer/float in asm input" { ); } +test "struct/array/union types as input values" { + asm volatile ("" + : + : [_] "m" (@as([1]u32, undefined)) + ); // fails + asm volatile ("" + : + : [_] "m" (@as(struct { x: u32, y: u8 }, undefined)) + ); // fails + asm volatile ("" + : + : [_] "m" (@as(union { x: u32, y: u8 }, undefined)) + ); // fails +} + extern fn this_is_my_alias() i32; export fn derp() i32 { From 4bf093f1a00e481d923452955ab9c394c30b8694 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 4 May 2021 18:45:52 +0200 Subject: [PATCH 2/8] compiler-rt: Better selection of __clzsi implementation To be honest all this detection logic is starting to become a real PITA, the ARM32 version can be possibly removed as the generic version optimizes pretty well... --- lib/std/special/compiler_rt/clzsi2.zig | 27 +++++++++++++++------ lib/std/special/compiler_rt/clzsi2_test.zig | 2 ++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/std/special/compiler_rt/clzsi2.zig b/lib/std/special/compiler_rt/clzsi2.zig index c10786b462..d7464d5ea9 100644 --- a/lib/std/special/compiler_rt/clzsi2.zig +++ b/lib/std/special/compiler_rt/clzsi2.zig @@ -26,6 +26,8 @@ fn __clzsi2_generic(a: i32) callconv(.C) i32 { } fn __clzsi2_thumb1() callconv(.Naked) void { + @setRuntimeSafety(false); + // Similar to the generic version with the last two rounds replaced by a LUT asm volatile ( \\ movs r1, #32 @@ -58,6 +60,8 @@ fn __clzsi2_thumb1() callconv(.Naked) void { } fn __clzsi2_arm32() callconv(.Naked) void { + @setRuntimeSafety(false); + asm volatile ( \\ // Assumption: n != 0 \\ // r0: n @@ -104,13 +108,22 @@ fn __clzsi2_arm32() callconv(.Naked) void { unreachable; } -pub const __clzsi2 = switch (std.Target.current.cpu.arch) { - .arm, .armeb => if (std.Target.arm.featureSetHas(std.Target.current.cpu.features, .noarm)) - __clzsi2_thumb1 - else - __clzsi2_arm32, - .thumb, .thumbeb => __clzsi2_thumb1, - else => __clzsi2_generic, +pub const __clzsi2 = impl: { + switch (std.Target.current.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => { + const use_thumb1 = + (std.Target.current.cpu.arch.isThumb() or + std.Target.arm.featureSetHas(std.Target.current.cpu.features, .noarm)) and + !std.Target.arm.featureSetHas(std.Target.current.cpu.features, .thumb2); + + if (use_thumb1) break :impl __clzsi2_thumb1 + // From here on we're either targeting Thumb2 or ARM. + else if (!std.Target.current.cpu.arch.isThumb()) break :impl __clzsi2_arm32 + // Use the generic implementation otherwise. + else break :impl __clzsi2_generic; + }, + else => break :impl __clzsi2_generic, + } }; test "test clzsi2" { diff --git a/lib/std/special/compiler_rt/clzsi2_test.zig b/lib/std/special/compiler_rt/clzsi2_test.zig index 2b860afd22..c74a1c3ec2 100644 --- a/lib/std/special/compiler_rt/clzsi2_test.zig +++ b/lib/std/special/compiler_rt/clzsi2_test.zig @@ -7,6 +7,8 @@ const clzsi2 = @import("clzsi2.zig"); const testing = @import("std").testing; fn test__clzsi2(a: u32, expected: i32) void { + // XXX At high optimization levels this test may be horribly miscompiled if + // one of the naked implementations is selected. var nakedClzsi2 = clzsi2.__clzsi2; var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2); var x = @bitCast(i32, a); From afbcb6209dbe6812679324aab564884085b8cf44 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 4 May 2021 18:52:53 +0200 Subject: [PATCH 3/8] std: Initial bringup for Linux on Thumb2 There are some small problems here and there, mostly due to the pointers having the lsb set and disrupting the fn alignment tests and the `@FrameSize` implementation. --- lib/std/os/bits/linux.zig | 2 +- lib/std/os/linux.zig | 1 + lib/std/os/linux/thumb.zig | 168 ++++++++++++++++++++++++++++++ lib/std/os/linux/tls.zig | 6 +- lib/std/special/c.zig | 2 +- lib/std/start.zig | 2 +- lib/std/zig/system.zig | 9 ++ test/stage1/behavior/align.zig | 3 + test/stage1/behavior/async_fn.zig | 3 + test/stage1/behavior/atomics.zig | 5 +- 10 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 lib/std/os/linux/thumb.zig diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 94da5cc99a..97cdbef782 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -18,7 +18,7 @@ pub usingnamespace switch (builtin.arch) { .i386 => @import("linux/i386.zig"), .x86_64 => @import("linux/x86_64.zig"), .aarch64 => @import("linux/arm64.zig"), - .arm => @import("linux/arm-eabi.zig"), + .arm, .thumb => @import("linux/arm-eabi.zig"), .riscv64 => @import("linux/riscv64.zig"), .sparcv9 => @import("linux/sparc64.zig"), .mips, .mipsel => @import("linux/mips.zig"), diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 4a67ca7685..6c88d9eae1 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -23,6 +23,7 @@ pub usingnamespace switch (builtin.arch) { .x86_64 => @import("linux/x86_64.zig"), .aarch64 => @import("linux/arm64.zig"), .arm => @import("linux/arm-eabi.zig"), + .thumb => @import("linux/thumb.zig"), .riscv64 => @import("linux/riscv64.zig"), .sparcv9 => @import("linux/sparc64.zig"), .mips, .mipsel => @import("linux/mips.zig"), diff --git a/lib/std/os/linux/thumb.zig b/lib/std/os/linux/thumb.zig new file mode 100644 index 0000000000..5db9d2cbf4 --- /dev/null +++ b/lib/std/os/linux/thumb.zig @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +usingnamespace @import("../bits.zig"); + +// The syscall interface is identical to the ARM one but we're facing an extra +// challenge: r7, the register where the syscall number is stored, may be +// reserved for the frame pointer. +// Save and restore r7 around the syscall without touching the stack pointer not +// to break the frame chain. + +pub fn syscall0(number: SYS) usize { + @setRuntimeSafety(false); + + var buf: [2]usize = .{ @enumToInt(number), undefined }; + return asm volatile ( + \\ str r7, [%[tmp], #4] + \\ ldr r7, [%[tmp]] + \\ svc #0 + \\ ldr r7, [%[tmp], #4] + : [ret] "={r0}" (-> usize) + : [tmp] "{r1}" (buf) + : "memory" + ); +} + +pub fn syscall1(number: SYS, arg1: usize) usize { + @setRuntimeSafety(false); + + var buf: [2]usize = .{ @enumToInt(number), undefined }; + return asm volatile ( + \\ str r7, [%[tmp], #4] + \\ ldr r7, [%[tmp]] + \\ svc #0 + \\ ldr r7, [%[tmp], #4] + : [ret] "={r0}" (-> usize) + : [tmp] "{r1}" (buf), + [arg1] "{r0}" (arg1) + : "memory" + ); +} + +pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { + @setRuntimeSafety(false); + + var buf: [2]usize = .{ @enumToInt(number), undefined }; + return asm volatile ( + \\ str r7, [%[tmp], #4] + \\ ldr r7, [%[tmp]] + \\ svc #0 + \\ ldr r7, [%[tmp], #4] + : [ret] "={r0}" (-> usize) + : [tmp] "{r2}" (buf), + [arg1] "{r0}" (arg1), + [arg2] "{r1}" (arg2) + : "memory" + ); +} + +pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { + @setRuntimeSafety(false); + + var buf: [2]usize = .{ @enumToInt(number), undefined }; + return asm volatile ( + \\ str r7, [%[tmp], #4] + \\ ldr r7, [%[tmp]] + \\ svc #0 + \\ ldr r7, [%[tmp], #4] + : [ret] "={r0}" (-> usize) + : [tmp] "{r3}" (buf), + [arg1] "{r0}" (arg1), + [arg2] "{r1}" (arg2), + [arg3] "{r2}" (arg3) + : "memory" + ); +} + +pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { + @setRuntimeSafety(false); + + var buf: [2]usize = .{ @enumToInt(number), undefined }; + return asm volatile ( + \\ str r7, [%[tmp], #4] + \\ ldr r7, [%[tmp]] + \\ svc #0 + \\ ldr r7, [%[tmp], #4] + : [ret] "={r0}" (-> usize) + : [tmp] "{r4}" (buf), + [arg1] "{r0}" (arg1), + [arg2] "{r1}" (arg2), + [arg3] "{r2}" (arg3), + [arg4] "{r3}" (arg4) + : "memory" + ); +} + +pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { + @setRuntimeSafety(false); + + var buf: [2]usize = .{ @enumToInt(number), undefined }; + return asm volatile ( + \\ str r7, [%[tmp], #4] + \\ ldr r7, [%[tmp]] + \\ svc #0 + \\ ldr r7, [%[tmp], #4] + : [ret] "={r0}" (-> usize) + : [tmp] "{r5}" (buf), + [arg1] "{r0}" (arg1), + [arg2] "{r1}" (arg2), + [arg3] "{r2}" (arg3), + [arg4] "{r3}" (arg4), + [arg5] "{r4}" (arg5) + : "memory" + ); +} + +pub fn syscall6( + number: SYS, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: usize, + arg5: usize, + arg6: usize, +) usize { + @setRuntimeSafety(false); + + var buf: [2]usize = .{ @enumToInt(number), undefined }; + return asm volatile ( + \\ str r7, [%[tmp], #4] + \\ ldr r7, [%[tmp]] + \\ svc #0 + \\ ldr r7, [%[tmp], #4] + : [ret] "={r0}" (-> usize) + : [tmp] "{r6}" (buf), + [arg1] "{r0}" (arg1), + [arg2] "{r1}" (arg2), + [arg3] "{r2}" (arg3), + [arg4] "{r3}" (arg4), + [arg5] "{r4}" (arg5), + [arg6] "{r5}" (arg6) + : "memory" + ); +} + +/// This matches the libc clone function. +pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; + +pub fn restore() callconv(.Naked) void { + return asm volatile ( + \\ mov r7, %[number] + \\ svc #0 + : + : [number] "I" (@enumToInt(SYS.sigreturn)) + ); +} + +pub fn restore_rt() callconv(.Naked) void { + return asm volatile ( + \\ mov r7, %[number] + \\ svc #0 + : + : [number] "I" (@enumToInt(SYS.rt_sigreturn)) + : "memory" + ); +} diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index 4a36b0d485..0830dcbfda 100644 --- a/lib/std/os/linux/tls.zig +++ b/lib/std/os/linux/tls.zig @@ -53,7 +53,7 @@ const TLSVariant = enum { }; const tls_variant = switch (builtin.arch) { - .arm, .armeb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI, + .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI, .x86_64, .i386, .sparcv9 => TLSVariant.VariantII, else => @compileError("undefined tls_variant for this architecture"), }; @@ -62,7 +62,7 @@ const tls_variant = switch (builtin.arch) { const tls_tcb_size = switch (builtin.arch) { // ARM EABI mandates enough space for two pointers: the first one points to // the DTV while the second one is unspecified but reserved - .arm, .armeb, .aarch64, .aarch64_be => 2 * @sizeOf(usize), + .arm, .armeb, .thumb, .aarch64, .aarch64_be => 2 * @sizeOf(usize), // One pointer-sized word that points either to the DTV or the TCB itself else => @sizeOf(usize), }; @@ -150,7 +150,7 @@ pub fn setThreadPointer(addr: usize) void { : [addr] "r" (addr) ); }, - .arm => { + .arm, .thumb => { const rc = std.os.linux.syscall1(.set_tls, addr); assert(rc == 0); }, diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index c7084f3a11..29feae830f 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -385,7 +385,7 @@ fn clone() callconv(.Naked) void { \\ svc #0 ); }, - .arm => { + .arm, .thumb => { // __clone(func, stack, flags, arg, ptid, tls, ctid) // r0, r1, r2, r3, +0, +4, +8 diff --git a/lib/std/start.zig b/lib/std/start.zig index 89f5eb0b1f..e1e331a682 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -176,7 +176,7 @@ fn _start() callconv(.Naked) noreturn { : [argc] "={esp}" (-> [*]usize) ); }, - .aarch64, .aarch64_be, .arm, .armeb => { + .aarch64, .aarch64_be, .arm, .armeb, .thumb => { argc_argv_ptr = asm volatile ( \\ mov fp, #0 \\ mov lr, #0 diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 42099c6efe..d9657d9db4 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -349,6 +349,15 @@ pub const NativeTargetInfo = struct { } } }, + .arm, .armeb => { + // XXX What do we do if the target has the noarm feature? + // What do we do if the user specifies +thumb_mode? + }, + .thumb, .thumbeb => { + result.target.cpu.features.addFeature( + @enumToInt(std.Target.arm.Feature.thumb_mode), + ); + }, else => {}, } cross_target.updateCpuFeatures(&result.target.cpu.features); diff --git a/test/stage1/behavior/align.zig b/test/stage1/behavior/align.zig index 0a0cc3bcc0..38f5df0176 100644 --- a/test/stage1/behavior/align.zig +++ b/test/stage1/behavior/align.zig @@ -141,6 +141,7 @@ fn alignedBig() align(16) i32 { test "@alignCast functions" { // function alignment is a compile error on wasm32/wasm64 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; + if (builtin.arch == .thumb) return error.SkipZigTest; expect(fnExpectsOnly1(simple4) == 0x19); } @@ -157,6 +158,7 @@ fn simple4() align(4) i32 { test "generic function with align param" { // function alignment is a compile error on wasm32/wasm64 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; + if (builtin.arch == .thumb) return error.SkipZigTest; expect(whyWouldYouEverDoThis(1) == 0x1); expect(whyWouldYouEverDoThis(4) == 0x1); @@ -338,6 +340,7 @@ test "align(@alignOf(T)) T does not force resolution of T" { test "align(N) on functions" { // function alignment is a compile error on wasm32/wasm64 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; + if (builtin.arch == .thumb) return error.SkipZigTest; expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0); } diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index 0765eac7e8..09db0eeb29 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -110,6 +110,9 @@ test "calling an inferred async function" { } test "@frameSize" { + if (builtin.arch == .thumb or builtin.arch == .thumbeb) + return error.SkipZigTest; + const S = struct { fn doTheTest() void { { diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig index f9703e7308..d49ca730e6 100644 --- a/test/stage1/behavior/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -149,9 +149,10 @@ fn testAtomicStore() void { } test "atomicrmw with floats" { - if (builtin.arch == .aarch64 or builtin.arch == .arm or builtin.arch == .riscv64) { + switch (builtin.arch) { // https://github.com/ziglang/zig/issues/4457 - return error.SkipZigTest; + .aarch64, .arm, .thumb, .riscv64 => return error.SkipZigTest, + else => {}, } testAtomicRmwFloat(); comptime testAtomicRmwFloat(); From ad33e34836866e0528ca7601155c5dafe538347b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 5 May 2021 19:04:14 +0200 Subject: [PATCH 4/8] tests: re-enable reduce behavior tests for wasm32 --- test/stage1/behavior/vector.zig | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index d3276496de..4b88ce020a 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -510,19 +510,6 @@ test "vector reduce operation" { const N = @typeInfo(@TypeOf(x)).Array.len; const TX = @typeInfo(@TypeOf(x)).Array.child; - // wasmtime: unknown import: `env::fminf` has not been defined - // https://github.com/ziglang/zig/issues/8131 - switch (std.builtin.arch) { - .wasm32 => switch (@typeInfo(TX)) { - .Float => switch (op) { - .Min, .Max, => return, - else => {}, - }, - else => {}, - }, - else => {}, - } - var r = @reduce(op, @as(Vector(N, TX), x)); switch (@typeInfo(TX)) { .Int, .Bool => expectEqual(expected, r), From ef8a666797d6fb2849762dd157379ca227762c90 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 5 May 2021 09:21:32 +0200 Subject: [PATCH 5/8] zld: cleanup relocs and flag errors on unhandled symbol types --- src/link/MachO/Object.zig | 24 ++++++++++++++++++++---- src/link/MachO/Symbol.zig | 10 ++++++++++ src/link/MachO/Zld.zig | 9 +++++++++ src/link/MachO/reloc/aarch64.zig | 2 +- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 31cc63cfe0..3b8aa7a6cf 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -343,14 +343,22 @@ pub fn parseSymbols(self: *Object) !void { _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); for (slice) |sym| { + const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); + if (Symbol.isStab(sym)) { - log.err("TODO handle stabs embedded within object files", .{}); - return error.HandleStabsInObjects; + log.err("stab {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + } + if (Symbol.isIndr(sym)) { + log.err("indirect symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + } + if (Symbol.isAbs(sym)) { + log.err("absolute symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; } - const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); const name = try self.allocator.dupe(u8, sym_name); - const symbol: *Symbol = symbol: { if (Symbol.isSect(sym)) { const linkage: Symbol.Regular.Linkage = linkage: { @@ -374,6 +382,14 @@ pub fn parseSymbols(self: *Object) !void { break :symbol ®ular.base; } + if (sym.n_value != 0) { + log.err("common symbol {s} in {s}", .{ sym_name, self.name.? }); + return error.UnhandledSymbolType; + // const comm_size = sym.n_value; + // const comm_align = (sym.n_desc >> 8) & 0x0f; + // log.warn("Common symbol: size 0x{x}, align 0x{x}", .{ comm_size, comm_align }); + } + const undef = try self.allocator.create(Symbol.Unresolved); errdefer self.allocator.destroy(undef); undef.* = .{ diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 4b8ee3c77c..f928c807a3 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -133,6 +133,16 @@ pub fn isUndf(sym: macho.nlist_64) bool { return type_ == macho.N_UNDF; } +pub fn isIndr(sym: macho.nlist_64) bool { + const type_ = macho.N_TYPE & sym.n_type; + return type_ == macho.N_INDR; +} + +pub fn isAbs(sym: macho.nlist_64) bool { + const type_ = macho.N_TYPE & sym.n_type; + return type_ == macho.N_ABS; +} + pub fn isWeakDef(sym: macho.nlist_64) bool { return (sym.n_desc & macho.N_WEAK_DEF) != 0; } diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 1b343fad3f..50a4f77109 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -1553,9 +1553,16 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { // TLV is handled via a separate offset mechanism. // Calculate the offset to the initializer. if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { + log.warn("HIT", .{}); + log.warn(" | rel {any}", .{rel.cast(reloc.Unsigned).?}); + log.warn(" | name {s}", .{rel.target.symbol.name}); + log.warn(" | target address 0x{x}", .{args.target_addr}); + // TODO we don't want to save offset to tlv_bootstrap if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv; + log.warn(" | object {s}", .{rel.target.symbol.cast(Symbol.Regular).?.file.name.?}); + const base_addr = blk: { if (self.tlv_data_section_index) |index| { const tlv_data = target_seg.sections.items[index]; @@ -1565,6 +1572,8 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { break :blk tlv_bss.addr; } }; + log.warn(" | base address 0x{x}", .{base_addr}); + log.warn(" | offset 0x{x}", .{args.target_addr - base_addr}); // Since we require TLV data to always preceed TLV bss section, we calculate // offsets wrt to the former if it is defined; otherwise, wrt to the latter. try self.threadlocal_offsets.append(self.allocator, args.target_addr - base_addr); diff --git a/src/link/MachO/reloc/aarch64.zig b/src/link/MachO/reloc/aarch64.zig index dbc233b3a5..c08934d84b 100644 --- a/src/link/MachO/reloc/aarch64.zig +++ b/src/link/MachO/reloc/aarch64.zig @@ -25,7 +25,7 @@ pub const Branch = struct { log.debug(" | displacement 0x{x}", .{displacement}); var inst = branch.inst; - inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2); + inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32()); } }; From 88d40fc005894ef84eb8bf54314da293772c4bba Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 6 May 2021 17:02:39 +0200 Subject: [PATCH 6/8] zld: sort tlv offsets by source address --- src/link/MachO/Archive.zig | 9 ++++++--- src/link/MachO/Object.zig | 2 +- src/link/MachO/Zld.zig | 40 ++++++++++++++++++++------------------ 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 5a0b9609ad..702a807a4d 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -16,7 +16,7 @@ allocator: *Allocator, arch: ?std.Target.Cpu.Arch = null, file: ?fs.File = null, header: ?ar_hdr = null, -name: ?[]u8 = null, +name: ?[]const u8 = null, /// Parsed table of contents. /// Each symbol name points to a list of all definition @@ -195,7 +195,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { } /// Caller owns the Object instance. -pub fn parseObject(self: Archive, offset: u32) !Object { +pub fn parseObject(self: Archive, offset: u32) !*Object { var reader = self.file.?.reader(); try reader.context.seekTo(offset); @@ -217,7 +217,10 @@ pub fn parseObject(self: Archive, offset: u32) !Object { break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_name }); }; - var object = Object.init(self.allocator); + var object = try self.allocator.create(Object); + errdefer self.allocator.destroy(object); + + object.* = Object.init(self.allocator); object.arch = self.arch.?; object.file = try fs.cwd().openFile(self.name.?, .{}); object.name = name; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 3b8aa7a6cf..4d2ade7aad 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -22,7 +22,7 @@ arch: ?std.Target.Cpu.Arch = null, header: ?macho.mach_header_64 = null, file: ?fs.File = null, file_offset: ?u32 = null, -name: ?[]u8 = null, +name: ?[]const u8 = null, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, sections: std.ArrayListUnmanaged(Section) = .{}, diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 50a4f77109..4d19da1e97 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -82,7 +82,7 @@ unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, strtab_dir: std.StringHashMapUnmanaged(u32) = .{}, -threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, +threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, stubs: std.ArrayListUnmanaged(*Symbol) = .{}, got_entries: std.ArrayListUnmanaged(*Symbol) = .{}, @@ -92,6 +92,15 @@ stub_helper_stubs_start_off: ?u64 = null, mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, +const TlvOffset = struct { + source_addr: u64, + offset: u64, + + fn cmp(context: void, a: TlvOffset, b: TlvOffset) bool { + return a.source_addr < b.source_addr; + } +}; + const MappingKey = struct { object_id: u16, source_sect_id: u16, @@ -277,7 +286,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { object.* = Object.init(self.allocator); object.arch = self.arch.?; - object.name = try self.allocator.dupe(u8, input.name); + object.name = input.name; object.file = input.file; try object.parse(); try self.objects.append(self.allocator, object); @@ -288,7 +297,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { archive.* = Archive.init(self.allocator); archive.arch = self.arch.?; - archive.name = try self.allocator.dupe(u8, input.name); + archive.name = input.name; archive.file = input.file; try archive.parse(); try self.archives.append(self.allocator, archive); @@ -1362,10 +1371,7 @@ fn resolveSymbols(self: *Zld) !void { }; assert(offsets.items.len > 0); - const object = try self.allocator.create(Object); - errdefer self.allocator.destroy(object); - - object.* = try archive.parseObject(offsets.items[0]); + const object = try archive.parseObject(offsets.items[0]); try self.objects.append(self.allocator, object); try self.resolveSymbolsInObject(object); @@ -1553,16 +1559,9 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { // TLV is handled via a separate offset mechanism. // Calculate the offset to the initializer. if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: { - log.warn("HIT", .{}); - log.warn(" | rel {any}", .{rel.cast(reloc.Unsigned).?}); - log.warn(" | name {s}", .{rel.target.symbol.name}); - log.warn(" | target address 0x{x}", .{args.target_addr}); - // TODO we don't want to save offset to tlv_bootstrap if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv; - log.warn(" | object {s}", .{rel.target.symbol.cast(Symbol.Regular).?.file.name.?}); - const base_addr = blk: { if (self.tlv_data_section_index) |index| { const tlv_data = target_seg.sections.items[index]; @@ -1572,11 +1571,12 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { break :blk tlv_bss.addr; } }; - log.warn(" | base address 0x{x}", .{base_addr}); - log.warn(" | offset 0x{x}", .{args.target_addr - base_addr}); // Since we require TLV data to always preceed TLV bss section, we calculate // offsets wrt to the former if it is defined; otherwise, wrt to the latter. - try self.threadlocal_offsets.append(self.allocator, args.target_addr - base_addr); + try self.threadlocal_offsets.append(self.allocator, .{ + .source_addr = args.source_addr, + .offset = args.target_addr - base_addr, + }); } }, .got_page, .got_page_off, .got_load, .got => { @@ -2102,10 +2102,12 @@ fn flush(self: *Zld) !void { var stream = std.io.fixedBufferStream(buffer); var writer = stream.writer(); + std.sort.sort(TlvOffset, self.threadlocal_offsets.items, {}, TlvOffset.cmp); + const seek_amt = 2 * @sizeOf(u64); - while (self.threadlocal_offsets.popOrNull()) |offset| { + for (self.threadlocal_offsets.items) |tlv| { try writer.context.seekBy(seek_amt); - try writer.writeIntLittle(u64, offset); + try writer.writeIntLittle(u64, tlv.offset); } try self.file.?.pwriteAll(buffer, sect.offset); From 96e593145dcdb53ca02f2a365abc68415c4302b6 Mon Sep 17 00:00:00 2001 From: lars Date: Thu, 6 May 2021 08:33:45 +0200 Subject: [PATCH 7/8] stage1: improve message for missing fn return type Coming from other languages it might be tempting for programmers to accidentally leave out the return type instead of returning 'void'. The error for this used to be error: invalid token: '{' pub fn main() { ^ which is misleading. The '{' is expected but only after a return type. The new message is error: expected return type (use 'void' to return nothing), found: '{' pub fn main() { ^ which not only points out the real error but also hints at a (probably) very common case where someone coming from e.g. Go is used to not specifying a return type if a function returns nothing and thus forgets to put 'void' there. It might seem overkill to hint at the 'void' option but then the compiler error messages are our user interface to the programmer. We can be better than other languages in our error messages and leaving out the return type seems to be a rather clear indication of the above mentioned issue. Adding this will help more than distract. --- src/stage1/parser.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp index d57277cd51..f152f245b7 100644 --- a/src/stage1/parser.cpp +++ b/src/stage1/parser.cpp @@ -825,7 +825,16 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { AstNode *return_type = nullptr; if (anytype == nullptr) { exmark = eat_token_if(pc, TokenIdBang); - return_type = ast_expect(pc, ast_parse_type_expr); + return_type = ast_parse_type_expr(pc); + if (return_type == nullptr) { + Token *next = peek_token(pc); + ast_error( + pc, + next, + "expected return type (use 'void' to return nothing), found: '%s'", + token_name(next->id) + ); + } } AstNode *res = ast_create_node(pc, NodeTypeFnProto, first); From 84d5cc31c560749eda1e36b7bc9e6cf542eee550 Mon Sep 17 00:00:00 2001 From: Michael Dusan Date: Thu, 6 May 2021 12:53:48 -0400 Subject: [PATCH 8/8] fix test to restore cwd after chdir - `../zig-cache/tmp` is no longer created by subsequent test(s) --- lib/std/os/test.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index f0f0a7c988..16521d7e27 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -43,6 +43,8 @@ test "chdir smoke test" { // Next, change current working directory to one level above const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute try os.chdir(parent); + // Restore cwd because process may have other tests that do not tolerate chdir. + defer os.chdir(old_cwd) catch unreachable; var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined; const new_cwd = try os.getcwd(new_cwd_buf[0..]); expect(mem.eql(u8, parent, new_cwd));