mirror of
https://github.com/torvalds/linux.git
synced 2025-04-12 06:49:52 +00:00
nolibc changes for 6.15
Changes ------- * 32bit s390 support * opendir() and friends * openat() support * sscanf() support * various cleanups -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQTg4lxklFHAidmUs57B+h1jyw5bOAUCZ8w24QAKCRDB+h1jyw5b OIRTAQD1KvUV0qYOKU3xqOiUxDe84DyFv4mgu1iZoqmd0po/CQEAs8HU4XWp4Ls0 tPkcLqlhiVPgwhtipjklW8uy6JcFzAI= =+Zma -----END PGP SIGNATURE----- Merge tag 'nolibc-20250308-for-6.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu Pull nolibc updates from Paul McKenney: - 32bit s390 support - opendir() and friends - openat() support - sscanf() support - various cleanups [ Paul has just forwarded the pull request from Thomas Weißschuh, so the tag signature is from Thomas, not Paul - Linus ] * tag 'nolibc-20250308-for-6.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu: (26 commits) tools/nolibc: don't use asm/ UAPI headers selftests/nolibc: stop testing constructor order selftests/nolibc: use O_RDONLY flag instead of 0 tools/nolibc: drop outdated example from overview comment tools/nolibc: process open() vararg as mode_t tools/nolibc: always use openat(2) instead of open(2) tools/nolibc: add support for openat(2) selftests/nolibc: add armthumb configuration selftests/nolibc: explicitly enable ARM mode Revert "selftests: kselftest: Fix build failure with NOLIBC" tools/nolibc: add support for [v]sscanf() tools/nolibc: add support for 32-bit s390 selftests/nolibc: rename s390 to s390x selftests/nolibc: only run constructor tests on nolibc selftests/nolibc: split up architecture list in run-tests.sh tools/nolibc: add support for directory access tools/nolibc: add support for sys_llseek() selftests/nolibc: always keep test kernel configuration up to date selftests/nolibc: execute defconfig before other targets selftests/nolibc: drop call to mrproper target ...
This commit is contained in:
commit
418becac37
@ -29,6 +29,7 @@ all_files := \
|
||||
compiler.h \
|
||||
crt.h \
|
||||
ctype.h \
|
||||
dirent.h \
|
||||
errno.h \
|
||||
nolibc.h \
|
||||
signal.h \
|
||||
|
@ -179,6 +179,7 @@
|
||||
})
|
||||
|
||||
/* startup code, note that it's called __start on MIPS */
|
||||
void __start(void);
|
||||
void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector __start(void)
|
||||
{
|
||||
__asm__ volatile (
|
||||
|
@ -5,8 +5,8 @@
|
||||
|
||||
#ifndef _NOLIBC_ARCH_S390_H
|
||||
#define _NOLIBC_ARCH_S390_H
|
||||
#include <asm/signal.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include "compiler.h"
|
||||
#include "crt.h"
|
||||
@ -143,8 +143,13 @@
|
||||
void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector _start(void)
|
||||
{
|
||||
__asm__ volatile (
|
||||
#ifdef __s390x__
|
||||
"lgr %r2, %r15\n" /* save stack pointer to %r2, as arg1 of _start_c */
|
||||
"aghi %r15, -160\n" /* allocate new stackframe */
|
||||
#else
|
||||
"lr %r2, %r15\n"
|
||||
"ahi %r15, -96\n"
|
||||
#endif
|
||||
"xc 0(8,%r15), 0(%r15)\n" /* clear backchain */
|
||||
"brasl %r14, _start_c\n" /* transfer to c runtime */
|
||||
);
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "arch-powerpc.h"
|
||||
#elif defined(__riscv)
|
||||
#include "arch-riscv.h"
|
||||
#elif defined(__s390x__)
|
||||
#elif defined(__s390x__) || defined(__s390__)
|
||||
#include "arch-s390.h"
|
||||
#elif defined(__loongarch__)
|
||||
#include "arch-loongarch.h"
|
||||
|
@ -10,6 +10,7 @@
|
||||
char **environ __attribute__((weak));
|
||||
const unsigned long *_auxv __attribute__((weak));
|
||||
|
||||
void _start(void);
|
||||
static void __stack_chk_init(void);
|
||||
static void exit(int);
|
||||
|
||||
@ -22,6 +23,7 @@ extern void (*const __init_array_end[])(int, char **, char**) __attribute__((wea
|
||||
extern void (*const __fini_array_start[])(void) __attribute__((weak));
|
||||
extern void (*const __fini_array_end[])(void) __attribute__((weak));
|
||||
|
||||
void _start_c(long *sp);
|
||||
__attribute__((weak,used))
|
||||
void _start_c(long *sp)
|
||||
{
|
||||
|
98
tools/include/nolibc/dirent.h
Normal file
98
tools/include/nolibc/dirent.h
Normal file
@ -0,0 +1,98 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
|
||||
/*
|
||||
* Directory access for NOLIBC
|
||||
* Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
|
||||
*/
|
||||
|
||||
#ifndef _NOLIBC_DIRENT_H
|
||||
#define _NOLIBC_DIRENT_H
|
||||
|
||||
#include "stdint.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <linux/limits.h>
|
||||
|
||||
struct dirent {
|
||||
ino_t d_ino;
|
||||
char d_name[NAME_MAX + 1];
|
||||
};
|
||||
|
||||
/* See comment of FILE in stdio.h */
|
||||
typedef struct {
|
||||
char dummy[1];
|
||||
} DIR;
|
||||
|
||||
static __attribute__((unused))
|
||||
DIR *fdopendir(int fd)
|
||||
{
|
||||
if (fd < 0) {
|
||||
SET_ERRNO(EBADF);
|
||||
return NULL;
|
||||
}
|
||||
return (DIR *)(intptr_t)~fd;
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
DIR *opendir(const char *name)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(name, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
return fdopendir(fd);
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
int closedir(DIR *dirp)
|
||||
{
|
||||
intptr_t i = (intptr_t)dirp;
|
||||
|
||||
if (i >= 0) {
|
||||
SET_ERRNO(EBADF);
|
||||
return -1;
|
||||
}
|
||||
return close(~i);
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
|
||||
{
|
||||
char buf[sizeof(struct linux_dirent64) + NAME_MAX + 1];
|
||||
struct linux_dirent64 *ldir = (void *)buf;
|
||||
intptr_t i = (intptr_t)dirp;
|
||||
int fd, ret;
|
||||
|
||||
if (i >= 0)
|
||||
return EBADF;
|
||||
|
||||
fd = ~i;
|
||||
|
||||
ret = sys_getdents64(fd, ldir, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return -ret;
|
||||
if (ret == 0) {
|
||||
*result = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* getdents64() returns as many entries as fit the buffer.
|
||||
* readdir() can only return one entry at a time.
|
||||
* Make sure the non-returned ones are not skipped.
|
||||
*/
|
||||
ret = lseek(fd, ldir->d_off, SEEK_SET);
|
||||
if (ret == -1)
|
||||
return errno;
|
||||
|
||||
entry->d_ino = ldir->d_ino;
|
||||
/* the destination should always be big enough */
|
||||
strlcpy(entry->d_name, ldir->d_name, sizeof(entry->d_name));
|
||||
*result = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* make sure to include all global symbols */
|
||||
#include "nolibc.h"
|
||||
|
||||
#endif /* _NOLIBC_DIRENT_H */
|
@ -7,7 +7,7 @@
|
||||
#ifndef _NOLIBC_ERRNO_H
|
||||
#define _NOLIBC_ERRNO_H
|
||||
|
||||
#include <asm/errno.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#ifndef NOLIBC_IGNORE_ERRNO
|
||||
#define SET_ERRNO(v) do { errno = (v); } while (0)
|
||||
|
@ -31,8 +31,7 @@
|
||||
* - The third level is the libc call definition. It exposes the lower raw
|
||||
* sys_<name>() calls in a way that looks like what a libc usually does,
|
||||
* takes care of specific input values, and of setting errno upon error.
|
||||
* There can be minor variations compared to standard libc calls. For
|
||||
* example the open() call always takes 3 args here.
|
||||
* There can be minor variations compared to standard libc calls.
|
||||
*
|
||||
* The errno variable is declared static and unused. This way it can be
|
||||
* optimized away if not used. However this means that a program made of
|
||||
@ -105,6 +104,7 @@
|
||||
#include "string.h"
|
||||
#include "time.h"
|
||||
#include "stackprotector.h"
|
||||
#include "dirent.h"
|
||||
|
||||
/* Used by programs to avoid std includes */
|
||||
#define NOLIBC
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "sys.h"
|
||||
|
||||
/* This one is not marked static as it's needed by libgcc for divide by zero */
|
||||
int raise(int signal);
|
||||
__attribute__((weak,unused,section(".text.nolibc_raise")))
|
||||
int raise(int signal)
|
||||
{
|
||||
|
@ -18,6 +18,7 @@
|
||||
* triggering stack protector errors themselves
|
||||
*/
|
||||
|
||||
void __stack_chk_fail(void);
|
||||
__attribute__((weak,used,noreturn,section(".text.nolibc_stack_chk")))
|
||||
void __stack_chk_fail(void)
|
||||
{
|
||||
@ -28,6 +29,7 @@ void __stack_chk_fail(void)
|
||||
for (;;);
|
||||
}
|
||||
|
||||
void __stack_chk_fail_local(void);
|
||||
__attribute__((weak,noreturn,section(".text.nolibc_stack_chk")))
|
||||
void __stack_chk_fail_local(void)
|
||||
{
|
||||
|
@ -349,6 +349,104 @@ int printf(const char *fmt, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
int vsscanf(const char *str, const char *format, va_list args)
|
||||
{
|
||||
uintmax_t uval;
|
||||
intmax_t ival;
|
||||
int base;
|
||||
char *endptr;
|
||||
int matches;
|
||||
int lpref;
|
||||
|
||||
matches = 0;
|
||||
|
||||
while (1) {
|
||||
if (*format == '%') {
|
||||
/* start of pattern */
|
||||
lpref = 0;
|
||||
format++;
|
||||
|
||||
if (*format == 'l') {
|
||||
/* same as in printf() */
|
||||
lpref = 1;
|
||||
format++;
|
||||
if (*format == 'l') {
|
||||
lpref = 2;
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
if (*format == '%') {
|
||||
/* literal % */
|
||||
if ('%' != *str)
|
||||
goto done;
|
||||
str++;
|
||||
format++;
|
||||
continue;
|
||||
} else if (*format == 'd') {
|
||||
ival = strtoll(str, &endptr, 10);
|
||||
if (lpref == 0)
|
||||
*va_arg(args, int *) = ival;
|
||||
else if (lpref == 1)
|
||||
*va_arg(args, long *) = ival;
|
||||
else if (lpref == 2)
|
||||
*va_arg(args, long long *) = ival;
|
||||
} else if (*format == 'u' || *format == 'x' || *format == 'X') {
|
||||
base = *format == 'u' ? 10 : 16;
|
||||
uval = strtoull(str, &endptr, base);
|
||||
if (lpref == 0)
|
||||
*va_arg(args, unsigned int *) = uval;
|
||||
else if (lpref == 1)
|
||||
*va_arg(args, unsigned long *) = uval;
|
||||
else if (lpref == 2)
|
||||
*va_arg(args, unsigned long long *) = uval;
|
||||
} else if (*format == 'p') {
|
||||
*va_arg(args, void **) = (void *)strtoul(str, &endptr, 16);
|
||||
} else {
|
||||
SET_ERRNO(EILSEQ);
|
||||
goto done;
|
||||
}
|
||||
|
||||
format++;
|
||||
str = endptr;
|
||||
matches++;
|
||||
|
||||
} else if (*format == '\0') {
|
||||
goto done;
|
||||
} else if (isspace(*format)) {
|
||||
/* skip spaces in format and str */
|
||||
while (isspace(*format))
|
||||
format++;
|
||||
while (isspace(*str))
|
||||
str++;
|
||||
} else if (*format == *str) {
|
||||
/* literal match */
|
||||
format++;
|
||||
str++;
|
||||
} else {
|
||||
if (!matches)
|
||||
matches = EOF;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return matches;
|
||||
}
|
||||
|
||||
static __attribute__((unused, format(scanf, 2, 3)))
|
||||
int sscanf(const char *str, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int ret;
|
||||
|
||||
va_start(args, format);
|
||||
ret = vsscanf(str, format, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
void perror(const char *msg)
|
||||
{
|
||||
|
@ -30,6 +30,7 @@ static __attribute__((unused)) char itoa_buffer[21];
|
||||
*/
|
||||
|
||||
/* must be exported, as it's used by libgcc for various divide functions */
|
||||
void abort(void);
|
||||
__attribute__((weak,unused,noreturn,section(".text.nolibc_abort")))
|
||||
void abort(void)
|
||||
{
|
||||
|
@ -32,6 +32,7 @@ int memcmp(const void *s1, const void *s2, size_t n)
|
||||
/* might be ignored by the compiler without -ffreestanding, then found as
|
||||
* missing.
|
||||
*/
|
||||
void *memmove(void *dst, const void *src, size_t len);
|
||||
__attribute__((weak,unused,section(".text.nolibc_memmove")))
|
||||
void *memmove(void *dst, const void *src, size_t len)
|
||||
{
|
||||
@ -56,6 +57,7 @@ void *memmove(void *dst, const void *src, size_t len)
|
||||
|
||||
#ifndef NOLIBC_ARCH_HAS_MEMCPY
|
||||
/* must be exported, as it's used by libgcc on ARM */
|
||||
void *memcpy(void *dst, const void *src, size_t len);
|
||||
__attribute__((weak,unused,section(".text.nolibc_memcpy")))
|
||||
void *memcpy(void *dst, const void *src, size_t len)
|
||||
{
|
||||
@ -73,6 +75,7 @@ void *memcpy(void *dst, const void *src, size_t len)
|
||||
/* might be ignored by the compiler without -ffreestanding, then found as
|
||||
* missing.
|
||||
*/
|
||||
void *memset(void *dst, int b, size_t len);
|
||||
__attribute__((weak,unused,section(".text.nolibc_memset")))
|
||||
void *memset(void *dst, int b, size_t len)
|
||||
{
|
||||
@ -124,6 +127,7 @@ char *strcpy(char *dst, const char *src)
|
||||
* thus itself, hence the asm() statement below that's meant to disable this
|
||||
* confusing practice.
|
||||
*/
|
||||
size_t strlen(const char *str);
|
||||
__attribute__((weak,unused,section(".text.nolibc_strlen")))
|
||||
size_t strlen(const char *str)
|
||||
{
|
||||
|
@ -10,10 +10,10 @@
|
||||
#include "std.h"
|
||||
|
||||
/* system includes */
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/signal.h> /* for SIGCHLD */
|
||||
#include <asm/ioctls.h>
|
||||
#include <asm/mman.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/signal.h> /* for SIGCHLD */
|
||||
#include <linux/termios.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/loop.h>
|
||||
#include <linux/time.h>
|
||||
@ -23,7 +23,6 @@
|
||||
#include <linux/prctl.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/signal.h>
|
||||
|
||||
#include "arch.h"
|
||||
#include "errno.h"
|
||||
@ -532,20 +531,16 @@ uid_t getuid(void)
|
||||
|
||||
|
||||
/*
|
||||
* int ioctl(int fd, unsigned long req, void *value);
|
||||
* int ioctl(int fd, unsigned long cmd, ... arg);
|
||||
*/
|
||||
|
||||
static __attribute__((unused))
|
||||
int sys_ioctl(int fd, unsigned long req, void *value)
|
||||
long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return my_syscall3(__NR_ioctl, fd, req, value);
|
||||
return my_syscall3(__NR_ioctl, fd, cmd, arg);
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
int ioctl(int fd, unsigned long req, void *value)
|
||||
{
|
||||
return __sysret(sys_ioctl(fd, req, value));
|
||||
}
|
||||
#define ioctl(fd, cmd, arg) __sysret(sys_ioctl(fd, cmd, (unsigned long)(arg)))
|
||||
|
||||
/*
|
||||
* int kill(pid_t pid, int signal);
|
||||
@ -601,10 +596,37 @@ off_t sys_lseek(int fd, off_t offset, int whence)
|
||||
#endif
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
int sys_llseek(int fd, unsigned long offset_high, unsigned long offset_low,
|
||||
__kernel_loff_t *result, int whence)
|
||||
{
|
||||
#ifdef __NR_llseek
|
||||
return my_syscall5(__NR_llseek, fd, offset_high, offset_low, result, whence);
|
||||
#else
|
||||
return __nolibc_enosys(__func__, fd, offset_high, offset_low, result, whence);
|
||||
#endif
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
off_t lseek(int fd, off_t offset, int whence)
|
||||
{
|
||||
return __sysret(sys_lseek(fd, offset, whence));
|
||||
__kernel_loff_t loff = 0;
|
||||
off_t result;
|
||||
int ret;
|
||||
|
||||
result = sys_lseek(fd, offset, whence);
|
||||
if (result == -ENOSYS) {
|
||||
/* Only exists on 32bit where nolibc off_t is also 32bit */
|
||||
ret = sys_llseek(fd, 0, offset, &loff, whence);
|
||||
if (ret < 0)
|
||||
result = ret;
|
||||
else if (loff != (off_t)loff)
|
||||
result = -EOVERFLOW;
|
||||
else
|
||||
result = loff;
|
||||
}
|
||||
|
||||
return __sysret(result);
|
||||
}
|
||||
|
||||
|
||||
@ -742,6 +764,31 @@ int mount(const char *src, const char *tgt,
|
||||
return __sysret(sys_mount(src, tgt, fst, flags, data));
|
||||
}
|
||||
|
||||
/*
|
||||
* int openat(int dirfd, const char *path, int flags[, mode_t mode]);
|
||||
*/
|
||||
|
||||
static __attribute__((unused))
|
||||
int sys_openat(int dirfd, const char *path, int flags, mode_t mode)
|
||||
{
|
||||
return my_syscall4(__NR_openat, dirfd, path, flags, mode);
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
int openat(int dirfd, const char *path, int flags, ...)
|
||||
{
|
||||
mode_t mode = 0;
|
||||
|
||||
if (flags & O_CREAT) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, flags);
|
||||
mode = va_arg(args, mode_t);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
return __sysret(sys_openat(dirfd, path, flags, mode));
|
||||
}
|
||||
|
||||
/*
|
||||
* int open(const char *path, int flags[, mode_t mode]);
|
||||
@ -750,13 +797,7 @@ int mount(const char *src, const char *tgt,
|
||||
static __attribute__((unused))
|
||||
int sys_open(const char *path, int flags, mode_t mode)
|
||||
{
|
||||
#ifdef __NR_openat
|
||||
return my_syscall4(__NR_openat, AT_FDCWD, path, flags, mode);
|
||||
#elif defined(__NR_open)
|
||||
return my_syscall3(__NR_open, path, flags, mode);
|
||||
#else
|
||||
return __nolibc_enosys(__func__, path, flags, mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
static __attribute__((unused))
|
||||
@ -768,7 +809,7 @@ int open(const char *path, int flags, ...)
|
||||
va_list args;
|
||||
|
||||
va_start(args, flags);
|
||||
mode = va_arg(args, int);
|
||||
mode = va_arg(args, mode_t);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
@ -444,10 +444,6 @@ static inline __noreturn __printf(1, 2) void ksft_exit_skip(const char *msg, ...
|
||||
static inline int ksft_min_kernel_version(unsigned int min_major,
|
||||
unsigned int min_minor)
|
||||
{
|
||||
#ifdef NOLIBC
|
||||
ksft_print_msg("NOLIBC: Can't check kernel version: Function not implemented\n");
|
||||
return 0;
|
||||
#else
|
||||
unsigned int major, minor;
|
||||
struct utsname info;
|
||||
|
||||
@ -455,7 +451,6 @@ static inline int ksft_min_kernel_version(unsigned int min_major,
|
||||
ksft_exit_fail_msg("Can't parse kernel version\n");
|
||||
|
||||
return major > min_major || (major == min_major && minor >= min_minor);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* __KSELFTEST_H */
|
||||
|
@ -47,6 +47,7 @@ XARCH_riscv = riscv64
|
||||
XARCH = $(or $(XARCH_$(ARCH)),$(ARCH))
|
||||
|
||||
# map from user input variants to their kernel supported architectures
|
||||
ARCH_armthumb = arm
|
||||
ARCH_ppc = powerpc
|
||||
ARCH_ppc64 = powerpc
|
||||
ARCH_ppc64le = powerpc
|
||||
@ -54,6 +55,7 @@ ARCH_mips32le = mips
|
||||
ARCH_mips32be = mips
|
||||
ARCH_riscv32 = riscv
|
||||
ARCH_riscv64 = riscv
|
||||
ARCH_s390x = s390
|
||||
ARCH := $(or $(ARCH_$(XARCH)),$(XARCH))
|
||||
|
||||
# kernel image names by architecture
|
||||
@ -62,6 +64,7 @@ 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_armthumb = arch/arm/boot/zImage
|
||||
IMAGE_mips32le = vmlinuz
|
||||
IMAGE_mips32be = vmlinuz
|
||||
IMAGE_ppc = vmlinux
|
||||
@ -70,6 +73,7 @@ IMAGE_ppc64le = arch/powerpc/boot/zImage
|
||||
IMAGE_riscv = arch/riscv/boot/Image
|
||||
IMAGE_riscv32 = arch/riscv/boot/Image
|
||||
IMAGE_riscv64 = arch/riscv/boot/Image
|
||||
IMAGE_s390x = arch/s390/boot/bzImage
|
||||
IMAGE_s390 = arch/s390/boot/bzImage
|
||||
IMAGE_loongarch = arch/loongarch/boot/vmlinuz.efi
|
||||
IMAGE = $(objtree)/$(IMAGE_$(XARCH))
|
||||
@ -81,19 +85,20 @@ DEFCONFIG_x86_64 = defconfig
|
||||
DEFCONFIG_x86 = defconfig
|
||||
DEFCONFIG_arm64 = defconfig
|
||||
DEFCONFIG_arm = multi_v7_defconfig
|
||||
DEFCONFIG_armthumb = multi_v7_defconfig
|
||||
DEFCONFIG_mips32le = malta_defconfig
|
||||
DEFCONFIG_mips32be = malta_defconfig
|
||||
DEFCONFIG_mips32be = malta_defconfig generic/eb.config
|
||||
DEFCONFIG_ppc = pmac32_defconfig
|
||||
DEFCONFIG_ppc64 = powernv_be_defconfig
|
||||
DEFCONFIG_ppc64le = powernv_defconfig
|
||||
DEFCONFIG_riscv = defconfig
|
||||
DEFCONFIG_riscv32 = rv32_defconfig
|
||||
DEFCONFIG_riscv64 = defconfig
|
||||
DEFCONFIG_s390 = defconfig
|
||||
DEFCONFIG_s390x = defconfig
|
||||
DEFCONFIG_s390 = defconfig compat.config
|
||||
DEFCONFIG_loongarch = defconfig
|
||||
DEFCONFIG = $(DEFCONFIG_$(XARCH))
|
||||
|
||||
EXTRACONFIG_mips32be = -d CONFIG_CPU_LITTLE_ENDIAN -e CONFIG_CPU_BIG_ENDIAN
|
||||
EXTRACONFIG = $(EXTRACONFIG_$(XARCH))
|
||||
|
||||
# optional tests to run (default = all)
|
||||
@ -105,6 +110,7 @@ QEMU_ARCH_x86_64 = x86_64
|
||||
QEMU_ARCH_x86 = x86_64
|
||||
QEMU_ARCH_arm64 = aarch64
|
||||
QEMU_ARCH_arm = arm
|
||||
QEMU_ARCH_armthumb = arm
|
||||
QEMU_ARCH_mips32le = mipsel # works with malta_defconfig
|
||||
QEMU_ARCH_mips32be = mips
|
||||
QEMU_ARCH_ppc = ppc
|
||||
@ -113,6 +119,7 @@ QEMU_ARCH_ppc64le = ppc64
|
||||
QEMU_ARCH_riscv = riscv64
|
||||
QEMU_ARCH_riscv32 = riscv32
|
||||
QEMU_ARCH_riscv64 = riscv64
|
||||
QEMU_ARCH_s390x = s390x
|
||||
QEMU_ARCH_s390 = s390x
|
||||
QEMU_ARCH_loongarch = loongarch64
|
||||
QEMU_ARCH = $(QEMU_ARCH_$(XARCH))
|
||||
@ -133,6 +140,7 @@ QEMU_ARGS_x86_64 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(
|
||||
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_armthumb = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS_mips32le = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS_mips32be = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS_ppc = -M g3beige -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
@ -141,6 +149,7 @@ QEMU_ARGS_ppc64le = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC
|
||||
QEMU_ARGS_riscv = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS_riscv32 = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS_riscv64 = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS_s390x = -M s390-ccw-virtio -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS_s390 = -M s390-ccw-virtio -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS_loongarch = -M virt -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
|
||||
QEMU_ARGS = -m 1G $(QEMU_ARGS_$(XARCH)) $(QEMU_ARGS_BIOS) $(QEMU_ARGS_EXTRA)
|
||||
@ -156,15 +165,18 @@ Q=@
|
||||
endif
|
||||
|
||||
CFLAGS_i386 = $(call cc-option,-m32)
|
||||
CFLAGS_arm = -marm
|
||||
CFLAGS_armthumb = -mthumb -march=armv6t2
|
||||
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_s390x = -m64
|
||||
CFLAGS_s390 = -m31
|
||||
CFLAGS_mips32le = -EL -mabi=32 -fPIC
|
||||
CFLAGS_mips32be = -EB -mabi=32
|
||||
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 -W -Wall -Wextra \
|
||||
$(call cc-option,-fno-stack-protector) \
|
||||
$(call cc-option,-fno-stack-protector) $(call cc-option,-Wmissing-prototypes) \
|
||||
$(CFLAGS_$(XARCH)) $(CFLAGS_STACKPROTECTOR) $(CFLAGS_EXTRA)
|
||||
LDFLAGS :=
|
||||
|
||||
@ -220,7 +232,7 @@ all: run
|
||||
|
||||
sysroot: sysroot/$(ARCH)/include
|
||||
|
||||
sysroot/$(ARCH)/include:
|
||||
sysroot/$(ARCH)/include: | defconfig
|
||||
$(Q)rm -rf sysroot/$(ARCH) sysroot/sysroot
|
||||
$(QUIET_MKDIR)mkdir -p sysroot
|
||||
$(Q)$(MAKE) -C $(srctree) outputmakefile
|
||||
@ -264,16 +276,16 @@ initramfs: nolibc-test
|
||||
$(Q)cp nolibc-test initramfs/init
|
||||
|
||||
defconfig:
|
||||
$(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) mrproper $(DEFCONFIG) prepare
|
||||
$(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(DEFCONFIG)
|
||||
$(Q)if [ -n "$(EXTRACONFIG)" ]; then \
|
||||
$(srctree)/scripts/config --file $(objtree)/.config $(EXTRACONFIG); \
|
||||
$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) olddefconfig < /dev/null; \
|
||||
fi
|
||||
|
||||
kernel:
|
||||
kernel: | defconfig
|
||||
$(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) < /dev/null
|
||||
|
||||
kernel-standalone: initramfs
|
||||
kernel-standalone: initramfs | defconfig
|
||||
$(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs < /dev/null
|
||||
|
||||
# run the tests after building the kernel
|
||||
|
@ -11,16 +11,16 @@ void *linkage_test_errno_addr(void)
|
||||
return &errno;
|
||||
}
|
||||
|
||||
int linkage_test_constructor_test_value;
|
||||
int linkage_test_constructor_test_value = 0;
|
||||
|
||||
__attribute__((constructor))
|
||||
static void constructor1(void)
|
||||
{
|
||||
linkage_test_constructor_test_value = 2;
|
||||
linkage_test_constructor_test_value |= 1 << 0;
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void constructor2(void)
|
||||
{
|
||||
linkage_test_constructor_test_value *= 3;
|
||||
linkage_test_constructor_test_value |= 1 << 1;
|
||||
}
|
||||
|
@ -43,6 +43,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include "nolibc-test-linkage.h"
|
||||
|
||||
/* for the type of int_fast16_t and int_fast32_t, musl differs from glibc and nolibc */
|
||||
@ -690,14 +692,14 @@ int expect_strtox(int llen, void *func, const char *input, int base, intmax_t ex
|
||||
__attribute__((constructor))
|
||||
static void constructor1(void)
|
||||
{
|
||||
constructor_test_value = 1;
|
||||
constructor_test_value |= 1 << 0;
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void constructor2(int argc, char **argv, char **envp)
|
||||
{
|
||||
if (argc && argv && envp)
|
||||
constructor_test_value *= 2;
|
||||
constructor_test_value |= 1 << 1;
|
||||
}
|
||||
|
||||
int run_startup(int min, int max)
|
||||
@ -736,9 +738,9 @@ int run_startup(int min, int max)
|
||||
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(constructor); EXPECT_EQ(1, constructor_test_value, 2); break;
|
||||
CASE_TEST(constructor); EXPECT_EQ(is_nolibc, constructor_test_value, 0x3); break;
|
||||
CASE_TEST(linkage_errno); EXPECT_PTREQ(1, linkage_test_errno_addr(), &errno); break;
|
||||
CASE_TEST(linkage_constr); EXPECT_EQ(1, linkage_test_constructor_test_value, 6); break;
|
||||
CASE_TEST(linkage_constr); EXPECT_EQ(1, linkage_test_constructor_test_value, 0x3); break;
|
||||
case __LINE__:
|
||||
return ret; /* must be last */
|
||||
/* note: do not set any defaults so as to permit holes above */
|
||||
@ -767,6 +769,44 @@ int test_getdents64(const char *dir)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_dirent(void)
|
||||
{
|
||||
int comm = 0, cmdline = 0;
|
||||
struct dirent dirent, *result;
|
||||
DIR *dir;
|
||||
int ret;
|
||||
|
||||
dir = opendir("/proc/self");
|
||||
if (!dir)
|
||||
return 1;
|
||||
|
||||
while (1) {
|
||||
errno = 0;
|
||||
ret = readdir_r(dir, &dirent, &result);
|
||||
if (ret != 0)
|
||||
return 1;
|
||||
if (!result)
|
||||
break;
|
||||
|
||||
if (strcmp(dirent.d_name, "comm") == 0)
|
||||
comm++;
|
||||
else if (strcmp(dirent.d_name, "cmdline") == 0)
|
||||
cmdline++;
|
||||
}
|
||||
|
||||
if (errno)
|
||||
return 1;
|
||||
|
||||
ret = closedir(dir);
|
||||
if (ret)
|
||||
return 1;
|
||||
|
||||
if (comm != 1 || cmdline != 1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_getpagesize(void)
|
||||
{
|
||||
int x = getpagesize();
|
||||
@ -988,6 +1028,22 @@ int test_rlimit(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_openat(void)
|
||||
{
|
||||
int dev, null;
|
||||
|
||||
dev = openat(AT_FDCWD, "/dev", O_DIRECTORY);
|
||||
if (dev < 0)
|
||||
return -1;
|
||||
|
||||
null = openat(dev, "null", O_RDONLY);
|
||||
close(dev);
|
||||
if (null < 0)
|
||||
return -1;
|
||||
|
||||
close(null);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Run syscall tests between IDs <min> and <max>.
|
||||
* Return 0 on success, non-zero on failure.
|
||||
@ -1059,6 +1115,7 @@ int run_syscall(int min, int max)
|
||||
CASE_TEST(fork); EXPECT_SYSZR(1, test_fork()); break;
|
||||
CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1); break;
|
||||
CASE_TEST(getdents64_null); EXPECT_SYSER(1, test_getdents64("/dev/null"), -1, ENOTDIR); break;
|
||||
CASE_TEST(directories); EXPECT_SYSZR(proc, test_dirent()); break;
|
||||
CASE_TEST(gettimeofday_tv); EXPECT_SYSZR(1, gettimeofday(&tv, NULL)); break;
|
||||
CASE_TEST(gettimeofday_tv_tz);EXPECT_SYSZR(1, gettimeofday(&tv, &tz)); break;
|
||||
CASE_TEST(getpagesize); EXPECT_SYSZR(1, test_getpagesize()); break;
|
||||
@ -1073,8 +1130,9 @@ int run_syscall(int min, int max)
|
||||
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(NULL, 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(open_tty); EXPECT_SYSNE(1, tmp = open("/dev/null", O_RDONLY), -1); if (tmp != -1) close(tmp); break;
|
||||
CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", O_RDONLY), -1, ENOENT); if (tmp != -1) close(tmp); break;
|
||||
CASE_TEST(openat_dir); EXPECT_SYSZR(1, test_openat()); 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;
|
||||
@ -1284,6 +1342,73 @@ static int expect_vfprintf(int llen, int c, const char *expected, const char *fm
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_scanf(void)
|
||||
{
|
||||
unsigned long long ull;
|
||||
unsigned long ul;
|
||||
unsigned int u;
|
||||
long long ll;
|
||||
long l;
|
||||
void *p;
|
||||
int i;
|
||||
|
||||
/* return __LINE__ to point to the specific failure */
|
||||
|
||||
/* test EOF */
|
||||
if (sscanf("", "foo") != EOF)
|
||||
return __LINE__;
|
||||
|
||||
/* test simple literal without placeholder */
|
||||
if (sscanf("foo", "foo") != 0)
|
||||
return __LINE__;
|
||||
|
||||
/* test single placeholder */
|
||||
if (sscanf("123", "%d", &i) != 1)
|
||||
return __LINE__;
|
||||
|
||||
if (i != 123)
|
||||
return __LINE__;
|
||||
|
||||
/* test multiple place holders and separators */
|
||||
if (sscanf("a123b456c0x90", "a%db%uc%p", &i, &u, &p) != 3)
|
||||
return __LINE__;
|
||||
|
||||
if (i != 123)
|
||||
return __LINE__;
|
||||
|
||||
if (u != 456)
|
||||
return __LINE__;
|
||||
|
||||
if (p != (void *)0x90)
|
||||
return __LINE__;
|
||||
|
||||
/* test space handling */
|
||||
if (sscanf("a b1", "a b%d", &i) != 1)
|
||||
return __LINE__;
|
||||
|
||||
if (i != 1)
|
||||
return __LINE__;
|
||||
|
||||
/* test literal percent */
|
||||
if (sscanf("a%1", "a%%%d", &i) != 1)
|
||||
return __LINE__;
|
||||
|
||||
if (i != 1)
|
||||
return __LINE__;
|
||||
|
||||
/* test stdint.h types */
|
||||
if (sscanf("1|2|3|4|5|6",
|
||||
"%d|%ld|%lld|%u|%lu|%llu",
|
||||
&i, &l, &ll, &u, &ul, &ull) != 6)
|
||||
return __LINE__;
|
||||
|
||||
if (i != 1 || l != 2 || ll != 3 ||
|
||||
u != 4 || ul != 5 || ull != 6)
|
||||
return __LINE__;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run_vfprintf(int min, int max)
|
||||
{
|
||||
int test;
|
||||
@ -1305,6 +1430,7 @@ static int run_vfprintf(int min, int max)
|
||||
CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break;
|
||||
CASE_TEST(hex); EXPECT_VFPRINTF(1, "f", "%x", 0xf); break;
|
||||
CASE_TEST(pointer); EXPECT_VFPRINTF(3, "0x1", "%p", (void *) 0x1); break;
|
||||
CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break;
|
||||
case __LINE__:
|
||||
return ret; /* must be last */
|
||||
/* note: do not set any defaults so as to permit holes above */
|
||||
|
@ -17,7 +17,16 @@ perform_download=0
|
||||
test_mode=system
|
||||
werror=1
|
||||
llvm=
|
||||
archs="i386 x86_64 arm64 arm mips32le mips32be ppc ppc64 ppc64le riscv32 riscv64 s390 loongarch"
|
||||
all_archs=(
|
||||
i386 x86_64
|
||||
arm64 arm armthumb
|
||||
mips32le mips32be
|
||||
ppc ppc64 ppc64le
|
||||
riscv32 riscv64
|
||||
s390x s390
|
||||
loongarch
|
||||
)
|
||||
archs="${all_archs[@]}"
|
||||
|
||||
TEMP=$(getopt -o 'j:d:c:b:a:m:pelh' -n "$0" -- "$@")
|
||||
|
||||
@ -94,19 +103,21 @@ fi
|
||||
crosstool_arch() {
|
||||
case "$1" in
|
||||
arm64) echo aarch64;;
|
||||
armthumb) echo arm;;
|
||||
ppc) echo powerpc;;
|
||||
ppc64) echo powerpc64;;
|
||||
ppc64le) echo powerpc64;;
|
||||
riscv) echo riscv64;;
|
||||
loongarch) echo loongarch64;;
|
||||
mips*) echo mips;;
|
||||
s390*) echo s390;;
|
||||
*) echo "$1";;
|
||||
esac
|
||||
}
|
||||
|
||||
crosstool_abi() {
|
||||
case "$1" in
|
||||
arm) echo linux-gnueabi;;
|
||||
arm | armthumb) echo linux-gnueabi;;
|
||||
*) echo linux;;
|
||||
esac
|
||||
}
|
||||
@ -157,10 +168,6 @@ test_arch() {
|
||||
fi
|
||||
MAKE=(make -j"${nproc}" XARCH="${arch}" CROSS_COMPILE="${cross_compile}" LLVM="${llvm}" O="${build_dir}")
|
||||
|
||||
mkdir -p "$build_dir"
|
||||
if [ "$test_mode" = "system" ] && [ ! -f "${build_dir}/.config" ]; then
|
||||
swallow_output "${MAKE[@]}" defconfig
|
||||
fi
|
||||
case "$test_mode" in
|
||||
'system')
|
||||
test_target=run
|
||||
@ -173,6 +180,13 @@ test_arch() {
|
||||
exit 1
|
||||
esac
|
||||
printf '%-15s' "$arch:"
|
||||
if [ "$arch" = "s390" ] && ([ "$llvm" = "1" ] || [ "$test_mode" = "user" ]); then
|
||||
echo "Unsupported configuration"
|
||||
return
|
||||
fi
|
||||
|
||||
mkdir -p "$build_dir"
|
||||
swallow_output "${MAKE[@]}" defconfig
|
||||
swallow_output "${MAKE[@]}" CFLAGS_EXTRA="$CFLAGS_EXTRA" "$test_target" V=1
|
||||
cp run.out run.out."${arch}"
|
||||
"${MAKE[@]}" report | grep passed
|
||||
|
Loading…
x
Reference in New Issue
Block a user