Merge tag 'linux-kselftest-nolibc-6.6-rc1' of git://git.kernel.org/pub/scm/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 Aug 2023 02:03:24 +0000 (19:03 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 Aug 2023 02:03:24 +0000 (19:03 -0700)
Pull nolibc updates from Shuah Khan:
 "Nolibc:
   - improved portability by removing build errors with -ENOSYS
   - added syscall6() on MIPS to support pselect6() and mmap()
   - added setvbuf(), rmdir(), pipe(), pipe2()
   - add support for ppc/ppc64
   - environ is no longer optional
   - fixed frame pointer issues at -O0
   - dropped sys_stat() in favor of sys_statx()
   - centralized _start_c() to remove lots of asm code
   - switched size_t to __SIZE_TYPE__

  Selftests:
   - improved status reporting (success/warning/failure counts, path to
     log file)
   - various code cleanups (indent, unused variables, ...)
   - more consistent test numbering
   - enabled compiler warnings
   - dropped unreliable chmod_net test
   - improved reliability (create /dev/zero & /tmp, rely less on /proc)
   - new tests (brk/sbrk/mmap/munmap)
   - improved compatibility with musl
   - new run-nolibc-test target to build and run natively
   - new run-libc-test target to build and run against native libc
   - made the cmdline parser more reliable against boolean arguments
   - dropped dependency on memfd for vfprintf() test
   - nolibc-test is no longer stripped
   - added support for extending ARCH via XARCH

  Other:
   - add Thomas as co-maintainer"

* tag 'linux-kselftest-nolibc-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: (103 commits)
  tools/nolibc: avoid undesired casts in the __sysret() macro
  tools/nolibc: keep brk(), sbrk(), mmap() away from __sysret()
  tools/nolibc: silence ppc64 compile warnings
  selftests/nolibc: libc-test: use HOSTCC instead of CC
  tools/nolibc: stackprotector.h: make __stack_chk_init static
  selftests/nolibc: allow report with existing test log
  selftests/nolibc: add test support for ppc64
  selftests/nolibc: add test support for ppc64le
  selftests/nolibc: add test support for ppc
  selftests/nolibc: add XARCH and ARCH mapping support
  tools/nolibc: add support for powerpc64
  tools/nolibc: add support for powerpc
  MAINTAINERS: nolibc: add myself as co-maintainer
  selftests/nolibc: enable compiler warnings
  selftests/nolibc: don't strip nolibc-test
  selftests/nolibc: prevent out of bounds access in expect_vfprintf
  selftests/nolibc: use correct return type for read() and write()
  selftests/nolibc: avoid sign-compare warnings
  selftests/nolibc: avoid unused parameter warnings
  selftests/nolibc: make functions static if possible
  ...

23 files changed:
MAINTAINERS
tools/include/nolibc/Makefile
tools/include/nolibc/arch-aarch64.h
tools/include/nolibc/arch-arm.h
tools/include/nolibc/arch-i386.h
tools/include/nolibc/arch-loongarch.h
tools/include/nolibc/arch-mips.h
tools/include/nolibc/arch-powerpc.h [new file with mode: 0644]
tools/include/nolibc/arch-riscv.h
tools/include/nolibc/arch-s390.h
tools/include/nolibc/arch-x86_64.h
tools/include/nolibc/arch.h
tools/include/nolibc/crt.h [new file with mode: 0644]
tools/include/nolibc/nolibc.h
tools/include/nolibc/stackprotector.h
tools/include/nolibc/stdint.h
tools/include/nolibc/stdio.h
tools/include/nolibc/stdlib.h
tools/include/nolibc/sys.h
tools/include/nolibc/types.h
tools/include/nolibc/unistd.h
tools/testing/selftests/nolibc/Makefile
tools/testing/selftests/nolibc/nolibc-test.c

index 7f64262..4b8d634 100644 (file)
@@ -15010,6 +15010,7 @@ F:      include/linux/power/bq27xxx_battery.h
 
 NOLIBC HEADER FILE
 M:     Willy Tarreau <w@1wt.eu>
+M:     Thomas Weißschuh <linux@weissschuh.net>
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/wtarreau/nolibc.git
 F:     tools/include/nolibc/
index 64d67b0..909b6eb 100644 (file)
@@ -27,6 +27,7 @@ nolibc_arch := $(patsubst arm64,aarch64,$(ARCH))
 arch_file := arch-$(nolibc_arch).h
 all_files := \
                compiler.h \
+               crt.h \
                ctype.h \
                errno.h \
                nolibc.h \
index 11f294a..6c33c46 100644 (file)
@@ -8,34 +8,7 @@
 #define _NOLIBC_ARCH_AARCH64_H
 
 #include "compiler.h"
-
-/* The struct returned by the newfstatat() syscall. Differs slightly from the
- * x86_64's stat one by field ordering, so be careful.
- */
-struct sys_stat_struct {
-       unsigned long   st_dev;
-       unsigned long   st_ino;
-       unsigned int    st_mode;
-       unsigned int    st_nlink;
-       unsigned int    st_uid;
-       unsigned int    st_gid;
-
-       unsigned long   st_rdev;
-       unsigned long   __pad1;
-       long            st_size;
-       int             st_blksize;
-       int             __pad2;
-
-       long            st_blocks;
-       long            st_atime;
-       unsigned long   st_atime_nsec;
-       long            st_mtime;
-
-       unsigned long   st_mtime_nsec;
-       long            st_ctime;
-       unsigned long   st_ctime_nsec;
-       unsigned int    __unused[2];
-};
+#include "crt.h"
 
 /* Syscalls for AARCH64 :
  *   - registers are 64-bit
@@ -56,8 +29,8 @@ struct sys_stat_struct {
 ({                                                                            \
        register long _num  __asm__ ("x8") = (num);                           \
        register long _arg1 __asm__ ("x0");                                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "svc #0\n"                                                    \
                : "=r"(_arg1)                                                 \
                : "r"(_num)                                                   \
@@ -70,8 +43,8 @@ struct sys_stat_struct {
 ({                                                                            \
        register long _num  __asm__ ("x8") = (num);                           \
        register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "svc #0\n"                                                    \
                : "=r"(_arg1)                                                 \
                : "r"(_arg1),                                                 \
@@ -86,8 +59,8 @@ struct sys_stat_struct {
        register long _num  __asm__ ("x8") = (num);                           \
        register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
        register long _arg2 __asm__ ("x1") = (long)(arg2);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "svc #0\n"                                                    \
                : "=r"(_arg1)                                                 \
                : "r"(_arg1), "r"(_arg2),                                     \
@@ -103,8 +76,8 @@ struct sys_stat_struct {
        register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
        register long _arg2 __asm__ ("x1") = (long)(arg2);                    \
        register long _arg3 __asm__ ("x2") = (long)(arg3);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "svc #0\n"                                                    \
                : "=r"(_arg1)                                                 \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3),                         \
@@ -121,8 +94,8 @@ struct sys_stat_struct {
        register long _arg2 __asm__ ("x1") = (long)(arg2);                    \
        register long _arg3 __asm__ ("x2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("x3") = (long)(arg4);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "svc #0\n"                                                    \
                : "=r"(_arg1)                                                 \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4),             \
@@ -140,8 +113,8 @@ struct sys_stat_struct {
        register long _arg3 __asm__ ("x2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("x3") = (long)(arg4);                    \
        register long _arg5 __asm__ ("x4") = (long)(arg5);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "svc #0\n"                                                    \
                : "=r" (_arg1)                                                \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
@@ -160,8 +133,8 @@ struct sys_stat_struct {
        register long _arg4 __asm__ ("x3") = (long)(arg4);                    \
        register long _arg5 __asm__ ("x4") = (long)(arg5);                    \
        register long _arg6 __asm__ ("x5") = (long)(arg6);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "svc #0\n"                                                    \
                : "=r" (_arg1)                                                \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
@@ -171,33 +144,13 @@ struct sys_stat_struct {
        _arg1;                                                                \
 })
 
-char **environ __attribute__((weak));
-const unsigned long *_auxv __attribute__((weak));
-
 /* startup code */
-void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void)
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
 {
        __asm__ volatile (
-#ifdef _NOLIBC_STACKPROTECTOR
-               "bl __stack_chk_init\n"   /* initialize stack protector                     */
-#endif
-               "ldr x0, [sp]\n"     /* argc (x0) was in the stack                          */
-               "add x1, sp, 8\n"    /* argv (x1) = sp                                      */
-               "lsl x2, x0, 3\n"    /* envp (x2) = 8*argc ...                              */
-               "add x2, x2, 8\n"    /*           + 8 (skip null)                           */
-               "add x2, x2, x1\n"   /*           + argv                                    */
-               "adrp x3, environ\n"          /* x3 = &environ (high bits)                  */
-               "str x2, [x3, #:lo12:environ]\n" /* store envp into environ                 */
-               "mov x4, x2\n"       /* search for auxv (follows NULL after last env)       */
-               "0:\n"
-               "ldr x5, [x4], 8\n"  /* x5 = *x4; x4 += 8                                   */
-               "cbnz x5, 0b\n"      /* and stop at NULL after last env                     */
-               "adrp x3, _auxv\n"   /* x3 = &_auxv (high bits)                             */
-               "str x4, [x3, #:lo12:_auxv]\n" /* store x4 into _auxv                       */
-               "and sp, x1, -16\n"  /* sp must be 16-byte aligned in the callee            */
-               "bl main\n"          /* main() returns the status code, we'll exit with it. */
-               "mov x8, 93\n"       /* NR_exit == 93                                       */
-               "svc #0\n"
+               "mov x0, sp\n"          /* save stack pointer to x0, as arg1 of _start_c */
+               "and sp, x0, -16\n"     /* sp must be 16-byte aligned in the callee      */
+               "bl  _start_c\n"        /* transfer to c runtime                         */
        );
        __builtin_unreachable();
 }
index ca4c669..cae4afa 100644 (file)
@@ -8,43 +8,7 @@
 #define _NOLIBC_ARCH_ARM_H
 
 #include "compiler.h"
-
-/* The struct returned by the stat() syscall, 32-bit only, the syscall returns
- * exactly 56 bytes (stops before the unused array). In big endian, the format
- * differs as devices are returned as short only.
- */
-struct sys_stat_struct {
-#if defined(__ARMEB__)
-       unsigned short st_dev;
-       unsigned short __pad1;
-#else
-       unsigned long  st_dev;
-#endif
-       unsigned long  st_ino;
-       unsigned short st_mode;
-       unsigned short st_nlink;
-       unsigned short st_uid;
-       unsigned short st_gid;
-
-#if defined(__ARMEB__)
-       unsigned short st_rdev;
-       unsigned short __pad2;
-#else
-       unsigned long  st_rdev;
-#endif
-       unsigned long  st_size;
-       unsigned long  st_blksize;
-       unsigned long  st_blocks;
-
-       unsigned long  st_atime;
-       unsigned long  st_atime_nsec;
-       unsigned long  st_mtime;
-       unsigned long  st_mtime_nsec;
-
-       unsigned long  st_ctime;
-       unsigned long  st_ctime_nsec;
-       unsigned long  __unused[2];
-};
+#include "crt.h"
 
 /* Syscalls for ARM in ARM or Thumb modes :
  *   - registers are 32-bit
@@ -90,8 +54,8 @@ struct sys_stat_struct {
 ({                                                                            \
        register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
        register long _arg1 __asm__ ("r0");                                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                _NOLIBC_THUMB_SET_R7                                          \
                "svc #0\n"                                                    \
                _NOLIBC_THUMB_RESTORE_R7                                      \
@@ -107,8 +71,8 @@ struct sys_stat_struct {
 ({                                                                            \
        register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
        register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                _NOLIBC_THUMB_SET_R7                                          \
                "svc #0\n"                                                    \
                _NOLIBC_THUMB_RESTORE_R7                                      \
@@ -125,8 +89,8 @@ struct sys_stat_struct {
        register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
        register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
        register long _arg2 __asm__ ("r1") = (long)(arg2);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                _NOLIBC_THUMB_SET_R7                                          \
                "svc #0\n"                                                    \
                _NOLIBC_THUMB_RESTORE_R7                                      \
@@ -144,8 +108,8 @@ struct sys_stat_struct {
        register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
        register long _arg2 __asm__ ("r1") = (long)(arg2);                    \
        register long _arg3 __asm__ ("r2") = (long)(arg3);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                _NOLIBC_THUMB_SET_R7                                          \
                "svc #0\n"                                                    \
                _NOLIBC_THUMB_RESTORE_R7                                      \
@@ -164,8 +128,8 @@ struct sys_stat_struct {
        register long _arg2 __asm__ ("r1") = (long)(arg2);                    \
        register long _arg3 __asm__ ("r2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("r3") = (long)(arg4);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                _NOLIBC_THUMB_SET_R7                                          \
                "svc #0\n"                                                    \
                _NOLIBC_THUMB_RESTORE_R7                                      \
@@ -185,8 +149,8 @@ struct sys_stat_struct {
        register long _arg3 __asm__ ("r2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("r3") = (long)(arg4);                    \
        register long _arg5 __asm__ ("r4") = (long)(arg5);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                _NOLIBC_THUMB_SET_R7                                          \
                "svc #0\n"                                                    \
                _NOLIBC_THUMB_RESTORE_R7                                      \
@@ -207,8 +171,8 @@ struct sys_stat_struct {
        register long _arg4 __asm__ ("r3") = (long)(arg4);                    \
        register long _arg5 __asm__ ("r4") = (long)(arg5);                    \
        register long _arg6 __asm__ ("r5") = (long)(arg6);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                _NOLIBC_THUMB_SET_R7                                          \
                "svc #0\n"                                                    \
                _NOLIBC_THUMB_RESTORE_R7                                      \
@@ -220,49 +184,14 @@ struct sys_stat_struct {
        _arg1;                                                                \
 })
 
-
-char **environ __attribute__((weak));
-const unsigned long *_auxv __attribute__((weak));
-
 /* startup code */
-void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void)
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
 {
        __asm__ volatile (
-#ifdef _NOLIBC_STACKPROTECTOR
-               "bl __stack_chk_init\n"       /* initialize stack protector                          */
-#endif
-               "pop {%r0}\n"                 /* argc was in the stack                               */
-               "mov %r1, %sp\n"              /* argv = sp                                           */
-
-               "add %r2, %r0, $1\n"          /* envp = (argc + 1) ...                               */
-               "lsl %r2, %r2, $2\n"          /*        * 4        ...                               */
-               "add %r2, %r2, %r1\n"         /*        + argv                                       */
-               "ldr %r3, 1f\n"               /* r3 = &environ (see below)                           */
-               "str %r2, [r3]\n"             /* store envp into environ                             */
-
-               "mov r4, r2\n"                /* search for auxv (follows NULL after last env)       */
-               "0:\n"
-               "mov r5, r4\n"                /* r5 = r4                                             */
-               "add r4, r4, #4\n"            /* r4 += 4                                             */
-               "ldr r5,[r5]\n"               /* r5 = *r5 = *(r4-4)                                  */
-               "cmp r5, #0\n"                /* and stop at NULL after last env                     */
-               "bne 0b\n"
-               "ldr %r3, 2f\n"               /* r3 = &_auxv (low bits)                              */
-               "str r4, [r3]\n"              /* store r4 into _auxv                                 */
-
-               "mov %r3, $8\n"               /* AAPCS : sp must be 8-byte aligned in the            */
-               "neg %r3, %r3\n"              /*         callee, and bl doesn't push (lr=pc)         */
-               "and %r3, %r3, %r1\n"         /* so we do sp = r1(=sp) & r3(=-8);                    */
-               "mov %sp, %r3\n"
-
-               "bl main\n"                   /* main() returns the status code, we'll exit with it. */
-               "movs r7, $1\n"               /* NR_exit == 1                                        */
-               "svc $0x00\n"
-               ".align 2\n"                  /* below are the pointers to a few variables           */
-               "1:\n"
-               ".word environ\n"
-               "2:\n"
-               ".word _auxv\n"
+               "mov %r0, sp\n"         /* save stack pointer to %r0, as arg1 of _start_c */
+               "and ip, %r0, #-8\n"    /* sp must be 8-byte aligned in the callee        */
+               "mov sp, ip\n"
+               "bl  _start_c\n"        /* transfer to c runtime                          */
        );
        __builtin_unreachable();
 }
index 3d672d9..64415b9 100644 (file)
@@ -8,32 +8,7 @@
 #define _NOLIBC_ARCH_I386_H
 
 #include "compiler.h"
-
-/* The struct returned by the stat() syscall, 32-bit only, the syscall returns
- * exactly 56 bytes (stops before the unused array).
- */
-struct sys_stat_struct {
-       unsigned long  st_dev;
-       unsigned long  st_ino;
-       unsigned short st_mode;
-       unsigned short st_nlink;
-       unsigned short st_uid;
-       unsigned short st_gid;
-
-       unsigned long  st_rdev;
-       unsigned long  st_size;
-       unsigned long  st_blksize;
-       unsigned long  st_blocks;
-
-       unsigned long  st_atime;
-       unsigned long  st_atime_nsec;
-       unsigned long  st_mtime;
-       unsigned long  st_mtime_nsec;
-
-       unsigned long  st_ctime;
-       unsigned long  st_ctime_nsec;
-       unsigned long  __unused[2];
-};
+#include "crt.h"
 
 /* Syscalls for i386 :
  *   - mostly similar to x86_64
@@ -57,8 +32,8 @@ struct sys_stat_struct {
 ({                                                                            \
        long _ret;                                                            \
        register long _num __asm__ ("eax") = (num);                           \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "int $0x80\n"                                                 \
                : "=a" (_ret)                                                 \
                : "0"(_num)                                                   \
@@ -72,8 +47,8 @@ struct sys_stat_struct {
        long _ret;                                                            \
        register long _num __asm__ ("eax") = (num);                           \
        register long _arg1 __asm__ ("ebx") = (long)(arg1);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "int $0x80\n"                                                 \
                : "=a" (_ret)                                                 \
                : "r"(_arg1),                                                 \
@@ -89,8 +64,8 @@ struct sys_stat_struct {
        register long _num __asm__ ("eax") = (num);                           \
        register long _arg1 __asm__ ("ebx") = (long)(arg1);                   \
        register long _arg2 __asm__ ("ecx") = (long)(arg2);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "int $0x80\n"                                                 \
                : "=a" (_ret)                                                 \
                : "r"(_arg1), "r"(_arg2),                                     \
@@ -107,8 +82,8 @@ struct sys_stat_struct {
        register long _arg1 __asm__ ("ebx") = (long)(arg1);                   \
        register long _arg2 __asm__ ("ecx") = (long)(arg2);                   \
        register long _arg3 __asm__ ("edx") = (long)(arg3);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "int $0x80\n"                                                 \
                : "=a" (_ret)                                                 \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3),                         \
@@ -126,8 +101,8 @@ struct sys_stat_struct {
        register long _arg2 __asm__ ("ecx") = (long)(arg2);                   \
        register long _arg3 __asm__ ("edx") = (long)(arg3);                   \
        register long _arg4 __asm__ ("esi") = (long)(arg4);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "int $0x80\n"                                                 \
                : "=a" (_ret)                                                 \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4),             \
@@ -146,8 +121,8 @@ struct sys_stat_struct {
        register long _arg3 __asm__ ("edx") = (long)(arg3);                   \
        register long _arg4 __asm__ ("esi") = (long)(arg4);                   \
        register long _arg5 __asm__ ("edi") = (long)(arg5);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "int $0x80\n"                                                 \
                : "=a" (_ret)                                                 \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
@@ -180,9 +155,6 @@ struct sys_stat_struct {
        _eax;                                                   \
 })
 
-char **environ __attribute__((weak));
-const unsigned long *_auxv __attribute__((weak));
-
 /* startup code */
 /*
  * i386 System V ABI mandates:
@@ -190,33 +162,15 @@ const unsigned long *_auxv __attribute__((weak));
  * 2) The deepest stack frame should be set to zero
  *
  */
