diff options
Diffstat (limited to 'tools/testing/selftests/nolibc')
| -rw-r--r-- | tools/testing/selftests/nolibc/Makefile | 325 | ||||
| -rw-r--r-- | tools/testing/selftests/nolibc/Makefile.include | 13 | ||||
| -rw-r--r-- | tools/testing/selftests/nolibc/Makefile.nolibc | 367 | ||||
| -rw-r--r-- | tools/testing/selftests/nolibc/nolibc-test-ignore-errno.c | 6 | ||||
| -rw-r--r-- | tools/testing/selftests/nolibc/nolibc-test-linkage.c | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/nolibc/nolibc-test.c | 756 | ||||
| -rwxr-xr-x | tools/testing/selftests/nolibc/run-tests.sh | 23 |
7 files changed, 1077 insertions, 415 deletions
diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile index 58bcbbd029bc..6bace04227a7 100644 --- a/tools/testing/selftests/nolibc/Makefile +++ b/tools/testing/selftests/nolibc/Makefile @@ -1,319 +1,26 @@ # SPDX-License-Identifier: GPL-2.0 -# Makefile for nolibc tests -# we're in ".../tools/testing/selftests/nolibc" -ifeq ($(srctree),) -srctree := $(patsubst %/tools/testing/selftests/,%,$(dir $(CURDIR))) -endif -include $(srctree)/tools/scripts/utilities.mak -# We need this for the "__cc-option" macro. -include $(srctree)/scripts/Makefile.compiler +TEST_GEN_PROGS := nolibc-test libc-test -ifneq ($(O),) -ifneq ($(call is-absolute,$(O)),y) -$(error Only absolute O= parameters are supported) -endif -objtree := $(O) -else -objtree ?= $(srctree) -endif +include ../lib.mk +include $(top_srcdir)/scripts/Makefile.compiler -ifeq ($(ARCH),) -include $(srctree)/scripts/subarch.include -ARCH = $(SUBARCH) -endif +cc-option = $(call __cc-option, $(CC),,$(1),$(2)) -cc-option = $(call __cc-option, $(CC),$(CLANG_CROSS_FLAGS),$(1),$(2)) +include Makefile.include -# XARCH extends the kernel's ARCH with a few variants of the same -# architecture that only differ by the configuration, the toolchain -# and the Qemu program used. It is copied as-is into ARCH except for -# a few specific values which are mapped like this: -# -# XARCH | ARCH | config -# -------------|-----------|------------------------- -# ppc | powerpc | 32 bits -# ppc64 | powerpc | 64 bits big endian -# ppc64le | powerpc | 64 bits little endian -# -# It is recommended to only use XARCH, though it does not harm if -# ARCH is already set. For simplicity, ARCH is sufficient for all -# architectures where both are equal. +$(OUTPUT)/nolibc-test: CFLAGS = -nostdlib -nostdinc -static \ + -isystem $(top_srcdir)/tools/include/nolibc -isystem $(top_srcdir)/usr/include \ + $(CFLAGS_NOLIBC_TEST) +$(OUTPUT)/nolibc-test: LDLIBS = $(if $(LLVM),,-lgcc) +$(OUTPUT)/nolibc-test: $(NOLIBC_TEST_SOURCES) | headers -# configure default variants for target kernel supported architectures -XARCH_powerpc = ppc -XARCH_mips = mips32le -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 -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 -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_armthumb = arch/arm/boot/zImage -IMAGE_mips32le = vmlinuz -IMAGE_mips32be = vmlinuz -IMAGE_ppc = vmlinux -IMAGE_ppc64 = vmlinux -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)) -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_armthumb = multi_v7_defconfig -DEFCONFIG_mips32le = 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_s390x = defconfig -DEFCONFIG_s390 = defconfig compat.config -DEFCONFIG_loongarch = defconfig -DEFCONFIG = $(DEFCONFIG_$(XARCH)) - -EXTRACONFIG = $(EXTRACONFIG_$(XARCH)) - -# optional tests to run (default = all) -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_armthumb = arm -QEMU_ARCH_mips32le = mipsel # works with malta_defconfig -QEMU_ARCH_mips32be = mips -QEMU_ARCH_ppc = ppc -QEMU_ARCH_ppc64 = ppc64 -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)) - -QEMU_ARCH_USER_ppc64le = ppc64le -QEMU_ARCH_USER = $(or $(QEMU_ARCH_USER_$(XARCH)),$(QEMU_ARCH_$(XARCH))) - -QEMU_BIOS_DIR = /usr/share/edk2/ -QEMU_BIOS_loongarch = $(QEMU_BIOS_DIR)/loongarch64/OVMF_CODE.fd - -ifneq ($(QEMU_BIOS_$(XARCH)),) -QEMU_ARGS_BIOS = -bios $(QEMU_BIOS_$(XARCH)) -endif - -# 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_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=%)" -QEMU_ARGS_ppc64 = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_ppc64le = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_riscv = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_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) - -# OUTPUT is only set when run from the main makefile, otherwise -# it defaults to this nolibc directory. -OUTPUT ?= $(CURDIR)/ - -ifeq ($(V),1) -Q= -else -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_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,-Wmissing-prototypes) \ - $(CFLAGS_$(XARCH)) $(CFLAGS_STACKPROTECTOR) $(CFLAGS_EXTRA) -LDFLAGS := - -LIBGCC := -lgcc - -ifneq ($(LLVM),) -# Not needed for clang -LIBGCC := -endif - -# Modify CFLAGS based on LLVM= -include $(srctree)/tools/scripts/Makefile.include - -# GCC uses "s390", clang "systemz" -CLANG_CROSS_FLAGS := $(subst --target=s390-linux,--target=systemz-linux,$(CLANG_CROSS_FLAGS)) - -REPORT ?= awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{if (!f) printf("\n"); f++; print;} /\[SKIPPED\][\r]*$$/{s++} \ - END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \ - if (f || !p) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \ - printf("\nSee all results in %s\n", ARGV[1]); }' +$(OUTPUT)/libc-test: $(NOLIBC_TEST_SOURCES) + $(call msg,CC,,$@) + $(Q)$(LINK.c) $^ -o $@ help: - @echo "Supported targets under selftests/nolibc:" - @echo " all call the \"run\" target below" - @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 " libc-test build an executable using the compiler's default libc instead" - @echo " run-user runs the executable under QEMU (uses \$$XARCH, \$$TEST)" - @echo " initramfs.cpio prepare the initramfs archive with nolibc-test" - @echo " initramfs prepare the initramfs tree with nolibc-test" - @echo " defconfig create a fresh new default config (uses \$$XARCH)" - @echo " kernel (re)build the kernel (uses \$$XARCH)" - @echo " kernel-standalone (re)build the kernel with the initramfs (uses \$$XARCH)" - @echo " run runs the kernel in QEMU after building it (uses \$$XARCH, \$$TEST)" - @echo " rerun runs a previously prebuilt kernel in QEMU (uses \$$XARCH, \$$TEST)" - @echo " clean clean the sysroot, initramfs, build and output files" - @echo "" - @echo "The output file is \"run.out\". Test ranges may be passed using \$$TEST." - @echo "" - @echo "Currently using the following variables:" - @echo " ARCH = $(ARCH)" - @echo " XARCH = $(XARCH)" - @echo " CROSS_COMPILE = $(CROSS_COMPILE)" - @echo " CC = $(CC)" - @echo " OUTPUT = $(OUTPUT)" - @echo " TEST = $(TEST)" - @echo " QEMU_ARCH = $(if $(QEMU_ARCH),$(QEMU_ARCH),UNKNOWN_ARCH) [determined from \$$XARCH]" - @echo " IMAGE_NAME = $(if $(IMAGE_NAME),$(IMAGE_NAME),UNKNOWN_ARCH) [determined from \$$XARCH]" - @echo "" - -all: run - -sysroot: sysroot/$(ARCH)/include - -sysroot/$(ARCH)/include: | defconfig - $(Q)rm -rf sysroot/$(ARCH) sysroot/sysroot - $(QUIET_MKDIR)mkdir -p sysroot - $(Q)$(MAKE) -C $(srctree) outputmakefile - $(Q)$(MAKE) -C $(srctree)/tools/include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone - $(Q)mv sysroot/sysroot sysroot/$(ARCH) - -ifneq ($(NOLIBC_SYSROOT),0) -nolibc-test: nolibc-test.c nolibc-test-linkage.c sysroot/$(ARCH)/include - $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ - -nostdlib -nostdinc -static -Isysroot/$(ARCH)/include nolibc-test.c nolibc-test-linkage.c $(LIBGCC) -else -nolibc-test: nolibc-test.c nolibc-test-linkage.c - $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ - -nostdlib -static -include $(srctree)/tools/include/nolibc/nolibc.h nolibc-test.c nolibc-test-linkage.c $(LIBGCC) -endif - -libc-test: nolibc-test.c nolibc-test-linkage.c - $(QUIET_CC)$(HOSTCC) -o $@ nolibc-test.c nolibc-test-linkage.c - -# local libc-test -run-libc-test: libc-test - $(Q)./libc-test > "$(CURDIR)/run.out" || : - $(Q)$(REPORT) $(CURDIR)/run.out - -# local nolibc-test -run-nolibc-test: nolibc-test - $(Q)./nolibc-test > "$(CURDIR)/run.out" || : - $(Q)$(REPORT) $(CURDIR)/run.out - -# qemu user-land test -run-user: nolibc-test - $(Q)qemu-$(QEMU_ARCH_USER) ./nolibc-test > "$(CURDIR)/run.out" || : - $(Q)$(REPORT) $(CURDIR)/run.out - -initramfs.cpio: kernel nolibc-test - $(QUIET_GEN)echo 'file /init nolibc-test 755 0 0' | $(objtree)/usr/gen_init_cpio - > initramfs.cpio - -initramfs: nolibc-test - $(QUIET_MKDIR)mkdir -p initramfs - $(call QUIET_INSTALL, initramfs/init) - $(Q)cp nolibc-test initramfs/init - -defconfig: - $(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: | defconfig - $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) < /dev/null - -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 -run: kernel initramfs.cpio - $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out" - $(Q)$(REPORT) $(CURDIR)/run.out - -# re-run the tests from an existing kernel -rerun: - $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out" - $(Q)$(REPORT) $(CURDIR)/run.out - -# report with existing test log -report: - $(Q)$(REPORT) $(CURDIR)/run.out - -clean: - $(call QUIET_CLEAN, sysroot) - $(Q)rm -rf sysroot - $(call QUIET_CLEAN, nolibc-test) - $(Q)rm -f nolibc-test - $(call QUIET_CLEAN, libc-test) - $(Q)rm -f libc-test - $(call QUIET_CLEAN, initramfs.cpio) - $(Q)rm -rf initramfs.cpio - $(call QUIET_CLEAN, initramfs) - $(Q)rm -rf initramfs - $(call QUIET_CLEAN, run.out) - $(Q)rm -rf run.out + @echo "For the custom nolibc testsuite use '$(MAKE) -f Makefile.nolibc'; available targets:" + @$(MAKE) -f Makefile.nolibc help -.PHONY: sysroot/$(ARCH)/include +.PHONY: help diff --git a/tools/testing/selftests/nolibc/Makefile.include b/tools/testing/selftests/nolibc/Makefile.include new file mode 100644 index 000000000000..96fe2bc2191e --- /dev/null +++ b/tools/testing/selftests/nolibc/Makefile.include @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +__CFLAGS_STACKPROTECTOR = $(call cc-option,-fstack-protector-all) $(call cc-option,-mstack-protector-guard=global) +_CFLAGS_STACKPROTECTOR ?= $(call try-run, \ + echo 'void foo(void) {}' | $(CC) -x c - -o - -S $(CLANG_CROSS_FLAGS) $(__CFLAGS_STACKPROTECTOR) | grep -q __stack_chk_guard, \ + $(__CFLAGS_STACKPROTECTOR)) +_CFLAGS_SANITIZER ?= $(call cc-option,-fsanitize=undefined -fsanitize-trap=all) +CFLAGS_NOLIBC_TEST ?= -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 \ + -W -Wall -Wextra -Wundef \ + $(call cc-option,-fno-stack-protector) $(call cc-option,-Wmissing-prototypes) \ + $(_CFLAGS_STACKPROTECTOR) $(_CFLAGS_SANITIZER) + +NOLIBC_TEST_SOURCES := nolibc-test.c nolibc-test-linkage.c nolibc-test-ignore-errno.c diff --git a/tools/testing/selftests/nolibc/Makefile.nolibc b/tools/testing/selftests/nolibc/Makefile.nolibc new file mode 100644 index 000000000000..f30bc68470cc --- /dev/null +++ b/tools/testing/selftests/nolibc/Makefile.nolibc @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for nolibc tests +# we're in ".../tools/testing/selftests/nolibc" +ifeq ($(srctree),) +srctree := $(patsubst %/tools/testing/selftests/,%,$(dir $(CURDIR))) +endif + +include $(srctree)/tools/scripts/utilities.mak +# We need this for the "__cc-option" macro. +include $(srctree)/scripts/Makefile.compiler + +ifneq ($(O),) +ifneq ($(call is-absolute,$(O)),y) +$(error Only absolute O= parameters are supported) +endif +objtree := $(O) +else +objtree ?= $(srctree) +endif + +ifeq ($(ARCH),) +include $(srctree)/scripts/subarch.include +ARCH = $(SUBARCH) +endif + +cc-option = $(call __cc-option, $(CC),$(CLANG_CROSS_FLAGS),$(1),$(2)) + +# XARCH extends the kernel's ARCH with a few variants of the same +# architecture that only differ by the configuration, the toolchain +# and the Qemu program used. It is copied as-is into ARCH except for +# a few specific values which are mapped like this: +# +# XARCH | ARCH | config +# -------------|-----------|------------------------- +# ppc | powerpc | 32 bits +# ppc64 | powerpc | 64 bits big endian +# ppc64le | powerpc | 64 bits little endian +# +# It is recommended to only use XARCH, though it does not harm if +# ARCH is already set. For simplicity, ARCH is sufficient for all +# architectures where both are equal. + +# configure default variants for target kernel supported architectures +XARCH_powerpc = ppc +XARCH_mips = mips32le +XARCH_riscv = riscv64 +XARCH = $(or $(XARCH_$(ARCH)),$(ARCH)) + +# map from user input variants to their kernel supported architectures +ARCH_x32 = x86 +ARCH_armthumb = arm +ARCH_ppc = powerpc +ARCH_ppc64 = powerpc +ARCH_ppc64le = powerpc +ARCH_mips32le = mips +ARCH_mips32be = mips +ARCH_mipsn32le = mips +ARCH_mipsn32be = mips +ARCH_mips64le = mips +ARCH_mips64be = mips +ARCH_riscv32 = riscv +ARCH_riscv64 = riscv +ARCH_s390x = s390 +ARCH_sparc32 = sparc +ARCH_sparc64 = sparc +ARCH_sh4 = sh +ARCH := $(or $(ARCH_$(XARCH)),$(XARCH)) + +# kernel image names by architecture +IMAGE_i386 = arch/x86/boot/bzImage +IMAGE_x86_64 = arch/x86/boot/bzImage +IMAGE_x32 = 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_mipsn32le = vmlinuz +IMAGE_mipsn32be = vmlinuz +IMAGE_mips64le = vmlinuz +IMAGE_mips64be = vmlinuz +IMAGE_ppc = vmlinux +IMAGE_ppc64 = vmlinux +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_loongarch = arch/loongarch/boot/vmlinuz.efi +IMAGE_sparc32 = arch/sparc/boot/image +IMAGE_sparc64 = arch/sparc/boot/image +IMAGE_m68k = vmlinux +IMAGE_sh4 = arch/sh/boot/zImage +IMAGE = $(objtree)/$(IMAGE_$(XARCH)) +IMAGE_NAME = $(notdir $(IMAGE)) + +# default kernel configurations that appear to be usable +DEFCONFIG_i386 = defconfig +DEFCONFIG_x86_64 = defconfig +DEFCONFIG_x32 = 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 generic/eb.config +DEFCONFIG_mipsn32le = malta_defconfig generic/64r2.config +DEFCONFIG_mipsn32be = malta_defconfig generic/64r6.config generic/eb.config +DEFCONFIG_mips64le = malta_defconfig generic/64r6.config +DEFCONFIG_mips64be = malta_defconfig generic/64r2.config 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_s390x = defconfig +DEFCONFIG_loongarch = defconfig +DEFCONFIG_sparc32 = sparc32_defconfig +DEFCONFIG_sparc64 = sparc64_defconfig +DEFCONFIG_m68k = virt_defconfig +DEFCONFIG_sh4 = rts7751r2dplus_defconfig +DEFCONFIG = $(DEFCONFIG_$(XARCH)) + +EXTRACONFIG_x32 = -e CONFIG_X86_X32_ABI +EXTRACONFIG_arm = -e CONFIG_NAMESPACES +EXTRACONFIG_armthumb = -e CONFIG_NAMESPACES +EXTRACONFIG_m68k = -e CONFIG_BLK_DEV_INITRD +EXTRACONFIG_sh4 = -e CONFIG_BLK_DEV_INITRD -e CONFIG_CMDLINE_FROM_BOOTLOADER +EXTRACONFIG = $(EXTRACONFIG_$(XARCH)) + +# optional tests to run (default = all) +TEST = + +# QEMU_ARCH: arch names used by qemu +QEMU_ARCH_i386 = i386 +QEMU_ARCH_x86_64 = x86_64 +QEMU_ARCH_x32 = 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_mipsn32le = mips64el +QEMU_ARCH_mipsn32be = mips64 +QEMU_ARCH_mips64le = mips64el +QEMU_ARCH_mips64be = mips64 +QEMU_ARCH_ppc = ppc +QEMU_ARCH_ppc64 = ppc64 +QEMU_ARCH_ppc64le = ppc64 +QEMU_ARCH_riscv = riscv64 +QEMU_ARCH_riscv32 = riscv32 +QEMU_ARCH_riscv64 = riscv64 +QEMU_ARCH_s390x = s390x +QEMU_ARCH_loongarch = loongarch64 +QEMU_ARCH_sparc32 = sparc +QEMU_ARCH_sparc64 = sparc64 +QEMU_ARCH_m68k = m68k +QEMU_ARCH_sh4 = sh4 +QEMU_ARCH = $(QEMU_ARCH_$(XARCH)) + +QEMU_ARCH_USER_ppc64le = ppc64le +QEMU_ARCH_USER_mipsn32le = mipsn32el +QEMU_ARCH_USER_mipsn32be = mipsn32 +QEMU_ARCH_USER = $(or $(QEMU_ARCH_USER_$(XARCH)),$(QEMU_ARCH_$(XARCH))) + +QEMU_BIOS_DIR = /usr/share/edk2/ +QEMU_BIOS_loongarch = $(QEMU_BIOS_DIR)/loongarch64/OVMF_CODE.fd + +ifneq ($(QEMU_BIOS_$(XARCH)),) +QEMU_ARGS_BIOS = -bios $(QEMU_BIOS_$(XARCH)) +endif + +# 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_x32 = -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_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_mipsn32le = -M malta -cpu 5KEc -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mipsn32be = -M malta -cpu I6400 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mips64le = -M malta -cpu I6400 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mips64be = -M malta -cpu 5KEc -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_ppc = -M g3beige -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_ppc64 = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_ppc64le = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_riscv = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_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_loongarch = -M virt -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_sparc32 = -M SS-5 -m 256M -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_sparc64 = -M sun4u -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_m68k = -M virt -append "console=ttyGF0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_sh4 = -M r2d -serial file:/dev/stdout -append "console=ttySC1,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS = -m 1G $(QEMU_ARGS_$(XARCH)) $(QEMU_ARGS_BIOS) $(QEMU_ARGS_EXTRA) + +# OUTPUT is only set when run from the main makefile, otherwise +# it defaults to this nolibc directory. +OUTPUT ?= $(CURDIR)/ + +ifeq ($(V),1) +Q= +else +Q=@ +endif + +CFLAGS_i386 = $(call cc-option,-m32) +CFLAGS_x32 = -mx32 +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_s390x = -m64 +CFLAGS_mips32le = -EL -mabi=32 -fPIC +CFLAGS_mips32be = -EB -mabi=32 +CFLAGS_mipsn32le = -EL -mabi=n32 -fPIC -march=mips64r2 +CFLAGS_mipsn32be = -EB -mabi=n32 -march=mips64r6 +CFLAGS_mips64le = -EL -mabi=64 -march=mips64r6 +CFLAGS_mips64be = -EB -mabi=64 -march=mips64r2 +CFLAGS_loongarch = $(if $(LLVM),-fuse-ld=lld) +CFLAGS_sparc32 = $(call cc-option,-m32) -mcpu=v8 +CFLAGS_sh4 = -ml -m4 +ifeq ($(origin XARCH),command line) +CFLAGS_XARCH = $(CFLAGS_$(XARCH)) +endif + +LDLIBS_ppc = $(if $(LLVM),,-lgcc) +LDLIBS = $(LDLIBS_$(XARCH)) + +include Makefile.include + +CFLAGS ?= $(CFLAGS_NOLIBC_TEST) $(CFLAGS_XARCH) $(CFLAGS_EXTRA) +LDFLAGS := + +# Modify CFLAGS based on LLVM= +include $(srctree)/tools/scripts/Makefile.include + +REPORT ?= awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{if (!f) printf("\n"); f++; print;} /\[SKIPPED\][\r]*$$/{s++} \ + /^Total number of errors:/{done++} \ + END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \ + if (f || !p || !done) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \ + printf("\nSee all results in %s\n", ARGV[1]); }' + +# Execute the toplevel kernel Makefile +KBUILD_MAKE = $(MAKE) -C $(srctree) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) LLVM= + +help: + @echo "Supported targets under selftests/nolibc:" + @echo " all call the \"run\" target below" + @echo " help this help" + @echo " sysroot create the nolibc sysroot here (uses \$$ARCH)" + @echo " nolibc-test build the executable (uses \$$CC or \$$CROSS_COMPILE)" + @echo " libc-test build an executable using the compiler's default libc instead" + @echo " run-user runs the executable under QEMU (uses \$$XARCH, \$$TEST)" + @echo " initramfs.cpio prepare the initramfs archive with nolibc-test" + @echo " initramfs prepare the initramfs tree with nolibc-test" + @echo " defconfig create a fresh new default config (uses \$$XARCH)" + @echo " kernel (re)build the kernel (uses \$$XARCH, \$$CROSS_COMPILE)" + @echo " kernel-standalone (re)build the kernel with the initramfs (uses \$$XARCH, \$$CROSS_COMPILE)" + @echo " run runs the kernel in QEMU after building it (uses \$$XARCH, \$$TEST)" + @echo " rerun runs a previously prebuilt kernel in QEMU (uses \$$XARCH, \$$TEST)" + @echo " clean clean the sysroot, initramfs, build and output files" + @echo "" + @echo "The output file is \"run.out\". Test ranges may be passed using \$$TEST." + @echo "" + @echo "Currently using the following variables:" + @echo " ARCH = $(ARCH)" + @echo " XARCH = $(XARCH)" + @echo " CROSS_COMPILE = $(CROSS_COMPILE)" + @echo " CC = $(CC)" + @echo " OUTPUT = $(OUTPUT)" + @echo " TEST = $(TEST)" + @echo " QEMU_ARCH = $(if $(QEMU_ARCH),$(QEMU_ARCH),UNKNOWN_ARCH) [determined from \$$XARCH]" + @echo " IMAGE_NAME = $(if $(IMAGE_NAME),$(IMAGE_NAME),UNKNOWN_ARCH) [determined from \$$XARCH]" + @echo "" + +all: run + +sysroot: sysroot/$(ARCH)/include + +sysroot/$(ARCH)/include: + $(Q)rm -rf sysroot/$(ARCH) sysroot/sysroot + $(QUIET_MKDIR)mkdir -p sysroot + $(Q)$(MAKE) -C $(srctree) outputmakefile + $(Q)$(MAKE) -C $(srctree)/tools/include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone headers_check + $(Q)mv sysroot/sysroot sysroot/$(ARCH) + +nolibc-test: $(NOLIBC_TEST_SOURCES) sysroot/$(ARCH)/include + $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ + -nostdlib -nostdinc -static -Isysroot/$(ARCH)/include $(NOLIBC_TEST_SOURCES) $(LDLIBS) + +libc-test: $(NOLIBC_TEST_SOURCES) + $(QUIET_CC)$(HOSTCC) -o $@ $(NOLIBC_TEST_SOURCES) + +# local libc-test +run-libc-test: libc-test + $(Q)./libc-test > "$(CURDIR)/run.out" || : + $(Q)$(REPORT) $(CURDIR)/run.out + +# local nolibc-test +run-nolibc-test: nolibc-test + $(Q)./nolibc-test > "$(CURDIR)/run.out" || : + $(Q)$(REPORT) $(CURDIR)/run.out + +# qemu user-land test +run-user: nolibc-test + $(Q)qemu-$(QEMU_ARCH_USER) ./nolibc-test > "$(CURDIR)/run.out" || : + $(Q)$(REPORT) $(CURDIR)/run.out + +initramfs.cpio: kernel nolibc-test + $(QUIET_GEN)echo 'file /init nolibc-test 755 0 0' | $(objtree)/usr/gen_init_cpio - > initramfs.cpio + +initramfs: nolibc-test + $(QUIET_MKDIR)mkdir -p initramfs + $(call QUIET_INSTALL, initramfs/init) + $(Q)cp nolibc-test initramfs/init + +defconfig: + $(Q)$(KBUILD_MAKE) $(DEFCONFIG) + $(Q)if [ -n "$(EXTRACONFIG)" ]; then \ + $(srctree)/scripts/config --file $(objtree)/.config $(EXTRACONFIG); \ + $(KBUILD_MAKE) olddefconfig < /dev/null; \ + fi + +kernel: + $(Q)$(KBUILD_MAKE) $(IMAGE_NAME) < /dev/null + +kernel-standalone: initramfs + $(Q)$(KBUILD_MAKE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs < /dev/null + +# run the tests after building the kernel +run: kernel initramfs.cpio + $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial file:/dev/stdout $(QEMU_ARGS) > "$(CURDIR)/run.out" + $(Q)$(REPORT) $(CURDIR)/run.out + +# re-run the tests from an existing kernel +rerun: + $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial file:/dev/stdout $(QEMU_ARGS) > "$(CURDIR)/run.out" + $(Q)$(REPORT) $(CURDIR)/run.out + +# report with existing test log +report: + $(Q)$(REPORT) $(CURDIR)/run.out + +clean: + $(call QUIET_CLEAN, sysroot) + $(Q)rm -rf sysroot + $(call QUIET_CLEAN, nolibc-test) + $(Q)rm -f nolibc-test + $(call QUIET_CLEAN, libc-test) + $(Q)rm -f libc-test + $(call QUIET_CLEAN, initramfs.cpio) + $(Q)rm -rf initramfs.cpio + $(call QUIET_CLEAN, initramfs) + $(Q)rm -rf initramfs + $(call QUIET_CLEAN, run.out) + $(Q)rm -rf run.out + +.PHONY: sysroot/$(ARCH)/include diff --git a/tools/testing/selftests/nolibc/nolibc-test-ignore-errno.c b/tools/testing/selftests/nolibc/nolibc-test-ignore-errno.c new file mode 100644 index 000000000000..aea816da4fca --- /dev/null +++ b/tools/testing/selftests/nolibc/nolibc-test-ignore-errno.c @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define NOLIBC_IGNORE_ERRNO + +/* Include all of nolibc and make sure everything compiles */ +#include <stdlib.h> diff --git a/tools/testing/selftests/nolibc/nolibc-test-linkage.c b/tools/testing/selftests/nolibc/nolibc-test-linkage.c index a7ca8325863f..0636d1b6e808 100644 --- a/tools/testing/selftests/nolibc/nolibc-test-linkage.c +++ b/tools/testing/selftests/nolibc/nolibc-test-linkage.c @@ -2,9 +2,7 @@ #include "nolibc-test-linkage.h" -#ifndef NOLIBC #include <errno.h> -#endif void *linkage_test_errno_addr(void) { diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 5884a891c491..d3c4facb54c0 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -9,24 +9,24 @@ * $(CC) -nostdlib -I/path/to/nolibc/sysroot => _NOLIBC_* guards are present * $(CC) with default libc => NOLIBC* never defined */ -#ifndef NOLIBC #include <stdio.h> #include <stdlib.h> #include <string.h> -#ifndef _NOLIBC_STDIO_H -/* standard libcs need more includes */ #include <sys/auxv.h> -#include <sys/io.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/mount.h> #include <sys/prctl.h> +#include <sys/ptrace.h> +#include <sys/random.h> #include <sys/reboot.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/sysmacros.h> #include <sys/time.h> +#include <sys/timerfd.h> +#include <sys/uio.h> #include <sys/utsname.h> #include <sys/wait.h> #include <dirent.h> @@ -38,10 +38,13 @@ #include <stdarg.h> #include <stddef.h> #include <stdint.h> +#include <time.h> #include <unistd.h> #include <limits.h> -#endif -#endif +#include <ctype.h> +#include <stdbool.h> +#include <byteswap.h> +#include <endian.h> #pragma GCC diagnostic ignored "-Wmissing-prototypes" @@ -66,6 +69,8 @@ static const char *argv0; /* will be used by constructor tests */ static int constructor_test_value; +static const int is_le = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__; + static const int is_nolibc = #ifdef NOLIBC 1 @@ -74,6 +79,30 @@ static const int is_nolibc = #endif ; +static const int is_glibc = +#ifdef __GLIBC__ + 1 +#else + 0 +#endif +; + +#if !defined(NOLIBC) +/* Some disabled tests may not compile. */ + +/* strlcat() and strlcpy() may not be in the system headers. */ +#undef strlcat +#undef strlcpy +#define strlcat(d, s, l) 0 +#define strlcpy(d, s, l) 0 + +/* readdir_r() is likely to be marked deprecated */ +#undef readdir_r +#define readdir_r(dir, dirent, result) ((errno = EINVAL), -1) + +#define _syscall(...) 0 +#endif + /* definition of a series of tests */ struct test { const char *name; /* test name */ @@ -142,21 +171,6 @@ static const char *errorname(int err) } } -static void align_result(size_t llen) -{ - const size_t align = 64; - char buf[align]; - size_t n; - - if (llen >= align) - return; - - n = align - llen; - memset(buf, ' ', n); - buf[n] = '\0'; - fputs(buf, stdout); -} - enum RESULT { OK, FAIL, @@ -174,8 +188,10 @@ static void result(int llen, enum RESULT r) else msg = " [FAIL]"; - align_result(llen); - puts(msg); + llen = 64 - llen; + if (llen < 0) + llen = 0; + printf("%*s%s\n", llen, "", msg); } /* The tests below are intended to be used by the macroes, which evaluate @@ -198,8 +214,8 @@ int expect_zr(int expr, int llen) } -#define EXPECT_NZ(cond, expr, val) \ - do { if (!(cond)) result(llen, SKIPPED); else ret += expect_nz(expr, llen; } while (0) +#define EXPECT_NZ(cond, expr) \ + do { if (!(cond)) result(llen, SKIPPED); else ret += expect_nz(expr, llen); } while (0) static __attribute__((unused)) int expect_nz(int expr, int llen) @@ -304,10 +320,7 @@ int expect_syszr(int expr, int llen) { int ret = 0; - if (errno == ENOSYS) { - llen += printf(" = ENOSYS"); - result(llen, SKIPPED); - } else if (expr) { + if (expr) { ret = 1; llen += printf(" = %d %s ", expr, errorname(errno)); result(llen, FAIL); @@ -347,10 +360,7 @@ int expect_sysne(int expr, int llen, int val) { int ret = 0; - if (errno == ENOSYS) { - llen += printf(" = ENOSYS"); - result(llen, SKIPPED); - } else if (expr == val) { + if (expr == val) { ret = 1; llen += printf(" = %d %s ", expr, errorname(errno)); result(llen, FAIL); @@ -375,9 +385,7 @@ int expect_syserr2(int expr, int expret, int experr1, int experr2, int llen) int _errno = errno; llen += printf(" = %d %s ", expr, errorname(_errno)); - if (errno == ENOSYS) { - result(llen, SKIPPED); - } else if (expr != expret || (_errno != experr1 && _errno != experr2)) { + if (expr != expret || (_errno != experr1 && _errno != experr2)) { ret = 1; if (experr2 == 0) llen += printf(" != (%d %s) ", expret, errorname(experr1)); @@ -688,7 +696,6 @@ int expect_strtox(int llen, void *func, const char *input, int base, intmax_t ex #define CASE_TEST(name) \ case __LINE__: llen += printf("%d %s", test, #name); -/* constructors validate that they are executed in definition order */ __attribute__((constructor)) static void constructor1(void) { @@ -702,6 +709,37 @@ static void constructor2(int argc, char **argv, char **envp) constructor_test_value |= 1 << 1; } +int test_program_invocation_name(void) +{ + char buf[100]; + char *dirsep; + ssize_t r; + int fd; + + fd = open("/proc/self/cmdline", O_RDONLY); + if (fd == -1) + return 1; + + r = read(fd, buf, sizeof(buf)); + close(fd); + if (r < 1 || r == sizeof(buf)) + return 1; + + buf[r - 1] = '\0'; + + if (strcmp(program_invocation_name, buf) != 0) + return 1; + + dirsep = strrchr(buf, '/'); + if (!dirsep || dirsep[1] == '\0') + return 1; + + if (strcmp(program_invocation_short_name, dirsep + 1) != 0) + return 1; + + return 0; +} + int run_startup(int min, int max) { int test; @@ -716,6 +754,7 @@ int run_startup(int min, int max) #ifdef NOLIBC test_auxv = _auxv; #endif + bool proc = access("/proc", R_OK) == 0; for (test = min; test >= 0 && test <= max; test++) { int llen = 0; /* line length */ @@ -741,6 +780,7 @@ int run_startup(int min, int max) 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, 0x3); break; + CASE_TEST(prog_name); EXPECT_ZR(proc, test_program_invocation_name()); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */ @@ -807,6 +847,26 @@ static int test_dirent(void) return 0; } +int test_getrandom(void) +{ + uint64_t rng = 0; + ssize_t ret; + + ret = getrandom(&rng, sizeof(rng), GRND_NONBLOCK); + if (ret == -1 && errno == EAGAIN) + return 0; /* No entropy available yet */ + + if (ret != sizeof(rng)) + return ret; + + if (!rng) { + errno = EINVAL; + return -1; + } + + return 0; +} + int test_getpagesize(void) { int x = getpagesize(); @@ -836,7 +896,87 @@ int test_getpagesize(void) return !c; } -int test_fork(void) +int test_file_stream(void) +{ + FILE *f; + int r; + + f = fopen("/dev/null", "r"); + if (!f) + return -1; + + errno = 0; + r = fwrite("foo", 1, 3, f); + if (r != 0 || ((is_nolibc || is_glibc) && errno != EBADF)) { + fclose(f); + return -1; + } + + r = fclose(f); + if (r == EOF) + return -1; + + return 0; +} + +int test_file_stream_wsr(void) +{ + const char dataout[] = "foo"; + const size_t datasz = sizeof(dataout); + char datain[datasz]; + int fd, r; + FILE *f; + + fd = open("/tmp", O_TMPFILE | O_RDWR, 0644); + if (fd == -1) + return -1; + + f = fdopen(fd, "w+"); + if (!f) + return -1; + + errno = 0; + r = fwrite(dataout, 1, datasz, f); + if (r != datasz) + return -1; + + /* Attempt to read from the file without rewinding, + * we should read 0 items. + */ + r = fread(datain, 1, datasz, f); + if (r) + return -1; + + /* Rewind the file to the start */ + r = fseek(f, 0, SEEK_SET); + if (r) + return -1; + + /* Attempt to read back more than was written to + * make sure we handle short reads properly. + * fread() should return the number of complete items. + */ + r = fread(datain, 1, datasz + 1, f); + if (r != datasz) + return -1; + + /* Data we read should match the data we just wrote */ + if (memcmp(datain, dataout, datasz) != 0) + return -1; + + r = fclose(f); + if (r) + return -1; + + return 0; +} + +enum fork_type { + FORK_STANDARD, + FORK_VFORK, +}; + +int test_fork(enum fork_type type) { int status; pid_t pid; @@ -845,14 +985,23 @@ int test_fork(void) fflush(stdout); fflush(stderr); - pid = fork(); + switch (type) { + case FORK_STANDARD: + pid = fork(); + break; + case FORK_VFORK: + pid = vfork(); + break; + default: + return 1; + } switch (pid) { case -1: return 1; case 0: - exit(123); + _exit(123); default: pid = waitpid(pid, &status, 0); @@ -883,6 +1032,102 @@ int test_stat_timestamps(void) return 0; } +int test_timer(void) +{ + struct itimerspec timerspec; + struct sigevent evp; + timer_t timer; + int ret; + + evp.sigev_notify = SIGEV_NONE; + + ret = timer_create(CLOCK_MONOTONIC, &evp, &timer); + if (ret) + return ret; + + timerspec = (struct itimerspec) { + .it_value.tv_sec = 1000000, + }; + ret = timer_settime(timer, 0, &timerspec, NULL); + if (ret) + goto err; + + timerspec = (struct itimerspec) { + .it_value.tv_sec = -1, + .it_value.tv_nsec = -1, + .it_interval.tv_sec = -1, + .it_interval.tv_nsec = -1, + }; + ret = timer_gettime(timer, &timerspec); + if (ret) + goto err; + + errno = EINVAL; + ret = -1; + + if (timerspec.it_interval.tv_sec || timerspec.it_interval.tv_nsec) + goto err; + + if (timerspec.it_value.tv_sec > 1000000) + goto err; + + ret = timer_delete(timer); + if (ret) + return ret; + + return 0; + +err: + timer_delete(timer); + return ret; +} + +int test_timerfd(void) +{ + struct itimerspec timerspec; + int timer, ret; + + timer = timerfd_create(CLOCK_MONOTONIC, 0); + if (timer == -1) + return -1; + + timerspec = (struct itimerspec) { + .it_value.tv_sec = 1000000, + }; + ret = timerfd_settime(timer, 0, &timerspec, NULL); + if (ret) + goto err; + + timerspec = (struct itimerspec) { + .it_value.tv_sec = -1, + .it_value.tv_nsec = -1, + .it_interval.tv_sec = -1, + .it_interval.tv_nsec = -1, + }; + ret = timerfd_gettime(timer, &timerspec); + if (ret) + goto err; + + errno = EINVAL; + ret = -1; + + if (timerspec.it_interval.tv_sec || timerspec.it_interval.tv_nsec) + goto err; + + if (timerspec.it_value.tv_sec > 1000000) + goto err; + + ret = close(timer); + if (ret) + return ret; + + return 0; + +err: + close(timer); + return ret; +} + int test_uname(void) { struct utsname buf; @@ -926,7 +1171,7 @@ int test_mmap_munmap(void) { int ret, fd, i, page_size; void *mem; - size_t file_size, length; + size_t file_size, length, mem_length; off_t offset, pa_offset; struct stat stat_buf; const char * const files[] = { @@ -966,14 +1211,22 @@ int test_mmap_munmap(void) offset = 0; length = file_size - offset; pa_offset = offset & ~(page_size - 1); + mem_length = length + offset - pa_offset; - mem = mmap(NULL, length + offset - pa_offset, PROT_READ, MAP_SHARED, fd, pa_offset); + mem = mmap(NULL, mem_length, PROT_READ, MAP_SHARED, fd, pa_offset); if (mem == MAP_FAILED) { ret = 1; goto end; } - ret = munmap(mem, length + offset - pa_offset); + mem = mremap(mem, mem_length, mem_length * 2, MREMAP_MAYMOVE, 0); + if (mem == MAP_FAILED) { + munmap(mem, mem_length); + ret = 1; + goto end; + } + + ret = munmap(mem, mem_length * 2); end: close(fd); @@ -1045,6 +1298,72 @@ int test_openat(void) return 0; } +int test_namespace(void) +{ + int original_ns, new_ns, ret; + ino_t original_ns_ino; + struct stat stat_buf; + + original_ns = open("/proc/self/ns/uts", O_RDONLY); + if (original_ns == -1) + return -1; + + ret = fstat(original_ns, &stat_buf); + if (ret) + goto out; + + original_ns_ino = stat_buf.st_ino; + + ret = unshare(CLONE_NEWUTS); + if (ret) + goto out; + + new_ns = open("/proc/self/ns/uts", O_RDONLY); + if (new_ns == -1) { + ret = new_ns; + goto out; + } + + ret = fstat(new_ns, &stat_buf); + close(new_ns); + if (ret) + goto out; + + if (stat_buf.st_ino == original_ns_ino) { + errno = EINVAL; + ret = -1; + goto out; + } + + ret = setns(original_ns, CLONE_NEWUTS); + if (ret) + goto out; + + new_ns = open("/proc/self/ns/uts", O_RDONLY); + if (new_ns == -1) { + ret = new_ns; + goto out; + } + + ret = fstat(new_ns, &stat_buf); + if (ret) + goto out; + + close(new_ns); + + if (stat_buf.st_ino != original_ns_ino) { + errno = EINVAL; + ret = -1; + goto out; + } + + ret = 0; + +out: + close(original_ns); + return ret; +} + /* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. */ @@ -1052,11 +1371,16 @@ int run_syscall(int min, int max) { struct timeval tv; struct timezone tz; + struct timespec ts; struct stat stat_buf; int euid0; int proc; int test; int tmp; + struct iovec iov_one = { + .iov_base = &tmp, + .iov_len = 1, + }; int ret = 0; void *p1, *p2; int has_gettid = 1; @@ -1083,6 +1407,11 @@ int run_syscall(int min, int max) * test numbers. */ switch (test + __LINE__ + 1) { + CASE_TEST(access); EXPECT_SYSZR(proc, access("/proc/self", R_OK)); break; + CASE_TEST(access_bad); EXPECT_SYSER(proc, access("/proc/self", W_OK), -1, EPERM); break; + CASE_TEST(clock_getres); EXPECT_SYSZR(1, clock_getres(CLOCK_MONOTONIC, &ts)); break; + CASE_TEST(clock_gettime); EXPECT_SYSZR(1, clock_gettime(CLOCK_MONOTONIC, &ts)); break; + CASE_TEST(clock_settime); EXPECT_SYSER(1, clock_settime(CLOCK_MONOTONIC, &ts), -1, EINVAL); break; CASE_TEST(getpid); EXPECT_SYSNE(1, getpid(), -1); break; CASE_TEST(getppid); EXPECT_SYSNE(1, getppid(), -1); break; CASE_TEST(gettid); EXPECT_SYSNE(has_gettid, gettid(), -1); break; @@ -1103,6 +1432,7 @@ int run_syscall(int min, int max) CASE_TEST(chroot_root); EXPECT_SYSZR(euid0, chroot("/")); break; CASE_TEST(chroot_blah); EXPECT_SYSER(1, chroot("/proc/self/blah"), -1, ENOENT); break; CASE_TEST(chroot_exe); EXPECT_SYSER(1, chroot(argv0), -1, ENOTDIR); break; + CASE_TEST(clock_nanosleep); ts.tv_nsec = -1; EXPECT_EQ(1, EINVAL, clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL)); break; CASE_TEST(close_m1); EXPECT_SYSER(1, close(-1), -1, EBADF); break; CASE_TEST(close_dup); EXPECT_SYSZR(1, close(dup(0))); break; CASE_TEST(dup_0); tmp = dup(0); EXPECT_SYSNE(1, tmp, -1); close(tmp); break; @@ -1112,10 +1442,15 @@ int run_syscall(int min, int max) CASE_TEST(dup3_0); tmp = dup3(0, 100, 0); EXPECT_SYSNE(1, tmp, -1); close(tmp); break; CASE_TEST(dup3_m1); tmp = dup3(-1, 100, 0); EXPECT_SYSER(1, tmp, -1, EBADF); if (tmp != -1) close(tmp); break; CASE_TEST(execve_root); EXPECT_SYSER(1, execve("/", (char*[]){ [0] = "/", [1] = NULL }, NULL), -1, EACCES); break; - CASE_TEST(fork); EXPECT_SYSZR(1, test_fork()); break; + CASE_TEST(fchdir_stdin); EXPECT_SYSER(1, fchdir(STDIN_FILENO), -1, ENOTDIR); break; + CASE_TEST(fchdir_badfd); EXPECT_SYSER(1, fchdir(-1), -1, EBADF); break; + CASE_TEST(file_stream); EXPECT_SYSZR(1, test_file_stream()); break; + CASE_TEST(file_stream_wsr); EXPECT_SYSZR(1, test_file_stream_wsr()); break; + CASE_TEST(fork); EXPECT_SYSZR(1, test_fork(FORK_STANDARD)); 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(directories); EXPECT_SYSZR(is_nolibc && proc, test_dirent()); break; + CASE_TEST(getrandom); EXPECT_SYSZR(1, test_getrandom()); 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; @@ -1130,6 +1465,7 @@ 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(nanosleep); ts.tv_nsec = -1; EXPECT_SYSER(1, nanosleep(&ts, NULL), -1, EINVAL); 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; @@ -1147,19 +1483,31 @@ int run_syscall(int min, int max) CASE_TEST(select_fault); EXPECT_SYSER(1, select(1, (void *)1, NULL, NULL, 0), -1, EFAULT); break; CASE_TEST(stat_blah); EXPECT_SYSER(1, stat("/proc/self/blah", &stat_buf), -1, ENOENT); break; CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break; + CASE_TEST(stat_rdev); EXPECT_SYSZR(1, ({ int ret = stat("/dev/null", &stat_buf); ret ?: stat_buf.st_rdev != makedev(1, 3); })); break; CASE_TEST(stat_timestamps); EXPECT_SYSZR(1, test_stat_timestamps()); break; CASE_TEST(symlink_root); EXPECT_SYSER(1, symlink("/", "/"), -1, EEXIST); break; + CASE_TEST(timer); EXPECT_SYSZR(1, test_timer()); break; + CASE_TEST(timerfd); EXPECT_SYSZR(1, test_timerfd()); break; CASE_TEST(uname); EXPECT_SYSZR(proc, test_uname()); break; CASE_TEST(uname_fault); EXPECT_SYSER(1, uname(NULL), -1, EFAULT); break; CASE_TEST(unlink_root); EXPECT_SYSER(1, unlink("/"), -1, EISDIR); break; CASE_TEST(unlink_blah); EXPECT_SYSER(1, unlink("/proc/self/blah"), -1, ENOENT); break; + CASE_TEST(vfork); EXPECT_SYSZR(1, test_fork(FORK_VFORK)); break; CASE_TEST(wait_child); EXPECT_SYSER(1, wait(&tmp), -1, ECHILD); break; CASE_TEST(waitpid_min); EXPECT_SYSER(1, waitpid(INT_MIN, &tmp, WNOHANG), -1, ESRCH); break; CASE_TEST(waitpid_child); EXPECT_SYSER(1, waitpid(getpid(), &tmp, WNOHANG), -1, ECHILD); break; CASE_TEST(write_badf); EXPECT_SYSER(1, write(-1, &tmp, 1), -1, EBADF); break; CASE_TEST(write_zero); EXPECT_SYSZR(1, write(1, &tmp, 0)); break; + CASE_TEST(readv_badf); EXPECT_SYSER(1, readv(-1, &iov_one, 1), -1, EBADF); break; + CASE_TEST(readv_zero); EXPECT_SYSZR(1, readv(0, NULL, 0)); break; + CASE_TEST(writev_badf); EXPECT_SYSER(1, writev(-1, &iov_one, 1), -1, EBADF); break; + CASE_TEST(writev_zero); EXPECT_SYSZR(1, writev(1, NULL, 0)); break; + CASE_TEST(ptrace); tmp = ptrace(PTRACE_CONT, getpid(), NULL, NULL); EXPECT_SYSER(tmp != -1 && errno != ENOSYS, tmp, -1, EFAULT); break; CASE_TEST(syscall_noargs); EXPECT_SYSEQ(1, syscall(__NR_getpid), getpid()); break; CASE_TEST(syscall_args); EXPECT_SYSER(1, syscall(__NR_statx, 0, NULL, 0, 0, NULL), -1, EFAULT); break; + CASE_TEST(_syscall_noargs); EXPECT_SYSEQ(is_nolibc, _syscall(__NR_getpid), getpid()); break; + CASE_TEST(_syscall_args); EXPECT_SYSEQ(is_nolibc, _syscall(__NR_statx, 0, NULL, 0, 0, NULL), -EFAULT); break; + CASE_TEST(namespace); EXPECT_SYSZR(euid0 && proc, test_namespace()); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */ @@ -1168,6 +1516,99 @@ int run_syscall(int min, int max) return ret; } +int test_difftime(void) +{ + if (difftime(200., 100.) != 100.) + return 1; + + if (difftime(100., 200.) != -100.) + return 1; + + return 0; +} + +int test_time_types(void) +{ +#ifdef NOLIBC + struct __kernel_timespec kts; + struct timespec ts; + + if (!__builtin_types_compatible_p(time_t, __kernel_time64_t)) + return 1; + + if (sizeof(ts) != sizeof(kts)) + return 1; + + if (!__builtin_types_compatible_p(__typeof__(ts.tv_sec), __typeof__(kts.tv_sec))) + return 1; + + if (!__builtin_types_compatible_p(__typeof__(ts.tv_nsec), __typeof__(kts.tv_nsec))) + return 1; + + if (offsetof(__typeof__(ts), tv_sec) != offsetof(__typeof__(kts), tv_sec)) + return 1; + + if (offsetof(__typeof__(ts), tv_nsec) != offsetof(__typeof__(kts), tv_nsec)) + return 1; +#endif /* NOLIBC */ + + return 0; +} + +int test_malloc(void) +{ + size_t sz_array1, sz_array2, sz_array3; + int *array1, *array2, *array3; + int pagesize = getpagesize(); + size_t idx; + + if (pagesize < 0) + return 1; + + /* Dependent on the page size, as that is the granularity of our allocator. */ + sz_array1 = pagesize / 2; + array1 = malloc(sz_array1 * sizeof(*array1)); + if (!array1) + return 2; + + for (idx = 0; idx < sz_array1; idx++) + array1[idx] = idx; + + sz_array2 = pagesize * 2; + array2 = calloc(sz_array2, sizeof(*array2)); + if (!array2) { + free(array1); + return 3; + } + + for (idx = 0; idx < sz_array2; idx++) { + if (array2[idx] != 0) { + free(array2); + return 4; + } + array2[idx] = idx + sz_array1; + } + + /* Resize array1 into array3 and append array2 at the end. */ + sz_array3 = sz_array1 + sz_array2; + array3 = realloc(array1, sz_array3 * sizeof(*array3)); + if (!array3) { + free(array2); + free(array1); + return 5; + } + memcpy(array3 + sz_array1, array2, sizeof(*array2) * sz_array2); + free(array2); + + /* The contents must be contiguous now. */ + for (idx = 0; idx < sz_array3; idx++) + if (array3[idx] != (int)idx) + return 6; + + free(array3); + return 0; +} + int run_stdlib(int min, int max) { int test; @@ -1180,7 +1621,7 @@ int run_stdlib(int min, int max) * Add some more chars after the \0, to test functions that overwrite the buffer set * the \0 at the exact right position. */ - char buf[10] = "test123456"; + char buf[11] = "test123456"; buf[4] = '\0'; @@ -1211,6 +1652,9 @@ int run_stdlib(int min, int max) CASE_TEST(strlcpy_2); EXPECT_STRBUFEQ(is_nolibc, strlcpy(buf, "bar", 2), buf, 3, "b"); break; CASE_TEST(strlcpy_3); EXPECT_STRBUFEQ(is_nolibc, strlcpy(buf, "bar", 3), buf, 3, "ba"); break; CASE_TEST(strlcpy_4); EXPECT_STRBUFEQ(is_nolibc, strlcpy(buf, "bar", 4), buf, 3, "bar"); break; + CASE_TEST(strstr_foobar_foo); EXPECT_STREQ(1, strstr("foobar", "foo"), "foobar"); break; + CASE_TEST(strstr_foobar_bar); EXPECT_STREQ(1, strstr("foobar", "bar"), "bar"); break; + CASE_TEST(strstr_foobar_baz); EXPECT_PTREQ(1, strstr("foobar", "baz"), NULL); break; CASE_TEST(memcmp_20_20); EXPECT_EQ(1, memcmp("aaa\x20", "aaa\x20", 4), 0); break; CASE_TEST(memcmp_20_60); EXPECT_LT(1, memcmp("aaa\x20", "aaa\x60", 4), 0); break; CASE_TEST(memcmp_60_20); EXPECT_GT(1, memcmp("aaa\x60", "aaa\x20", 4), 0); break; @@ -1281,6 +1725,32 @@ int run_stdlib(int min, int max) CASE_TEST(strerror_EINVAL); EXPECT_STREQ(is_nolibc, strerror(EINVAL), "errno=22"); break; CASE_TEST(strerror_int_max); EXPECT_STREQ(is_nolibc, strerror(INT_MAX), "errno=2147483647"); break; CASE_TEST(strerror_int_min); EXPECT_STREQ(is_nolibc, strerror(INT_MIN), "errno=-2147483648"); break; + CASE_TEST(tolower); EXPECT_EQ(1, tolower('A'), 'a'); break; + CASE_TEST(tolower_noop); EXPECT_EQ(1, tolower('a'), 'a'); break; + CASE_TEST(toupper); EXPECT_EQ(1, toupper('a'), 'A'); break; + CASE_TEST(toupper_noop); EXPECT_EQ(1, toupper('A'), 'A'); break; + CASE_TEST(abs); EXPECT_EQ(1, abs(-10), 10); break; + CASE_TEST(abs_noop); EXPECT_EQ(1, abs(10), 10); break; + CASE_TEST(difftime); EXPECT_ZR(1, test_difftime()); break; + CASE_TEST(memchr_foobar6_o); EXPECT_STREQ(1, memchr("foobar", 'o', 6), "oobar"); break; + CASE_TEST(memchr_foobar3_b); EXPECT_STRZR(1, memchr("foobar", 'b', 3)); break; + CASE_TEST(time_types); EXPECT_ZR(is_nolibc, test_time_types()); break; + CASE_TEST(makedev); EXPECT_EQ(1, makedev(0x12, 0x34), 0x1234); break; + CASE_TEST(major); EXPECT_EQ(1, major(0x1234), 0x12); break; + CASE_TEST(minor); EXPECT_EQ(1, minor(0x1234), 0x34); break; + CASE_TEST(makedev_big); EXPECT_EQ(1, makedev(0x11223344, 0x55667788), 0x1122355667734488); break; + CASE_TEST(major_big); EXPECT_EQ(1, major(0x1122355667734488), 0x11223344); break; + CASE_TEST(minor_big); EXPECT_EQ(1, minor(0x1122355667734488), 0x55667788); break; + CASE_TEST(malloc); EXPECT_ZR(1, test_malloc()); break; + CASE_TEST(bswap_16); EXPECT_EQ(1, bswap_16(0x0123), 0x2301); break; + CASE_TEST(bswap_32); EXPECT_EQ(1, bswap_32(0x01234567), 0x67452301); break; + CASE_TEST(bswap_64); EXPECT_EQ(1, bswap_64(0x0123456789abcdef), 0xefcdab8967452301); break; + CASE_TEST(htobe16); EXPECT_EQ(1, htobe16(is_le ? 0x0123 : 0x2301), 0x2301); break; + CASE_TEST(htole16); EXPECT_EQ(1, htole16(is_le ? 0x0123 : 0x2301), 0x0123); break; + CASE_TEST(htobe32); EXPECT_EQ(1, htobe32(is_le ? 0x01234567 : 0x67452301), 0x67452301); break; + CASE_TEST(htole32); EXPECT_EQ(1, htole32(is_le ? 0x01234567 : 0x67452301), 0x01234567); break; + CASE_TEST(htobe64); EXPECT_EQ(1, htobe64(is_le ? 0x0123456789000000 : 0x8967452301), 0x8967452301); break; + CASE_TEST(htole64); EXPECT_EQ(1, htole64(is_le ? 0x0123456789 : 0x8967452301000000), 0x0123456789); break; case __LINE__: return ret; /* must be last */ @@ -1290,56 +1760,60 @@ int run_stdlib(int min, int max) return ret; } -#define EXPECT_VFPRINTF(c, expected, fmt, ...) \ - ret += expect_vfprintf(llen, c, expected, fmt, ##__VA_ARGS__) +#define EXPECT_VFPRINTF(cond, expected, fmt, ...) \ + do { if (!(cond)) result(llen, SKIPPED); else ret += expect_vfprintf(llen, expected, fmt, ##__VA_ARGS__); } while (0) -static int expect_vfprintf(int llen, int c, const char *expected, const char *fmt, ...) +#define VFPRINTF_LEN 25 +static int expect_vfprintf(int llen, const char *expected, const char *fmt, ...) { - int ret, pipefd[2]; - ssize_t w, r; - char buf[100]; - FILE *memfile; + char buf[VFPRINTF_LEN + 80]; + unsigned int cmp_len; va_list args; + ssize_t written, expected_len; - ret = pipe(pipefd); - if (ret == -1) { - llen += printf(" pipe() != %s", strerror(errno)); - result(llen, FAIL); - return 1; - } - - memfile = fdopen(pipefd[1], "w"); - if (!memfile) { - result(llen, FAIL); - return 1; - } + /* Fill and terminate buf[] to check for overlong/absent writes */ + memset(buf, 0xa5, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = 0; va_start(args, fmt); - w = vfprintf(memfile, fmt, args); + /* Limit buffer length to test truncation */ + written = vsnprintf(buf, VFPRINTF_LEN + 1, fmt, args); va_end(args); - if (w != c) { - llen += printf(" written(%d) != %d", (int)w, c); + llen += printf(" \"%s\"", buf); + + expected_len = strlen(expected); + if (expected_len > VFPRINTF_LEN) { + /* Indicate truncated in test output */ + llen += printf("+"); + cmp_len = VFPRINTF_LEN; + } else { + cmp_len = expected_len; + } + + if (memcmp(expected, buf, cmp_len) || buf[cmp_len]) { + llen += printf(" should be \"%.*s\"", VFPRINTF_LEN, expected); result(llen, FAIL); return 1; } - fclose(memfile); - - r = read(pipefd[0], buf, sizeof(buf) - 1); - - if (r != w) { - llen += printf(" written(%d) != read(%d)", (int)w, (int)r); + if (written != expected_len) { + llen += printf(" written(%d) != %d", (int)written, (int)expected_len); result(llen, FAIL); return 1; } - buf[r] = '\0'; - llen += printf(" \"%s\" = \"%s\"", expected, buf); - ret = strncmp(expected, buf, c); + /* Check for any overwrites after the actual data. */ + while (++cmp_len < sizeof(buf) - 1) { + if ((unsigned char)buf[cmp_len] != 0xa5) { + llen += printf(" overwrote buf[%d] with 0x%x", cmp_len, buf[cmp_len]); + result(llen, FAIL); + return 1; + } + } - result(llen, ret ? FAIL : OK); - return ret; + result(llen, OK); + return 0; } static int test_scanf(void) @@ -1409,7 +1883,52 @@ static int test_scanf(void) return 0; } -static int run_vfprintf(int min, int max) +static int test_printf_error(void) +{ + int fd, ret, saved_errno; + + fd = open("/dev/full", O_RDWR); + if (fd == -1) + return 1; + + errno = 0; + ret = dprintf(fd, "foo"); + saved_errno = errno; + close(fd); + + if (ret != -1) + return 2; + + if (saved_errno != ENOSPC) + return 3; + + return 0; +} + +int test_asprintf(void) +{ + char *str; + int ret; + + ret = asprintf(&str, "foo%s", "bar"); + if (ret == -1) + return 1; + + if (ret != 6) { + free(str); + return 2; + } + + if (memcmp(str, "foobar", 6) != 0) { + free(str); + return 3; + } + + free(str); + return 0; +} + +static int run_printf(int min, int max) { int test; int ret = 0; @@ -1421,16 +1940,58 @@ static int run_vfprintf(int min, int max) * test numbers. */ switch (test + __LINE__ + 1) { - CASE_TEST(empty); EXPECT_VFPRINTF(0, "", ""); break; - CASE_TEST(simple); EXPECT_VFPRINTF(3, "foo", "foo"); break; - CASE_TEST(string); EXPECT_VFPRINTF(3, "foo", "%s", "foo"); break; - CASE_TEST(number); EXPECT_VFPRINTF(4, "1234", "%d", 1234); break; - CASE_TEST(negnumber); EXPECT_VFPRINTF(5, "-1234", "%d", -1234); break; - CASE_TEST(unsigned); EXPECT_VFPRINTF(5, "12345", "%u", 12345); break; - 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(empty); EXPECT_VFPRINTF(1, "", ""); break; + CASE_TEST(simple); EXPECT_VFPRINTF(1, "foo", "foo"); break; + CASE_TEST(string); EXPECT_VFPRINTF(1, "foo", "%s", "foo"); break; + CASE_TEST(number); EXPECT_VFPRINTF(1, "1234", "%d", 1234); break; + CASE_TEST(negnumber); EXPECT_VFPRINTF(1, "-1234", "%d", -1234); break; + CASE_TEST(num_sign); EXPECT_VFPRINTF(1, "| 1|+2|+3|+4|5|", "|% d|%+d|% +d|%+ d|%#d|", 1, 2, 3, 4, 5); break; + CASE_TEST(unsigned); EXPECT_VFPRINTF(1, "12345", "%u", 12345); break; + CASE_TEST(signed_max); EXPECT_VFPRINTF(1, "2147483647", "%i", ~0u >> 1); break; + CASE_TEST(signed_min); EXPECT_VFPRINTF(1, "-2147483648", "%i", (~0u >> 1) + 1); break; + CASE_TEST(unsigned_max); EXPECT_VFPRINTF(1, "4294967295", "%u", ~0u); break; + CASE_TEST(char); EXPECT_VFPRINTF(1, "|c|d| e|", "|%c|%.0c|%4c|", 'c', 'd', 'e'); break; + CASE_TEST(octal); EXPECT_VFPRINTF(1, "|17| 0033||", "|%o|%6.4o|%.0o|", 017, 033, 0); break; + CASE_TEST(octal_max); EXPECT_VFPRINTF(1, "1777777777777777777777", "%llo", ~0ULL); break; + CASE_TEST(octal_alt); EXPECT_VFPRINTF(1, "|0|01|02|034|0|0|", "|%#o|%#o|%#02o|%#02o|%#.0o|%0-#o|", 0, 1, 2, 034, 0, 0); break; + CASE_TEST(hex_nolibc); EXPECT_VFPRINTF(is_nolibc, "|f|d|", "|%x|%X|", 0xf, 0xd); break; + CASE_TEST(hex_libc); EXPECT_VFPRINTF(!is_nolibc, "|f|D|", "|%x|%X|", 0xf, 0xd); break; + CASE_TEST(hex_alt); EXPECT_VFPRINTF(1, "|0x1| 0x2| 0|", "|%#x|%#5x|%#5x|", 1, 2, 0); break; + CASE_TEST(hex_alt_prec); EXPECT_VFPRINTF(1, "| 0x02|0x03| 0x123|", "|%#5.2x|%#04x|%#6.2x|", 2, 3, 0x123); break; + CASE_TEST(hex_0_alt); EXPECT_VFPRINTF(1, "|0|0000| 00|", "|%#x|%#04x|%#5.2x|", 0, 0, 0); break; + CASE_TEST(pointer); EXPECT_VFPRINTF(1, "0x1", "%p", (void *) 0x1); break; + CASE_TEST(pointer_NULL); EXPECT_VFPRINTF(is_nolibc || is_glibc, "|(nil)|(nil)|", "|%p|%.4p|", (void *)0, (void *)0); break; + CASE_TEST(string_NULL); EXPECT_VFPRINTF(is_nolibc || is_glibc, "|(null)||(null)|", "|%s|%.5s|%.6s|", (void *)0, (void *)0, (void *)0); break; + CASE_TEST(percent); EXPECT_VFPRINTF(1, "a%d42%69%", "a%%d%d%%%d%%", 42, 69); break; + CASE_TEST(perc_qual); EXPECT_VFPRINTF(is_nolibc || is_glibc, "a%d2", "a%-14l%d%d", 2); break; + CASE_TEST(invalid); EXPECT_VFPRINTF(is_nolibc || is_glibc, "a%12yx3%y42%P", "a%12yx%d%y%d%P", 3, 42); break; + CASE_TEST(intmax_max); EXPECT_VFPRINTF(1, "9223372036854775807", "%lld", ~0ULL >> 1); break; + CASE_TEST(intmax_min); EXPECT_VFPRINTF(is_nolibc || is_glibc, "-9223372036854775808", "%Li", (~0ULL >> 1) + 1); break; + CASE_TEST(uintmax_max); EXPECT_VFPRINTF(1, "18446744073709551615", "%ju", ~0ULL); break; + CASE_TEST(truncation); EXPECT_VFPRINTF(1, "012345678901234567890123456789", "%s", "012345678901234567890123456789"); break; + CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); break; + CASE_TEST(string_trunc); EXPECT_VFPRINTF(1, " 12345", "%10.5s", "1234567890"); break; + CASE_TEST(string_var); EXPECT_VFPRINTF(1, "| ab|ef | ij|kl |", "|%*.*s|%*.*s|%*.*s|%*.*s|", 3, 2, "abcd", -3, 2, "efgh", 3, -1, "ij", -3, -1, "kl"); break; + CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); break; + CASE_TEST(number_left); EXPECT_VFPRINTF(1, "|-5 |", "|%-8d|", -5); break; + CASE_TEST(string_align); EXPECT_VFPRINTF(1, "|foo |", "|%-8s|", "foo"); break; + CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " 1", "%30d", 1); break; + CASE_TEST(width_tr_lft); EXPECT_VFPRINTF(1, "1 ", "%-30d", 1); break; + CASE_TEST(number_pad); EXPECT_VFPRINTF(1, "0000000005", "%010d", 5); break; + CASE_TEST(number_pad); EXPECT_VFPRINTF(1, "|0000000005|0x1234|", "|%010d|%#01x|", 5, 0x1234); break; + CASE_TEST(num_pad_neg); EXPECT_VFPRINTF(1, "-000000005", "%010d", -5); break; + CASE_TEST(num_pad_hex); EXPECT_VFPRINTF(1, "00fffffffb", "%010x", -5); break; + CASE_TEST(num_pad_trunc);EXPECT_VFPRINTF(is_nolibc, " 0000000000000000000000000000005", "%035d", 5); break; /* max 31 '0' can be added */ + CASE_TEST(num_p_tr_libc);EXPECT_VFPRINTF(!is_nolibc, "00000000000000000000000000000000005", "%035d", 5); break; + CASE_TEST(number_prec); EXPECT_VFPRINTF(1, " 00005", "%10.5d", 5); break; + CASE_TEST(num_prec_neg); EXPECT_VFPRINTF(1, " -00005", "%10.5d", -5); break; + CASE_TEST(number_var); EXPECT_VFPRINTF(1, "| -00005|5 |", "|%*.*d|%*.*d|", 10, 5, -5, -2, -10, 5); break; + CASE_TEST(num_0_prec_0); EXPECT_VFPRINTF(1, "|| |+||||", "|%.0d|% .0d|%+.0d|%.0u|%.0x|%#.0x|", 0, 0, 0, 0, 0, 0); break; + CASE_TEST(errno); errno = 22; EXPECT_VFPRINTF(is_nolibc, "errno=22", "%m"); break; + CASE_TEST(errno-neg); errno = -22; EXPECT_VFPRINTF(is_nolibc, "errno=-22 ", "%-12m"); break; CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; + CASE_TEST(printf_error); EXPECT_ZR(1, test_printf_error()); break; + CASE_TEST(asprintf); EXPECT_ZR(1, test_asprintf()); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */ @@ -1439,6 +2000,7 @@ static int run_vfprintf(int min, int max) return ret; } +__attribute__((no_sanitize("undefined"))) static int smash_stack(void) { char buf[100]; @@ -1455,8 +2017,7 @@ static int run_protection(int min __attribute__((unused)), int max __attribute__((unused))) { pid_t pid; - int llen = 0, ret; - siginfo_t siginfo = {}; + int llen = 0, status; struct rlimit rlimit = { 0, 0 }; llen += printf("0 -fstackprotector "); @@ -1494,11 +2055,10 @@ static int run_protection(int min __attribute__((unused)), return 1; default: - ret = waitid(P_PID, pid, &siginfo, WEXITED); + pid = waitpid(pid, &status, 0); - if (ret != 0 || siginfo.si_signo != SIGCHLD || - siginfo.si_code != CLD_KILLED || siginfo.si_status != SIGABRT) { - llen += printf("waitid()"); + if (pid == -1 || !WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) { + llen += printf("waitpid()"); result(llen, FAIL); return 1; } @@ -1519,12 +2079,14 @@ int prepare(void) if (stat("/dev/.", &stat_buf) == 0 || mkdir("/dev", 0755) == 0) { if (stat("/dev/console", &stat_buf) != 0 || stat("/dev/null", &stat_buf) != 0 || - stat("/dev/zero", &stat_buf) != 0) { + stat("/dev/zero", &stat_buf) != 0 || + stat("/dev/full", &stat_buf) != 0) { /* try devtmpfs first, otherwise fall back to manual creation */ if (mount("/dev", "/dev", "devtmpfs", 0, 0) != 0) { mknod("/dev/console", 0600 | S_IFCHR, makedev(5, 1)); mknod("/dev/null", 0666 | S_IFCHR, makedev(1, 3)); mknod("/dev/zero", 0666 | S_IFCHR, makedev(1, 5)); + mknod("/dev/full", 0666 | S_IFCHR, makedev(1, 7)); } } } @@ -1570,7 +2132,7 @@ static const struct test test_names[] = { { .name = "startup", .func = run_startup }, { .name = "syscall", .func = run_syscall }, { .name = "stdlib", .func = run_stdlib }, - { .name = "vfprintf", .func = run_vfprintf }, + { .name = "printf", .func = run_printf }, { .name = "protection", .func = run_protection }, { 0 } }; diff --git a/tools/testing/selftests/nolibc/run-tests.sh b/tools/testing/selftests/nolibc/run-tests.sh index 0299a0912d40..cd439096fdf3 100755 --- a/tools/testing/selftests/nolibc/run-tests.sh +++ b/tools/testing/selftests/nolibc/run-tests.sh @@ -7,7 +7,7 @@ set -e trap 'echo Aborting...' 'ERR' -crosstool_version=13.2.0 +crosstool_version=15.2.0 hostarch=x86_64 nproc=$(( $(nproc) + 2)) cache_dir="${XDG_CACHE_HOME:-"$HOME"/.cache}" @@ -18,13 +18,16 @@ test_mode=system werror=1 llvm= all_archs=( - i386 x86_64 + i386 x86_64 x32 arm64 arm armthumb - mips32le mips32be + mips32le mips32be mipsn32le mipsn32be mips64le mips64be ppc ppc64 ppc64le riscv32 riscv64 - s390x s390 + s390x loongarch + sparc32 sparc64 + m68k + sh4 ) archs="${all_archs[@]}" @@ -111,6 +114,8 @@ crosstool_arch() { loongarch) echo loongarch64;; mips*) echo mips;; s390*) echo s390;; + sparc*) echo sparc64;; + x32*) echo x86_64;; *) echo "$1";; esac } @@ -164,9 +169,9 @@ test_arch() { cross_compile=$(realpath "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/${ct_arch}-${ct_abi}-") build_dir="${build_location}/${arch}" if [ "$werror" -ne 0 ]; then - CFLAGS_EXTRA="$CFLAGS_EXTRA -Werror" + CFLAGS_EXTRA="$CFLAGS_EXTRA -Werror -Wl,--fatal-warnings" fi - MAKE=(make -j"${nproc}" XARCH="${arch}" CROSS_COMPILE="${cross_compile}" LLVM="${llvm}" O="${build_dir}") + MAKE=(make -f Makefile.nolibc -j"${nproc}" XARCH="${arch}" CROSS_COMPILE="${cross_compile}" LLVM="${llvm}" O="${build_dir}") case "$test_mode" in 'system') @@ -180,7 +185,11 @@ test_arch() { exit 1 esac printf '%-15s' "$arch:" - if [ "$arch" = "s390" ] && ([ "$llvm" = "1" ] || [ "$test_mode" = "user" ]); then + if [ "$arch" = "m68k" -o "$arch" = "sh4" ] && [ "$llvm" = "1" ]; then + echo "Unsupported configuration" + return + fi + if [ "$arch" = "x32" ] && [ "$test_mode" = "user" ]; then echo "Unsupported configuration" return fi |
