]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
refactor: build system improvements — ulibc out-of-tree, -Werror, noexecstack, doom... master
authorTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 17:07:48 +0000 (14:07 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 17:07:48 +0000 (14:07 -0300)
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

12 files changed:
Makefile
tests/test_host_utils.sh
user/cmds/common.mk
user/cmds/env/env.c
user/cmds/init/init.c
user/cmds/sh/sh.c
user/doom/Makefile
user/ulibc/Makefile
user/ulibc/include/unistd.h
user/ulibc/src/pthread.c
user/ulibc/src/socket.c
user/ulibc/src/unistd.c

index 92274357226585cc81a8bb6fa9fd9c7c1b9137bf..ca9f620a6172cdf047564b39a0ebf3d0ed896f9f 100644 (file)
--- 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
index 7ed2c00b68bf571a1c52abac2dbde019283d6352..70bc67fd2f992b4382d6cc17b26034691fe10cdd 100755 (executable)
@@ -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
index 13a72055b3bad0e9c0573e40308724b626150430..ace5630dd27decca22ad2935ee5a6aa88d55e491 100644 (file)
@@ -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)
index a7d727a4bb66ee4ca8746788142114d5f3fc2128..410a8bea60f578bf48f2aeb06fc098b86c6dedaa 100644 (file)
@@ -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;
 }
index 02e08f458c87dfc7c7776ef878a1a4492797acea..cbc8c5e48be6c36caa64f317bc9766d6d4a4d2a8 100644 (file)
@@ -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);
         }
index 1af3ab32e12960d6757b2694f09af780cfa30b36..b71c3c8af10793085882fcb8d37b1d0f1f390c1b 100644 (file)
@@ -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);
         }
index df4c2066b9b068b7f510c7a116358777a18f9100..1cc8eb2a6123bfaf37480a2b152c22a6c5849631 100644 (file)
 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
index 9a86fe52b23efe94899096e272d53a83b8d9e33b..0e186b43c493d8daa0a21051c623fb50e2f878d0 100644 (file)
@@ -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
index ab2c301734cc92f8c0ce7aaee36ac518df26a0e7..d56c3002a49b73bedc650b854d6e9143fd5799dc 100644 (file)
@@ -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);
index 5146ef1509ace856c232f05c64149b76fe7a4172..9c52da6b5b0df63371ea520b503c7290a316c3d7 100644 (file)
@@ -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) {
index 3ee60e36506c3f647135e4ce05452a1a6ec99515..519e3bc251ed4c2f874237f2adc80d9f7bc1b7b7 100644 (file)
@@ -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));
 }
 
index ab4bc1e291528ff4bf65d9b6114acac8f8b63ebe..944aec66632d36567b420309c04a9eb21a8f4c00 100644 (file)
@@ -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";