-void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void)
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
 {
        __asm__ volatile (
-#ifdef _NOLIBC_STACKPROTECTOR
-               "call __stack_chk_init\n"   /* initialize stack protector                    */
-#endif
-               "pop %eax\n"                /* argc   (first arg, %eax)                      */
-               "mov %esp, %ebx\n"          /* argv[] (second arg, %ebx)                     */
-               "lea 4(%ebx,%eax,4),%ecx\n" /* then a NULL then envp (third arg, %ecx)       */
-               "mov %ecx, environ\n"       /* save environ                                  */
-               "xor %ebp, %ebp\n"          /* zero the stack frame                          */
-               "mov %ecx, %edx\n"          /* search for auxv (follows NULL after last env) */
-               "0:\n"
-               "add $4, %edx\n"            /* search for auxv using edx, it follows the     */
-               "cmp -4(%edx), %ebp\n"      /* ... NULL after last env (ebp is zero here)    */
-               "jnz 0b\n"
-               "mov %edx, _auxv\n"         /* save it into _auxv                            */
-               "and $-16, %esp\n"          /* x86 ABI : esp must be 16-byte aligned before  */
-               "sub $4, %esp\n"            /* the call instruction (args are aligned)       */
-               "push %ecx\n"               /* push all registers on the stack so that we    */
-               "push %ebx\n"               /* support both regparm and plain stack modes    */
-               "push %eax\n"
-               "call main\n"               /* main() returns the status code in %eax        */
-               "mov %eax, %ebx\n"          /* retrieve exit code (32-bit int)               */
-               "movl $1, %eax\n"           /* NR_exit == 1                                  */
-               "int $0x80\n"               /* exit now                                      */
-               "hlt\n"                     /* ensure it does not                            */
+               "xor  %ebp, %ebp\n"       /* zero the stack frame                                */
+               "mov  %esp, %eax\n"       /* save stack pointer to %eax, as arg1 of _start_c     */
+               "and  $-16, %esp\n"       /* last pushed argument must be 16-byte aligned        */
+               "push %eax\n"             /* push arg1 on stack to support plain stack modes too */
+               "call _start_c\n"         /* transfer to c runtime                               */
+               "hlt\n"                   /* ensure it does not return                           */
        );
        __builtin_unreachable();
 }
index ad3f266..bf98f62 100644 (file)
@@ -8,6 +8,7 @@
 #define _NOLIBC_ARCH_LOONGARCH_H
 
 #include "compiler.h"
+#include "crt.h"
 
 /* Syscalls for LoongArch :
  *   - stack is 16-byte aligned
  * On LoongArch, select() is not implemented so we have to use pselect6().
  */
 #define __ARCH_WANT_SYS_PSELECT6
+#define _NOLIBC_SYSCALL_CLOBBERLIST \
+       "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8"
 
 #define my_syscall0(num)                                                      \
 ({                                                                            \
        register long _num  __asm__ ("a7") = (num);                           \
        register long _arg1 __asm__ ("a0");                                   \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "syscall 0\n"                                                 \
                : "=r"(_arg1)                                                 \
                : "r"(_num)                                                   \
-               : "memory", "$t0", "$t1", "$t2", "$t3",                       \
-                 "$t4", "$t5", "$t6", "$t7", "$t8"                           \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg1;                                                                \
 })
        register long _num  __asm__ ("a7") = (num);                           \
        register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "syscall 0\n"                                                 \
                : "+r"(_arg1)                                                 \
                : "r"(_num)                                                   \
-               : "memory", "$t0", "$t1", "$t2", "$t3",                       \
-                 "$t4", "$t5", "$t6", "$t7", "$t8"                           \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg1;                                                                \
 })
        register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
        register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "syscall 0\n"                                                 \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2),                                                 \
                  "r"(_num)                                                   \
-               : "memory", "$t0", "$t1", "$t2", "$t3",                       \
-                 "$t4", "$t5", "$t6", "$t7", "$t8"                           \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg1;                                                                \
 })
        register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
        register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "syscall 0\n"                                                 \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2), "r"(_arg3),                                     \
                  "r"(_num)                                                   \
-               : "memory", "$t0", "$t1", "$t2", "$t3",                       \
-                 "$t4", "$t5", "$t6", "$t7", "$t8"                           \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg1;                                                                \
 })
        register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "syscall 0\n"                                                 \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2), "r"(_arg3), "r"(_arg4),                         \
                  "r"(_num)                                                   \
-               : "memory", "$t0", "$t1", "$t2", "$t3",                       \
-                 "$t4", "$t5", "$t6", "$t7", "$t8"                           \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg1;                                                                \
 })
        register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
        register long _arg5 __asm__ ("a4") = (long)(arg5);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "syscall 0\n"                                                 \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5),             \
                  "r"(_num)                                                   \
-               : "memory", "$t0", "$t1", "$t2", "$t3",                       \
-                 "$t4", "$t5", "$t6", "$t7", "$t8"                           \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg1;                                                                \
 })
        register long _arg5 __asm__ ("a4") = (long)(arg5);                    \
        register long _arg6 __asm__ ("a5") = (long)(arg6);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "syscall 0\n"                                                 \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \
                  "r"(_num)                                                   \
-               : "memory", "$t0", "$t1", "$t2", "$t3",                       \
-                 "$t4", "$t5", "$t6", "$t7", "$t8"                           \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg1;                                                                \
 })
 
-char **environ __attribute__((weak));
-const unsigned long *_auxv __attribute__((weak));
-
 #if __loongarch_grlen == 32
-#define LONGLOG      "2"
-#define SZREG        "4"
-#define REG_L        "ld.w"
-#define LONG_S       "st.w"
-#define LONG_ADD     "add.w"
-#define LONG_ADDI    "addi.w"
-#define LONG_SLL     "slli.w"
 #define LONG_BSTRINS "bstrins.w"
 #else /* __loongarch_grlen == 64 */
-#define LONGLOG      "3"
-#define SZREG        "8"
-#define REG_L        "ld.d"
-#define LONG_S       "st.d"
-#define LONG_ADD     "add.d"
-#define LONG_ADDI    "addi.d"
-#define LONG_SLL     "slli.d"
 #define LONG_BSTRINS "bstrins.d"
 #endif
 
 /* startup code */
-void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void)
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
 {
        __asm__ volatile (
-#ifdef _NOLIBC_STACKPROTECTOR
-               "bl __stack_chk_init\n"               /* initialize stack protector                          */
-#endif
-               REG_L        " $a0, $sp, 0\n"         /* argc (a0) was in the stack                          */
-               LONG_ADDI    " $a1, $sp, "SZREG"\n"   /* argv (a1) = sp + SZREG                              */
-               LONG_SLL     " $a2, $a0, "LONGLOG"\n" /* envp (a2) = SZREG*argc ...                          */
-               LONG_ADDI    " $a2, $a2, "SZREG"\n"   /*             + SZREG (skip null)                     */
-               LONG_ADD     " $a2, $a2, $a1\n"       /*             + argv                                  */
-
-               "move          $a3, $a2\n"            /* iterate a3 over envp to find auxv (after NULL)      */
-               "0:\n"                                /* do {                                                */
-               REG_L        " $a4, $a3, 0\n"         /*   a4 = *a3;                                         */
-               LONG_ADDI    " $a3, $a3, "SZREG"\n"   /*   a3 += sizeof(void*);                              */
-               "bne           $a4, $zero, 0b\n"      /* } while (a4);                                       */
-               "la.pcrel      $a4, _auxv\n"          /* a4 = &_auxv                                         */
-               LONG_S       " $a3, $a4, 0\n"         /* store a3 into _auxv                                 */
-
-               "la.pcrel      $a3, environ\n"        /* a3 = &environ                                       */
-               LONG_S       " $a2, $a3, 0\n"         /* store envp(a2) into environ                         */
-               LONG_BSTRINS " $sp, $zero, 3, 0\n"    /* sp must be 16-byte aligned                          */
-               "bl            main\n"                /* main() returns the status code, we'll exit with it. */
-               "li.w          $a7, 93\n"             /* NR_exit == 93                                       */
-               "syscall       0\n"
+               "move          $a0, $sp\n"         /* save stack pointer to $a0, as arg1 of _start_c */
+               LONG_BSTRINS " $sp, $zero, 3, 0\n" /* $sp must be 16-byte aligned                    */
+               "bl            _start_c\n"         /* transfer to c runtime                          */
        );
        __builtin_unreachable();
 }
index db24e08..4ab6fa5 100644 (file)
@@ -8,34 +8,7 @@
 #define _NOLIBC_ARCH_MIPS_H
 
 #include "compiler.h"
-
-/* The struct returned by the stat() syscall. 88 bytes are returned by the
- * syscall.
- */
-struct sys_stat_struct {
-       unsigned int  st_dev;
-       long          st_pad1[3];
-       unsigned long st_ino;
-       unsigned int  st_mode;
-       unsigned int  st_nlink;
-       unsigned int  st_uid;
-       unsigned int  st_gid;
-       unsigned int  st_rdev;
-       long          st_pad2[2];
-       long          st_size;
-       long          st_pad3;
-
-       long          st_atime;
-       long          st_atime_nsec;
-       long          st_mtime;
-       long          st_mtime_nsec;
-
-       long          st_ctime;
-       long          st_ctime_nsec;
-       long          st_blksize;
-       long          st_blocks;
-       long          st_pad4[14];
-};
+#include "crt.h"
 
 /* Syscalls for MIPS ABI O32 :
  *   - WARNING! there's always a delayed slot!
@@ -57,19 +30,22 @@ struct sys_stat_struct {
  *     don't have to experience issues with register constraints.
  */
 
+#define _NOLIBC_SYSCALL_CLOBBERLIST \
+       "memory", "cc", "at", "v1", "hi", "lo", \
+       "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"
+
 #define my_syscall0(num)                                                      \
 ({                                                                            \
        register long _num __asm__ ("v0") = (num);                            \
        register long _arg4 __asm__ ("a3");                                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "addiu $sp, $sp, -32\n"                                       \
                "syscall\n"                                                   \
                "addiu $sp, $sp, 32\n"                                        \
                : "=r"(_num), "=r"(_arg4)                                     \
                : "r"(_num)                                                   \
-               : "memory", "cc", "at", "v1", "hi", "lo",                     \
-                 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg4 ? -_num : _num;                                                 \
 })
@@ -79,16 +55,15 @@ struct sys_stat_struct {
        register long _num __asm__ ("v0") = (num);                            \
        register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
        register long _arg4 __asm__ ("a3");                                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "addiu $sp, $sp, -32\n"                                       \
                "syscall\n"                                                   \
                "addiu $sp, $sp, 32\n"                                        \
                : "=r"(_num), "=r"(_arg4)                                     \
                : "0"(_num),                                                  \
                  "r"(_arg1)                                                  \
-               : "memory", "cc", "at", "v1", "hi", "lo",                     \
-                 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg4 ? -_num : _num;                                                 \
 })
@@ -99,16 +74,15 @@ struct sys_stat_struct {
        register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
        register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
        register long _arg4 __asm__ ("a3");                                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "addiu $sp, $sp, -32\n"                                       \
                "syscall\n"                                                   \
                "addiu $sp, $sp, 32\n"                                        \
                : "=r"(_num), "=r"(_arg4)                                     \
                : "0"(_num),                                                  \
                  "r"(_arg1), "r"(_arg2)                                      \
-               : "memory", "cc", "at", "v1", "hi", "lo",                     \
-                 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg4 ? -_num : _num;                                                 \
 })
@@ -120,16 +94,15 @@ struct sys_stat_struct {
        register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
        register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("a3");                                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "addiu $sp, $sp, -32\n"                                       \
                "syscall\n"                                                   \
                "addiu $sp, $sp, 32\n"                                        \
                : "=r"(_num), "=r"(_arg4)                                     \
                : "0"(_num),                                                  \
                  "r"(_arg1), "r"(_arg2), "r"(_arg3)                          \
-               : "memory", "cc", "at", "v1", "hi", "lo",                     \
-                 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg4 ? -_num : _num;                                                 \
 })
@@ -141,16 +114,15 @@ struct sys_stat_struct {
        register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
        register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "addiu $sp, $sp, -32\n"                                       \
                "syscall\n"                                                   \
                "addiu $sp, $sp, 32\n"                                        \
                : "=r" (_num), "=r"(_arg4)                                    \
                : "0"(_num),                                                  \
                  "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4)              \
-               : "memory", "cc", "at", "v1", "hi", "lo",                     \
-                 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg4 ? -_num : _num;                                                 \
 })
@@ -163,65 +135,58 @@ struct sys_stat_struct {
        register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
        register long _arg5 = (long)(arg5);                                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "addiu $sp, $sp, -32\n"                                       \
                "sw %7, 16($sp)\n"                                            \
-               "syscall\n  "                                                 \
+               "syscall\n"                                                   \
                "addiu $sp, $sp, 32\n"                                        \
                : "=r" (_num), "=r"(_arg4)                                    \
                : "0"(_num),                                                  \
                  "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5)  \
-               : "memory", "cc", "at", "v1", "hi", "lo",                     \
-                 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
        );                                                                    \
        _arg4 ? -_num : _num;                                                 \
 })
 
-char **environ __attribute__((weak));
-const unsigned long *_auxv __attribute__((weak));
+#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)                  \
+({                                                                            \
+       register long _num __asm__ ("v0")  = (num);                           \
+       register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
+       register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
+       register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
+       register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
+       register long _arg5 = (long)(arg5);                                   \
+       register long _arg6 = (long)(arg6);                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
+               "addiu $sp, $sp, -32\n"                                       \
+               "sw %7, 16($sp)\n"                                            \
+               "sw %8, 20($sp)\n"                                            \
+               "syscall\n"                                                   \
+               "addiu $sp, $sp, 32\n"                                        \
+               : "=r" (_num), "=r"(_arg4)                                    \
+               : "0"(_num),                                                  \
+                 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
+                 "r"(_arg6)                                                  \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
+       );                                                                    \
+       _arg4 ? -_num : _num;                                                 \
+})
 
 /* startup code, note that it's called __start on MIPS */
-void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector __start(void)
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector __start(void)
 {
        __asm__ volatile (
-               /*".set nomips16\n"*/
                ".set push\n"
-               ".set    noreorder\n"
+               ".set noreorder\n"
                ".option pic0\n"
-#ifdef _NOLIBC_STACKPROTECTOR
-               "jal __stack_chk_init\n" /* initialize stack protector                         */
-               "nop\n"                  /* delayed slot                                       */
-#endif
-               /*".ent __start\n"*/
-               /*"__start:\n"*/
-               "lw $a0,($sp)\n"        /* argc was in the stack                               */
-               "addiu  $a1, $sp, 4\n"  /* argv = sp + 4                                       */
-               "sll $a2, $a0, 2\n"     /* a2 = argc * 4                                       */
-               "add   $a2, $a2, $a1\n" /* envp = argv + 4*argc ...                            */
-               "addiu $a2, $a2, 4\n"   /*        ... + 4                                      */
-               "lui $a3, %hi(environ)\n"     /* load environ into a3 (hi)                     */
-               "addiu $a3, %lo(environ)\n"   /* load environ into a3 (lo)                     */
-               "sw $a2,($a3)\n"              /* store envp(a2) into environ                   */
-
-               "move $t0, $a2\n"             /* iterate t0 over envp, look for NULL           */
-               "0:"                          /* do {                                          */
-               "lw $a3, ($t0)\n"             /*   a3=*(t0);                                   */
-               "bne $a3, $0, 0b\n"           /* } while (a3);                                 */
-               "addiu $t0, $t0, 4\n"         /* delayed slot: t0+=4;                          */
-               "lui $a3, %hi(_auxv)\n"       /* load _auxv into a3 (hi)                       */
-               "addiu $a3, %lo(_auxv)\n"     /* load _auxv into a3 (lo)                       */
-               "sw $t0, ($a3)\n"             /* store t0 into _auxv                           */
-
-               "li $t0, -8\n"
-               "and $sp, $sp, $t0\n"   /* sp must be 8-byte aligned                           */
-               "addiu $sp,$sp,-16\n"   /* the callee expects to save a0..a3 there!            */
-               "jal main\n"            /* main() returns the status code, we'll exit with it. */
-               "nop\n"                 /* delayed slot                                        */
-               "move $a0, $v0\n"       /* retrieve 32-bit exit code from v0                   */
-               "li $v0, 4001\n"        /* NR_exit == 4001                                     */
-               "syscall\n"
-               /*".end __start\n"*/
+               "move  $a0, $sp\n"       /* save stack pointer to $a0, as arg1 of _start_c */
+               "li    $t0, -8\n"
+               "and   $sp, $sp, $t0\n"  /* $sp must be 8-byte aligned                     */
+               "addiu $sp, $sp, -16\n"  /* the callee expects to save a0..a3 there        */
+               "jal   _start_c\n"       /* transfer to c runtime                          */
+               " nop\n"                 /* delayed slot                                   */
                ".set pop\n"
        );
        __builtin_unreachable();
diff --git a/tools/include/nolibc/arch-powerpc.h b/tools/include/nolibc/arch-powerpc.h
new file mode 100644 (file)
index 0000000..ac212e6
--- /dev/null
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
+/*
+ * PowerPC specific definitions for NOLIBC
+ * Copyright (C) 2023 Zhangjin Wu <falcon@tinylab.org>
+ */
+
+#ifndef _NOLIBC_ARCH_POWERPC_H
+#define _NOLIBC_ARCH_POWERPC_H
+
+#include "compiler.h"
+#include "crt.h"
+
+/* Syscalls for PowerPC :
+ *   - stack is 16-byte aligned
+ *   - syscall number is passed in r0
+ *   - arguments are in r3, r4, r5, r6, r7, r8, r9
+ *   - the system call is performed by calling "sc"
+ *   - syscall return comes in r3, and the summary overflow bit is checked
+ *     to know if an error occurred, in which case errno is in r3.
+ *   - the arguments are cast to long and assigned into the target
+ *     registers which are then simply passed as registers to the asm code,
+ *     so that we don't have to experience issues with register constraints.
+ */
+
+#define _NOLIBC_SYSCALL_CLOBBERLIST \
+       "memory", "cr0", "r12", "r11", "r10", "r9"
+
+#define my_syscall0(num)                                                     \
+({                                                                           \
+       register long _ret  __asm__ ("r3");                                  \
+       register long _num  __asm__ ("r0") = (num);                          \
+                                                                            \
+       __asm__ volatile (                                                   \
+               "       sc\n"                                                \
+               "       bns+ 1f\n"                                           \
+               "       neg  %0, %0\n"                                       \
+               "1:\n"                                                       \
+               : "=r"(_ret), "+r"(_num)                                     \
+               :                                                            \
+               : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5", "r4"  \
+       );                                                                   \
+       _ret;                                                                \
+})
+
+#define my_syscall1(num, arg1)                                               \
+({                                                                           \
+       register long _ret  __asm__ ("r3");                                  \
+       register long _num  __asm__ ("r0") = (num);                          \
+       register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
+                                                                            \
+       __asm__ volatile (                                                   \
+               "       sc\n"                                                \
+               "       bns+ 1f\n"                                           \
+               "       neg  %0, %0\n"                                       \
+               "1:\n"                                                       \
+               : "=r"(_ret), "+r"(_num)                                     \
+               : "0"(_arg1)                                                 \
+               : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5", "r4"  \
+       );                                                                   \
+       _ret;                                                                \
+})
+
+
+#define my_syscall2(num, arg1, arg2)                                         \
+({                                                                           \
+       register long _ret  __asm__ ("r3");                                  \
+       register long _num  __asm__ ("r0") = (num);                          \
+       register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
+                                                                            \
+       __asm__ volatile (                                                   \
+               "       sc\n"                                                \
+               "       bns+ 1f\n"                                           \
+               "       neg  %0, %0\n"                                       \
+               "1:\n"                                                       \
+               : "=r"(_ret), "+r"(_num), "+r"(_arg2)                        \
+               : "0"(_arg1)                                                 \
+               : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5"        \
+       );                                                                   \
+       _ret;                                                                \
+})
+
+
+#define my_syscall3(num, arg1, arg2, arg3)                                   \
+({                                                                           \
+       register long _ret  __asm__ ("r3");                                  \
+       register long _num  __asm__ ("r0") = (num);                          \
+       register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
+       register long _arg3 __asm__ ("r5") = (long)(arg3);                   \
+                                                                            \
+       __asm__ volatile (                                                   \
+               "       sc\n"                                                \
+               "       bns+ 1f\n"                                           \
+               "       neg  %0, %0\n"                                       \
+               "1:\n"                                                       \
+               : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3)           \
+               : "0"(_arg1)                                                 \
+               : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6"              \
+       );                                                                   \
+       _ret;                                                                \
+})
+
+
+#define my_syscall4(num, arg1, arg2, arg3, arg4)                             \
+({                                                                           \
+       register long _ret  __asm__ ("r3");                                  \
+       register long _num  __asm__ ("r0") = (num);                          \
+       register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
+       register long _arg3 __asm__ ("r5") = (long)(arg3);                   \
+       register long _arg4 __asm__ ("r6") = (long)(arg4);                   \
+                                                                            \
+       __asm__ volatile (                                                   \
+               "       sc\n"                                                \
+               "       bns+ 1f\n"                                           \
+               "       neg  %0, %0\n"                                       \
+               "1:\n"                                                       \
+               : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \
+                 "+r"(_arg4)                                                \
+               : "0"(_arg1)                                                 \
+               : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7"                    \
+       );                                                                   \
+       _ret;                                                                \
+})
+
+
+#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                       \
+({                                                                           \
+       register long _ret  __asm__ ("r3");                                  \
+       register long _num  __asm__ ("r0") = (num);                          \
+       register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
+       register long _arg3 __asm__ ("r5") = (long)(arg3);                   \
+       register long _arg4 __asm__ ("r6") = (long)(arg4);                   \
+       register long _arg5 __asm__ ("r7") = (long)(arg5);                   \
+                                                                            \
+       __asm__ volatile (                                                   \
+               "       sc\n"                                                \
+               "       bns+ 1f\n"                                           \
+               "       neg  %0, %0\n"                                       \
+               "1:\n"                                                       \
+               : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \
+                 "+r"(_arg4), "+r"(_arg5)                                   \
+               : "0"(_arg1)                                                 \
+               : _NOLIBC_SYSCALL_CLOBBERLIST, "r8"                          \
+       );                                                                   \
+       _ret;                                                                \
+})
+
+#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)                 \
+({                                                                           \
+       register long _ret  __asm__ ("r3");                                  \
+       register long _num  __asm__ ("r0") = (num);                          \
+       register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
+       register long _arg3 __asm__ ("r5") = (long)(arg3);                   \
+       register long _arg4 __asm__ ("r6") = (long)(arg4);                   \
+       register long _arg5 __asm__ ("r7") = (long)(arg5);                   \
+       register long _arg6 __asm__ ("r8") = (long)(arg6);                   \
+                                                                            \
+       __asm__ volatile (                                                   \
+               "       sc\n"                                                \
+               "       bns+ 1f\n"                                           \
+               "       neg  %0, %0\n"                                       \
+               "1:\n"                                                       \
+               : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \
+                 "+r"(_arg4), "+r"(_arg5), "+r"(_arg6)                      \
+               : "0"(_arg1)                                                 \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                \
+       );                                                                   \
+       _ret;                                                                \
+})
+
+#ifndef __powerpc64__
+/* FIXME: For 32-bit PowerPC, with newer gcc compilers (e.g. gcc 13.1.0),
+ * "omit-frame-pointer" fails with __attribute__((no_stack_protector)) but
+ * works with __attribute__((__optimize__("-fno-stack-protector")))
+ */
+#ifdef __no_stack_protector
+#undef __no_stack_protector
+#define __no_stack_protector __attribute__((__optimize__("-fno-stack-protector")))
+#endif
+#endif /* !__powerpc64__ */
+
+/* startup code */
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
+{
+#ifdef __powerpc64__
+#if _CALL_ELF == 2
+       /* with -mabi=elfv2, save TOC/GOT pointer to r2
+        * r12 is global entry pointer, we use it to compute TOC from r12
+        * https://www.llvm.org/devmtg/2014-04/PDFs/Talks/Euro-LLVM-2014-Weigand.pdf
+        * https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.pdf
+        */
+       __asm__ volatile (
+               "addis  2, 12, .TOC. - _start@ha\n"
+               "addi   2,  2, .TOC. - _start@l\n"
+       );
+#endif /* _CALL_ELF == 2 */
+
+       __asm__ volatile (
+               "mr     3, 1\n"         /* save stack pointer to r3, as arg1 of _start_c */
+               "clrrdi 1, 1, 4\n"      /* align the stack to 16 bytes                   */
+               "li     0, 0\n"         /* zero the frame pointer                        */
+               "stdu   1, -32(1)\n"    /* the initial stack frame                       */
+               "bl     _start_c\n"     /* transfer to c runtime                         */
+       );
+#else
+       __asm__ volatile (
+               "mr     3, 1\n"         /* save stack pointer to r3, as arg1 of _start_c */
+               "clrrwi 1, 1, 4\n"      /* align the stack to 16 bytes                   */
+               "li     0, 0\n"         /* zero the frame pointer                        */
+               "stwu   1, -16(1)\n"    /* the initial stack frame                       */
+               "bl     _start_c\n"     /* transfer to c runtime                         */
+       );
+#endif
+       __builtin_unreachable();
+}
+
+#endif /* _NOLIBC_ARCH_POWERPC_H */
index a2e8564..950cc22 100644 (file)
@@ -8,41 +8,7 @@
 #define _NOLIBC_ARCH_RISCV_H
 
 #include "compiler.h"
-
-struct sys_stat_struct {
-       unsigned long   st_dev;         /* Device.  */
-       unsigned long   st_ino;         /* File serial number.  */
-       unsigned int    st_mode;        /* File mode.  */
-       unsigned int    st_nlink;       /* Link count.  */
-       unsigned int    st_uid;         /* User ID of the file's owner.  */
-       unsigned int    st_gid;         /* Group ID of the file's group. */
-       unsigned long   st_rdev;        /* Device number, if device.  */
-       unsigned long   __pad1;
-       long            st_size;        /* Size of file, in bytes.  */
-       int             st_blksize;     /* Optimal block size for I/O.  */
-       int             __pad2;
-       long            st_blocks;      /* Number 512-byte blocks allocated. */
-       long            st_atime;       /* Time of last access.  */
-       unsigned long   st_atime_nsec;
-       long            st_mtime;       /* Time of last modification.  */
-       unsigned long   st_mtime_nsec;
-       long            st_ctime;       /* Time of last status change.  */
-       unsigned long   st_ctime_nsec;
-       unsigned int    __unused4;
-       unsigned int    __unused5;
-};
-
-#if   __riscv_xlen == 64
-#define PTRLOG "3"
-#define SZREG  "8"
-#define REG_L  "ld"
-#define REG_S  "sd"
-#elif __riscv_xlen == 32
-#define PTRLOG "2"
-#define SZREG  "4"
-#define REG_L  "lw"
-#define REG_S  "sw"
-#endif
+#include "crt.h"
 
 /* Syscalls for RISCV :
  *   - stack is 16-byte aligned
@@ -63,7 +29,7 @@ struct sys_stat_struct {
        register long _num  __asm__ ("a7") = (num);                           \
        register long _arg1 __asm__ ("a0");                                   \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "ecall\n\t"                                                   \
                : "=r"(_arg1)                                                 \
                : "r"(_num)                                                   \
@@ -77,7 +43,7 @@ struct sys_stat_struct {
        register long _num  __asm__ ("a7") = (num);                           \
        register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "ecall\n"                                                     \
                : "+r"(_arg1)                                                 \
                : "r"(_num)                                                   \
@@ -92,7 +58,7 @@ struct sys_stat_struct {
        register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
        register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "ecall\n"                                                     \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2),                                                 \
@@ -109,7 +75,7 @@ struct sys_stat_struct {
        register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
        register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "ecall\n\t"                                                   \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2), "r"(_arg3),                                     \
@@ -127,7 +93,7 @@ struct sys_stat_struct {
        register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
        register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "ecall\n"                                                     \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2), "r"(_arg3), "r"(_arg4),                         \
@@ -146,7 +112,7 @@ struct sys_stat_struct {
        register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
        register long _arg5 __asm__ ("a4") = (long)(arg5);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "ecall\n"                                                     \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5),             \
@@ -166,7 +132,7 @@ struct sys_stat_struct {
        register long _arg5 __asm__ ("a4") = (long)(arg5);                    \
        register long _arg6 __asm__ ("a5") = (long)(arg6);                    \
                                                                              \
-       __asm__  volatile (                                                   \
+       __asm__ volatile (                                                    \
                "ecall\n"                                                     \
                : "+r"(_arg1)                                                 \
                : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \
@@ -176,40 +142,17 @@ struct sys_stat_struct {
        _arg1;                                                                \
 })
 
-char **environ __attribute__((weak));
-const unsigned long *_auxv __attribute__((weak));
-
 /* startup code */
-void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void)
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
 {
        __asm__ volatile (
                ".option push\n"
                ".option norelax\n"
-               "lla   gp, __global_pointer$\n"
+               "lla  gp, __global_pointer$\n"
                ".option pop\n"
-#ifdef _NOLIBC_STACKPROTECTOR
-               "call __stack_chk_init\n"    /* initialize stack protector                          */
-#endif
-               REG_L" a0, 0(sp)\n"          /* argc (a0) was in the stack                          */
-               "add   a1, sp, "SZREG"\n"    /* argv (a1) = sp                                      */
-               "slli  a2, a0, "PTRLOG"\n"   /* envp (a2) = SZREG*argc ...                          */
-               "add   a2, a2, "SZREG"\n"    /*             + SZREG (skip null)                     */
-               "add   a2,a2,a1\n"           /*             + argv                                  */
-
-               "add   a3, a2, zero\n"       /* iterate a3 over envp to find auxv (after NULL)      */
-               "0:\n"                       /* do {                                                */
-               REG_L" a4, 0(a3)\n"          /*   a4 = *a3;                                         */
-               "add   a3, a3, "SZREG"\n"    /*   a3 += sizeof(void*);                              */
-               "bne   a4, zero, 0b\n"       /* } while (a4);                                       */
-               "lui   a4, %hi(_auxv)\n"     /* a4 = &_auxv (high bits)                             */
-               REG_S" a3, %lo(_auxv)(a4)\n" /* store a3 into _auxv                                 */
-
-               "lui   a3, %hi(environ)\n"   /* a3 = &environ (high bits)                           */
-               REG_S" a2,%lo(environ)(a3)\n"/* store envp(a2) into environ                         */
-               "andi  sp,a1,-16\n"          /* sp must be 16-byte aligned                          */
-               "call  main\n"               /* main() returns the status code, we'll exit with it. */
-               "li a7, 93\n"                /* NR_exit == 93                                       */
-               "ecall\n"
+               "mv   a0, sp\n"           /* save stack pointer to a0, as arg1 of _start_c */
+               "andi sp, a0, -16\n"      /* sp must be 16-byte aligned                    */
+               "call _start_c\n"         /* transfer to c runtime                         */
        );
        __builtin_unreachable();
 }
index 516dff5..5d60fd4 100644 (file)
@@ -9,31 +9,7 @@
 #include <asm/unistd.h>
 
 #include "compiler.h"
-
-/* The struct returned by the stat() syscall, equivalent to stat64(). The
- * syscall returns 116 bytes and stops in the middle of __unused.
- */
-
-struct sys_stat_struct {
-       unsigned long   st_dev;
-       unsigned long   st_ino;
-       unsigned long   st_nlink;
-       unsigned int    st_mode;
-       unsigned int    st_uid;
-       unsigned int    st_gid;
-       unsigned int    __pad1;
-       unsigned long   st_rdev;
-       unsigned long   st_size;
-       unsigned long   st_atime;
-       unsigned long   st_atime_nsec;
-       unsigned long   st_mtime;
-       unsigned long   st_mtime_nsec;
-       unsigned long   st_ctime;
-       unsigned long   st_ctime_nsec;
-       unsigned long   st_blksize;
-       long            st_blocks;
-       unsigned long   __unused[3];
-};
+#include "crt.h"
 
 /* Syscalls for s390:
  *   - registers are 64-bit
@@ -52,7 +28,7 @@ struct sys_stat_struct {
        register long _num __asm__ ("1") = (num);                       \
        register long _rc __asm__ ("2");                                \
                                                                        \
-       __asm__  volatile (                                             \
+       __asm__ volatile (                                              \
                "svc 0\n"                                               \
                : "=d"(_rc)                                             \
                : "d"(_num)                                             \
@@ -66,7 +42,7 @@ struct sys_stat_struct {
        register long _num __asm__ ("1") = (num);                       \
        register long _arg1 __asm__ ("2") = (long)(arg1);               \
                                                                        \
-       __asm__  volatile (                                             \
+       __asm__ volatile (                                              \
                "svc 0\n"                                               \
                : "+d"(_arg1)                                           \
                : "d"(_num)                                             \
@@ -81,7 +57,7 @@ struct sys_stat_struct {
        register long _arg1 __asm__ ("2") = (long)(arg1);               \
        register long _arg2 __asm__ ("3") = (long)(arg2);               \
                                                                        \
-       __asm__  volatile (                                             \
+       __asm__ volatile (                                              \
                "svc 0\n"                                               \
                : "+d"(_arg1)                                           \
                : "d"(_arg2), "d"(_num)                                 \
@@ -97,7 +73,7 @@ struct sys_stat_struct {
        register long _arg2 __asm__ ("3") = (long)(arg2);               \
        register long _arg3 __asm__ ("4") = (long)(arg3);               \
                                                                        \
-       __asm__  volatile (                                             \
+       __asm__ volatile (                                              \
                "svc 0\n"                                               \
                : "+d"(_arg1)                                           \
                : "d"(_arg2), "d"(_arg3), "d"(_num)                     \
@@ -114,7 +90,7 @@ struct sys_stat_struct {
        register long _arg3 __asm__ ("4") = (long)(arg3);               \
        register long _arg4 __asm__ ("5") = (long)(arg4);               \
                                                                        \
-       __asm__  volatile (                                             \
+       __asm__ volatile (                                              \
                "svc 0\n"                                               \
                : "+d"(_arg1)                                           \
                : "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_num)         \
@@ -132,7 +108,7 @@ struct sys_stat_struct {
        register long _arg4 __asm__ ("5") = (long)(arg4);               \
        register long _arg5 __asm__ ("6") = (long)(arg5);               \
                                                                        \
-       __asm__  volatile (                                             \
+       __asm__ volatile (                                              \
                "svc 0\n"                                               \
                : "+d"(_arg1)                                           \
                : "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_arg5),       \
@@ -152,7 +128,7 @@ struct sys_stat_struct {
        register long _arg5 __asm__ ("6") = (long)(arg5);               \
        register long _arg6 __asm__ ("7") = (long)(arg6);               \
                                                                        \
-       __asm__  volatile (                                             \
+       __asm__ volatile (                                              \
                "svc 0\n"                                               \
                : "+d"(_arg1)                                           \
                : "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_arg5),       \
@@ -162,41 +138,14 @@ struct sys_stat_struct {
        _arg1;                                                          \
 })
 
-char **environ __attribute__((weak));
-const unsigned long *_auxv __attribute__((weak));
-
 /* startup code */
-void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void)
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
 {
        __asm__ volatile (
-               "lg     %r2,0(%r15)\n"          /* argument count */
-               "la     %r3,8(%r15)\n"          /* argument pointers */
-
-               "xgr    %r0,%r0\n"              /* r0 will be our NULL value */
-               /* search for envp */
-               "lgr    %r4,%r3\n"              /* start at argv */
-               "0:\n"
-               "clg    %r0,0(%r4)\n"           /* entry zero? */
-               "la     %r4,8(%r4)\n"           /* advance pointer */
-               "jnz    0b\n"                   /* no -> test next pointer */
-                                               /* yes -> r4 now contains start of envp */
-               "larl   %r1,environ\n"
-               "stg    %r4,0(%r1)\n"
-
-               /* search for auxv */
-               "lgr    %r5,%r4\n"              /* start at envp */
-               "1:\n"
-               "clg    %r0,0(%r5)\n"           /* entry zero? */
-               "la     %r5,8(%r5)\n"           /* advance pointer */
-               "jnz    1b\n"                   /* no -> test next pointer */
-               "larl   %r1,_auxv\n"            /* yes -> store value in _auxv */
-               "stg    %r5,0(%r1)\n"
-
-               "aghi   %r15,-160\n"            /* allocate new stackframe */
-               "xc     0(8,%r15),0(%r15)\n"    /* clear backchain */
-               "brasl  %r14,main\n"            /* ret value of main is arg to exit */
-               "lghi   %r1,1\n"                /* __NR_exit */
-               "svc    0\n"
+               "lgr    %r2, %r15\n"          /* save stack pointer to %r2, as arg1 of _start_c */
+               "aghi   %r15, -160\n"         /* allocate new stackframe                        */
+               "xc     0(8,%r15), 0(%r15)\n" /* clear backchain                                */
+               "brasl  %r14, _start_c\n"     /* transfer to c runtime                          */
        );
        __builtin_unreachable();
 }
index 6fc4d83..e5ccb92 100644 (file)
@@ -8,33 +8,7 @@
 #define _NOLIBC_ARCH_X86_64_H
 
 #include "compiler.h"
-
-/* The struct returned by the stat() syscall, equivalent to stat64(). The
- * syscall returns 116 bytes and stops in the middle of __unused.
- */
-struct sys_stat_struct {
-       unsigned long st_dev;
-       unsigned long st_ino;
-       unsigned long st_nlink;
-       unsigned int  st_mode;
-       unsigned int  st_uid;
-
-       unsigned int  st_gid;
-       unsigned int  __pad0;
-       unsigned long st_rdev;
-       long          st_size;
-       long          st_blksize;
-
-       long          st_blocks;
-       unsigned long st_atime;
-       unsigned long st_atime_nsec;
-       unsigned long st_mtime;
-
-       unsigned long st_mtime_nsec;
-       unsigned long st_ctime;
-       unsigned long st_ctime_nsec;
-       long          __unused[3];
-};
+#include "crt.h"
 
 /* Syscalls for x86_64 :
  *   - registers are 64-bit
@@ -59,8 +33,8 @@ struct sys_stat_struct {
 ({                                                                            \
        long _ret;                                                            \
        register long _num  __asm__ ("rax") = (num);                          \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "syscall\n"                                                   \
                : "=a"(_ret)                                                  \
                : "0"(_num)                                                   \
@@ -74,8 +48,8 @@ struct sys_stat_struct {
        long _ret;                                                            \
        register long _num  __asm__ ("rax") = (num);                          \
        register long _arg1 __asm__ ("rdi") = (long)(arg1);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "syscall\n"                                                   \
                : "=a"(_ret)                                                  \
                : "r"(_arg1),                                                 \
@@ -91,8 +65,8 @@ struct sys_stat_struct {
        register long _num  __asm__ ("rax") = (num);                          \
        register long _arg1 __asm__ ("rdi") = (long)(arg1);                   \
        register long _arg2 __asm__ ("rsi") = (long)(arg2);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "syscall\n"                                                   \
                : "=a"(_ret)                                                  \
                : "r"(_arg1), "r"(_arg2),                                     \
@@ -109,8 +83,8 @@ struct sys_stat_struct {
        register long _arg1 __asm__ ("rdi") = (long)(arg1);                   \
        register long _arg2 __asm__ ("rsi") = (long)(arg2);                   \
        register long _arg3 __asm__ ("rdx") = (long)(arg3);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "syscall\n"                                                   \
                : "=a"(_ret)                                                  \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3),                         \
@@ -128,8 +102,8 @@ struct sys_stat_struct {
        register long _arg2 __asm__ ("rsi") = (long)(arg2);                   \
        register long _arg3 __asm__ ("rdx") = (long)(arg3);                   \
        register long _arg4 __asm__ ("r10") = (long)(arg4);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "syscall\n"                                                   \
                : "=a"(_ret)                                                  \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4),             \
@@ -148,8 +122,8 @@ struct sys_stat_struct {
        register long _arg3 __asm__ ("rdx") = (long)(arg3);                   \
        register long _arg4 __asm__ ("r10") = (long)(arg4);                   \
        register long _arg5 __asm__ ("r8")  = (long)(arg5);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "syscall\n"                                                   \
                : "=a"(_ret)                                                  \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
@@ -169,8 +143,8 @@ struct sys_stat_struct {
        register long _arg4 __asm__ ("r10") = (long)(arg4);                   \
        register long _arg5 __asm__ ("r8")  = (long)(arg5);                   \
        register long _arg6 __asm__ ("r9")  = (long)(arg6);                   \
-                                                                             \
-       __asm__  volatile (                                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
                "syscall\n"                                                   \
                : "=a"(_ret)                                                  \
                : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
@@ -180,9 +154,6 @@ struct sys_stat_struct {
        _ret;                                                                 \
 })
 
-char **environ __attribute__((weak));
-const unsigned long *_auxv __attribute__((weak));
-
 /* startup code */
 /*
  * x86-64 System V ABI mandates:
@@ -190,29 +161,14 @@ const unsigned long *_auxv __attribute__((weak));
  * 2) The deepest stack frame should be zero (the %rbp).
  *
  */
-void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void)
+void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
 {
        __asm__ volatile (
-#ifdef _NOLIBC_STACKPROTECTOR
-               "call __stack_chk_init\n"   /* initialize stack protector                          */
-#endif
-               "pop %rdi\n"                /* argc   (first arg, %rdi)                            */
-               "mov %rsp, %rsi\n"          /* argv[] (second arg, %rsi)                           */
-               "lea 8(%rsi,%rdi,8),%rdx\n" /* then a NULL then envp (third arg, %rdx)             */
-               "mov %rdx, environ\n"       /* save environ                                        */
-               "xor %ebp, %ebp\n"          /* zero the stack frame                                */
-               "mov %rdx, %rax\n"          /* search for auxv (follows NULL after last env)       */
-               "0:\n"
-               "add $8, %rax\n"            /* search for auxv using rax, it follows the           */
-               "cmp -8(%rax), %rbp\n"      /* ... NULL after last env (rbp is zero here)          */
-               "jnz 0b\n"
-               "mov %rax, _auxv\n"         /* save it into _auxv                                  */
-               "and $-16, %rsp\n"          /* x86 ABI : esp must be 16-byte aligned before call   */
-               "call main\n"               /* main() returns the status code, we'll exit with it. */
-               "mov %eax, %edi\n"          /* retrieve exit code (32 bit)                         */
-               "mov $60, %eax\n"           /* NR_exit == 60                                       */
-               "syscall\n"                 /* really exit                                         */
-               "hlt\n"                     /* ensure it does not return                           */
+               "xor  %ebp, %ebp\n"       /* zero the stack frame                            */
+               "mov  %rsp, %rdi\n"       /* save stack pointer to %rdi, as arg1 of _start_c */
+               "and  $-16, %rsp\n"       /* %rsp must be 16-byte aligned before call        */
+               "call _start_c\n"         /* transfer to c runtime                           */
+               "hlt\n"                   /* ensure it does not return                       */
        );
        __builtin_unreachable();
 }
index 82b4393..e276fb0 100644 (file)
@@ -25,6 +25,8 @@
 #include "arch-aarch64.h"
 #elif defined(__mips__) && defined(_ABIO32)
 #include "arch-mips.h"
+#elif defined(__powerpc__)
+#include "arch-powerpc.h"
 #elif defined(__riscv)
 #include "arch-riscv.h"
 #elif defined(__s390x__)
diff --git a/tools/include/nolibc/crt.h b/tools/include/nolibc/crt.h
new file mode 100644 (file)
index 0000000..a5f33fe
--- /dev/null
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
+/*
+ * C Run Time support for NOLIBC
+ * Copyright (C) 2023 Zhangjin Wu <falcon@tinylab.org>
+ */
+
+#ifndef _NOLIBC_CRT_H
+#define _NOLIBC_CRT_H
+
+char **environ __attribute__((weak));
+const unsigned long *_auxv __attribute__((weak));
+
+static void __stack_chk_init(void);
+static void exit(int);
+
+void _start_c(long *sp)
+{
+       long argc;
+       char **argv;
+       char **envp;
+       const unsigned long *auxv;
+       /* silence potential warning: conflicting types for 'main' */
+       int _nolibc_main(int, char **, char **) __asm__ ("main");
+
+       /* initialize stack protector */
+       __stack_chk_init();
+
+       /*
+        * sp  :    argc          <-- argument count, required by main()
+        * argv:    argv[0]       <-- argument vector, required by main()
+        *          argv[1]
+        *          ...
+        *          argv[argc-1]
+        *          null
+        * environ: environ[0]    <-- environment variables, required by main() and getenv()
+        *          environ[1]
+        *          ...
+        *          null
+        * _auxv:   _auxv[0]      <-- auxiliary vector, required by getauxval()
+        *          _auxv[1]
+        *          ...
+        *          null
+        */
+
+       /* assign argc and argv */
+       argc = *sp;
+       argv = (void *)(sp + 1);
+
+       /* find environ */
+       environ = envp = argv + argc + 1;
+
+       /* find _auxv */
+       for (auxv = (void *)envp; *auxv++;)
+               ;
+       _auxv = auxv;
+
+       /* go to application */
+       exit(_nolibc_main(argc, argv, envp));
+}
+
+#endif /* _NOLIBC_CRT_H */
index 05a228a..1f8d821 100644 (file)
  * Syscalls are split into 3 levels:
  *   - The lower level is the arch-specific syscall() definition, consisting in
  *     assembly code in compound expressions. These are called my_syscall0() to
- *     my_syscall6() depending on the number of arguments. The MIPS
- *     implementation is limited to 5 arguments. All input arguments are cast
- *     to a long stored in a register. These expressions always return the
- *     syscall's return value as a signed long value which is often either a
- *     pointer or the negated errno value.
+ *     my_syscall6() depending on the number of arguments. All input arguments
+ *     are castto a long stored in a register. These expressions always return
+ *     the syscall's return value as a signed long value which is often either
+ *     a pointer or the negated errno value.
  *
  *   - The second level is mostly architecture-independent. It is made of
  *     static functions called sys_<name>() which rely on my_syscallN()
index 88f7b2d..13f1d0e 100644 (file)
@@ -37,14 +37,15 @@ void __stack_chk_fail_local(void)
 __attribute__((weak,section(".data.nolibc_stack_chk")))
 uintptr_t __stack_chk_guard;
 
-__attribute__((weak,section(".text.nolibc_stack_chk"))) __no_stack_protector
-void __stack_chk_init(void)
+static __no_stack_protector void __stack_chk_init(void)
 {
        my_syscall3(__NR_getrandom, &__stack_chk_guard, sizeof(__stack_chk_guard), 0);
        /* a bit more randomness in case getrandom() fails, ensure the guard is never 0 */
        if (__stack_chk_guard != (uintptr_t) &__stack_chk_guard)
                __stack_chk_guard ^= (uintptr_t) &__stack_chk_guard;
 }
+#else /* !defined(_NOLIBC_STACKPROTECTOR) */
+static void __stack_chk_init(void) {}
 #endif /* defined(_NOLIBC_STACKPROTECTOR) */
 
 #endif /* _NOLIBC_STACKPROTECTOR_H */
index 4b28243..6665e27 100644 (file)
@@ -15,7 +15,7 @@ typedef unsigned int       uint32_t;
 typedef   signed int        int32_t;
 typedef unsigned long long uint64_t;
 typedef   signed long long  int64_t;
-typedef unsigned long        size_t;
+typedef __SIZE_TYPE__        size_t;
 typedef   signed long       ssize_t;
 typedef unsigned long     uintptr_t;
 typedef   signed long      intptr_t;
index 0eef91d..cae402c 100644 (file)
 #define EOF (-1)
 #endif
 
+/* Buffering mode used by setvbuf.  */
+#define _IOFBF 0       /* Fully buffered. */
+#define _IOLBF 1       /* Line buffered. */
+#define _IONBF 2       /* No buffering. */
+
 /* just define FILE as a non-empty type. The value of the pointer gives
  * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
  * are immediately identified as abnormal entries (i.e. possible copies
@@ -350,6 +355,28 @@ void perror(const char *msg)
        fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
 }
 
+static __attribute__((unused))
+int setvbuf(FILE *stream __attribute__((unused)),
+           char *buf __attribute__((unused)),
+           int mode,
+           size_t size __attribute__((unused)))
+{
+       /*
+        * nolibc does not support buffering so this is a nop. Just check mode
+        * is valid as required by the spec.
+        */
+       switch (mode) {
+       case _IOFBF:
+       case _IOLBF:
+       case _IONBF:
+               break;
+       default:
+               return EOF;
+       }
+
+       return 0;
+}
+
 /* make sure to include all global symbols */
 #include "nolibc.h"
 
index 902162f..bacfd35 100644 (file)
@@ -83,11 +83,10 @@ void free(void *ptr)
  * declared as a char **, and must be terminated by a NULL (it is recommended
  * to set this variable to the "envp" argument of main()). If the requested
  * environment variable exists its value is returned otherwise NULL is
- * returned. getenv() is forcefully inlined so that the reference to "environ"
- * will be dropped if unused, even at -O0.
+ * returned.
  */
 static __attribute__((unused))
-char *_getenv(const char *name, char **environ)
+char *getenv(const char *name)
 {
        int idx, i;
 
@@ -102,13 +101,6 @@ char *_getenv(const char *name, char **environ)
        return NULL;
 }
 
-static __inline__ __attribute__((unused,always_inline))
-char *getenv(const char *name)
-{
-       extern char **environ;
-       return _getenv(name, environ);
-}
-
 static __attribute__((unused))
 unsigned long getauxval(unsigned long type)
 {
index 856249a..fdb6bd6 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/auxvec.h>
 #include <linux/fcntl.h> /* for O_* and AT_* */
 #include <linux/stat.h>  /* for statx() */
-#include <linux/reboot.h> /* for LINUX_REBOOT_* */
 #include <linux/prctl.h>
 
 #include "arch.h"
 #include "types.h"
 
 
+/* Syscall return helper: takes the syscall value in argument and checks for an
+ * error in it. This may only be used with signed returns (int or long), but
+ * not with pointers. An error is any value < 0. When an error is encountered,
+ * -ret is set into errno and -1 is returned. Otherwise the returned value is
+ * passed as-is with its type preserved.
+ */
+
+#define __sysret(arg)                                                  \
+({                                                                     \
+       __typeof__(arg) __sysret_arg = (arg);                           \
+       (__sysret_arg < 0)                              /* error ? */   \
+               ? (({ SET_ERRNO(-__sysret_arg); }), -1) /* ret -1 with errno = -arg */ \
+               : __sysret_arg;                         /* return original value */ \
+})
+
+
 /* Functions in this file only describe syscalls. They're declared static so
  * that the compiler usually decides to inline them while still being allowed
  * to pass a pointer to one of their instances. Each syscall exists in two
@@ -78,10 +93,10 @@ int brk(void *addr)
 static __attribute__((unused))
 void *sbrk(intptr_t inc)
 {
-       void *ret;
-
        /* first call to find current end */
-       if ((ret = sys_brk(0)) && (sys_brk(ret + inc) == ret + inc))
+       void *ret = sys_brk(0);
+
+       if (ret && sys_brk(ret + inc) == ret + inc)
                return ret + inc;
 
        SET_ERRNO(ENOMEM);
@@ -102,13 +117,7 @@ int sys_chdir(const char *path)
 static __attribute__((unused))
 int chdir(const char *path)
 {
-       int ret = sys_chdir(path);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_chdir(path));
 }
 
 
@@ -124,20 +133,14 @@ int sys_chmod(const char *path, mode_t mode)
 #elif defined(__NR_chmod)
        return my_syscall2(__NR_chmod, path, mode);
 #else
-#error Neither __NR_fchmodat nor __NR_chmod defined, cannot implement sys_chmod()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int chmod(const char *path, mode_t mode)
 {
-       int ret = sys_chmod(path, mode);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_chmod(path, mode));
 }
 
 
@@ -153,20 +156,14 @@ int sys_chown(const char *path, uid_t owner, gid_t group)
 #elif defined(__NR_chown)
        return my_syscall3(__NR_chown, path, owner, group);
 #else
-#error Neither __NR_fchownat nor __NR_chown defined, cannot implement sys_chown()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int chown(const char *path, uid_t owner, gid_t group)
 {
-       int ret = sys_chown(path, owner, group);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_chown(path, owner, group));
 }
 
 
@@ -183,13 +180,7 @@ int sys_chroot(const char *path)
 static __attribute__((unused))
 int chroot(const char *path)
 {
-       int ret = sys_chroot(path);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_chroot(path));
 }
 
 
@@ -206,13 +197,7 @@ int sys_close(int fd)
 static __attribute__((unused))
 int close(int fd)
 {
-       int ret = sys_close(fd);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_close(fd));
 }
 
 
@@ -229,13 +214,7 @@ int sys_dup(int fd)
 static __attribute__((unused))
 int dup(int fd)
 {
-       int ret = sys_dup(fd);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_dup(fd));
 }
 
 
@@ -251,20 +230,14 @@ int sys_dup2(int old, int new)
 #elif defined(__NR_dup2)
        return my_syscall2(__NR_dup2, old, new);
 #else
-#error Neither __NR_dup3 nor __NR_dup2 defined, cannot implement sys_dup2()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int dup2(int old, int new)
 {
-       int ret = sys_dup2(old, new);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_dup2(old, new));
 }
 
 
@@ -282,13 +255,7 @@ int sys_dup3(int old, int new, int flags)
 static __attribute__((unused))
 int dup3(int old, int new, int flags)
 {
-       int ret = sys_dup3(old, new, flags);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_dup3(old, new, flags));
 }
 #endif
 
@@ -306,13 +273,7 @@ int sys_execve(const char *filename, char *const argv[], char *const envp[])
 static __attribute__((unused))
 int execve(const char *filename, char *const argv[], char *const envp[])
 {
-       int ret = sys_execve(filename, argv, envp);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_execve(filename, argv, envp));
 }
 
 
@@ -351,7 +312,7 @@ pid_t sys_fork(void)
 #elif defined(__NR_fork)
        return my_syscall0(__NR_fork);
 #else
-#error Neither __NR_clone nor __NR_fork defined, cannot implement sys_fork()
+       return -ENOSYS;
 #endif
 }
 #endif
@@ -359,13 +320,7 @@ pid_t sys_fork(void)
 static __attribute__((unused))
 pid_t fork(void)
 {
-       pid_t ret = sys_fork();
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_fork());
 }
 
 
@@ -382,13 +337,7 @@ int sys_fsync(int fd)
 static __attribute__((unused))
 int fsync(int fd)
 {
-       int ret = sys_fsync(fd);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_fsync(fd));
 }
 
 
