Commit afbcb6209d

LemonBoy <thatlemon@gmail.com>
2021-05-04 18:52:53
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.
1 parent 4bf093f
Changed files (10)
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"),
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"
+    );
+}
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);
         },
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"),
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
 
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);
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
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);
 }
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 {
             {
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();