diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-28 19:03:24 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-28 19:03:24 -0700 |
commit | 1c59d383390f970b891b503b7f79b63a02db2ec5 (patch) | |
tree | d8f965dd0deac6bcc28a4022b51b772129ebdad6 | |
parent | 815c24a085dd8ab9bb7381e455afdb3f9c260e38 (diff) | |
parent | 556fb7131e03b0283672fb40f6dc2d151752aaa7 (diff) |
Merge tag 'linux-kselftest-nolibc-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
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, 1221 insertions, 1166 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 7f6426284e73..4b8d634f3a4e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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/ diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index 64d67b080744..909b6eb500fe 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -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 \ diff --git a/tools/include/nolibc/arch-aarch64.h b/tools/include/nolibc/arch-aarch64.h index 11f294a406b7..6c33c46848e3 100644 --- a/tools/include/nolibc/arch-aarch64.h +++ b/tools/include/nolibc/arch-aarch64.h @@ -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(); } diff --git a/tools/include/nolibc/arch-arm.h b/tools/include/nolibc/arch-arm.h index ca4c66987497..cae4afa7c1c7 100644 --- a/tools/include/nolibc/arch-arm.h +++ b/tools/include/nolibc/arch-arm.h @@ -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(); } diff --git a/tools/include/nolibc/arch-i386.h b/tools/include/nolibc/arch-i386.h index 3d672d925e9e..64415b9fac77 100644 --- a/tools/include/nolibc/arch-i386.h +++ b/tools/include/nolibc/arch-i386.h @@ -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(); } diff --git a/tools/include/nolibc/arch-loongarch.h b/tools/include/nolibc/arch-loongarch.h index ad3f266e7093..bf98f6220195 100644 --- a/tools/include/nolibc/arch-loongarch.h +++ b/tools/include/nolibc/arch-loongarch.h @@ -8,6 +8,7 @@ #define _NOLIBC_ARCH_LOONGARCH_H #include "compiler.h" +#include "crt.h" /* Syscalls for LoongArch : * - stack is 16-byte aligned @@ -22,18 +23,19 @@ * 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; \ }) @@ -43,12 +45,11 @@ 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; \ }) @@ -59,13 +60,12 @@ 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; \ }) @@ -77,13 +77,12 @@ 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; \ }) @@ -96,13 +95,12 @@ 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; \ }) @@ -116,13 +114,12 @@ 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; \ }) @@ -137,67 +134,29 @@ 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(); } diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h index db24e0837a39..4ab6fa54beee 100644 --- a/tools/include/nolibc/arch-mips.h +++ b/tools/include/nolibc/arch-mips.h @@ -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 index 000000000000..ac212e6185b2 --- /dev/null +++ b/tools/include/nolibc/arch-powerpc.h @@ -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 */ diff --git a/tools/include/nolibc/arch-riscv.h b/tools/include/nolibc/arch-riscv.h index a2e8564e66d6..950cc2283fd7 100644 --- a/tools/include/nolibc/arch-riscv.h +++ b/tools/include/nolibc/arch-riscv.h @@ -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(); } diff --git a/tools/include/nolibc/arch-s390.h b/tools/include/nolibc/arch-s390.h index 516dff5bff8b..5d60fd43f883 100644 --- a/tools/include/nolibc/arch-s390.h +++ b/tools/include/nolibc/arch-s390.h @@ -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(); } diff --git a/tools/include/nolibc/arch-x86_64.h b/tools/include/nolibc/arch-x86_64.h index 6fc4d8392742..e5ccb926c903 100644 --- a/tools/include/nolibc/arch-x86_64.h +++ b/tools/include/nolibc/arch-x86_64.h @@ -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(); } diff --git a/tools/include/nolibc/arch.h b/tools/include/nolibc/arch.h index 82b43935650f..e276fb0680af 100644 --- a/tools/include/nolibc/arch.h +++ b/tools/include/nolibc/arch.h @@ -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 index 000000000000..a5f33fef1672 --- /dev/null +++ b/tools/include/nolibc/crt.h @@ -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 */ diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h index 05a228a6ee78..1f8d821000ac 100644 --- a/tools/include/nolibc/nolibc.h +++ b/tools/include/nolibc/nolibc.h @@ -13,11 +13,10 @@ * 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() diff --git a/tools/include/nolibc/stackprotector.h b/tools/include/nolibc/stackprotector.h index 88f7b2d098ff..13f1d0e60387 100644 --- a/tools/include/nolibc/stackprotector.h +++ b/tools/include/nolibc/stackprotector.h @@ -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 */ diff --git a/tools/include/nolibc/stdint.h b/tools/include/nolibc/stdint.h index 4b282435a59a..6665e272e213 100644 --- a/tools/include/nolibc/stdint.h +++ b/tools/include/nolibc/stdint.h @@ -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; diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 0eef91daf289..cae402c11e57 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -21,6 +21,11 @@ #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" diff --git a/tools/include/nolibc/stdlib.h b/tools/include/nolibc/stdlib.h index 902162f80337..bacfd35c5156 100644 --- a/tools/include/nolibc/stdlib.h +++ b/tools/include/nolibc/stdlib.h @@ -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) { diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 856249a11890..fdb6bd6c0e2f 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -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" @@ -29,6 +28,22 @@ #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 */ diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index f96e28bff4ba..8cfc4c860fa4 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -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 @@ -81,11 +83,25 @@ #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) diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h index 0e832e10a0b2..e38f3660c051 100644 --- a/tools/include/nolibc/unistd.h +++ b/tools/include/nolibc/unistd.h @@ -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__) diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile index 1b7b3c82f8ad..dfe66776a331 100644 --- a/tools/testing/selftests/nolibc/Makefile +++ b/tools/testing/selftests/nolibc/Makefile @@ -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) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 970df9e55131..fb3bf91462e2 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -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> @@ -40,8 +41,21 @@ #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 |