@@ -405,13 +354,7 @@ int sys_getdents64(int fd, struct linux_dirent64 *dirp, int count)
 static __attribute__((unused))
 int getdents64(int fd, struct linux_dirent64 *dirp, int count)
 {
-       int ret = sys_getdents64(fd, dirp, count);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_getdents64(fd, dirp, count));
 }
 
 
@@ -449,13 +392,7 @@ pid_t sys_getpgid(pid_t pid)
 static __attribute__((unused))
 pid_t getpgid(pid_t pid)
 {
-       pid_t ret = sys_getpgid(pid);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_getpgid(pid));
 }
 
 
@@ -529,21 +466,13 @@ pid_t gettid(void)
 static unsigned long getauxval(unsigned long key);
 
 /*
- * long getpagesize(void);
+ * int getpagesize(void);
  */
 
 static __attribute__((unused))
-long getpagesize(void)
+int getpagesize(void)
 {
-       long ret;
-
-       ret = getauxval(AT_PAGESZ);
-       if (!ret) {
-               SET_ERRNO(ENOENT);
-               return -1;
-       }
-
-       return ret;
+       return __sysret((int)getauxval(AT_PAGESZ) ?: -ENOENT);
 }
 
 
@@ -554,19 +483,17 @@ long getpagesize(void)
 static __attribute__((unused))
 int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
 {
+#ifdef __NR_gettimeofday
        return my_syscall2(__NR_gettimeofday, tv, tz);
+#else
+       return -ENOSYS;
+#endif
 }
 
 static __attribute__((unused))
 int gettimeofday(struct timeval *tv, struct timezone *tz)
 {
-       int ret = sys_gettimeofday(tv, tz);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_gettimeofday(tv, tz));
 }
 
 
