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:
Linus Torvalds 2025-03-24 17:59:29 -07:00
commit 418becac37
19 changed files with 457 additions and 56 deletions

View File

@ -29,6 +29,7 @@ all_files := \
compiler.h \
crt.h \
ctype.h \
dirent.h \
errno.h \
nolibc.h \
signal.h \

View File

@ -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 (

View File

@ -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 */
);

View File

@ -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"

View File

@ -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)
{

View 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 */

View File

@ -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)

View File

@ -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

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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