Commit f01d4c8a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nolibc.2023.02.06a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu

Pull nolibc updates from Paul McKenney:

 - Add s390 support

 - Add support for the ARM Thumb1 instruction set

 - Fix O_* flags definitions for open() and fcntl()

 - Make errno a weak symbol instead of a static variable

 - Export environ as a weak symbol

 - Export _auxv as a weak symbol for auxilliary vector retrieval

 - Implement getauxval() and getpagesize()

 - Further improve self tests, including permitting userland testing of
   the nolibc library

* tag 'nolibc.2023.02.06a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu: (28 commits)
  selftests/nolibc: Add a "run-user" target to test the program in user land
  selftests/nolibc: Support "x86_64" for arch name
  selftests/nolibc: Add `getpagesize(2)` selftest
  nolibc/sys: Implement `getpagesize(2)` function
  nolibc/stdlib: Implement `getauxval(3)` function
  tools/nolibc: add auxiliary vector retrieval for s390
  tools/nolibc: add auxiliary vector retrieval for mips
  tools/nolibc: add auxiliary vector retrieval for riscv
  tools/nolibc: add auxiliary vector retrieval for arm
  tools/nolibc: add auxiliary vector retrieval for arm64
  tools/nolibc: add auxiliary vector retrieval for x86_64
  tools/nolibc: add auxiliary vector retrieval for i386
  tools/nolibc: export environ as a weak symbol on s390
  tools/nolibc: export environ as a weak symbol on riscv
  tools/nolibc: export environ as a weak symbol on mips
  tools/nolibc: export environ as a weak symbol on arm
  tools/nolibc: export environ as a weak symbol on arm64
  tools/nolibc: export environ as a weak symbol on i386
  tools/nolibc: export environ as a weak symbol on x86_64
  tools/nolibc: make errno a weak symbol instead of a static one
  ...