@@ -604,13 +531,7 @@ int sys_ioctl(int fd, unsigned long req, void *value)
 static __attribute__((unused))
 int ioctl(int fd, unsigned long req, void *value)
 {
-       int ret = sys_ioctl(fd, req, value);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_ioctl(fd, req, value));
 }
 
 /*
@@ -626,13 +547,7 @@ int sys_kill(pid_t pid, int signal)
 static __attribute__((unused))
 int kill(pid_t pid, int signal)
 {
-       int ret = sys_kill(pid, signal);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_kill(pid, signal));
 }
 
 
@@ -648,20 +563,14 @@ int sys_link(const char *old, const char *new)
 #elif defined(__NR_link)
        return my_syscall2(__NR_link, old, new);
 #else
-#error Neither __NR_linkat nor __NR_link defined, cannot implement sys_link()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int link(const char *old, const char *new)
 {
-       int ret = sys_link(old, new);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_link(old, new));
 }
 
 
@@ -672,19 +581,17 @@ int link(const char *old, const char *new)
 static __attribute__((unused))
 off_t sys_lseek(int fd, off_t offset, int whence)
 {
+#ifdef __NR_lseek
        return my_syscall3(__NR_lseek, fd, offset, whence);
+#else
+       return -ENOSYS;
+#endif
 }
 
 static __attribute__((unused))
 off_t lseek(int fd, off_t offset, int whence)
 {
-       off_t ret = sys_lseek(fd, offset, whence);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_lseek(fd, offset, whence));
 }
 
 
@@ -700,20 +607,36 @@ int sys_mkdir(const char *path, mode_t mode)
 #elif defined(__NR_mkdir)
        return my_syscall2(__NR_mkdir, path, mode);
 #else
-#error Neither __NR_mkdirat nor __NR_mkdir defined, cannot implement sys_mkdir()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int mkdir(const char *path, mode_t mode)
 {
-       int ret = sys_mkdir(path, mode);
+       return __sysret(sys_mkdir(path, mode));
+}
 
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+/*
+ * int rmdir(const char *path);
+ */
+
+static __attribute__((unused))
+int sys_rmdir(const char *path)
+{
+#ifdef __NR_rmdir
+       return my_syscall1(__NR_rmdir, path);
+#elif defined(__NR_unlinkat)
+       return my_syscall3(__NR_unlinkat, AT_FDCWD, path, AT_REMOVEDIR);
+#else
+       return -ENOSYS;
+#endif
+}
+
+static __attribute__((unused))
+int rmdir(const char *path)
+{
+       return __sysret(sys_rmdir(path));
 }
 
 
@@ -729,42 +652,21 @@ long sys_mknod(const char *path, mode_t mode, dev_t dev)
 #elif defined(__NR_mknod)
        return my_syscall3(__NR_mknod, path, mode, dev);
 #else
-#error Neither __NR_mknodat nor __NR_mknod defined, cannot implement sys_mknod()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int mknod(const char *path, mode_t mode, dev_t dev)
 {
-       int ret = sys_mknod(path, mode, dev);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_mknod(path, mode, dev));
 }
 
-#ifndef MAP_SHARED
-#define MAP_SHARED             0x01    /* Share changes */
-#define MAP_PRIVATE            0x02    /* Changes are private */
-#define MAP_SHARED_VALIDATE    0x03    /* share + validate extension flags */
-#endif
-
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void *)-1)
-#endif
-
 #ifndef sys_mmap
 static __attribute__((unused))
 void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,
               off_t offset)
 {
-#ifndef my_syscall6
-       /* Function not implemented. */
-       return (void *)-ENOSYS;
-#else
-
        int n;
 
 #if defined(__NR_mmap2)
@@ -775,10 +677,14 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,
 #endif
 
        return (void *)my_syscall6(n, addr, length, prot, flags, fd, offset);
-#endif
 }
 #endif
 
+/* Note that on Linux, MAP_FAILED is -1 so we can use the generic __sysret()
+ * which returns -1 upon error and still satisfy user land that checks for
+ * MAP_FAILED.
+ */
+
 static __attribute__((unused))
 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
 {
@@ -800,13 +706,7 @@ int sys_munmap(void *addr, size_t length)
 static __attribute__((unused))
 int munmap(void *addr, size_t length)
 {
-       int ret = sys_munmap(addr, length);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_munmap(addr, length));
 }
 
 /*
@@ -826,13 +726,7 @@ int mount(const char *src, const char *tgt,
           const char *fst, unsigned long flags,
           const void *data)
 {
-       int ret = sys_mount(src, tgt, fst, flags, data);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_mount(src, tgt, fst, flags, data));
 }
 
 
@@ -848,7 +742,7 @@ int sys_open(const char *path, int flags, mode_t mode)
 #elif defined(__NR_open)
        return my_syscall3(__NR_open, path, flags, mode);
 #else
-#error Neither __NR_openat nor __NR_open defined, cannot implement sys_open()
+       return -ENOSYS;
 #endif
 }
 
@@ -856,7 +750,6 @@ static __attribute__((unused))
 int open(const char *path, int flags, ...)
 {
        mode_t mode = 0;
-       int ret;
 
        if (flags & O_CREAT) {
                va_list args;
@@ -866,13 +759,31 @@ int open(const char *path, int flags, ...)
                va_end(args);
        }
 
-       ret = sys_open(path, flags, mode);
+       return __sysret(sys_open(path, flags, mode));
+}
 
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+
+/*
+ * int pipe2(int pipefd[2], int flags);
+ * int pipe(int pipefd[2]);
+ */
+
+static __attribute__((unused))
+int sys_pipe2(int pipefd[2], int flags)
+{
+       return my_syscall2(__NR_pipe2, pipefd, flags);
+}
+
+static __attribute__((unused))
+int pipe2(int pipefd[2], int flags)
+{
+       return __sysret(sys_pipe2(pipefd, flags));
+}
+
+static __attribute__((unused))
+int pipe(int pipefd[2])
+{
+       return pipe2(pipefd, 0);
 }
 
 
@@ -892,13 +803,7 @@ static __attribute__((unused))
 int prctl(int option, unsigned long arg2, unsigned long arg3,
                      unsigned long arg4, unsigned long arg5)
 {
-       int ret = sys_prctl(option, arg2, arg3, arg4, arg5);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_prctl(option, arg2, arg3, arg4, arg5));
 }
 
 
@@ -915,13 +820,7 @@ int sys_pivot_root(const char *new, const char *old)
 static __attribute__((unused))
 int pivot_root(const char *new, const char *old)
 {
-       int ret = sys_pivot_root(new, old);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_pivot_root(new, old));
 }
 
 
@@ -943,20 +842,14 @@ int sys_poll(struct pollfd *fds, int nfds, int timeout)
 #elif defined(__NR_poll)
        return my_syscall3(__NR_poll, fds, nfds, timeout);
 #else
-#error Neither __NR_ppoll nor __NR_poll defined, cannot implement sys_poll()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int poll(struct pollfd *fds, int nfds, int timeout)
 {
-       int ret = sys_poll(fds, nfds, timeout);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_poll(fds, nfds, timeout));
 }
 
 
@@ -973,13 +866,7 @@ ssize_t sys_read(int fd, void *buf, size_t count)
 static __attribute__((unused))
 ssize_t read(int fd, void *buf, size_t count)
 {
-       ssize_t ret = sys_read(fd, buf, count);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_read(fd, buf, count));
 }
 
 
@@ -997,13 +884,7 @@ ssize_t sys_reboot(int magic1, int magic2, int cmd, void *arg)
 static __attribute__((unused))
 int reboot(int cmd)
 {
-       int ret = sys_reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, 0);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, 0));
 }
 
 
@@ -1020,13 +901,7 @@ int sys_sched_yield(void)
 static __attribute__((unused))
 int sched_yield(void)
 {
-       int ret = sys_sched_yield();
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_sched_yield());
 }
 
 
@@ -1059,20 +934,14 @@ int sys_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeva
 #endif
        return my_syscall5(__NR__newselect, nfds, rfds, wfds, efds, timeout);
 #else
-#error None of __NR_select, __NR_pselect6, nor __NR__newselect defined, cannot implement sys_select()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout)
 {
-       int ret = sys_select(nfds, rfds, wfds, efds, timeout);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_select(nfds, rfds, wfds, efds, timeout));
 }
 
 
@@ -1089,13 +958,7 @@ int sys_setpgid(pid_t pid, pid_t pgid)
 static __attribute__((unused))
 int setpgid(pid_t pid, pid_t pgid)
 {
-       int ret = sys_setpgid(pid, pgid);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_setpgid(pid, pgid));
 }
 
 
@@ -1112,55 +975,41 @@ pid_t sys_setsid(void)
 static __attribute__((unused))
 pid_t setsid(void)
 {
-       pid_t ret = sys_setsid();
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_setsid());
 }
 
-#if defined(__NR_statx)
 /*
  * int statx(int fd, const char *path, int flags, unsigned int mask, struct statx *buf);
+ * int stat(const char *path, struct stat *buf);
  */
 
 static __attribute__((unused))
 int sys_statx(int fd, const char *path, int flags, unsigned int mask, struct statx *buf)
 {
+#ifdef __NR_statx
        return my_syscall5(__NR_statx, fd, path, flags, mask, buf);
+#else
+       return -ENOSYS;
+#endif
 }
 
 static __attribute__((unused))
 int statx(int fd, const char *path, int flags, unsigned int mask, struct statx *buf)
 {
-       int ret = sys_statx(fd, path, flags, mask, buf);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_statx(fd, path, flags, mask, buf));
 }
-#endif
 
-/*
- * int stat(const char *path, struct stat *buf);
- * Warning: the struct stat's layout is arch-dependent.
- */
 
-#if defined(__NR_statx) && !defined(__NR_newfstatat) && !defined(__NR_stat)
-/*
- * Maybe we can just use statx() when available for all architectures?
- */
 static __attribute__((unused))
-int sys_stat(const char *path, struct stat *buf)
+int stat(const char *path, struct stat *buf)
 {
        struct statx statx;
        long ret;
 
-       ret = sys_statx(AT_FDCWD, path, AT_NO_AUTOMOUNT, STATX_BASIC_STATS, &statx);
+       ret = __sysret(sys_statx(AT_FDCWD, path, AT_NO_AUTOMOUNT, STATX_BASIC_STATS, &statx));
+       if (ret == -1)
+               return ret;
+
        buf->st_dev          = ((statx.stx_dev_minor & 0xff)
                               | (statx.stx_dev_major << 8)
                               | ((statx.stx_dev_minor & ~0xff) << 12));
@@ -1181,53 +1030,8 @@ int sys_stat(const char *path, struct stat *buf)
        buf->st_mtim.tv_nsec = statx.stx_mtime.tv_nsec;
        buf->st_ctim.tv_sec  = statx.stx_ctime.tv_sec;
        buf->st_ctim.tv_nsec = statx.stx_ctime.tv_nsec;
-       return ret;
-}
-#else
-static __attribute__((unused))
-int sys_stat(const char *path, struct stat *buf)
-{
-       struct sys_stat_struct stat;
-       long ret;
-
-#ifdef __NR_newfstatat
-       /* only solution for arm64 */
-       ret = my_syscall4(__NR_newfstatat, AT_FDCWD, path, &stat, 0);
-#elif defined(__NR_stat)
-       ret = my_syscall2(__NR_stat, path, &stat);
-#else
-#error Neither __NR_newfstatat nor __NR_stat defined, cannot implement sys_stat()
-#endif
-       buf->st_dev          = stat.st_dev;
-       buf->st_ino          = stat.st_ino;
-       buf->st_mode         = stat.st_mode;
-       buf->st_nlink        = stat.st_nlink;
-       buf->st_uid          = stat.st_uid;
-       buf->st_gid          = stat.st_gid;
-       buf->st_rdev         = stat.st_rdev;
-       buf->st_size         = stat.st_size;
-       buf->st_blksize      = stat.st_blksize;
-       buf->st_blocks       = stat.st_blocks;
-       buf->st_atim.tv_sec  = stat.st_atime;
-       buf->st_atim.tv_nsec = stat.st_atime_nsec;
-       buf->st_mtim.tv_sec  = stat.st_mtime;
-       buf->st_mtim.tv_nsec = stat.st_mtime_nsec;
-       buf->st_ctim.tv_sec  = stat.st_ctime;
-       buf->st_ctim.tv_nsec = stat.st_ctime_nsec;
-       return ret;
-}
-#endif
 
-static __attribute__((unused))
-int stat(const char *path, struct stat *buf)
-{
-       int ret = sys_stat(path, buf);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return 0;
 }
 
 
@@ -1243,20 +1047,14 @@ int sys_symlink(const char *old, const char *new)
 #elif defined(__NR_symlink)
        return my_syscall2(__NR_symlink, old, new);
 #else
-#error Neither __NR_symlinkat nor __NR_symlink defined, cannot implement sys_symlink()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int symlink(const char *old, const char *new)
 {
-       int ret = sys_symlink(old, new);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_symlink(old, new));
 }
 
 
@@ -1290,13 +1088,7 @@ int sys_umount2(const char *path, int flags)
 static __attribute__((unused))
 int umount2(const char *path, int flags)
 {
-       int ret = sys_umount2(path, flags);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_umount2(path, flags));
 }
 
 
@@ -1312,20 +1104,14 @@ int sys_unlink(const char *path)
 #elif defined(__NR_unlink)
        return my_syscall1(__NR_unlink, path);
 #else
-#error Neither __NR_unlinkat nor __NR_unlink defined, cannot implement sys_unlink()
+       return -ENOSYS;
 #endif
 }
 
 static __attribute__((unused))
 int unlink(const char *path)
 {
-       int ret = sys_unlink(path);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_unlink(path));
 }
 
 
@@ -1338,44 +1124,30 @@ int unlink(const char *path)
 static __attribute__((unused))
 pid_t sys_wait4(pid_t pid, int *status, int options, struct rusage *rusage)
 {
+#ifdef __NR_wait4
        return my_syscall4(__NR_wait4, pid, status, options, rusage);
+#else
+       return -ENOSYS;
+#endif
 }
 
 static __attribute__((unused))
 pid_t wait(int *status)
 {
-       pid_t ret = sys_wait4(-1, status, 0, NULL);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_wait4(-1, status, 0, NULL));
 }
 
 static __attribute__((unused))
 pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage)
 {
-       pid_t ret = sys_wait4(pid, status, options, rusage);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_wait4(pid, status, options, rusage));
 }
 
 
 static __attribute__((unused))
 pid_t waitpid(pid_t pid, int *status, int options)
 {
-       pid_t ret = sys_wait4(pid, status, options, NULL);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_wait4(pid, status, options, NULL));
 }
 
 
@@ -1392,13 +1164,7 @@ ssize_t sys_write(int fd, const void *buf, size_t count)
 static __attribute__((unused))
 ssize_t write(int fd, const void *buf, size_t count)
 {
-       ssize_t ret = sys_write(fd, buf, count);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_write(fd, buf, count));
 }
 
 
@@ -1415,13 +1181,7 @@ int sys_memfd_create(const char *name, unsigned int flags)
 static __attribute__((unused))
 int memfd_create(const char *name, unsigned int flags)
 {
-       ssize_t ret = sys_memfd_create(name, flags);
-
-       if (ret < 0) {
-               SET_ERRNO(-ret);
-               ret = -1;
-       }
-       return ret;
+       return __sysret(sys_memfd_create(name, flags));
 }
 
 /* make sure to include all global symbols */
index f96e28b..8cfc4c8 100644 (file)
@@ -8,13 +8,15 @@
 #define _NOLIBC_TYPES_H
 
 #include "std.h"
-#include <linux/time.h>
+#include <linux/mman.h>
+#include <linux/reboot.h> /* for LINUX_REBOOT_* */
 #include <linux/stat.h>
+#include <linux/time.h>
 
 
 /* Only the generic macros and types may be defined here. The arch-specific
- * ones such as the O_RDONLY and related macros used by fcntl() and open(), or
- * the layout of sys_stat_struct must not be defined here.
+ * ones such as the O_RDONLY and related macros used by fcntl() and open()
+ * must not be defined here.
  */
 
 /* stat flags (WARNING, octal here). We need to check for an existing
 #define MAXPATHLEN     (PATH_MAX)
 #endif
 
+/* flags for mmap */
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
 /* whence values for lseek() */
 #define SEEK_SET       0
 #define SEEK_CUR       1
 #define SEEK_END       2
 
+/* flags for reboot */
+#define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART
+#define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT
+#define RB_ENABLE_CAD   LINUX_REBOOT_CMD_CAD_ON
+#define RB_DISABLE_CAD  LINUX_REBOOT_CMD_CAD_OFF
+#define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF
+#define RB_SW_SUSPEND   LINUX_REBOOT_CMD_SW_SUSPEND
+#define RB_KEXEC        LINUX_REBOOT_CMD_KEXEC
+
 /* Macros used on waitpid()'s return status */
 #define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
 #define WIFEXITED(status)   (((status) & 0x7f) == 0)
index 0e832e1..e38f366 100644 (file)
@@ -56,18 +56,9 @@ int tcsetpgrp(int fd, pid_t pid)
        return ioctl(fd, TIOCSPGRP, &pid);
 }
 
-#define _syscall(N, ...)                                                      \
-({                                                                            \
-       long _ret = my_syscall##N(__VA_ARGS__);                               \
-       if (_ret < 0) {                                                       \
-               SET_ERRNO(-_ret);                                             \
-               _ret = -1;                                                    \
-       }                                                                     \
-       _ret;                                                                 \
-})
-
-#define _syscall_narg(...) __syscall_narg(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
 #define __syscall_narg(_0, _1, _2, _3, _4, _5, _6, N, ...) N
