Commit 96fe971051

LemonBoy <thatlemon@gmail.com>
2020-10-19 11:40:46
std: Minor changes to startup code
* Smaller startup sequence for ppc64 * Terminate the frame-pointer chain when executing _start * Make the stack traces work on ppc64 * Make the stack traces coloured on ppc64, some ioctls numbers are different and the whole set of constants should be audited.
1 parent 3ab4d11
Changed files (4)
lib/std/os/bits/linux.zig
@@ -29,6 +29,7 @@ pub usingnamespace @import("linux/prctl.zig");
 pub usingnamespace @import("linux/securebits.zig");
 
 const is_mips = builtin.arch.isMIPS();
+const is_ppc64 = builtin.arch.isPPC64();
 
 pub const pid_t = i32;
 pub const fd_t = i32;
@@ -540,8 +541,8 @@ pub const TIOCGPGRP = 0x540F;
 pub const TIOCSPGRP = 0x5410;
 pub const TIOCOUTQ = if (is_mips) 0x7472 else 0x5411;
 pub const TIOCSTI = 0x5412;
-pub const TIOCGWINSZ = if (is_mips) 0x40087468 else 0x5413;
-pub const TIOCSWINSZ = if (is_mips) 0x80087467 else 0x5414;
+pub const TIOCGWINSZ = if (is_mips or is_ppc64) 0x40087468 else 0x5413;
+pub const TIOCSWINSZ = if (is_mips or is_ppc64) 0x80087467 else 0x5414;
 pub const TIOCMGET = 0x5415;
 pub const TIOCMBIS = 0x5416;
 pub const TIOCMBIC = 0x5417;
lib/std/debug.zig
@@ -327,9 +327,9 @@ pub fn writeStackTrace(
 }
 
 pub const StackIterator = struct {
-    // Skip every frame before this address is found
+    // Skip every frame before this address is found.
     first_address: ?usize,
-    // Last known value of the frame pointer register
+    // Last known value of the frame pointer register.
     fp: usize,
 
     pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
@@ -339,14 +339,19 @@ pub const StackIterator = struct {
         };
     }
 
-    // On some architectures such as x86 the frame pointer is the address where
-    // the previous fp is stored, while on some other architectures such as
-    // RISC-V it points to the "top" of the frame, just above where the previous
-    // fp and the return address are stored.
+    // Negative offset of the saved BP wrt the frame pointer.
     const fp_offset = if (builtin.arch.isRISCV())
+        // On RISC-V the frame pointer points to the top of the saved register
+        // area, on pretty much every other architecture it points to the stack
+        // slot where the previous frame pointer is saved.
         2 * @sizeOf(usize)
     else
         0;
+    // Positive offset of the saved PC wrt the frame pointer.
+    const pc_offset = if (builtin.arch == .powerpc64le)
+        2 * @sizeOf(usize)
+    else
+        @sizeOf(usize);
 
     pub fn next(self: *StackIterator) ?usize {
         var address = self.next_internal() orelse return null;
@@ -364,7 +369,7 @@ pub const StackIterator = struct {
     fn next_internal(self: *StackIterator) ?usize {
         const fp = math.sub(usize, self.fp, fp_offset) catch return null;
 
-        // Sanity check
+        // Sanity check.
         if (fp == 0 or !mem.isAligned(fp, @alignOf(usize)))
             return null;
 
@@ -373,11 +378,14 @@ pub const StackIterator = struct {
         // Sanity check: the stack grows down thus all the parent frames must be
         // be at addresses that are greater (or equal) than the previous one.
         // A zero frame pointer often signals this is the last frame, that case
-        // is gracefully handled by the next call to next_internal
+        // is gracefully handled by the next call to next_internal.
         if (new_fp != 0 and new_fp < self.fp)
             return null;
 
-        const new_pc = @intToPtr(*const usize, fp + @sizeOf(usize)).*;
+        const new_pc = @intToPtr(
+            *const usize,
+            math.add(usize, fp, pc_offset) catch return null,
+        ).*;
 
         self.fp = new_fp;
 
lib/std/start.zig
@@ -102,46 +102,49 @@ fn _start() callconv(.Naked) noreturn {
 
     switch (builtin.arch) {
         .x86_64 => {
-            starting_stack_ptr = asm (""
+            starting_stack_ptr = asm volatile (
+                \\ xor %%rbp, %%rbp
                 : [argc] "={rsp}" (-> [*]usize)
             );
         },
         .i386 => {
-            starting_stack_ptr = asm (""
+            starting_stack_ptr = asm volatile (
+                \\ xor %%ebp, %%ebp
                 : [argc] "={esp}" (-> [*]usize)
             );
         },
-        .aarch64, .aarch64_be, .arm => {
-            starting_stack_ptr = asm ("mov %[argc], sp"
-                : [argc] "=r" (-> [*]usize)
+        .aarch64, .aarch64_be, .arm, .armeb => {
+            starting_stack_ptr = asm volatile (
+                \\ mov fp, #0
+                \\ mov lr, #0
+                : [argc] "={sp}" (-> [*]usize)
             );
         },
         .riscv64 => {
-            starting_stack_ptr = asm ("mv %[argc], sp"
-                : [argc] "=r" (-> [*]usize)
+            starting_stack_ptr = asm volatile (
+                \\ li s0, 0
+                \\ li ra, 0
+                : [argc] "={sp}" (-> [*]usize)
             );
         },
         .mips, .mipsel => {
-            // Need noat here because LLVM is free to pick any register
-            starting_stack_ptr = asm (
-                \\ .set noat
-                \\ move %[argc], $sp
-                : [argc] "=r" (-> [*]usize)
+            // The lr is already zeroed on entry, as specified by the ABI.
+            starting_stack_ptr = asm volatile (
+                \\ move $fp, $0
+                : [argc] "={sp}" (-> [*]usize)
             );
         },
         .powerpc64le => {
-            // Before returning the stack pointer, we have to set up a backchain
-            // and a few other registers required by the ELFv2 ABI.
+            // Setup the initial stack frame and clear the back chain pointer.
             // TODO: Support powerpc64 (big endian) on ELFv2.
-            starting_stack_ptr = asm (
+            starting_stack_ptr = asm volatile (
                 \\ mr 4, 1
-                \\ subi 1, 1, 32
-                \\ li 5, 0
-                \\ std 5, 0(1)
-                \\ mr %[argc], 4
-                : [argc] "=r" (-> [*]usize)
+                \\ li 0, 0
+                \\ stdu 0, -32(1)
+                \\ mtlr 0
+                : [argc] "={r4}" (-> [*]usize)
                 :
-                : "r4", "r5"
+                : "r0"
             );
         },
         else => @compileError("unsupported arch"),
lib/std/target.zig
@@ -735,6 +735,13 @@ pub const Target = struct {
                 };
             }
 
+            pub fn isPPC64(arch: Arch) bool {
+                return switch (arch) {
+                    .powerpc64, .powerpc64le => true,
+                    else => false,
+                };
+            }
+
             pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model {
                 for (arch.allCpuModels()) |cpu| {
                     if (mem.eql(u8, cpu_name, cpu.name)) {