$(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) ---
# 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))))
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
@$(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
# ---------- 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
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
$(ELF): $(OBJS)
@echo " LD $@"
- @$(DYN_LD) -o $@ $(CRT0) $(OBJS) -lc
+ @$(DYN_LD) -z noexecstack -o $@ $(CRT0) $(OBJS) -lc
$(BUILDDIR)/%.o: %.c
@mkdir -p $(BUILDDIR)
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;
}
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);
}
if (pid == 0) {
- const char* argv[] = { "/bin/sh", NULL };
+ char* argv[] = { "/bin/sh", NULL };
execve("/bin/sh", argv, NULL);
_exit(127);
}
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);
}
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);
}
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 \
-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
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
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);
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) {
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));
}
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));
}
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;
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";