From: Tulio A M Mendes Date: Sat, 14 Mar 2026 17:07:48 +0000 (-0300) Subject: refactor: build system improvements — ulibc out-of-tree, -Werror, noexecstack, doom... X-Git-Url: https://projects.tadryanom.me/docs/static/git-favicon.png?a=commitdiff_plain;ds=inline;p=AdrOS.git refactor: build system improvements — ulibc out-of-tree, -Werror, noexecstack, doom target 1. ulibc out-of-tree build: .o files now go to build/$ARCH/user/ulibc/ instead of in-tree user/ulibc/src/. BUILDDIR passed from root Makefile. 2. ulibc -Werror: added -Werror, removed -Wno-incompatible-pointer-types. Fixed warnings: - execve signature in unistd.h: const char* const* -> char* const* (POSIX) - execve impl in unistd.c, execle: match new signature - pthread.c: removed unused thread_entry_trampoline - socket.c: suppress unused addrlen in sendto/recvfrom - sh.c, env.c, init.c: remove stale const casts for execve 3. .note.GNU-stack: added -z noexecstack to linker flags in common.mk and doom Makefile. GNU_STACK segment now RW (not RWE). 4. make doom: new phony target in root Makefile builds doom via sub-Make, passing ULIBC_BUILDDIR. Doom LDFLAGS now use out-of-tree ulibc path. 5. which test: added positive test (which finds sh) alongside the existing negative test (which rejects missing cmd). Tests: 103/103 smoke, 69/69 host utils, cppcheck clean --- diff --git a/Makefile b/Makefile index 9227435..ca9f620 100644 --- a/Makefile +++ b/Makefile @@ -189,13 +189,13 @@ iso: $(KERNEL_NAME) $(INITRD_IMG) $(MKINITRD): tools/mkinitrd.c include/xxhash32.h @gcc -Iinclude tools/mkinitrd.c -o $(MKINITRD) -# --- ulibc build (output into build/user/ulibc/) --- +# --- ulibc build (output into build/$ARCH/user/ulibc/) --- +ULIBC_BUILDDIR := $(CURDIR)/$(USER_BUILD)/ulibc + $(ULIBC_LIB) $(ULIBC_SO): FORCE - @mkdir -p $(USER_BUILD)/ulibc @$(MAKE) -C $(ULIBC_DIR) CC="$(USER_CC)" AS="$(USER_CC:gcc=as)" AR="$(USER_AR)" LD="$(USER_LD)" \ - libulibc.a libc.so --no-print-directory - @cp -u $(ULIBC_DIR)/libulibc.a $(ULIBC_LIB) - @cp -u $(ULIBC_DIR)/libc.so $(ULIBC_SO) + BUILDDIR="$(ULIBC_BUILDDIR)" \ + $(ULIBC_BUILDDIR)/libulibc.a $(ULIBC_BUILDDIR)/libc.so --no-print-directory FORCE: # --- Special builds (fulltest, ldso, pie_test) --- @@ -212,14 +212,14 @@ $(PIE_SO) $(PIE_ELF): user/cmds/pie_test/pie_main.c user/cmds/pie_test/pie_func. # Use absolute paths so they work from sub-Makefile directories ABS_ULIBC := $(CURDIR)/$(ULIBC_DIR) ABS_DYN_CC := $(USER_CC) -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -fPIC -fno-plt -I$(ABS_ULIBC)/include -ABS_DYN_LD := $(USER_LD) -m elf_i386 --dynamic-linker=/lib/ld.so -T $(CURDIR)/user/dyn_linker.ld -L$(ABS_ULIBC) -rpath /lib --unresolved-symbols=ignore-in-shared-libs +ABS_DYN_LD := $(USER_LD) -m elf_i386 --dynamic-linker=/lib/ld.so -T $(CURDIR)/user/dyn_linker.ld -L$(ULIBC_BUILDDIR) -rpath /lib --unresolved-symbols=ignore-in-shared-libs -z noexecstack # Generate build rules for each dynamically-linked command define USER_CMD_RULE $(USER_BUILD)/cmds/$(1)/$(1).elf: user/cmds/$(1)/$(1).c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB) @$$(MAKE) --no-print-directory -C user/cmds/$(1) TOPDIR=$$(CURDIR) \ BUILDDIR=$$(CURDIR)/$(USER_BUILD)/cmds/$(1) \ - DYN_CC="$$(ABS_DYN_CC)" DYN_LD="$$(ABS_DYN_LD)" CRT0="$(ABS_ULIBC)/src/crt0.o" + DYN_CC="$$(ABS_DYN_CC)" DYN_LD="$$(ABS_DYN_LD)" CRT0="$(ULIBC_BUILDDIR)/crt0.o" endef $(foreach cmd,$(USER_CMD_NAMES),$(eval $(call USER_CMD_RULE,$(cmd)))) @@ -237,6 +237,10 @@ INITRD_FILES := $(FULLTEST_ELF):sbin/fulltest \ INITRD_DEPS := $(MKINITRD) $(FULLTEST_ELF) $(USER_CMD_ELFS) $(LDSO_ELF) $(ULIBC_SO) $(PIE_SO) $(PIE_ELF) $(FSTAB) +# doom (build via 'make doom', included in initrd if present) +doom: $(ULIBC_LIB) $(ULIBC_SO) + @$(MAKE) --no-print-directory -C user/doom TOPDIR=$(CURDIR) ULIBC_BUILDDIR="$(ULIBC_BUILDDIR)" + # Include doom.elf if it has been built ifneq ($(wildcard $(DOOM_ELF)),) INITRD_FILES += $(DOOM_ELF):usr/games/doom @@ -388,4 +392,4 @@ clean: @$(MAKE) -C user/ulibc clean --no-print-directory 2>/dev/null || true @if [ -f user/doom/Makefile ]; then $(MAKE) -C user/doom clean --no-print-directory 2>/dev/null || true; fi -.PHONY: all clean iso run run-arm run-riscv run-mips cppcheck sparse analyzer check test test-1cpu test-battery test-host test-gdb test-all scan-build mkinitrd-asan +.PHONY: all clean iso run run-arm run-riscv run-mips doom cppcheck sparse analyzer check test test-1cpu test-battery test-host test-gdb test-all scan-build mkinitrd-asan diff --git a/tests/test_host_utils.sh b/tests/test_host_utils.sh index 7ed2c00..70bc67f 100755 --- a/tests/test_host_utils.sh +++ b/tests/test_host_utils.sh @@ -444,9 +444,14 @@ fi # ---------- which ---------- echo "--- which ---" if compile which_test user/cmds/which/which.c; then - # which looks in /bin and /sbin hardcoded, so just check it runs + # Positive: find a command that exists in /bin + out=$("$BUILDDIR/which_test" sh 2>/dev/null) || true + echo "$out" | grep -q "/bin/sh" && pass "which finds sh" || fail "which finds sh" "got: $out" + + # Negative: nonexistent command should return nonzero + rc=0 "$BUILDDIR/which_test" nonexistent_cmd > /dev/null 2>&1 || rc=$? - [ "${rc:-1}" -ne 0 ] && pass "which not found" || fail "which not found" "should return nonzero" + [ "$rc" -ne 0 ] && pass "which rejects missing cmd" || fail "which rejects missing cmd" "should return nonzero" else skip "which (compile failed)" fi diff --git a/user/cmds/common.mk b/user/cmds/common.mk index 13a7205..ace5630 100644 --- a/user/cmds/common.mk +++ b/user/cmds/common.mk @@ -10,9 +10,10 @@ TOPDIR ?= $(abspath ../../..) BUILDDIR ?= $(TOPDIR)/build/x86/user/cmds/$(NAME) ULIBC_DIR := $(TOPDIR)/user/ulibc +ULIBC_BUILDDIR ?= $(TOPDIR)/build/x86/user/ulibc DYN_CC ?= i686-elf-gcc -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -fPIC -fno-plt -I$(ULIBC_DIR)/include -DYN_LD ?= i686-elf-ld -m elf_i386 --dynamic-linker=/lib/ld.so -T $(TOPDIR)/user/dyn_linker.ld -L$(ULIBC_DIR) -rpath /lib -CRT0 ?= $(ULIBC_DIR)/src/crt0.o +DYN_LD ?= i686-elf-ld -m elf_i386 --dynamic-linker=/lib/ld.so -T $(TOPDIR)/user/dyn_linker.ld -L$(ULIBC_BUILDDIR) -rpath /lib +CRT0 ?= $(ULIBC_BUILDDIR)/crt0.o OBJS := $(addprefix $(BUILDDIR)/,$(SRCS:.c=.o)) ELF := $(BUILDDIR)/$(NAME).elf @@ -21,7 +22,7 @@ all: $(ELF) $(ELF): $(OBJS) @echo " LD $@" - @$(DYN_LD) -o $@ $(CRT0) $(OBJS) -lc + @$(DYN_LD) -z noexecstack -o $@ $(CRT0) $(OBJS) -lc $(BUILDDIR)/%.o: %.c @mkdir -p $(BUILDDIR) diff --git a/user/cmds/env/env.c b/user/cmds/env/env.c index a7d727a..410a8be 100644 --- a/user/cmds/env/env.c +++ b/user/cmds/env/env.c @@ -15,7 +15,7 @@ int main(int argc, char** argv) { return 0; } /* env COMMAND ARGS... — run command with current environment */ - execve(argv[1], (const char* const*)&argv[1], (const char* const*)__environ); + execve(argv[1], &argv[1], __environ); fprintf(stderr, "env: %s: not found\n", argv[1]); return 127; } diff --git a/user/cmds/init/init.c b/user/cmds/init/init.c index 02e08f4..cbc8c5e 100644 --- a/user/cmds/init/init.c +++ b/user/cmds/init/init.c @@ -165,9 +165,9 @@ static int run_process(const char* cmd) { argv[argc] = NULL; if (argc > 0) { - execve(argv[0], (const char* const*)argv, NULL); + execve(argv[0], argv, NULL); /* If execve fails, try with /bin/sh -c */ - const char* sh_argv[] = { "/bin/sh", "-c", cmd, NULL }; + char* sh_argv[] = { "/bin/sh", "-c", (char*)cmd, NULL }; execve("/bin/sh", sh_argv, NULL); } _exit(127); @@ -237,7 +237,7 @@ static void default_init(void) { } if (pid == 0) { - const char* argv[] = { "/bin/sh", NULL }; + char* argv[] = { "/bin/sh", NULL }; execve("/bin/sh", argv, NULL); _exit(127); } diff --git a/user/cmds/sh/sh.c b/user/cmds/sh/sh.c index 1af3ab3..b71c3c8 100644 --- a/user/cmds/sh/sh.c +++ b/user/cmds/sh/sh.c @@ -746,7 +746,7 @@ static void run_simple(char* cmd) { int fd = open(redir_out, flags); if (fd >= 0) { dup2(fd, 1); close(fd); } } - execve(path, (const char* const*)argv, (const char* const*)envp); + execve(path, argv, envp); fprintf(stderr, "sh: %s: not found\n", argv[0]); _exit(127); } @@ -837,7 +837,7 @@ static void run_pipeline(char* cmdline) { if (argc == 0) _exit(0); const char* path = resolve(argv[0]); char** envp = build_envp(); - execve(path, (const char* const*)argv, (const char* const*)envp); + execve(path, argv, envp); fprintf(stderr, "sh: %s: not found\n", argv[0]); _exit(127); } diff --git a/user/doom/Makefile b/user/doom/Makefile index df4c206..1cc8eb2 100644 --- a/user/doom/Makefile +++ b/user/doom/Makefile @@ -11,9 +11,11 @@ CC := i686-elf-gcc LD := i686-elf-ld -ULIBC_DIR := ../ulibc -ULIBC_INC := $(ULIBC_DIR)/include -CRT0 := $(ULIBC_DIR)/src/crt0.o +TOPDIR ?= $(abspath ../..) +ULIBC_DIR := ../ulibc +ULIBC_INC := $(ULIBC_DIR)/include +ULIBC_BUILDDIR ?= $(TOPDIR)/build/x86/user/ulibc +CRT0 := $(ULIBC_BUILDDIR)/crt0.o DYN_LINKER := ../dyn_linker.ld CFLAGS := -m32 -O2 -ffreestanding -nostdlib -fPIC -fno-plt \ @@ -24,7 +26,7 @@ CFLAGS := -m32 -O2 -ffreestanding -nostdlib -fPIC -fno-plt \ -DNORMALUNIX -DLINUX LIBGCC := $(shell $(CC) -print-libgcc-file-name) -LDFLAGS := -m elf_i386 --dynamic-linker=/lib/ld.so -T $(DYN_LINKER) -L$(ULIBC_DIR) -rpath /lib --unresolved-symbols=ignore-in-shared-libs +LDFLAGS := -m elf_i386 --dynamic-linker=/lib/ld.so -T $(DYN_LINKER) -L$(ULIBC_BUILDDIR) -rpath /lib --unresolved-symbols=ignore-in-shared-libs -z noexecstack # doomgeneric engine sources (nested inside cloned repo) # Exclude platform adapters and files requiring external libraries diff --git a/user/ulibc/Makefile b/user/ulibc/Makefile index 9a86fe5..0e186b4 100644 --- a/user/ulibc/Makefile +++ b/user/ulibc/Makefile @@ -4,41 +4,52 @@ AS ?= i686-elf-as AR ?= i686-elf-ar LD ?= i686-elf-ld -CFLAGS := -m32 -ffreestanding -fno-pie -no-pie -nostdlib -O2 -Wall -Wextra -Wno-incompatible-pointer-types -Iinclude -CFLAGS_PIC := -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -Wno-incompatible-pointer-types -Iinclude -fPIC -fno-plt +# Out-of-tree build directory (override from root Makefile) +BUILDDIR ?= . + +# Source-relative include path (works from any BUILDDIR) +SRCDIR := $(dir $(lastword $(MAKEFILE_LIST)))src +INCDIR := $(dir $(lastword $(MAKEFILE_LIST)))include + +CFLAGS := -m32 -ffreestanding -fno-pie -no-pie -nostdlib -O2 -Wall -Wextra -Werror -I$(INCDIR) +CFLAGS_PIC := -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -Werror -I$(INCDIR) -fPIC -fno-plt ASFLAGS := --32 -SRC_C := $(wildcard src/*.c) -SRC_S := $(wildcard src/*.S) -OBJ := $(SRC_C:.c=.o) $(SRC_S:.S=.o) +SRC_C := $(wildcard $(SRCDIR)/*.c) +SRC_S := $(wildcard $(SRCDIR)/*.S) +OBJ := $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRC_C)) $(patsubst $(SRCDIR)/%.S,$(BUILDDIR)/%.o,$(SRC_S)) # PIC objects for shared library (exclude crt0 — it's linked separately) -PIC_C := $(filter-out src/crt0.S,$(SRC_C)) -PIC_OBJ := $(PIC_C:.c=.pic.o) +PIC_C := $(filter-out $(SRCDIR)/crt0.S,$(SRC_C)) +PIC_OBJ := $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.pic.o,$(PIC_C)) -all: libulibc.a +all: $(BUILDDIR)/libulibc.a -libulibc.a: $(OBJ) +$(BUILDDIR)/libulibc.a: $(OBJ) @$(AR) rcs $@ $^ @echo " AR $@" -libc.so: $(PIC_OBJ) +$(BUILDDIR)/libc.so: $(PIC_OBJ) @$(LD) -m elf_i386 -shared -soname libc.so -o $@ $^ @echo " LD $@ (shared)" -src/%.o: src/%.c +$(BUILDDIR)/%.o: $(SRCDIR)/%.c + @mkdir -p $(BUILDDIR) @$(CC) $(CFLAGS) -c $< -o $@ @echo " CC $<" -src/%.pic.o: src/%.c +$(BUILDDIR)/%.pic.o: $(SRCDIR)/%.c + @mkdir -p $(BUILDDIR) @$(CC) $(CFLAGS_PIC) -c $< -o $@ @echo " CC [PIC] $<" -src/%.o: src/%.S +$(BUILDDIR)/%.o: $(SRCDIR)/%.S + @mkdir -p $(BUILDDIR) @$(AS) $(ASFLAGS) $< -o $@ @echo " AS $<" clean: + rm -f $(OBJ) $(PIC_OBJ) $(BUILDDIR)/libulibc.a $(BUILDDIR)/libc.so rm -f src/*.o src/*.pic.o libulibc.a libc.so .PHONY: all clean diff --git a/user/ulibc/include/unistd.h b/user/ulibc/include/unistd.h index ab2c301..d56c300 100644 --- a/user/ulibc/include/unistd.h +++ b/user/ulibc/include/unistd.h @@ -21,7 +21,7 @@ int dup(int oldfd); int dup2(int oldfd, int newfd); int pipe(int fds[2]); int fork(void); -int execve(const char* path, const char* const* argv, const char* const* envp); +int execve(const char* path, char* const argv[], char* const envp[]); int getpid(void); int getppid(void); int chdir(const char* path); diff --git a/user/ulibc/src/pthread.c b/user/ulibc/src/pthread.c index 5146ef1..9c52da6 100644 --- a/user/ulibc/src/pthread.c +++ b/user/ulibc/src/pthread.c @@ -27,27 +27,6 @@ struct thread_start_info { int exited; }; -/* Thread trampoline: called by the clone child. - * The child's ESP points to the top of the allocated stack. - * We read start_info from the bottom of the stack, call the user function, - * then call pthread_exit. */ -static void __attribute__((noreturn)) thread_entry_trampoline(void) { - /* The start_info pointer is at ESP (pushed before clone). - * After clone returns 0, EAX=0, and we land here with the - * stack set up so that start_info is accessible. - * - * Actually, with our clone design, we enter usermode with the - * register state. We stash the info pointer in a register-accessible - * location. Let's read it from ESI (we pass it as part of the stack). */ - - /* In our implementation, the child starts with the parent's register - * state but ESP overridden. We place start_info at a known location - * relative to the stack base. The trampoline wrapper below handles this. */ - for (;;) { - __asm__ volatile("nop"); - } -} - /* Wrapper that runs on the new thread's stack */ static void __attribute__((noreturn, used)) _pthread_trampoline(struct thread_start_info* info) { diff --git a/user/ulibc/src/socket.c b/user/ulibc/src/socket.c index 3ee60e3..519e3bc 100644 --- a/user/ulibc/src/socket.c +++ b/user/ulibc/src/socket.c @@ -32,11 +32,13 @@ int recv(int sockfd, void* buf, size_t len, int flags) { int sendto(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen) { + (void)addrlen; return __syscall_ret(_syscall5(SYS_SENDTO, sockfd, (int)buf, (int)len, flags, (int)dest_addr)); } int recvfrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen) { + (void)addrlen; return __syscall_ret(_syscall5(SYS_RECVFROM, sockfd, (int)buf, (int)len, flags, (int)src_addr)); } diff --git a/user/ulibc/src/unistd.c b/user/ulibc/src/unistd.c index ab4bc1e..944aec6 100644 --- a/user/ulibc/src/unistd.c +++ b/user/ulibc/src/unistd.c @@ -40,7 +40,7 @@ int fork(void) { return __syscall_ret(_syscall0(SYS_FORK)); } -int execve(const char* path, const char* const* argv, const char* const* envp) { +int execve(const char* path, char* const argv[], char* const envp[]) { return __syscall_ret(_syscall3(SYS_EXECVE, (int)path, (int)argv, (int)envp)); } @@ -236,7 +236,6 @@ void _exit(int status) { int execle(const char* path, const char* arg, ...) { /* Walk varargs to find argv[] and the trailing envp */ - extern int execve(const char*, const char* const*, const char* const*); const char* args[32]; int n = 0; __builtin_va_list ap; @@ -250,7 +249,7 @@ int execle(const char* path, const char* arg, ...) { args[n] = (void*)0; char* const* envp = __builtin_va_arg(ap, char* const*); __builtin_va_end(ap); - return execve(path, (const char* const*)args, (const char* const*)envp); + return execve(path, (char* const*)args, envp); } static char _login_buf[32] = "root";