From afbcb6209dbe6812679324aab564884085b8cf44 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 4 May 2021 18:52:53 +0200 Subject: [PATCH] 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 94da5cc99a6112772f4f9706443c897a25624ceb..97cdbef782492180cf3de1938ebb16dbdc534dd0 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 4a67ca768583668bf40f4baba60d45867880daf7..6c88d9eae1d80beaf10772f5dc42dd6e42e427e8 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 0000000000000000000000000000000000000000..5db9d2cbf4c0cec1a6b34c820ad5ae9631609d69 --- /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 4a36b0d48540efc94cc9f60825f9c5d51c67c813..0830dcbfdaf770cb3d9fe55bee7c006d113b6f9c 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 c7084f3a11b784ed8eb296e8f82b57a938070b90..29feae830f9e7d6637af4dd8f013942105e17896 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 89f5eb0b1f74ba42895a1b4447873b9acb5e4191..e1e331a68257147bdd5b37637c786fa25c9ec81d 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 42099c6efe79c5b83c6d66dee51be5b1f85ba30a..d9657d9db445697a50e35dbe7c7d6bddfdc59798 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 0a0cc3bcc043b16733d1f1726d4a82ee7b46dd8e..38f5df017650d957f8a31eb84d4086894d430e9d 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 0765eac7e8480addaf022c321f8c7571fbe41712..09db0eeb29f7316dad1fc7900429f10473d79fd2 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 f9703e7308309d36b59be9f7db6a4afdf04ab2c8..d49ca730e630beef4223b6bb4e6045aa9f9c768f 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(); -- 2.54.0