+#define _syscall_narg(...) __syscall_narg(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
+#define _syscall(N, ...) __sysret(my_syscall##N(__VA_ARGS__))
 #define _syscall_n(N, ...) _syscall(N, __VA_ARGS__)
 #define syscall(...) _syscall_n(_syscall_narg(__VA_ARGS__), ##__VA_ARGS__)
 
index 1b7b3c8..dfe6677 100644 (file)
@@ -14,6 +14,31 @@ include $(srctree)/scripts/subarch.include
 ARCH = $(SUBARCH)
 endif
 
+# XARCH extends the kernel's ARCH with a few variants of the same
+# architecture that only differ by the configuration, the toolchain
+# and the Qemu program used. It is copied as-is into ARCH except for
+# a few specific values which are mapped like this:
+#
+#  XARCH        | ARCH      | config
+#  -------------|-----------|-------------------------
+#  ppc          | powerpc   | 32 bits
+#  ppc64        | powerpc   | 64 bits big endian
+#  ppc64le      | powerpc   | 64 bits little endian
+#
+# It is recommended to only use XARCH, though it does not harm if
+# ARCH is already set. For simplicity, ARCH is sufficient for all
+# architectures where both are equal.
+
+# configure default variants for target kernel supported architectures
+XARCH_powerpc    = ppc
+XARCH            = $(or $(XARCH_$(ARCH)),$(ARCH))
+
+# map from user input variants to their kernel supported architectures
+ARCH_ppc         = powerpc
+ARCH_ppc64       = powerpc
+ARCH_ppc64le     = powerpc
+ARCH            := $(or $(ARCH_$(XARCH)),$(XARCH))
+
 # kernel image names by architecture
 IMAGE_i386       = arch/x86/boot/bzImage
 IMAGE_x86_64     = arch/x86/boot/bzImage
@@ -21,10 +46,13 @@ IMAGE_x86        = arch/x86/boot/bzImage
 IMAGE_arm64      = arch/arm64/boot/Image
 IMAGE_arm        = arch/arm/boot/zImage
 IMAGE_mips       = vmlinuz
+IMAGE_ppc        = vmlinux
+IMAGE_ppc64      = vmlinux
+IMAGE_ppc64le    = arch/powerpc/boot/zImage
 IMAGE_riscv      = arch/riscv/boot/Image
 IMAGE_s390       = arch/s390/boot/bzImage
 IMAGE_loongarch  = arch/loongarch/boot/vmlinuz.efi
-IMAGE            = $(IMAGE_$(ARCH))
+IMAGE            = $(IMAGE_$(XARCH))
 IMAGE_NAME       = $(notdir $(IMAGE))
 
 # default kernel configurations that appear to be usable
@@ -34,10 +62,13 @@ DEFCONFIG_x86        = defconfig
 DEFCONFIG_arm64      = defconfig
 DEFCONFIG_arm        = multi_v7_defconfig
 DEFCONFIG_mips       = malta_defconfig
+DEFCONFIG_ppc        = pmac32_defconfig
+DEFCONFIG_ppc64      = powernv_be_defconfig
+DEFCONFIG_ppc64le    = powernv_defconfig
 DEFCONFIG_riscv      = defconfig
 DEFCONFIG_s390       = defconfig
 DEFCONFIG_loongarch  = defconfig
-DEFCONFIG            = $(DEFCONFIG_$(ARCH))
+DEFCONFIG            = $(DEFCONFIG_$(XARCH))
 
 # optional tests to run (default = all)
 TEST =
@@ -49,10 +80,13 @@ QEMU_ARCH_x86        = x86_64
 QEMU_ARCH_arm64      = aarch64
 QEMU_ARCH_arm        = arm
 QEMU_ARCH_mips       = mipsel  # works with malta_defconfig
+QEMU_ARCH_ppc        = ppc
+QEMU_ARCH_ppc64      = ppc64
+QEMU_ARCH_ppc64le    = ppc64le
 QEMU_ARCH_riscv      = riscv64
 QEMU_ARCH_s390       = s390x
 QEMU_ARCH_loongarch  = loongarch64
-QEMU_ARCH            = $(QEMU_ARCH_$(ARCH))
+QEMU_ARCH            = $(QEMU_ARCH_$(XARCH))
 
 # QEMU_ARGS : some arch-specific args to pass to qemu
 QEMU_ARGS_i386       = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)"
@@ -61,10 +95,13 @@ QEMU_ARGS_x86        = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(
 QEMU_ARGS_arm64      = -M virt -cpu cortex-a53 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
 QEMU_ARGS_arm        = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
 QEMU_ARGS_mips       = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_ppc        = -M g3beige -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_ppc64      = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_ppc64le    = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
 QEMU_ARGS_riscv      = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
 QEMU_ARGS_s390       = -M s390-ccw-virtio -m 1G -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
 QEMU_ARGS_loongarch  = -M virt -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
-QEMU_ARGS            = $(QEMU_ARGS_$(ARCH)) $(QEMU_ARGS_EXTRA)
+QEMU_ARGS            = $(QEMU_ARGS_$(XARCH)) $(QEMU_ARGS_EXTRA)
 
 # OUTPUT is only set when run from the main makefile, otherwise
 # it defaults to this nolibc directory.
@@ -76,13 +113,21 @@ else
 Q=@
 endif
 
+CFLAGS_ppc = -m32 -mbig-endian -mno-vsx $(call cc-option,-mmultiple)
+CFLAGS_ppc64 = -m64 -mbig-endian -mno-vsx $(call cc-option,-mmultiple)
+CFLAGS_ppc64le = -m64 -mlittle-endian -mno-vsx $(call cc-option,-mabi=elfv2)
 CFLAGS_s390 = -m64
 CFLAGS_mips = -EL
 CFLAGS_STACKPROTECTOR ?= $(call cc-option,-mstack-protector-guard=global $(call cc-option,-fstack-protector-all))
-CFLAGS  ?= -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 \
+CFLAGS  ?= -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra \
                $(call cc-option,-fno-stack-protector) \
-               $(CFLAGS_$(ARCH)) $(CFLAGS_STACKPROTECTOR)
-LDFLAGS := -s
+               $(CFLAGS_$(XARCH)) $(CFLAGS_STACKPROTECTOR)
+LDFLAGS :=
+
+REPORT  ?= awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{if (!f) printf("\n"); f++; print;} /\[SKIPPED\][\r]*$$/{s++} \
+               END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \
+               if (f) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \
+               printf("\nSee all results in %s\n", ARGV[1]); }'
 
 help:
        @echo "Supported targets under selftests/nolibc:"
@@ -91,24 +136,25 @@ help:
        @echo "  sysroot      create the nolibc sysroot here (uses \$$ARCH)"
        @echo "  nolibc-test  build the executable (uses \$$CC and \$$CROSS_COMPILE)"
        @echo "  libc-test    build an executable using the compiler's default libc instead"
-       @echo "  run-user     runs the executable under QEMU (uses \$$ARCH, \$$TEST)"
+       @echo "  run-user     runs the executable under QEMU (uses \$$XARCH, \$$TEST)"
        @echo "  initramfs    prepare the initramfs with nolibc-test"
-       @echo "  defconfig    create a fresh new default config (uses \$$ARCH)"
-       @echo "  kernel       (re)build the kernel with the initramfs (uses \$$ARCH)"
-       @echo "  run          runs the kernel in QEMU after building it (uses \$$ARCH, \$$TEST)"
-       @echo "  rerun        runs a previously prebuilt kernel in QEMU (uses \$$ARCH, \$$TEST)"
+       @echo "  defconfig    create a fresh new default config (uses \$$XARCH)"
+       @echo "  kernel       (re)build the kernel with the initramfs (uses \$$XARCH)"
+       @echo "  run          runs the kernel in QEMU after building it (uses \$$XARCH, \$$TEST)"
+       @echo "  rerun        runs a previously prebuilt kernel in QEMU (uses \$$XARCH, \$$TEST)"
        @echo "  clean        clean the sysroot, initramfs, build and output files"
        @echo ""
        @echo "The output file is \"run.out\". Test ranges may be passed using \$$TEST."
        @echo ""
        @echo "Currently using the following variables:"
        @echo "  ARCH          = $(ARCH)"
+       @echo "  XARCH         = $(XARCH)"
        @echo "  CROSS_COMPILE = $(CROSS_COMPILE)"
        @echo "  CC            = $(CC)"
        @echo "  OUTPUT        = $(OUTPUT)"
        @echo "  TEST          = $(TEST)"
-       @echo "  QEMU_ARCH     = $(if $(QEMU_ARCH),$(QEMU_ARCH),UNKNOWN_ARCH) [determined from \$$ARCH]"
-       @echo "  IMAGE_NAME    = $(if $(IMAGE_NAME),$(IMAGE_NAME),UNKNOWN_ARCH) [determined from \$$ARCH]"
+       @echo "  QEMU_ARCH     = $(if $(QEMU_ARCH),$(QEMU_ARCH),UNKNOWN_ARCH) [determined from \$$XARCH]"
+       @echo "  IMAGE_NAME    = $(if $(IMAGE_NAME),$(IMAGE_NAME),UNKNOWN_ARCH) [determined from \$$XARCH]"
        @echo ""
 
 all: run
@@ -121,20 +167,33 @@ sysroot/$(ARCH)/include:
        $(Q)$(MAKE) -C ../../../include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone
        $(Q)mv sysroot/sysroot sysroot/$(ARCH)
 
+ifneq ($(NOLIBC_SYSROOT),0)
 nolibc-test: nolibc-test.c sysroot/$(ARCH)/include
        $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \
          -nostdlib -static -Isysroot/$(ARCH)/include $< -lgcc
+else
+nolibc-test: nolibc-test.c
+       $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \
+         -nostdlib -static -include ../../../include/nolibc/nolibc.h $< -lgcc
+endif
 
 libc-test: nolibc-test.c
-       $(QUIET_CC)$(CC) -o $@ $<
+       $(QUIET_CC)$(HOSTCC) -o $@ $<
+
+# local libc-test
+run-libc-test: libc-test
+       $(Q)./libc-test > "$(CURDIR)/run.out" || :
+       $(Q)$(REPORT) $(CURDIR)/run.out
+
+# local nolibc-test
+run-nolibc-test: nolibc-test
+       $(Q)./nolibc-test > "$(CURDIR)/run.out" || :
+       $(Q)$(REPORT) $(CURDIR)/run.out
 
 # qemu user-land test
 run-user: nolibc-test
        $(Q)qemu-$(QEMU_ARCH) ./nolibc-test > "$(CURDIR)/run.out" || :
-       $(Q)awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{f++} /\[SKIPPED\][\r]*$$/{s++} \
-                END{ printf("%d test(s) passed, %d skipped, %d failed.", p, s, f); \
-                if (s+f > 0) printf(" See all results in %s\n", ARGV[1]); else print; }' \
-                $(CURDIR)/run.out
+       $(Q)$(REPORT) $(CURDIR)/run.out
 
 initramfs: nolibc-test
        $(QUIET_MKDIR)mkdir -p initramfs
@@ -150,18 +209,16 @@ kernel: initramfs
 # run the tests after building the kernel
 run: kernel
        $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(srctree)/$(IMAGE)" -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out"
-       $(Q)awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{f++} /\[SKIPPED\][\r]*$$/{s++} \
-                END{ printf("%d test(s) passed, %d skipped, %d failed.", p, s, f); \
-                if (s+f > 0) printf(" See all results in %s\n", ARGV[1]); else print; }' \
-                $(CURDIR)/run.out
+       $(Q)$(REPORT) $(CURDIR)/run.out
 
 # re-run the tests from an existing kernel
 rerun:
        $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(srctree)/$(IMAGE)" -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out"
-       $(Q)awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{f++} /\[SKIPPED\][\r]*$$/{s++} \
-                END{ printf("%d test(s) passed, %d skipped, %d failed.", p, s, f); \
-                if (s+f > 0) printf(" See all results in %s\n", ARGV[1]); else print; }' \
-                $(CURDIR)/run.out
+       $(Q)$(REPORT) $(CURDIR)/run.out
+
+# report with existing test log
+report:
+       $(Q)$(REPORT) $(CURDIR)/run.out
 
 clean:
        $(call QUIET_CLEAN, sysroot)
index 970df9e..fb3bf91 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 
 #define _GNU_SOURCE
+#define _LARGEFILE64_SOURCE
 
 /* libc-specific include files
  * The program may be built in 3 ways:
@@ -14,7 +15,7 @@
 #include <string.h>
 #ifndef _NOLIBC_STDIO_H
 /* standard libcs need more includes */
-#include <linux/reboot.h>
+#include <sys/auxv.h>
 #include <sys/io.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #endif
 #endif
 
-/* will be used by nolibc by getenv() */
-char **environ;
+/* for the type of int_fast16_t and int_fast32_t, musl differs from glibc and nolibc */
+#define SINT_MAX_OF_TYPE(type) (((type)1 << (sizeof(type) * 8 - 2)) - (type)1 + ((type)1 << (sizeof(type) * 8 - 2)))
+#define SINT_MIN_OF_TYPE(type) (-SINT_MAX_OF_TYPE(type) - 1)
+
+/* will be used to test initialization of environ */
+static char **test_envp;
+
+/* will be used to test initialization of argv */
+static char **test_argv;
+
+/* will be used to test initialization of argc */
+static int test_argc;
+
+/* will be used by some test cases as readable file, please don't write it */
+static const char *argv0;
 
 /* definition of a series of tests */
 struct test {
@@ -66,7 +80,7 @@ char *itoa(int i)
 /* returns the error name (e.g. "ENOENT") for common errors, "SUCCESS" for 0,
  * or the decimal value for less common ones.
  */
-const char *errorname(int err)
+static const char *errorname(int err)
 {
        switch (err) {
        case 0: return "SUCCESS";
@@ -120,17 +134,26 @@ static void putcharn(char c, size_t n)
        fputs(buf, stdout);
 }
 
-static int pad_spc(int llen, int cnt, const char *fmt, ...)
-{
-       va_list args;
-       int ret;
-
-       putcharn(' ', cnt - llen);
+enum RESULT {
+       OK,
+       FAIL,
+       SKIPPED,
+};
 
-       va_start(args, fmt);
-       ret = vfprintf(stdout, fmt, args);
-       va_end(args);
-       return ret < 0 ? ret : ret + cnt - llen;
+static void result(int llen, enum RESULT r)
+{
+       const char *msg;
+
+       if (r == OK)
+               msg = " [OK]";
+       else if (r == SKIPPED)
+               msg = "[SKIPPED]";
+       else
+               msg = "[FAIL]";
+
+       if (llen < 64)
+               putcharn(' ', 64 - llen);
+       puts(msg);
 }
 
 /* The tests below are intended to be used by the macroes, which evaluate
@@ -140,173 +163,185 @@ static int pad_spc(int llen, int cnt, const char *fmt, ...)
  */
 
 #define EXPECT_ZR(cond, expr)                          \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_zr(expr, llen); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_zr(expr, llen); } while (0)
 
-static int expect_zr(int expr, int llen)
+static __attribute__((unused))
+int expect_zr(int expr, int llen)
 {
        int ret = !(expr == 0);
 
        llen += printf(" = %d ", expr);
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 
 #define EXPECT_NZ(cond, expr, val)                     \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_nz(expr, llen; } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_nz(expr, llen; } while (0)
 
-static int expect_nz(int expr, int llen)
+static __attribute__((unused))
+int expect_nz(int expr, int llen)
 {
        int ret = !(expr != 0);
 
        llen += printf(" = %d ", expr);
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 
 #define EXPECT_EQ(cond, expr, val)                             \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_eq(expr, llen, val); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_eq(expr, llen, val); } while (0)
 
-static int expect_eq(uint64_t expr, int llen, uint64_t val)
+static __attribute__((unused))
+int expect_eq(uint64_t expr, int llen, uint64_t val)
 {
        int ret = !(expr == val);
 
        llen += printf(" = %lld ", (long long)expr);
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 
 #define EXPECT_NE(cond, expr, val)                             \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_ne(expr, llen, val); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ne(expr, llen, val); } while (0)
 
-static int expect_ne(int expr, int llen, int val)
+static __attribute__((unused))
+int expect_ne(int expr, int llen, int val)
 {
        int ret = !(expr != val);
 
        llen += printf(" = %d ", expr);
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 
 #define EXPECT_GE(cond, expr, val)                             \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_ge(expr, llen, val); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ge(expr, llen, val); } while (0)
 
-static int expect_ge(int expr, int llen, int val)
+static __attribute__((unused))
+int expect_ge(int expr, int llen, int val)
 {
        int ret = !(expr >= val);
 
        llen += printf(" = %d ", expr);
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 
 #define EXPECT_GT(cond, expr, val)                             \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_gt(expr, llen, val); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_gt(expr, llen, val); } while (0)
 
-static int expect_gt(int expr, int llen, int val)
+static __attribute__((unused))
+int expect_gt(int expr, int llen, int val)
 {
        int ret = !(expr > val);
 
        llen += printf(" = %d ", expr);
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 
 #define EXPECT_LE(cond, expr, val)                             \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_le(expr, llen, val); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_le(expr, llen, val); } while (0)
 
-static int expect_le(int expr, int llen, int val)
+static __attribute__((unused))
+int expect_le(int expr, int llen, int val)
 {
        int ret = !(expr <= val);
 
        llen += printf(" = %d ", expr);
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 
 #define EXPECT_LT(cond, expr, val)                             \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_lt(expr, llen, val); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_lt(expr, llen, val); } while (0)
 
-static int expect_lt(int expr, int llen, int val)
+static __attribute__((unused))
+int expect_lt(int expr, int llen, int val)
 {
        int ret = !(expr < val);
 
        llen += printf(" = %d ", expr);
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 
 #define EXPECT_SYSZR(cond, expr)                               \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_syszr(expr, llen); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_syszr(expr, llen); } while (0)
 
-static int expect_syszr(int expr, int llen)
+static __attribute__((unused))
+int expect_syszr(int expr, int llen)
 {
        int ret = 0;
 
        if (expr) {
                ret = 1;
                llen += printf(" = %d %s ", expr, errorname(errno));
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
                llen += printf(" = %d ", expr);
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
 
 #define EXPECT_SYSEQ(cond, expr, val)                          \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_syseq(expr, llen, val); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_syseq(expr, llen, val); } while (0)
 
-static int expect_syseq(int expr, int llen, int val)
+static __attribute__((unused))
+int expect_syseq(int expr, int llen, int val)
 {
        int ret = 0;
 
        if (expr != val) {
                ret = 1;
                llen += printf(" = %d %s ", expr, errorname(errno));
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
                llen += printf(" = %d ", expr);
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
 
 #define EXPECT_SYSNE(cond, expr, val)                          \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_sysne(expr, llen, val); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_sysne(expr, llen, val); } while (0)
 
-static int expect_sysne(int expr, int llen, int val)
+static __attribute__((unused))
+int expect_sysne(int expr, int llen, int val)
 {
        int ret = 0;
 
        if (expr == val) {
                ret = 1;
                llen += printf(" = %d %s ", expr, errorname(errno));
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
                llen += printf(" = %d ", expr);
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
 
 #define EXPECT_SYSER2(cond, expr, expret, experr1, experr2)            \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_syserr2(expr, expret, experr1, experr2, llen); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_syserr2(expr, expret, experr1, experr2, llen); } while (0)
 
 #define EXPECT_SYSER(cond, expr, expret, experr)                       \
        EXPECT_SYSER2(cond, expr, expret, experr, 0)
 
-static int expect_syserr2(int expr, int expret, int experr1, int experr2, int llen)
+static __attribute__((unused))
+int expect_syserr2(int expr, int expret, int experr1, int experr2, int llen)
 {
        int ret = 0;
        int _errno = errno;
@@ -318,117 +353,238 @@ static int expect_syserr2(int expr, int expret, int experr1, int experr2, int ll
                        llen += printf(" != (%d %s) ", expret, errorname(experr1));
                else
                        llen += printf(" != (%d %s %s) ", expret, errorname(experr1), errorname(experr2));
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
 
 #define EXPECT_PTRZR(cond, expr)                               \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_ptrzr(expr, llen); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrzr(expr, llen); } while (0)
 
-static int expect_ptrzr(const void *expr, int llen)
+static __attribute__((unused))
+int expect_ptrzr(const void *expr, int llen)
 {
        int ret = 0;
 
        llen += printf(" = <%p> ", expr);
        if (expr) {
                ret = 1;
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
 
 #define EXPECT_PTRNZ(cond, expr)                               \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_ptrnz(expr, llen); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrnz(expr, llen); } while (0)
 
-static int expect_ptrnz(const void *expr, int llen)
+static __attribute__((unused))
+int expect_ptrnz(const void *expr, int llen)
 {
        int ret = 0;
 
        llen += printf(" = <%p> ", expr);
        if (!expr) {
                ret = 1;
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
+       } else {
+               result(llen, OK);
+       }
+       return ret;
+}
+
+#define EXPECT_PTREQ(cond, expr, cmp)                          \
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptreq(expr, llen, cmp); } while (0)
+
+static __attribute__((unused))
+int expect_ptreq(const void *expr, int llen, const void *cmp)
+{
+       int ret = 0;
+
+       llen += printf(" = <%p> ", expr);
+       if (expr != cmp) {
+               ret = 1;
+               result(llen, FAIL);
+       } else {
+               result(llen, OK);
+       }
+       return ret;
+}
+
+#define EXPECT_PTRNE(cond, expr, cmp)                          \
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrne(expr, llen, cmp); } while (0)
+
+static __attribute__((unused))
+int expect_ptrne(const void *expr, int llen, const void *cmp)
+{
+       int ret = 0;
+
+       llen += printf(" = <%p> ", expr);
+       if (expr == cmp) {
+               ret = 1;
+               result(llen, FAIL);
        } else {
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
+#define EXPECT_PTRGE(cond, expr, cmp)                          \
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrge(expr, llen, cmp); } while (0)
+
+static __attribute__((unused))
+int expect_ptrge(const void *expr, int llen, const void *cmp)
+{
+       int ret = !(expr >= cmp);
+
+       llen += printf(" = <%p> ", expr);
+       result(llen, ret ? FAIL : OK);
+       return ret;
+}
+
+#define EXPECT_PTRGT(cond, expr, cmp)                          \
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrgt(expr, llen, cmp); } while (0)
+
+static __attribute__((unused))
+int expect_ptrgt(const void *expr, int llen, const void *cmp)
+{
+       int ret = !(expr > cmp);
+
+       llen += printf(" = <%p> ", expr);
+       result(llen, ret ? FAIL : OK);
+       return ret;
+}
+
+
+#define EXPECT_PTRLE(cond, expr, cmp)                          \
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrle(expr, llen, cmp); } while (0)
+
+static __attribute__((unused))
+int expect_ptrle(const void *expr, int llen, const void *cmp)
+{
+       int ret = !(expr <= cmp);
+
+       llen += printf(" = <%p> ", expr);
+       result(llen, ret ? FAIL : OK);
+       return ret;
+}
+
+
+#define EXPECT_PTRLT(cond, expr, cmp)                          \
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrlt(expr, llen, cmp); } while (0)
+
+static __attribute__((unused))
+int expect_ptrlt(const void *expr, int llen, const void *cmp)
+{
+       int ret = !(expr < cmp);
+
+       llen += printf(" = <%p> ", expr);
+       result(llen, ret ? FAIL : OK);
+       return ret;
+}
+
+#define EXPECT_PTRER2(cond, expr, expret, experr1, experr2)            \
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrerr2(expr, expret, experr1, experr2, llen); } while (0)
+
+#define EXPECT_PTRER(cond, expr, expret, experr)                       \
+       EXPECT_PTRER2(cond, expr, expret, experr, 0)
+
+static __attribute__((unused))
+int expect_ptrerr2(const void *expr, const void *expret, int experr1, int experr2, int llen)
+{
+       int ret = 0;
+       int _errno = errno;
+
+       llen += printf(" = <%p> %s ", expr, errorname(_errno));
+       if (expr != expret || (_errno != experr1 && _errno != experr2)) {
+               ret = 1;
+               if (experr2 == 0)
+                       llen += printf(" != (<%p> %s) ", expret, errorname(experr1));
+               else
+                       llen += printf(" != (<%p> %s %s) ", expret, errorname(experr1), errorname(experr2));
+               result(llen, FAIL);
+       } else {
+               result(llen, OK);
+       }
+       return ret;
+}
 
 #define EXPECT_STRZR(cond, expr)                               \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_strzr(expr, llen); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_strzr(expr, llen); } while (0)
 
-static int expect_strzr(const char *expr, int llen)
+static __attribute__((unused))
+int expect_strzr(const char *expr, int llen)
 {
        int ret = 0;
 
        llen += printf(" = <%s> ", expr);
        if (expr) {
                ret = 1;
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
 
 #define EXPECT_STRNZ(cond, expr)                               \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_strnz(expr, llen); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_strnz(expr, llen); } while (0)
 
-static int expect_strnz(const char *expr, int llen)
+static __attribute__((unused))
+int expect_strnz(const char *expr, int llen)
 {
        int ret = 0;
 
        llen += printf(" = <%s> ", expr);
        if (!expr) {
                ret = 1;
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
 
 #define EXPECT_STREQ(cond, expr, cmp)                          \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_streq(expr, llen, cmp); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_streq(expr, llen, cmp); } while (0)
 
-static int expect_streq(const char *expr, int llen, const char *cmp)
+static __attribute__((unused))
+int expect_streq(const char *expr, int llen, const char *cmp)
 {
        int ret = 0;
 
        llen += printf(" = <%s> ", expr);
        if (strcmp(expr, cmp) != 0) {
                ret = 1;
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
 
 
 #define EXPECT_STRNE(cond, expr, cmp)                          \
-       do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_strne(expr, llen, cmp); } while (0)
+       do { if (!(cond)) result(llen, SKIPPED); else ret += expect_strne(expr, llen, cmp); } while (0)
 
-static int expect_strne(const char *expr, int llen, const char *cmp)
+static __attribute__((unused))
+int expect_strne(const char *expr, int llen, const char *cmp)
 {
        int ret = 0;
 
        llen += printf(" = <%s> ", expr);
        if (strcmp(expr, cmp) == 0) {
                ret = 1;
-               llen += pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
        } else {
-               llen += pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
        }
        return ret;
 }
@@ -438,6 +594,51 @@ static int expect_strne(const char *expr, int llen, const char *cmp)
 #define CASE_TEST(name) \
        case __LINE__: llen += printf("%d %s", test, #name);
 
+int run_startup(int min, int max)
+{
+       int test;
+       int ret = 0;
+       /* kernel at least passes HOME and TERM, shell passes more */
+       int env_total = 2;
+       /* checking NULL for argv/argv0, environ and _auxv is not enough, let's compare with sbrk(0) or &end */
+       extern char end;
+       char *brk = sbrk(0) != (void *)-1 ? sbrk(0) : &end;
+       /* differ from nolibc, both glibc and musl have no global _auxv */
+       const unsigned long *test_auxv = (void *)-1;
+#ifdef NOLIBC
+       test_auxv = _auxv;
+#endif
+
+       for (test = min; test >= 0 && test <= max; test++) {
+               int llen = 0; /* line length */
+
+               /* avoid leaving empty lines below, this will insert holes into
+                * test numbers.
+                */
+               switch (test + __LINE__ + 1) {
+               CASE_TEST(argc);             EXPECT_GE(1, test_argc, 1); break;
+               CASE_TEST(argv_addr);        EXPECT_PTRGT(1, test_argv, brk); break;
+               CASE_TEST(argv_environ);     EXPECT_PTRLT(1, test_argv, environ); break;
+               CASE_TEST(argv_total);       EXPECT_EQ(1, environ - test_argv - 1, test_argc ?: 1); break;
+               CASE_TEST(argv0_addr);       EXPECT_PTRGT(1, argv0, brk); break;
+               CASE_TEST(argv0_str);        EXPECT_STRNZ(1, argv0 > brk ? argv0 : NULL); break;
+               CASE_TEST(argv0_len);        EXPECT_GE(1,  argv0 > brk ? strlen(argv0) : 0, 1); break;
+               CASE_TEST(environ_addr);     EXPECT_PTRGT(1, environ, brk); break;
+               CASE_TEST(environ_envp);     EXPECT_PTREQ(1, environ, test_envp); break;
+               CASE_TEST(environ_auxv);     EXPECT_PTRLT(test_auxv != (void *)-1, environ, test_auxv); break;
+               CASE_TEST(environ_total);    EXPECT_GE(test_auxv != (void *)-1, (void *)test_auxv - (void *)environ - 1, env_total); break;
+               CASE_TEST(environ_HOME);     EXPECT_PTRNZ(1, getenv("HOME")); break;
+               CASE_TEST(auxv_addr);        EXPECT_PTRGT(test_auxv != (void *)-1, test_auxv, brk); break;
+               CASE_TEST(auxv_AT_UID);      EXPECT_EQ(1, getauxval(AT_UID), getuid()); break;
+               CASE_TEST(auxv_AT_PAGESZ);   EXPECT_GE(1, getauxval(AT_PAGESZ), 4096); break;
+               case __LINE__:
+                       return ret; /* must be last */
+               /* note: do not set any defaults so as to permit holes above */
+               }
+       }
+       return ret;
+}
+
 
 /* used by some syscall tests below */
 int test_getdents64(const char *dir)
@@ -458,9 +659,9 @@ int test_getdents64(const char *dir)
        return ret;
 }
 
-static int test_getpagesize(void)
+int test_getpagesize(void)
 {
-       long x = getpagesize();
+       int x = getpagesize();
        int c;
 
        if (x < 0)
@@ -487,7 +688,7 @@ static int test_getpagesize(void)
        return !c;
 }
 
-static int test_fork(void)
+int test_fork(void)
 {
        int status;
        pid_t pid;
@@ -512,14 +713,14 @@ static int test_fork(void)
        }
 }
 
-static int test_stat_timestamps(void)
+int test_stat_timestamps(void)
 {
        struct stat st;
 
        if (sizeof(st.st_atim.tv_sec) != sizeof(st.st_atime))
                return 1;
 
-       if (stat("/proc/self/", &st))
+       if (stat("/proc/self/", &st) && stat(argv0, &st) && stat("/", &st))
                return 1;
 
        if (st.st_atim.tv_sec != st.st_atime || st.st_atim.tv_nsec > 1000000000)
@@ -534,6 +735,86 @@ static int test_stat_timestamps(void)
        return 0;
 }
 
+int test_mmap_munmap(void)
+{
+       int ret, fd, i, page_size;
+       void *mem;
+       size_t file_size, length;
+       off_t offset, pa_offset;
+       struct stat stat_buf;
+       const char * const files[] = {
+               "/dev/zero",
+               "/proc/1/exe", "/proc/self/exe",
+               argv0,
+               NULL
+       };
+
+       page_size = getpagesize();
+       if (page_size < 0)
+               return 1;
+
+       /* find a right file to mmap, existed and accessible */
+       for (i = 0; files[i] != NULL; i++) {
+               ret = fd = open(files[i], O_RDONLY);
+               if (ret == -1)
+                       continue;
+               else
+                       break;
+       }
+       if (ret == -1)
+               return 1;
+
+       ret = stat(files[i], &stat_buf);
+       if (ret == -1)
+               goto end;
+
+       /* file size of the special /dev/zero is 0, let's assign one manually */
+       if (i == 0)
+               file_size = 3*page_size;
+       else
+               file_size = stat_buf.st_size;
+
+       offset = file_size - 1;
+       if (offset < 0)
+               offset = 0;
+       length = file_size - offset;
+       pa_offset = offset & ~(page_size - 1);
+
+       mem = mmap(NULL, length + offset - pa_offset, PROT_READ, MAP_SHARED, fd, pa_offset);
+       if (mem == MAP_FAILED) {
+               ret = 1;
+               goto end;
+       }
+
+       ret = munmap(mem, length + offset - pa_offset);
+
+end:
+       close(fd);
+       return !!ret;
+}
+
+int test_pipe(void)
+{
+       const char *const msg = "hello, nolibc";
+       int pipefd[2];
+       char buf[32];
+       size_t len;
+
+       if (pipe(pipefd) == -1)
+               return 1;
+
+       write(pipefd[1], msg, strlen(msg));
+       close(pipefd[1]);
+       len = read(pipefd[0], buf, sizeof(buf));
+       close(pipefd[0]);
+
+       if (len != strlen(msg))
+               return 1;
+
+       return !!memcmp(buf, msg, len);
+}
+
+
 /* Run syscall tests between IDs <min> and <max>.
  * Return 0 on success, non-zero on failure.
  */
@@ -548,6 +829,7 @@ int run_syscall(int min, int max)
        int tmp;
        int ret = 0;
        void *p1, *p2;
+       int has_gettid = 1;
 
        /* <proc> indicates whether or not /proc is mounted */
        proc = stat("/proc", &stat_buf) == 0;
@@ -555,6 +837,11 @@ int run_syscall(int min, int max)
        /* this will be used to skip certain tests that can't be run unprivileged */
        euid0 = geteuid() == 0;
 
+       /* from 2.30, glibc provides gettid() */
+#if defined(__GLIBC_MINOR__) && defined(__GLIBC__)
+       has_gettid = __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30);
+#endif
+
        for (test = min; test >= 0 && test <= max; test++) {
                int llen = 0; /* line length */
 
@@ -564,24 +851,24 @@ int run_syscall(int min, int max)
                switch (test + __LINE__ + 1) {
                CASE_TEST(getpid);            EXPECT_SYSNE(1, getpid(), -1); break;
                CASE_TEST(getppid);           EXPECT_SYSNE(1, getppid(), -1); break;
-#ifdef NOLIBC
-               CASE_TEST(gettid);            EXPECT_SYSNE(1, gettid(), -1); break;
-#endif
+               CASE_TEST(gettid);            EXPECT_SYSNE(has_gettid, gettid(), -1); break;
                CASE_TEST(getpgid_self);      EXPECT_SYSNE(1, getpgid(0), -1); break;
                CASE_TEST(getpgid_bad);       EXPECT_SYSER(1, getpgid(-1), -1, ESRCH); break;
                CASE_TEST(kill_0);            EXPECT_SYSZR(1, kill(getpid(), 0)); break;
                CASE_TEST(kill_CONT);         EXPECT_SYSZR(1, kill(getpid(), 0)); break;
                CASE_TEST(kill_BADPID);       EXPECT_SYSER(1, kill(INT_MAX, 0), -1, ESRCH); break;
+               CASE_TEST(sbrk_0);            EXPECT_PTRNE(1, sbrk(0), (void *)-1); break;
                CASE_TEST(sbrk);              if ((p1 = p2 = sbrk(4096)) != (void *)-1) p2 = sbrk(-4096); EXPECT_SYSZR(1, (p2 == (void *)-1) || p2 == p1); break;
                CASE_TEST(brk);               EXPECT_SYSZR(1, brk(sbrk(0))); break;
-               CASE_TEST(chdir_root);        EXPECT_SYSZR(1, chdir("/")); break;
+               CASE_TEST(chdir_root);        EXPECT_SYSZR(1, chdir("/")); chdir(getenv("PWD")); break;
                CASE_TEST(chdir_dot);         EXPECT_SYSZR(1, chdir(".")); break;
                CASE_TEST(chdir_blah);        EXPECT_SYSER(1, chdir("/blah"), -1, ENOENT); break;
+               CASE_TEST(chmod_argv0);       EXPECT_SYSZR(1, chmod(argv0, 0555)); break;
                CASE_TEST(chmod_self);        EXPECT_SYSER(proc, chmod("/proc/self", 0555), -1, EPERM); break;
                CASE_TEST(chown_self);        EXPECT_SYSER(proc, chown("/proc/self", 0, 0), -1, EPERM); break;
                CASE_TEST(chroot_root);       EXPECT_SYSZR(euid0, chroot("/")); break;
                CASE_TEST(chroot_blah);       EXPECT_SYSER(1, chroot("/proc/self/blah"), -1, ENOENT); break;
-               CASE_TEST(chroot_exe);        EXPECT_SYSER(proc, chroot("/proc/self/exe"), -1, ENOTDIR); break;
+               CASE_TEST(chroot_exe);        EXPECT_SYSER(1, chroot(argv0), -1, ENOTDIR); break;
                CASE_TEST(close_m1);          EXPECT_SYSER(1, close(-1), -1, EBADF); break;
                CASE_TEST(close_dup);         EXPECT_SYSZR(1, close(dup(0))); break;
                CASE_TEST(dup_0);             tmp = dup(0);  EXPECT_SYSNE(1, tmp, -1); close(tmp); break;
@@ -602,23 +889,28 @@ int run_syscall(int min, int max)
                CASE_TEST(link_root1);        EXPECT_SYSER(1, link("/", "/"), -1, EEXIST); break;
                CASE_TEST(link_blah);         EXPECT_SYSER(1, link("/proc/self/blah", "/blah"), -1, ENOENT); break;
                CASE_TEST(link_dir);          EXPECT_SYSER(euid0, link("/", "/blah"), -1, EPERM); break;
-               CASE_TEST(link_cross);        EXPECT_SYSER(proc, link("/proc/self/net", "/blah"), -1, EXDEV); break;
+               CASE_TEST(link_cross);        EXPECT_SYSER(proc, link("/proc/self/cmdline", "/blah"), -1, EXDEV); break;
                CASE_TEST(lseek_m1);          EXPECT_SYSER(1, lseek(-1, 0, SEEK_SET), -1, EBADF); break;
                CASE_TEST(lseek_0);           EXPECT_SYSER(1, lseek(0, 0, SEEK_SET), -1, ESPIPE); break;
                CASE_TEST(mkdir_root);        EXPECT_SYSER(1, mkdir("/", 0755), -1, EEXIST); break;
+               CASE_TEST(mmap_bad);          EXPECT_PTRER(1, mmap(NULL, 0, PROT_READ, MAP_PRIVATE, 0, 0), MAP_FAILED, EINVAL); break;
+               CASE_TEST(munmap_bad);        EXPECT_SYSER(1, munmap((void *)1, 0), -1, EINVAL); break;
+               CASE_TEST(mmap_munmap_good);  EXPECT_SYSZR(1, test_mmap_munmap()); break;
                CASE_TEST(open_tty);          EXPECT_SYSNE(1, tmp = open("/dev/null", 0), -1); if (tmp != -1) close(tmp); break;
                CASE_TEST(open_blah);         EXPECT_SYSER(1, tmp = open("/proc/self/blah", 0), -1, ENOENT); if (tmp != -1) close(tmp); break;
+               CASE_TEST(pipe);              EXPECT_SYSZR(1, test_pipe()); break;
                CASE_TEST(poll_null);         EXPECT_SYSZR(1, poll(NULL, 0, 0)); break;
                CASE_TEST(poll_stdout);       EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break;
                CASE_TEST(poll_fault);        EXPECT_SYSER(1, poll((void *)1, 1, 0), -1, EFAULT); break;
                CASE_TEST(prctl);             EXPECT_SYSER(1, prctl(PR_SET_NAME, (unsigned long)NULL, 0, 0, 0), -1, EFAULT); break;
                CASE_TEST(read_badf);         EXPECT_SYSER(1, read(-1, &tmp, 1), -1, EBADF); break;
+               CASE_TEST(rmdir_blah);        EXPECT_SYSER(1, rmdir("/blah"), -1, ENOENT); break;
                CASE_TEST(sched_yield);       EXPECT_SYSZR(1, sched_yield()); break;
                CASE_TEST(select_null);       EXPECT_SYSZR(1, ({ struct timeval tv = { 0 }; select(0, NULL, NULL, NULL, &tv); })); break;
                CASE_TEST(select_stdout);     EXPECT_SYSNE(1, ({ fd_set fds; FD_ZERO(&fds); FD_SET(1, &fds); select(2, NULL, &fds, NULL, NULL); }), -1); break;
                CASE_TEST(select_fault);      EXPECT_SYSER(1, select(1, (void *)1, NULL, NULL, 0), -1, EFAULT); break;
                CASE_TEST(stat_blah);         EXPECT_SYSER(1, stat("/proc/self/blah", &stat_buf), -1, ENOENT); break;
-               CASE_TEST(stat_fault);        EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break;
+               CASE_TEST(stat_fault);        EXPECT_SYSER(1, stat((void *)1, &stat_buf), -1, EFAULT); break;
                CASE_TEST(stat_timestamps);   EXPECT_SYSZR(1, test_stat_timestamps()); break;
                CASE_TEST(symlink_root);      EXPECT_SYSER(1, symlink("/", "/"), -1, EEXIST); break;
                CASE_TEST(unlink_root);       EXPECT_SYSER(1, unlink("/"), -1, EISDIR); break;
@@ -641,9 +933,7 @@ int run_syscall(int min, int max)
 int run_stdlib(int min, int max)
 {
        int test;
-       int tmp;
        int ret = 0;
-       void *p1, *p2;
 
        for (test = min; test >= 0 && test <= max; test++) {
                int llen = 0; /* line length */
@@ -698,32 +988,23 @@ int run_stdlib(int min, int max)
                CASE_TEST(limit_int_fast8_max);     EXPECT_EQ(1, INT_FAST8_MAX,    (int_fast8_t)     0x7f); break;
                CASE_TEST(limit_int_fast8_min);     EXPECT_EQ(1, INT_FAST8_MIN,    (int_fast8_t)     0x80); break;
                CASE_TEST(limit_uint_fast8_max);    EXPECT_EQ(1, UINT_FAST8_MAX,   (uint_fast8_t)    0xff); break;
-               CASE_TEST(limit_int_fast16_min);    EXPECT_EQ(1, INT_FAST16_MIN,   (int_fast16_t)    INTPTR_MIN); break;
-               CASE_TEST(limit_int_fast16_max);    EXPECT_EQ(1, INT_FAST16_MAX,   (int_fast16_t)    INTPTR_MAX); break;
+               CASE_TEST(limit_int_fast16_min);    EXPECT_EQ(1, INT_FAST16_MIN,   (int_fast16_t)    SINT_MIN_OF_TYPE(int_fast16_t)); break;
+               CASE_TEST(limit_int_fast16_max);    EXPECT_EQ(1, INT_FAST16_MAX,   (int_fast16_t)    SINT_MAX_OF_TYPE(int_fast16_t)); break;
                CASE_TEST(limit_uint_fast16_max);   EXPECT_EQ(1, UINT_FAST16_MAX,  (uint_fast16_t)   UINTPTR_MAX); break;
-               CASE_TEST(limit_int_fast32_min);    EXPECT_EQ(1, INT_FAST32_MIN,   (int_fast32_t)    INTPTR_MIN); break;
-               CASE_TEST(limit_int_fast32_max);    EXPECT_EQ(1, INT_FAST32_MAX,   (int_fast32_t)    INTPTR_MAX); break;
+               CASE_TEST(limit_int_fast32_min);    EXPECT_EQ(1, INT_FAST32_MIN,   (int_fast32_t)    SINT_MIN_OF_TYPE(int_fast32_t)); break;
+               CASE_TEST(limit_int_fast32_max);    EXPECT_EQ(1, INT_FAST32_MAX,   (int_fast32_t)    SINT_MAX_OF_TYPE(int_fast32_t)); break;
                CASE_TEST(limit_uint_fast32_max);   EXPECT_EQ(1, UINT_FAST32_MAX,  (uint_fast32_t)   UINTPTR_MAX); break;
                CASE_TEST(limit_int_fast64_min);    EXPECT_EQ(1, INT_FAST64_MIN,   (int_fast64_t)    INT64_MIN); break;
                CASE_TEST(limit_int_fast64_max);    EXPECT_EQ(1, INT_FAST64_MAX,   (int_fast64_t)    INT64_MAX); break;
                CASE_TEST(limit_uint_fast64_max);   EXPECT_EQ(1, UINT_FAST64_MAX,  (uint_fast64_t)   UINT64_MAX); break;
-#if __SIZEOF_LONG__ == 8
-               CASE_TEST(limit_intptr_min);        EXPECT_EQ(1, INTPTR_MIN,       (intptr_t)        0x8000000000000000LL); break;
-               CASE_TEST(limit_intptr_max);        EXPECT_EQ(1, INTPTR_MAX,       (intptr_t)        0x7fffffffffffffffLL); break;
-               CASE_TEST(limit_uintptr_max);       EXPECT_EQ(1, UINTPTR_MAX,      (uintptr_t)       0xffffffffffffffffULL); break;
-               CASE_TEST(limit_ptrdiff_min);       EXPECT_EQ(1, PTRDIFF_MIN,      (ptrdiff_t)       0x8000000000000000LL); break;
-               CASE_TEST(limit_ptrdiff_max);       EXPECT_EQ(1, PTRDIFF_MAX,      (ptrdiff_t)       0x7fffffffffffffffLL); break;
-               CASE_TEST(limit_size_max);          EXPECT_EQ(1, SIZE_MAX,         (size_t)          0xffffffffffffffffULL); break;
-#elif __SIZEOF_LONG__ == 4
-               CASE_TEST(limit_intptr_min);        EXPECT_EQ(1, INTPTR_MIN,       (intptr_t)        0x80000000); break;
-               CASE_TEST(limit_intptr_max);        EXPECT_EQ(1, INTPTR_MAX,       (intptr_t)        0x7fffffff); break;
-               CASE_TEST(limit_uintptr_max);       EXPECT_EQ(1, UINTPTR_MAX,      (uintptr_t)       0xffffffffU); break;
-               CASE_TEST(limit_ptrdiff_min);       EXPECT_EQ(1, PTRDIFF_MIN,      (ptrdiff_t)       0x80000000); break;
-               CASE_TEST(limit_ptrdiff_max);       EXPECT_EQ(1, PTRDIFF_MAX,      (ptrdiff_t)       0x7fffffff); break;
-               CASE_TEST(limit_size_max);          EXPECT_EQ(1, SIZE_MAX,         (size_t)          0xffffffffU); break;
-#else
-# warning "__SIZEOF_LONG__ is undefined"
-#endif /* __SIZEOF_LONG__ */
+               CASE_TEST(sizeof_long_sane);        EXPECT_EQ(1, sizeof(long) == 8 || sizeof(long) == 4, 1); break;
+               CASE_TEST(limit_intptr_min);        EXPECT_EQ(1, INTPTR_MIN,  sizeof(long) == 8 ? (intptr_t)  0x8000000000000000LL  : (intptr_t)  0x80000000); break;
+               CASE_TEST(limit_intptr_max);        EXPECT_EQ(1, INTPTR_MAX,  sizeof(long) == 8 ? (intptr_t)  0x7fffffffffffffffLL  : (intptr_t)  0x7fffffff); break;
+               CASE_TEST(limit_uintptr_max);       EXPECT_EQ(1, UINTPTR_MAX, sizeof(long) == 8 ? (uintptr_t) 0xffffffffffffffffULL : (uintptr_t) 0xffffffffU); break;
+               CASE_TEST(limit_ptrdiff_min);       EXPECT_EQ(1, PTRDIFF_MIN, sizeof(long) == 8 ? (ptrdiff_t) 0x8000000000000000LL  : (ptrdiff_t) 0x80000000); break;
+               CASE_TEST(limit_ptrdiff_max);       EXPECT_EQ(1, PTRDIFF_MAX, sizeof(long) == 8 ? (ptrdiff_t) 0x7fffffffffffffffLL  : (ptrdiff_t) 0x7fffffff); break;
+               CASE_TEST(limit_size_max);          EXPECT_EQ(1, SIZE_MAX,    sizeof(long) == 8 ? (size_t)    0xffffffffffffffffULL : (size_t)    0xffffffffU); break;
+
                case __LINE__:
                        return ret; /* must be last */
                /* note: do not set any defaults so as to permit holes above */
@@ -735,22 +1016,23 @@ int run_stdlib(int min, int max)
 #define EXPECT_VFPRINTF(c, expected, fmt, ...)                         \
        ret += expect_vfprintf(llen, c, expected, fmt, ##__VA_ARGS__)
 
-static int expect_vfprintf(int llen, size_t c, const char *expected, const char *fmt, ...)
+static int expect_vfprintf(int llen, int c, const char *expected, const char *fmt, ...)
 {
-       int ret, fd, w, r;
+       int ret, fd;
+       ssize_t w, r;
        char buf[100];
        FILE *memfile;
        va_list args;
 
-       fd = memfd_create("vfprintf", 0);
+       fd = open("/tmp", O_TMPFILE | O_EXCL | O_RDWR, 0600);
        if (fd == -1) {
-               pad_spc(llen, 64, "[FAIL]\n");
-               return 1;
+               result(llen, SKIPPED);
+               return 0;
        }
 
        memfile = fdopen(fd, "w+");
        if (!memfile) {
-               pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
                return 1;
        }
 
@@ -759,8 +1041,8 @@ static int expect_vfprintf(int llen, size_t c, const char *expected, const char
        va_end(args);
 
        if (w != c) {
-               llen += printf(" written(%d) != %d", w, (int) c);
-               pad_spc(llen, 64, "[FAIL]\n");
+               llen += printf(" written(%d) != %d", (int)w, c);
+               result(llen, FAIL);
                return 1;
        }
 
@@ -768,29 +1050,27 @@ static int expect_vfprintf(int llen, size_t c, const char *expected, const char
        lseek(fd, 0, SEEK_SET);
 
        r = read(fd, buf, sizeof(buf) - 1);
-       buf[r] = '\0';
 
        fclose(memfile);
 
        if (r != w) {
-               llen += printf(" written(%d) != read(%d)", w, r);
-               pad_spc(llen, 64, "[FAIL]\n");
+               llen += printf(" written(%d) != read(%d)", (int)w, (int)r);
+               result(llen, FAIL);
                return 1;
        }
 
+       buf[r] = '\0';
        llen += printf(" \"%s\" = \"%s\"", expected, buf);
        ret = strncmp(expected, buf, c);
 
-       pad_spc(llen, 64, ret ? "[FAIL]\n" : " [OK]\n");
+       result(llen, ret ? FAIL : OK);
        return ret;
 }
 
 static int run_vfprintf(int min, int max)
 {
        int test;
-       int tmp;
        int ret = 0;
-       void *p1, *p2;
 
        for (test = min; test >= 0 && test <= max; test++) {
                int llen = 0; /* line length */
@@ -828,7 +1108,8 @@ static int smash_stack(void)
        return 1;
 }
 
-static int run_protection(int min, int max)
+static int run_protection(int min __attribute__((unused)),
+                         int max __attribute__((unused)))
 {
        pid_t pid;
        int llen = 0, status;
@@ -837,14 +1118,14 @@ static int run_protection(int min, int max)
 
 #if !defined(_NOLIBC_STACKPROTECTOR)
        llen += printf("not supported");
-       pad_spc(llen, 64, "[SKIPPED]\n");
+       result(llen, SKIPPED);
        return 0;
 #endif
 
 #if defined(_NOLIBC_STACKPROTECTOR)
        if (!__stack_chk_guard) {
                llen += printf("__stack_chk_guard not initialized");
-               pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
                return 1;
        }
 #endif
@@ -855,7 +1136,7 @@ static int run_protection(int min, int max)
        switch (pid) {
        case -1:
                llen += printf("fork()");
-               pad_spc(llen, 64, "[FAIL]\n");
+               result(llen, FAIL);
                return 1;
 
        case 0:
@@ -871,10 +1152,10 @@ static int run_protection(int min, int max)
 
                if (pid == -1 || !WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) {
                        llen += printf("waitpid()");
-                       pad_spc(llen, 64, "[FAIL]\n");
+                       result(llen, FAIL);
                        return 1;
                }
-               pad_spc(llen, 64, " [OK]\n");
+               result(llen, OK);
                return 0;
        }
 }
@@ -890,11 +1171,13 @@ int prepare(void)
         */
        if (stat("/dev/.", &stat_buf) == 0 || mkdir("/dev", 0755) == 0) {
                if (stat("/dev/console", &stat_buf) != 0 ||
-                   stat("/dev/null", &stat_buf) != 0) {
+                   stat("/dev/null", &stat_buf) != 0 ||
+                   stat("/dev/zero", &stat_buf) != 0) {
                        /* try devtmpfs first, otherwise fall back to manual creation */
                        if (mount("/dev", "/dev", "devtmpfs", 0, 0) != 0) {
                                mknod("/dev/console", 0600 | S_IFCHR, makedev(5, 1));
                                mknod("/dev/null",    0666 | S_IFCHR, makedev(1, 3));
+                               mknod("/dev/zero",    0666 | S_IFCHR, makedev(1, 5));
                        }
                }
        }
@@ -921,16 +1204,23 @@ int prepare(void)
 
        /* try to mount /proc if not mounted. Silently fail otherwise */
        if (stat("/proc/.", &stat_buf) == 0 || mkdir("/proc", 0755) == 0) {
-               if (stat("/proc/self", &stat_buf) != 0)
-                       mount("/proc", "/proc", "proc", 0, 0);
+               if (stat("/proc/self", &stat_buf) != 0) {
+                       /* If not mountable, remove /proc completely to avoid misuse */
+                       if (mount("none", "/proc", "proc", 0, 0) != 0)
+                               rmdir("/proc");
+               }
        }
 
+       /* some tests rely on a writable /tmp */
+       mkdir("/tmp", 0755);
+
        return 0;
 }
 
 /* This is the definition of known test names, with their functions */
 static const struct test test_names[] = {
        /* add new tests here */
+       { .name = "startup",    .func = run_startup    },
        { .name = "syscall",    .func = run_syscall    },
        { .name = "stdlib",     .func = run_stdlib     },
        { .name = "vfprintf",   .func = run_vfprintf   },
@@ -938,6 +1228,35 @@ static const struct test test_names[] = {
        { 0 }
 };
 
+static int is_setting_valid(char *test)
+{
+       int idx, len, test_len, valid = 0;
+       char delimiter;
+
+       if (!test)
+               return valid;
+
+       test_len = strlen(test);
+
+       for (idx = 0; test_names[idx].name; idx++) {
+               len = strlen(test_names[idx].name);
+               if (test_len < len)
+                       continue;
+
+               if (strncmp(test, test_names[idx].name, len) != 0)
+                       continue;
+
+               delimiter = test[len];
+               if (delimiter != ':' && delimiter != ',' && delimiter != '\0')
+                       continue;
+
+               valid = 1;
+               break;
+       }
+
+       return valid;
+}
+
 int main(int argc, char **argv, char **envp)
 {
        int min = 0;
@@ -947,7 +1266,10 @@ int main(int argc, char **argv, char **envp)
        int idx;
        char *test;
 
-       environ = envp;
+       argv0 = argv[0];
+       test_argc = argc;
+       test_argv = argv;
+       test_envp = envp;
 
        /* when called as init, it's possible that no console was opened, for
         * example if no /dev file system was provided. We'll check that fd#1
@@ -963,10 +1285,10 @@ int main(int argc, char **argv, char **envp)
         *    syscall:5-15[:.*],stdlib:8-10
         */
        test = argv[1];
-       if (!test)
+       if (!is_setting_valid(test))
                test = getenv("NOLIBC_TEST");
 
-       if (test) {
+       if (is_setting_valid(test)) {
                char *comma, *colon, *dash, *value;
 
                do {
@@ -1044,17 +1366,13 @@ int main(int argc, char **argv, char **envp)
                 */
                printf("Leaving init with final status: %d\n", !!ret);
                if (ret == 0)
-                       reboot(LINUX_REBOOT_CMD_POWER_OFF);
+                       reboot(RB_POWER_OFF);
 #if defined(__x86_64__)
                /* QEMU started with "-device isa-debug-exit -no-reboot" will
                 * exit with status code 2N+1 when N is written to 0x501. We
                 * hard-code the syscall here as it's arch-dependent.
                 */
-#if defined(_NOLIBC_SYS_H)
-               else if (my_syscall3(__NR_ioperm, 0x501, 1, 1) == 0)
-#else
-               else if (ioperm(0x501, 1, 1) == 0)
-#endif
+               else if (syscall(__NR_ioperm, 0x501, 1, 1) == 0)
                        __asm__ volatile ("outb %%al, %%dx" :: "d"(0x501), "a"(0));
                /* if it does nothing, fall back to the regular panic */
 #endif