parents 525445ef c54ba417
......@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_AARCH64_H
#define _NOLIBC_ARCH_AARCH64_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x4000
/* The struct returned by the newfstatat() syscall. Differs slightly from the
* x86_64's stat one by field ordering, so be careful.
*/
......@@ -181,19 +169,31 @@ struct sys_stat_struct {
_arg1; \
})
char **environ __attribute__((weak));
const unsigned long *_auxv __attribute__((weak));
/* startup code */
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
__asm__ volatile (
"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"
"");
);
__builtin_unreachable();
}
#endif // _NOLIBC_ARCH_AARCH64_H
......@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_ARM_H
#define _NOLIBC_ARCH_ARM_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x4000
/* 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.
......@@ -70,20 +58,44 @@ struct sys_stat_struct {
* don't have to experience issues with register constraints.
* - the syscall number is always specified last in order to allow to force
* some registers before (gcc refuses a %-register at the last position).
* - in thumb mode without -fomit-frame-pointer, r7 is also used to store the
* frame pointer, and we cannot directly assign it as a register variable,
* nor can we clobber it. Instead we assign the r6 register and swap it
* with r7 before calling svc, and r6 is marked as clobbered.
* We're just using any regular register which we assign to r7 after saving
* it.
*
* Also, ARM supports the old_select syscall if newselect is not available
*/
#define __ARCH_WANT_SYS_OLD_SELECT
#if (defined(__THUMBEB__) || defined(__THUMBEL__)) && \
!defined(NOLIBC_OMIT_FRAME_POINTER)
/* swap r6,r7 needed in Thumb mode since we can't use nor clobber r7 */
#define _NOLIBC_SYSCALL_REG "r6"
#define _NOLIBC_THUMB_SET_R7 "eor r7, r6\neor r6, r7\neor r7, r6\n"
#define _NOLIBC_THUMB_RESTORE_R7 "mov r7, r6\n"
#else /* we're in ARM mode */
/* in Arm mode we can directly use r7 */
#define _NOLIBC_SYSCALL_REG "r7"
#define _NOLIBC_THUMB_SET_R7 ""
#define _NOLIBC_THUMB_RESTORE_R7 ""
#endif /* end THUMB */
#define my_syscall0(num) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0"); \
\
__asm__ volatile ( \
_NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_num) \
_NOLIBC_THUMB_RESTORE_R7 \
: "=r"(_arg1), "=r"(_num) \
: "r"(_arg1), \
"r"(_num) \
: "memory", "cc", "lr" \
); \
_arg1; \
......@@ -91,12 +103,14 @@ struct sys_stat_struct {
#define my_syscall1(num, arg1) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
\
__asm__ volatile ( \
_NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
: "=r"(_arg1) \
_NOLIBC_THUMB_RESTORE_R7 \
: "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), \
"r"(_num) \
: "memory", "cc", "lr" \
......@@ -106,13 +120,15 @@ struct sys_stat_struct {
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
\
__asm__ volatile ( \
_NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
: "=r"(_arg1) \
_NOLIBC_THUMB_RESTORE_R7 \
: "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), "r"(_arg2), \
"r"(_num) \
: "memory", "cc", "lr" \
......@@ -122,14 +138,16 @@ struct sys_stat_struct {
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
\
__asm__ volatile ( \
_NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
: "=r"(_arg1) \
_NOLIBC_THUMB_RESTORE_R7 \
: "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), \
"r"(_num) \
: "memory", "cc", "lr" \
......@@ -139,15 +157,17 @@ struct sys_stat_struct {
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
register long _arg4 __asm__ ("r3") = (long)(arg4); \
\
__asm__ volatile ( \
_NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
: "=r"(_arg1) \
_NOLIBC_THUMB_RESTORE_R7 \
: "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_num) \
: "memory", "cc", "lr" \
......@@ -157,7 +177,7 @@ struct sys_stat_struct {
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
......@@ -165,8 +185,10 @@ struct sys_stat_struct {
register long _arg5 __asm__ ("r4") = (long)(arg5); \
\
__asm__ volatile ( \
_NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
: "=r" (_arg1) \
_NOLIBC_THUMB_RESTORE_R7 \
: "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"r"(_num) \
: "memory", "cc", "lr" \
......@@ -174,31 +196,47 @@ struct sys_stat_struct {
_arg1; \
})
char **environ __attribute__((weak));
const unsigned long *_auxv __attribute__((weak));
/* startup code */
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
#if defined(__THUMBEB__) || defined(__THUMBEL__)
/* We enter here in 32-bit mode but if some previous functions were in
* 16-bit mode, the assembler cannot know, so we need to tell it we're in
* 32-bit now, then switch to 16-bit (is there a better way to do it than
* adding 1 by hand ?) and tell the asm we're now in 16-bit mode so that
* it generates correct instructions. Note that we do not support thumb1.
*/
".code 32\n"
"add r0, pc, #1\n"
"bx r0\n"
".code 16\n"
#endif
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
__asm__ volatile (
"pop {%r0}\n" // argc was in the stack
"mov %r1, %sp\n" // argv = sp
"add %r2, %r1, %r0, lsl #2\n" // envp = argv + 4*argc ...
"add %r2, %r2, $4\n" // ... + 4
"and %r3, %r1, $-8\n" // AAPCS : sp must be 8-byte aligned in the
"mov %sp, %r3\n" // callee, an bl doesn't push (lr=pc)
"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"
);
__builtin_unreachable();
}
#endif // _NOLIBC_ARCH_ARM_H
......@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_I386_H
#define _NOLIBC_ARCH_I386_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x10000
/* The struct returned by the stat() syscall, 32-bit only, the syscall returns
* exactly 56 bytes (stops before the unused array).
*/
......@@ -190,6 +178,9 @@ struct sys_stat_struct {
_eax; \
})
char **environ __attribute__((weak));
const unsigned long *_auxv __attribute__((weak));
/* startup code */
/*
* i386 System V ABI mandates:
......@@ -197,13 +188,20 @@ struct sys_stat_struct {
* 2) The deepest stack frame should be set to zero
*
*/
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
__asm__ volatile (
"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
......@@ -214,6 +212,8 @@ __asm__ (".section .text\n"
"movl $1, %eax\n" // NR_exit == 1
"int $0x80\n" // exit now
"hlt\n" // ensure it does not
"");
);
__builtin_unreachable();
}
#endif // _NOLIBC_ARCH_I386_H
......@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_MIPS_H
#define _NOLIBC_ARCH_MIPS_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_APPEND 0x0008
#define O_NONBLOCK 0x0080
#define O_CREAT 0x0100
#define O_TRUNC 0x0200
#define O_EXCL 0x0400
#define O_NOCTTY 0x0800
#define O_DIRECTORY 0x10000
/* The struct returned by the stat() syscall. 88 bytes are returned by the
* syscall.
*/
......@@ -188,20 +176,37 @@ struct sys_stat_struct {
_arg4 ? -_num : _num; \
})
char **environ __attribute__((weak));
const unsigned long *_auxv __attribute__((weak));
/* startup code, note that it's called __start on MIPS */
__asm__ (".section .text\n"
".weak __start\n"
".set nomips16\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __start(void)
{
__asm__ volatile (
//".set nomips16\n"
".set push\n"
".set noreorder\n"
".option pic0\n"
".ent __start\n"
"__start:\n"
//".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!
......@@ -210,8 +215,10 @@ __asm__ (".section .text\n"
"move $a0, $v0\n" // retrieve 32-bit exit code from v0
"li $v0, 4001\n" // NR_exit == 4001
"syscall\n"
".end __start\n"
//".end __start\n"
".set pop\n"
"");
);
__builtin_unreachable();
}
#endif // _NOLIBC_ARCH_MIPS_H
......@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_RISCV_H
#define _NOLIBC_ARCH_RISCV_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x10000
struct sys_stat_struct {
unsigned long st_dev; /* Device. */
unsigned long st_ino; /* File serial number. */
......@@ -182,10 +170,13 @@ struct sys_stat_struct {
_arg1; \
})
char **environ __attribute__((weak));
const unsigned long *_auxv __attribute__((weak));
/* startup code */
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
__asm__ volatile (
".option push\n"
".option norelax\n"
"lla gp, __global_pointer$\n"
......@@ -195,10 +186,23 @@ __asm__ (".section .text\n"
"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 {
"ld 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)
"sd a3, %lo(_auxv)(a4)\n" // store a3 into _auxv
"lui a3, %hi(environ)\n" // a3 = &environ (high bits)
"sd 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"
"");
);
__builtin_unreachable();
}
#endif // _NOLIBC_ARCH_RISCV_H
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* s390 specific definitions for NOLIBC
*/
#ifndef _NOLIBC_ARCH_S390_H
#define _NOLIBC_ARCH_S390_H
#include <asm/unistd.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];
};
/* Syscalls for s390:
* - registers are 64-bit
* - syscall number is passed in r1
* - arguments are in r2-r7
* - the system call is performed by calling the svc instruction
* - syscall return value is in r2
* - r1 and r2 are clobbered, others are preserved.
*
* Link s390 ABI: https://github.com/IBM/s390x-abi
*
*/
#define my_syscall0(num) \
({ \
register long _num __asm__ ("1") = (num); \
register long _rc __asm__ ("2"); \
\
__asm__ volatile ( \
"svc 0\n" \
: "=d"(_rc) \
: "d"(_num) \
: "memory", "cc" \
); \
_rc; \
})
#define my_syscall1(num, arg1) \
({ \
register long _num __asm__ ("1") = (num); \
register long _arg1 __asm__ ("2") = (long)(arg1); \
\
__asm__ volatile ( \
"svc 0\n" \
: "+d"(_arg1) \
: "d"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__ ("1") = (num); \
register long _arg1 __asm__ ("2") = (long)(arg1); \
register long _arg2 __asm__ ("3") = (long)(arg2); \
\
__asm__ volatile ( \
"svc 0\n" \
: "+d"(_arg1) \
: "d"(_arg2), "d"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
register long _num __asm__ ("1") = (num); \
register long _arg1 __asm__ ("2") = (long)(arg1); \
register long _arg2 __asm__ ("3") = (long)(arg2); \
register long _arg3 __asm__ ("4") = (long)(arg3); \
\
__asm__ volatile ( \
"svc 0\n" \
: "+d"(_arg1) \
: "d"(_arg2), "d"(_arg3), "d"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
register long _num __asm__ ("1") = (num); \
register long _arg1 __asm__ ("2") = (long)(arg1); \
register long _arg2 __asm__ ("3") = (long)(arg2); \
register long _arg3 __asm__ ("4") = (long)(arg3); \
register long _arg4 __asm__ ("5") = (long)(arg4); \
\
__asm__ volatile ( \
"svc 0\n" \
: "+d"(_arg1) \
: "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
register long _num __asm__ ("1") = (num); \
register long _arg1 __asm__ ("2") = (long)(arg1); \
register long _arg2 __asm__ ("3") = (long)(arg2); \
register long _arg3 __asm__ ("4") = (long)(arg3); \
register long _arg4 __asm__ ("5") = (long)(arg4); \
register long _arg5 __asm__ ("6") = (long)(arg5); \
\
__asm__ volatile ( \
"svc 0\n" \
: "+d"(_arg1) \
: "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_arg5), \
"d"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
({ \
register long _num __asm__ ("1") = (num); \
register long _arg1 __asm__ ("2") = (long)(arg1); \
register long _arg2 __asm__ ("3") = (long)(arg2); \
register long _arg3 __asm__ ("4") = (long)(arg3); \
register long _arg4 __asm__ ("5") = (long)(arg4); \
register long _arg5 __asm__ ("6") = (long)(arg5); \
register long _arg6 __asm__ ("7") = (long)(arg6); \
\
__asm__ volatile ( \
"svc 0\n" \
: "+d"(_arg1) \
: "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_arg5), \
"d"(_arg6), "d"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
char **environ __attribute__((weak));
const unsigned long *_auxv __attribute__((weak));
/* startup code */
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _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"
);
__builtin_unreachable();
}
struct s390_mmap_arg_struct {
unsigned long addr;
unsigned long len;
unsigned long prot;
unsigned long flags;
unsigned long fd;
unsigned long offset;
};
static __attribute__((unused))
void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,
off_t offset)
{
struct s390_mmap_arg_struct args = {
.addr = (unsigned long)addr,
.len = (unsigned long)length,
.prot = prot,
.flags = flags,
.fd = fd,
.offset = (unsigned long)offset
};
return (void *)my_syscall1(__NR_mmap, &args);
}
#define sys_mmap sys_mmap
#endif // _NOLIBC_ARCH_S390_H
......@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_X86_64_H
#define _NOLIBC_ARCH_X86_64_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x10000
/* The struct returned by the stat() syscall, equivalent to stat64(). The
* syscall returns 116 bytes and stops in the middle of __unused.
*/
......@@ -190,6 +178,9 @@ struct sys_stat_struct {
_ret; \
})
char **environ __attribute__((weak));
const unsigned long *_auxv __attribute__((weak));
/* startup code */
/*
* x86-64 System V ABI mandates:
......@@ -197,19 +188,28 @@ struct sys_stat_struct {
* 2) The deepest stack frame should be zero (the %rbp).
*
*/
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
{
__asm__ volatile (
"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
"");
);
__builtin_unreachable();
}
#endif // _NOLIBC_ARCH_X86_64_H
......@@ -27,6 +27,8 @@
#include "arch-mips.h"
#elif defined(__riscv)
#include "arch-riscv.h"
#elif defined(__s390x__)
#include "arch-s390.h"
#endif
#endif /* _NOLIBC_ARCH_H */
......@@ -9,11 +9,9 @@
#include <asm/errno.h>
/* this way it will be removed if unused */
static int errno;
#ifndef NOLIBC_IGNORE_ERRNO
#define SET_ERRNO(v) do { errno = (v); } while (0)
int errno __attribute__((weak));
#else
#define SET_ERRNO(v) do { } while (0)
#endif
......
......@@ -12,6 +12,7 @@
#include "types.h"
#include "sys.h"
#include "string.h"
#include <linux/auxvec.h>
struct nolibc_heap {
size_t len;
......@@ -108,6 +109,32 @@ char *getenv(const char *name)
return _getenv(name, environ);
}
static __attribute__((unused))
unsigned long getauxval(unsigned long type)
{
const unsigned long *auxv = _auxv;
unsigned long ret;
if (!auxv)
return 0;
while (1) {
if (!auxv[0] && !auxv[1]) {
ret = 0;
break;
}
if (auxv[0] == type) {
ret = auxv[1];
break;
}
auxv += 2;
}
return ret;
}
static __attribute__((unused))
void *malloc(size_t len)
{
......
......@@ -11,6 +11,7 @@
#include "std.h"
/* system includes */
#include <asm/fcntl.h> // for O_*
#include <asm/unistd.h>
#include <asm/signal.h> // for SIGCHLD
#include <asm/ioctls.h>
......@@ -18,6 +19,7 @@
#include <linux/fs.h>
#include <linux/loop.h>
#include <linux/time.h>
#include <linux/auxvec.h>
#include "arch.h"
#include "errno.h"
......@@ -498,6 +500,26 @@ pid_t gettid(void)
return sys_gettid();
}
static unsigned long getauxval(unsigned long key);
/*
* long getpagesize(void);
*/
static __attribute__((unused))
long getpagesize(void)
{
long ret;
ret = getauxval(AT_PAGESZ);
if (!ret) {
SET_ERRNO(ENOENT);
return -1;
}
return ret;
}
/*
* int gettimeofday(struct timeval *tv, struct timezone *tz);
......@@ -686,6 +708,7 @@ int mknod(const char *path, mode_t mode, dev_t dev)
#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)
......@@ -707,6 +730,7 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,
return (void *)my_syscall6(n, addr, length, prot, flags, fd, offset);
#endif
}
#endif
static __attribute__((unused))
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
......
......@@ -14,21 +14,25 @@ endif
# kernel image names by architecture
IMAGE_i386 = arch/x86/boot/bzImage
IMAGE_x86_64 = arch/x86/boot/bzImage
IMAGE_x86 = arch/x86/boot/bzImage
IMAGE_arm64 = arch/arm64/boot/Image
IMAGE_arm = arch/arm/boot/zImage
IMAGE_mips = vmlinuz
IMAGE_riscv = arch/riscv/boot/Image
IMAGE_s390 = arch/s390/boot/bzImage
IMAGE = $(IMAGE_$(ARCH))
IMAGE_NAME = $(notdir $(IMAGE))
# default kernel configurations that appear to be usable
DEFCONFIG_i386 = defconfig
DEFCONFIG_x86_64 = defconfig
DEFCONFIG_x86 = defconfig
DEFCONFIG_arm64 = defconfig
DEFCONFIG_arm = multi_v7_defconfig
DEFCONFIG_mips = malta_defconfig
DEFCONFIG_riscv = defconfig
DEFCONFIG_s390 = defconfig
DEFCONFIG = $(DEFCONFIG_$(ARCH))
# optional tests to run (default = all)
......@@ -36,20 +40,24 @@ TEST =
# QEMU_ARCH: arch names used by qemu
QEMU_ARCH_i386 = i386
QEMU_ARCH_x86_64 = x86_64
QEMU_ARCH_x86 = x86_64
QEMU_ARCH_arm64 = aarch64
QEMU_ARCH_arm = arm
QEMU_ARCH_mips = mipsel # works with malta_defconfig
QEMU_ARCH_riscv = riscv64
QEMU_ARCH_s390 = s390x
QEMU_ARCH = $(QEMU_ARCH_$(ARCH))
# 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=%)"
QEMU_ARGS_x86_64 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)"
QEMU_ARGS_x86 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)"
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_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 = $(QEMU_ARGS_$(ARCH))
# OUTPUT is only set when run from the main makefile, otherwise
......@@ -62,7 +70,8 @@ else
Q=@
endif
CFLAGS ?= -Os -fno-ident -fno-asynchronous-unwind-tables
CFLAGS_s390 = -m64
CFLAGS ?= -Os -fno-ident -fno-asynchronous-unwind-tables $(CFLAGS_$(ARCH))
LDFLAGS := -s
help:
......@@ -71,6 +80,7 @@ help:
@echo " help this help"
@echo " sysroot create the nolibc sysroot here (uses \$$ARCH)"
@echo " nolibc-test build the executable (uses \$$CC and \$$CROSS_COMPILE)"
@echo " run-user runs the executable under QEMU (uses \$$ARCH, \$$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)"
......@@ -104,6 +114,11 @@ nolibc-test: nolibc-test.c sysroot/$(ARCH)/include
$(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \
-nostdlib -static -Isysroot/$(ARCH)/include $< -lgcc
# qemu user-land test
run-user: nolibc-test
$(Q)qemu-$(QEMU_ARCH) ./nolibc-test > "$(CURDIR)/run.out" || :
$(Q)grep -w FAIL "$(CURDIR)/run.out" && echo "See all results in $(CURDIR)/run.out" || echo "$$(grep -c ^[0-9].*OK $(CURDIR)/run.out) test(s) passed."
initramfs: nolibc-test
$(QUIET_MKDIR)mkdir -p initramfs
$(call QUIET_INSTALL, initramfs/init)
......
......@@ -442,6 +442,35 @@ int test_getdents64(const char *dir)
return ret;
}
static int test_getpagesize(void)
{
long x = getpagesize();
int c;
if (x < 0)
return x;
#if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
/*
* x86 family is always 4K page.
*/
c = (x == 4096);
#elif defined(__aarch64__)
/*
* Linux aarch64 supports three values of page size: 4K, 16K, and 64K
* which are selected at kernel compilation time.
*/
c = (x == 4096 || x == (16 * 1024) || x == (64 * 1024));
#else
/*
* Assuming other architectures must have at least 4K page.
*/
c = (x >= 4096);
#endif
return !c;
}
/* Run syscall tests between IDs <min> and <max>.
* Return 0 on success, non-zero on failure.
*/
......@@ -502,6 +531,7 @@ int run_syscall(int min, int max)
CASE_TEST(gettimeofday_bad2); EXPECT_SYSER(1, gettimeofday(NULL, (void *)1), -1, EFAULT); break;
CASE_TEST(gettimeofday_bad2); EXPECT_SYSER(1, gettimeofday(NULL, (void *)1), -1, EFAULT); break;
#endif
CASE_TEST(getpagesize); EXPECT_SYSZR(1, test_getpagesize()); break;
CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break;
CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break;
CASE_TEST(link_root1); EXPECT_SYSER(1, link("/", "/"), -1, EEXIST); break;
......
......@@ -159,6 +159,9 @@ identify_boot_image () {
qemu-system-aarch64)
echo arch/arm64/boot/Image
;;
qemu-system-s390x)
echo arch/s390/boot/bzImage
;;
*)
echo vmlinux
;;
......@@ -184,6 +187,9 @@ identify_qemu () {
elif echo $u | grep -q aarch64
then
echo qemu-system-aarch64
elif echo $u | grep -q 'IBM S/390'
then
echo qemu-system-s390x
elif uname -a | grep -q ppc64
then
echo qemu-system-ppc64
......
......@@ -64,7 +64,7 @@ ___EOF___
# build using nolibc on supported archs (smaller executable) and fall
# back to regular glibc on other ones.
if echo -e "#if __x86_64__||__i386__||__i486__||__i586__||__i686__" \
"||__ARM_EABI__||__aarch64__\nyes\n#endif" \
"||__ARM_EABI__||__aarch64__||__s390x__\nyes\n#endif" \
| ${CROSS_COMPILE}gcc -E -nostdlib -xc - \
| grep -q '^yes'; then
# architecture supported by nolibc
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment