diff --git a/examples/typephp-os/Makefile b/examples/typephp-os/Makefile index ce882726..257a8a12 100644 --- a/examples/typephp-os/Makefile +++ b/examples/typephp-os/Makefile @@ -11,9 +11,6 @@ USER_COMMANDS := ls cd date uname pwd cat echo write touch mkdir rm rmdir mv fau USER_PROGRAMS := sh $(USER_COMMANDS) USER_SH_ELF := $(BUILD)/sh.elf USER_COMMAND_ELFS := $(addprefix $(BUILD)/,$(addsuffix .elf,$(USER_COMMANDS))) -USER_CRT0_OBJECT := $(OBJECT_DIR)/user-crt0.o -USER_LIBC_OBJECTS := $(OBJECT_DIR)/user-libc.o $(OBJECT_DIR)/user-dirent.o \ - $(OBJECT_DIR)/user-fcntl.o $(OBJECT_DIR)/user-syscall.o NANO_USER_ELF := $(BUILD)/tnhello.elf TYPEPHP_OS_LIBRARY := $(BUILD)/libtypephp-os.a TYPEPHP_OS_LIBRARY_OBJECT_DIR := $(BUILD)/libtypephp-os @@ -24,7 +21,8 @@ PHPX_DIR := $(or $(realpath ../../vendor/swoole/phpx),$(realpath ../../../phpx)) OPENLIBM_SOURCES := $(shell sed -n 's|^\(.*\.c\)$$|thirdparty/openlibm/\1|p' tools/openlibm-files.txt) COMPILER_RT_SOURCES := $(shell awk '$$2 ~ /\.c$$/ { print "thirdparty/compiler-rt/" $$2 }' tools/compiler-rt-builtins-files.sha256) COMPILER_RT_OBJECTS := $(patsubst thirdparty/compiler-rt/lib/builtins/%.c,$(COMPILER_RT_OBJECT_DIR)/%.o,$(COMPILER_RT_SOURCES)) -TYPEPHP_OS_LIBRARY_C_SOURCES := user/runtime/posix.c user/dirent.c user/fcntl.c \ +TYPEPHP_OS_LIBRARY_C_SOURCES := user/runtime/posix.c user/runtime/dirent.c \ + user/runtime/fcntl.c kernel/core/abi/error.c kernel/core/abi/string.c \ kernel/core/abi/libc.c $(OPENLIBM_SOURCES) TYPEPHP_OS_LIBRARY_CXX_SOURCES := user/runtime/host.cc kernel/core/abi/cxx.cpp TYPEPHP_OS_LIBRARY_ASM_SOURCES := user/runtime/crt0.S user/runtime/syscall.S \ @@ -45,7 +43,8 @@ KERNEL64_BOOTFLAGS := -m64 -O2 -ffreestanding -fno-builtin -fno-pic -fno-pie \ USER_CFLAGS := -m64 -O2 -ffreestanding -fno-builtin -fno-pic -fno-pie \ -fno-stack-protector -mno-red-zone -Wall -Wextra -Iuser/include -Ikernel/core/abi USER_HEADERS := $(shell find user/include -type f) -USER_LDFLAGS := -m elf_x86_64 -nostdlib -static -T user/linker.ld +USER_LDFLAGS := -m elf_x86_64 -nostdlib -static --undefined=_start \ + -T user/runtime/linker.ld TYPEPHP_OS_RUNTIME_DEFINES := -DTYPEPHP_NATIVE=1 -DPHP_NANO=1 -DPHPX_NANO=1 \ -D_POSIX_C_SOURCE=200809L -DTYPEPHP_NO_MAIN=1 -DPHPX_NO_EXCEPTION=1 \ -DPHPX_NO_RTTI=1 -DPHP_NANO_NO_LIBC=1 -DHAVE_SYS_UTSNAME_H=1 \ @@ -120,21 +119,6 @@ $(KERNEL64_ENTRY_OBJECT): boot/kernel64-entry.S | $(OBJECT_DIR) $(OBJECT_DIR)/%.o: user/%.c $(USER_HEADERS) kernel/core/abi/typephp_os_syscall.h | $(OBJECT_DIR) gcc $(USER_CFLAGS) -c $< -o $@ -$(USER_CRT0_OBJECT): user/crt0.S kernel/core/abi/typephp_os_syscall.h | $(OBJECT_DIR) - gcc $(USER_CFLAGS) -c $< -o $@ - -$(OBJECT_DIR)/user-libc.o: user/libc.c $(USER_HEADERS) kernel/core/abi/typephp_os_syscall.h | $(OBJECT_DIR) - gcc $(USER_CFLAGS) -c $< -o $@ - -$(OBJECT_DIR)/user-dirent.o: user/dirent.c $(USER_HEADERS) kernel/core/abi/typephp_os_syscall.h | $(OBJECT_DIR) - gcc $(USER_CFLAGS) -c $< -o $@ - -$(OBJECT_DIR)/user-fcntl.o: user/fcntl.c $(USER_HEADERS) kernel/core/abi/typephp_os_syscall.h | $(OBJECT_DIR) - gcc $(USER_CFLAGS) -c $< -o $@ - -$(OBJECT_DIR)/user-syscall.o: user/syscall.S kernel/core/abi/typephp_os_syscall.h | $(OBJECT_DIR) - gcc $(USER_CFLAGS) -c $< -o $@ - $(OPENLIBM_SOURCES): | thirdparty $(COMPILER_RT_SOURCES): | thirdparty @@ -168,11 +152,13 @@ $(TYPEPHP_OS_LIBRARY): $(TYPEPHP_OS_LIBRARY_OBJECTS) | $(BUILD) $(NANO_USER_ELF): $(NANO_USER_SOURCES) | thirdparty $(BUILD) cd ../.. && ./bin/tpc.php --nano examples/typephp-os/user/nano/hello.yml -$(USER_SH_ELF): $(OBJECT_DIR)/sh.o $(USER_CRT0_OBJECT) $(USER_LIBC_OBJECTS) $(COMPILER_RT_LIBRARY) user/linker.ld Makefile | $(BUILD) - ld $(USER_LDFLAGS) --defsym USER_BASE=0x40000000 -o $@ $(filter %.o,$^) $(COMPILER_RT_LIBRARY) +$(USER_SH_ELF): $(OBJECT_DIR)/sh.o $(TYPEPHP_OS_LIBRARY) $(COMPILER_RT_LIBRARY) user/runtime/linker.ld Makefile | $(BUILD) + ld $(USER_LDFLAGS) --defsym USER_BASE=0x40000000 -o $@ \ + $(OBJECT_DIR)/sh.o $(TYPEPHP_OS_LIBRARY) $(COMPILER_RT_LIBRARY) -$(USER_COMMAND_ELFS): $(BUILD)/%.elf: $(OBJECT_DIR)/%.o $(USER_CRT0_OBJECT) $(USER_LIBC_OBJECTS) $(COMPILER_RT_LIBRARY) user/linker.ld Makefile | $(BUILD) - ld $(USER_LDFLAGS) --defsym USER_BASE=0x41000000 -o $@ $(filter %.o,$^) $(COMPILER_RT_LIBRARY) +$(USER_COMMAND_ELFS): $(BUILD)/%.elf: $(OBJECT_DIR)/%.o $(TYPEPHP_OS_LIBRARY) $(COMPILER_RT_LIBRARY) user/runtime/linker.ld Makefile | $(BUILD) + ld $(USER_LDFLAGS) --defsym USER_BASE=0x41000000 -o $@ \ + $(OBJECT_DIR)/$*.o $(TYPEPHP_OS_LIBRARY) $(COMPILER_RT_LIBRARY) $(KERNEL): payload objcopy -O binary $(PAYLOAD_ELF) $(PAYLOAD_BIN) diff --git a/examples/typephp-os/README.md b/examples/typephp-os/README.md index fbe98203..ce20c5b2 100644 --- a/examples/typephp-os/README.md +++ b/examples/typephp-os/README.md @@ -66,7 +66,7 @@ Required host tools are TypePHP's PHP/Composer dependencies, GCC/G++, GNU binutils, GNU make, `dosfstools`, `mtools`, and `qemu-system-x86_64`. Third-party source is not committed. `make` invokes [`tools/fetch-thirdparty.sh`](tools/fetch-thirdparty.sh), which downloads fixed -archives and verifies their SHA-256 checksums according to +archives or explicit source manifests and verifies their SHA-256 checksums according to [`THIRDPARTY.md`](THIRDPARTY.md). From this directory, build and boot with: @@ -84,11 +84,14 @@ The build produces these useful files: - `build/typephp-os.img`: a persistent 32 MiB FAT16 disk image containing the independently built shell and command executables; - `build/sh.elf` and the command ELF files: independent ELF64 Ring-3 programs - linked with the small userspace bootstrap libc rather than a hosted libc. + linked with the shared TypePHP-OS runtime rather than a hosted libc. - `build/tnhello.elf`: a full TypePHP + PHPX + PHP Nano Ring-3 executable generated by tpc from `user/nano/hello.php`. -- `build/libtypephp-os.a`: the reusable userspace platform archive linked by - every tpc-generated TypePHP program with `-ltypephp-os`. +- `build/libtypephp-os.a`: the sole reusable userspace platform archive linked + by both C programs and every tpc-generated TypePHP program. Static archive + member selection keeps small C commands from pulling in the PHP Nano host. +- `build/libcompiler-rt-builtins.a`: selected upstream LLVM 128-bit integer + compiler helpers linked into freestanding kernel and userspace programs. Run the automated serial-output smoke test with: @@ -96,6 +99,10 @@ Run the automated serial-output smoke test with: make test ``` +To configure the mlibc header porting probe and build the restricted hosted +Toybox applet set, install Meson 1.3 or newer and run `make thirdparty-smoke`. +This is an integration audit, not part of the boot image. + The Makefile compiles the startup sources under `boot/` directly. The 32-bit Multiboot bootstrap cannot participate in the 64-bit payload link; the 64-bit entry object is injected into tpc's final link through the generic `objects` @@ -142,6 +149,8 @@ The QEMU smoke test currently verifies: - PHPX `Variant`, `Str`, `Array`, custom classes, and a typed `std::vector`; - OpenLibm implementations of the ordinary double-precision math ABI; +- LLVM compiler-rt signed and unsigned 128-bit integer helpers, exercised by + a real Ring-3 command that requires the helper symbols at link time; - RTC-derived UTC time exposed to userspace through `time()`; - Linux-compatible `uname(2)` shared by the `uname` command and PHP's `php_uname()` implementation; diff --git a/examples/typephp-os/ROADMAP.md b/examples/typephp-os/ROADMAP.md index 0cb45cbd..c6431049 100644 --- a/examples/typephp-os/ROADMAP.md +++ b/examples/typephp-os/ROADMAP.md @@ -28,6 +28,10 @@ behavior. `wait`, pipes, threads, scheduling signals, and job control will not be implemented. The shell may only launch one synchronous foreground ELF through the private TypePHP-OS service. +- `ioctl` and `termios` are outside the current scope. Third-party programs + must not drive implementation of these low-priority terminal interfaces; + they can be adopted only after their selected build no longer requires the + interfaces or another generally useful subsystem justifies them. ## Milestones @@ -81,9 +85,15 @@ behavior. a Linux-style initial stack. A full PHP Nano/PHPX executable is now produced by the ordinary tpc pipeline and runs in Ring 3 with working TypePHP `argc/argv`; standard `php_uname()` reaches the userspace `uname()` ABI and - reports TypePHP-OS. Shared TypePHP userspace support is compiled once into - `libtypephp-os.a`, allowing multiple tpc projects to link the same runtime - platform archive. Unavailable ABI functions remain explicit panic stubs. + reports TypePHP-OS. All shared userspace startup, syscall, POSIX/libc, + C++ ABI, and math support is compiled once into `libtypephp-os.a`; both the + C commands and multiple tpc projects link the same platform archive. + Unavailable ABI functions remain explicit panic stubs. + Selected upstream LLVM compiler-rt builtins now provide 128-bit integer + helper symbols. mlibc 7.0 and a five-applet Toybox build are pinned as + porting probes; neither replaces the working userspace runtime yet because + mlibc exceeds the C++17 baseline and Toybox's common runtime still imports + `ioctl`. Single-task identity calls report PID/TID 1 for the resident shell, 2 for its synchronous foreground command, and root UID/GID. Linux-compatible wall/monotonic clock structures are exposed at the current one-second RTC diff --git a/examples/typephp-os/THIRDPARTY.md b/examples/typephp-os/THIRDPARTY.md index 60f526fd..d7b73291 100644 --- a/examples/typephp-os/THIRDPARTY.md +++ b/examples/typephp-os/THIRDPARTY.md @@ -6,8 +6,9 @@ third-party source dependency must instead be declared by a script under - an immutable upstream version or commit; - the canonical download URL; -- a SHA-256 checksum for the downloaded archive; -- an explicit list of files copied into the build tree; +- a SHA-256 checksum for every downloaded archive or individual source file; +- an explicit list of files copied into the build tree, or an explicitly + documented complete release tree for source-port candidates; - the upstream license file. Run the common fetch entry point before compiling: @@ -16,10 +17,20 @@ Run the common fetch entry point before compiling: ./tools/fetch-thirdparty.sh ``` -`make` invokes this command automatically. A matching local version marker -makes repeated invocations offline and effectively free. Adding another -library must extend this same entry point or call a dedicated, equivalently -strict fetch script from it; third-party source must not be committed directly. +`make` invokes this command automatically. Each dependency has a dedicated +`fetch-*.sh` script, while `fetch-thirdparty.sh` is only the common entry point. +A matching local version marker makes repeated invocations offline and +effectively free. Third-party source must not be committed directly. + +Run the upstream integration probes separately: + +```shell +make thirdparty-smoke +``` + +This target requires Meson 1.3 or newer for mlibc. It configures a headers-only +mlibc sysroot and builds a deliberately small hosted Toybox. Neither candidate +is installed into the TypePHP-OS disk image by this probe. ## OpenLibm @@ -32,3 +43,57 @@ strict fetch script from it; third-party source must not be committed directly. Only the architecture/compatibility headers and C sources required by TypePHP-OS's current x86_64 double-precision math ABI are installed. The selection keeps unused implementations out of the freestanding payload. + +## LLVM compiler-rt builtins + +- Upstream: +- Version: `23.1.1` (`llvmorg-23.1.1`) +- Selected files and per-file SHA-256 values: + [`tools/compiler-rt-builtins-files.sha256`](tools/compiler-rt-builtins-files.sha256) + +The source selection provides the x86-64 128-bit integer shift, multiply, +divide, and remainder helpers. It is compiled into +`build/libcompiler-rt-builtins.a` and linked after every kernel, bootstrap C, +and TypePHP Nano userspace object set. The `builtins.elf` smoke command forces +signed and unsigned 128-bit division so the archive is tested as a real linker +dependency instead of merely being compiled. + +## mlibc + +- Upstream: +- Version: `v7.0.0` +- Commit: `7c2a178142625cc9852e59a1a090468c61a62d3b` +- Archive SHA-256: `56d62366a6a15bc3fd8741bebcb311ca543553f7b734b1e780aeae33691bd621` + +The complete release tree is retained as a libc porting reference. The smoke +target configures mlibc's official Meson build in headers-only/demo-sysdeps +mode and compiles a TypePHP-OS API header probe. mlibc 7.0's implementation +build requires C++23 and GCC 13 or newer, while TypePHP-OS keeps C++17 as its +runtime baseline. Its implementation therefore does not replace the current +small libc yet; doing so also requires a real TypePHP-OS sysdeps port. + +The mlibc 6 series does not have one uniform compiler baseline. Releases 6.0 +and 6.1 request C++20 and do not contain the GCC 13 gate added later, so they +are more practical with an older compiler. Releases 6.2 and 6.3 already +request C++23. None of them satisfies TypePHP-OS's C++17 baseline unchanged; +using 6.0/6.1 would therefore be a separate port/toolchain choice rather than +a transparent downgrade of the pinned reference. + +## Toybox + +- Upstream: +- Version: `0.8.14` +- Commit: `b7ec52ac35e075caffca5d330995d44e8dbfc8c3` +- Archive SHA-256: `827e4cdfd69f5da973e00e2a59b30b3c9857fb7fae74c362fd0b4f96be7929b0` +- Selected applets: [`tools/toybox-miniconfig`](tools/toybox-miniconfig) + +The hosted probe enables only `cat`, `date`, `echo`, `pwd`, and `uname`; it +does not enable Toybox's shell or process-management commands. The undefined +host ABI is written to `build/thirdparty-smoke/toybox-undefined-symbols.txt`. +`ioctl` is a system-call API used by Toybox's shared C support library, not a +Toybox command. None of those five applet source files calls it directly, but +the common terminal/daemon helpers still leave an `ioctl` reference in the +hosted binary. Toybox is therefore intentionally not linked into TypePHP-OS. +The project will not implement `ioctl` or `termios` merely to satisfy it; +adoption requires pruning the unused common helpers or completing a different +compatible libc/runtime path. diff --git a/examples/typephp-os/kernel/core/abi/error.c b/examples/typephp-os/kernel/core/abi/error.c new file mode 100644 index 00000000..7afc1f6e --- /dev/null +++ b/examples/typephp-os/kernel/core/abi/error.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +static int typephp_os_errno; +static char unknown_error[] = "Unknown error"; + +int *__errno_location(void) +{ + return &typephp_os_errno; +} + +char *strerror(int error) +{ + switch (error) { + case ENOENT: return "No such file or directory"; + case EIO: return "Input/output error"; + case ENOEXEC: return "Exec format error"; + case EBADF: return "Bad file descriptor"; + case ENOMEM: return "Cannot allocate memory"; + case EACCES: return "Permission denied"; + case EFAULT: return "Bad address"; + case EEXIST: return "File exists"; + case ENOTDIR: return "Not a directory"; + case EISDIR: return "Is a directory"; + case EINVAL: return "Invalid argument"; + case EMFILE: return "Too many open files"; + case EROFS: return "Read-only file system"; + case ERANGE: return "Numerical result out of range"; + case ENAMETOOLONG: return "File name too long"; + case ENOSYS: return "Function not implemented"; + case ENOTEMPTY: return "Directory not empty"; + default: return unknown_error; + } +} + +void perror(const char *prefix) +{ + if (prefix != NULL && prefix[0] != '\0') { + (void) write(STDERR_FILENO, prefix, strlen(prefix)); + (void) write(STDERR_FILENO, ": ", 2); + } + (void) write(STDERR_FILENO, strerror(errno), strlen(strerror(errno))); + (void) write(STDERR_FILENO, "\n", 1); +} diff --git a/examples/typephp-os/kernel/core/abi/libc.c b/examples/typephp-os/kernel/core/abi/libc.c index 8e35bf15..d8e61842 100644 --- a/examples/typephp-os/kernel/core/abi/libc.c +++ b/examples/typephp-os/kernel/core/abi/libc.c @@ -14,6 +14,7 @@ #include "typephp_os_abi.h" +#include #include #include #include @@ -35,7 +36,6 @@ enum { TYPEPHP_OS_ALIGNED_BLOCK_CAPACITY = 64 }; static uintptr_t arena_cursor; static uintptr_t arena_end; -static int typephp_os_errno; static typephp_os_aligned_block aligned_blocks[TYPEPHP_OS_ALIGNED_BLOCK_CAPACITY]; /* TypePHP OS has no hosted stdio object. These opaque values only satisfy @@ -63,11 +63,6 @@ __attribute__((weak)) unsigned int getgid(void) return 0; } -int *__errno_location(void) -{ - return &typephp_os_errno; -} - void __assert_fail( const char *assertion, const char *file, @@ -195,15 +190,6 @@ int memcmp(const void *left, const void *right, size_t size) return 0; } -size_t strlen(const char *string) -{ - const char *end = string; - while (*end != '\0') { - ++end; - } - return (size_t) (end - string); -} - char *strdup(const char *string) { const size_t size = strlen(string) + 1; @@ -417,10 +403,10 @@ char *setlocale(int category, const char *locale) return 0; } -char *getcwd(char *buffer, size_t size) +__attribute__((weak)) char *getcwd(char *buffer, size_t size) { if (buffer == 0 || size < 2) { - typephp_os_errno = 34; /* ERANGE */ + errno = ERANGE; return 0; } buffer[0] = '/'; diff --git a/examples/typephp-os/kernel/core/abi/string.c b/examples/typephp-os/kernel/core/abi/string.c new file mode 100644 index 00000000..b5774b3b --- /dev/null +++ b/examples/typephp-os/kernel/core/abi/string.c @@ -0,0 +1,10 @@ +#include + +size_t strlen(const char *string) +{ + const char *end = string; + while (*end != '\0') { + ++end; + } + return (size_t) (end - string); +} diff --git a/examples/typephp-os/kernel/core/abi/unimplemented.S b/examples/typephp-os/kernel/core/abi/unimplemented.S index 46a59206..bea46a3e 100644 --- a/examples/typephp-os/kernel/core/abi/unimplemented.S +++ b/examples/typephp-os/kernel/core/abi/unimplemented.S @@ -43,7 +43,6 @@ TYPEPHP_OS_UNIMPLEMENTED utime TYPEPHP_OS_UNIMPLEMENTED mblen TYPEPHP_OS_UNIMPLEMENTED localtime_r -TYPEPHP_OS_UNIMPLEMENTED strerror TYPEPHP_OS_UNIMPLEMENTED strftime TYPEPHP_OS_UNIMPLEMENTED _Unwind_Resume diff --git a/examples/typephp-os/tools/fetch-compiler-rt-builtins.sh b/examples/typephp-os/tools/fetch-compiler-rt-builtins.sh index 9ff5817e..9bd5a0d6 100755 --- a/examples/typephp-os/tools/fetch-compiler-rt-builtins.sh +++ b/examples/typephp-os/tools/fetch-compiler-rt-builtins.sh @@ -23,7 +23,8 @@ while read -r expected relative; do [ -n "${relative}" ] || continue destination="${staging}/${relative}" mkdir -p "$(dirname -- "${destination}")" - curl -L --fail --retry 3 -o "${destination}" "${base_url}/${relative}" + curl -L --fail --retry 3 --silent --show-error \ + -o "${destination}" "${base_url}/${relative}" printf '%s %s\n' "${expected}" "${destination}" | sha256sum -c - done < "${manifest}" diff --git a/examples/typephp-os/tools/fetch-mlibc.sh b/examples/typephp-os/tools/fetch-mlibc.sh index 6bb9d7d7..81a6dfd1 100755 --- a/examples/typephp-os/tools/fetch-mlibc.sh +++ b/examples/typephp-os/tools/fetch-mlibc.sh @@ -19,7 +19,7 @@ archive="${temporary}/mlibc.tar.gz" staging="${temporary}/installed" echo "Downloading mlibc v${version} (${commit})" -curl -L --fail --retry 3 -o "${archive}" \ +curl -L --fail --retry 3 --silent --show-error -o "${archive}" \ "https://github.com/managarm/mlibc/archive/refs/tags/v${version}.tar.gz" printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c - mkdir -p "${staging}" diff --git a/examples/typephp-os/tools/fetch-openlibm.sh b/examples/typephp-os/tools/fetch-openlibm.sh index 1b8b714d..c834d52b 100755 --- a/examples/typephp-os/tools/fetch-openlibm.sh +++ b/examples/typephp-os/tools/fetch-openlibm.sh @@ -20,7 +20,7 @@ source_dir="${temporary}/openlibm-${version}" staging="${temporary}/installed" echo "Downloading OpenLibm v${version}" -curl -L --fail --retry 3 \ +curl -L --fail --retry 3 --silent --show-error \ -o "${archive}" \ "https://github.com/JuliaMath/openlibm/archive/refs/tags/v${version}.tar.gz" echo "${archive_sha256} ${archive}" | sha256sum -c - diff --git a/examples/typephp-os/tools/fetch-toybox.sh b/examples/typephp-os/tools/fetch-toybox.sh index 92c8a817..45e7d21d 100755 --- a/examples/typephp-os/tools/fetch-toybox.sh +++ b/examples/typephp-os/tools/fetch-toybox.sh @@ -19,7 +19,7 @@ archive="${temporary}/toybox.tar.gz" staging="${temporary}/installed" echo "Downloading Toybox ${version} (${commit})" -curl -L --fail --retry 3 -o "${archive}" \ +curl -L --fail --retry 3 --silent --show-error -o "${archive}" \ "https://landley.net/toybox/downloads/toybox-${version}.tar.gz" printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c - mkdir -p "${staging}" diff --git a/examples/typephp-os/tools/test-thirdparty.sh b/examples/typephp-os/tools/test-thirdparty.sh index e2adcbb6..f237bebb 100755 --- a/examples/typephp-os/tools/test-thirdparty.sh +++ b/examples/typephp-os/tools/test-thirdparty.sh @@ -36,7 +36,7 @@ rm -rf "${mlibc_build}" "${mlibc_sysroot}" -Dlinux_option=disabled \ -Dglibc_option=disabled \ -Dbsd_option=disabled -DESTDIR="${mlibc_sysroot}" "${meson_command}" install -C "${mlibc_build}" +DESTDIR="${mlibc_sysroot}" "${meson_command}" install --quiet -C "${mlibc_build}" compiler_headers=$(gcc -print-file-name=include) gcc -std=c11 -ffreestanding -fsyntax-only -nostdinc \ -isystem "${mlibc_sysroot}/usr/include" \ diff --git a/examples/typephp-os/tools/toybox-miniconfig b/examples/typephp-os/tools/toybox-miniconfig index a84cec99..ad3d4232 100644 --- a/examples/typephp-os/tools/toybox-miniconfig +++ b/examples/typephp-os/tools/toybox-miniconfig @@ -1,6 +1,5 @@ CONFIG_TOYBOX=y CONFIG_TOYBOX_HELP=y -# CONFIG_TOYBOX_FORK is not set CONFIG_CAT=y CONFIG_DATE=y CONFIG_ECHO=y diff --git a/examples/typephp-os/user/README.md b/examples/typephp-os/user/README.md index 2e159864..31327aa2 100644 --- a/examples/typephp-os/user/README.md +++ b/examples/typephp-os/user/README.md @@ -25,12 +25,15 @@ The following rules are normative for this directory: it is not a public POSIX process-creation API. 6. New kernel services should first consider the remaining interfaces needed by a static libc and PHP Nano: memory mapping, files and directories, - clocks, TLS, terminal I/O, and single-task lifecycle. Socket support also - remains out of scope. -7. Freestanding test programs remain libc-free only as a bootstrap constraint; - it is not the final userspace programming model. + clocks, TLS, and single-task lifecycle. Socket support remains out of + scope. `ioctl` and `termios` are also intentionally deferred; basic console + `read`/`write` is the supported terminal contract. +7. Freestanding test programs remain hosted-libc-free only as a bootstrap + constraint; it is not the final userspace programming model. C and TypePHP + programs must link the same `libtypephp-os.a` platform runtime instead of + maintaining private startup or libc copies. -The current bootstrap libc provides `syscall`, `read`, `write`, `openat`, +The current shared userspace runtime provides `syscall`, `read`, `write`, `openat`, `open`, `close`, `lseek`, `getcwd`, `chdir`, `mkdir`, `rmdir`, `unlink`, `rename`, `stat`, `lstat`, `fstat`, `access`, `fsync`, `fdatasync`, `truncate`, `ftruncate`, `time`, `gettimeofday`, `clock_gettime`, `clock_getres`, `uname`, @@ -48,8 +51,8 @@ project-specific variants of standard functions. commands, it composes the complete PHP Nano and PHPX source manifests into an independent ELF64 executable. Shared startup, host, POSIX, libc/C++ ABI, and math support live under `runtime/` and are built once as `libtypephp-os.a`. -Each TypePHP userspace project links that archive with `-ltypephp-os` instead -of recompiling the platform layer. The shared crt0 accepts the same Linux-style +Both the C commands and every TypePHP userspace project link that archive with +`-ltypephp-os` instead of recompiling the platform layer. The shared crt0 accepts the same Linux-style initial stack, so the TypePHP entry receives the real command-line `argc` and `argv`. The standard extension's `php_uname()` remains implemented in the upstream `info.c`; the local `uname()` ABI reports `TypePHP-OS`. APIs whose diff --git a/examples/typephp-os/user/crt0.S b/examples/typephp-os/user/crt0.S deleted file mode 100644 index 393476bf..00000000 --- a/examples/typephp-os/user/crt0.S +++ /dev/null @@ -1,23 +0,0 @@ -.section .text -.code64 - -.global _start -.type _start, @function -.extern main -.extern _exit -_start: - # Linux-compatible process entry stack: - # argc, argv[], NULL, envp[], NULL, auxv[] - xor %ebp, %ebp - mov (%rsp), %rdi - lea 8(%rsp), %rsi - lea 16(%rsp,%rdi,8), %rdx - and $-16, %rsp - call main - - mov %eax, %edi - call _exit - ud2 -.size _start, . - _start - -.section .note.GNU-stack, "", @progbits diff --git a/examples/typephp-os/user/include/errno.h b/examples/typephp-os/user/include/errno.h index 93d647b0..7384d90a 100644 --- a/examples/typephp-os/user/include/errno.h +++ b/examples/typephp-os/user/include/errno.h @@ -1,7 +1,8 @@ #ifndef TYPEPHP_OS_USER_ERRNO_H #define TYPEPHP_OS_USER_ERRNO_H -extern int errno; +int *__errno_location(void); +#define errno (*__errno_location()) #define ENOENT 2 #define EIO 5 diff --git a/examples/typephp-os/user/libc.c b/examples/typephp-os/user/libc.c deleted file mode 100644 index 199f9e59..00000000 --- a/examples/typephp-os/user/libc.c +++ /dev/null @@ -1,295 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int errno; - -static char unknown_error[] = "Unknown error"; - -size_t strlen(const char *string) -{ - size_t length = 0; - while (string[length] != '\0') { - ++length; - } - return length; -} - -char *strerror(int error) -{ - switch (error) { - case ENOENT: return "No such file or directory"; - case EIO: return "Input/output error"; - case ENOEXEC: return "Exec format error"; - case EBADF: return "Bad file descriptor"; - case ENOMEM: return "Cannot allocate memory"; - case EACCES: return "Permission denied"; - case EFAULT: return "Bad address"; - case EEXIST: return "File exists"; - case ENOTDIR: return "Not a directory"; - case EISDIR: return "Is a directory"; - case EINVAL: return "Invalid argument"; - case EMFILE: return "Too many open files"; - case EROFS: return "Read-only file system"; - case ERANGE: return "Numerical result out of range"; - case ENAMETOOLONG: return "File name too long"; - case ENOSYS: return "Function not implemented"; - case ENOTEMPTY: return "Directory not empty"; - default: return unknown_error; - } -} - -void perror(const char *prefix) -{ - if (prefix != NULL && prefix[0] != '\0') { - (void) write(STDERR_FILENO, prefix, strlen(prefix)); - (void) write(STDERR_FILENO, ": ", 2); - } - (void) write(STDERR_FILENO, strerror(errno), strlen(strerror(errno))); - (void) write(STDERR_FILENO, "\n", 1); -} - -ssize_t read(int fd, void *buffer, size_t count) -{ - return (ssize_t) syscall(SYS_read, fd, buffer, count); -} - -ssize_t write(int fd, const void *buffer, size_t count) -{ - return (ssize_t) syscall(SYS_write, fd, buffer, count); -} - -int close(int fd) -{ - return (int) syscall(SYS_close, fd); -} - -off_t lseek(int fd, off_t offset, int whence) -{ - return (off_t) syscall(SYS_lseek, fd, offset, whence); -} - -int openat(int directory_fd, const char *path, int flags, ...) -{ - int mode = 0; - if ((flags & O_CREAT) != 0) { - va_list arguments; - va_start(arguments, flags); - mode = va_arg(arguments, int); - va_end(arguments); - } - return (int) syscall(SYS_openat, directory_fd, path, flags, mode); -} - -int open(const char *path, int flags, ...) -{ - int mode = 0; - if ((flags & O_CREAT) != 0) { - va_list arguments; - va_start(arguments, flags); - mode = va_arg(arguments, int); - va_end(arguments); - } - return openat(AT_FDCWD, path, flags, mode); -} - -int chdir(const char *path) -{ - return (int) syscall(SYS_chdir, path); -} - -int mkdir(const char *path, mode_t mode) -{ - return (int) syscall(SYS_mkdir, path, mode); -} - -int unlink(const char *path) -{ - return (int) syscall(SYS_unlink, path); -} - -int rmdir(const char *path) -{ - return (int) syscall(SYS_rmdir, path); -} - -int rename(const char *old_path, const char *new_path) -{ - return (int) syscall(SYS_typephp_rename, old_path, new_path); -} - -int access(const char *path, int mode) -{ - return (int) syscall(SYS_access, path, mode); -} - -int stat(const char *path, struct stat *value) -{ - return (int) syscall(SYS_stat, path, value); -} - -int lstat(const char *path, struct stat *value) -{ - return (int) syscall(SYS_lstat, path, value); -} - -int fstat(int fd, struct stat *value) -{ - return (int) syscall(SYS_fstat, fd, value); -} - -int fstatat(int directory_fd, const char *path, struct stat *value, int flags) -{ - return (int) syscall(SYS_newfstatat, directory_fd, path, value, flags); -} - -int faccessat(int directory_fd, const char *path, int mode, int flags) -{ - return (int) syscall(SYS_faccessat, directory_fd, path, mode, flags); -} - -int fsync(int fd) -{ - return (int) syscall(SYS_fsync, fd); -} - -int fdatasync(int fd) -{ - return (int) syscall(SYS_fdatasync, fd); -} - -int truncate(const char *path, off_t length) -{ - return (int) syscall(SYS_truncate, path, length); -} - -int ftruncate(int fd, off_t length) -{ - return (int) syscall(SYS_ftruncate, fd, length); -} - -int brk(void *address) -{ - long result = syscall(SYS_brk, address); - if ((void *) result != address) { - errno = ENOMEM; - return -1; - } - return 0; -} - -void *sbrk(intptr_t increment) -{ - intptr_t current = syscall(SYS_brk, 0); - intptr_t requested; - if ((increment > 0 && current > INTPTR_MAX - increment) - || (increment < 0 && current < INTPTR_MIN - increment)) { - errno = ENOMEM; - return (void *) -1; - } - requested = current + increment; - if (increment != 0 && syscall(SYS_brk, requested) != requested) { - errno = ENOMEM; - return (void *) -1; - } - return (void *) current; -} - -void *mmap(void *address, size_t length, int protection, int flags, int fd, long offset) -{ - return (void *) syscall(SYS_mmap, - address, length, protection, flags, fd, offset); -} - -int mprotect(void *address, size_t length, int protection) -{ - return (int) syscall(SYS_mprotect, address, length, protection); -} - -int munmap(void *address, size_t length) -{ - return (int) syscall(SYS_munmap, address, length); -} - -char *getcwd(char *buffer, size_t size) -{ - return syscall(SYS_getcwd, buffer, size) < 0 ? NULL : buffer; -} - -time_t time(time_t *result) -{ - return (time_t) syscall(SYS_time, result); -} - -int gettimeofday(struct timeval *value, void *timezone) -{ - return (int) syscall(SYS_gettimeofday, value, timezone); -} - -int clock_gettime(clockid_t clock_id, struct timespec *value) -{ - return (int) syscall(SYS_clock_gettime, clock_id, value); -} - -int clock_getres(clockid_t clock_id, struct timespec *value) -{ - return (int) syscall(SYS_clock_getres, clock_id, value); -} - -pid_t getpid(void) -{ - return (pid_t) syscall(SYS_getpid); -} - -pid_t getppid(void) -{ - return (pid_t) syscall(SYS_getppid); -} - -pid_t gettid(void) -{ - return (pid_t) syscall(SYS_gettid); -} - -uid_t getuid(void) -{ - return (uid_t) syscall(SYS_getuid); -} - -uid_t geteuid(void) -{ - return (uid_t) syscall(SYS_geteuid); -} - -gid_t getgid(void) -{ - return (gid_t) syscall(SYS_getgid); -} - -gid_t getegid(void) -{ - return (gid_t) syscall(SYS_getegid); -} - -int uname(struct utsname *value) -{ - return (int) syscall(SYS_uname, value); -} - -void _exit(int status) -{ - (void) syscall(SYS_exit_group, status); - for (;;) { - __asm__ volatile("pause"); - } -} diff --git a/examples/typephp-os/user/linker.ld b/examples/typephp-os/user/linker.ld deleted file mode 100644 index d462dd1b..00000000 --- a/examples/typephp-os/user/linker.ld +++ /dev/null @@ -1,24 +0,0 @@ -OUTPUT_FORMAT(elf64-x86-64) -OUTPUT_ARCH(i386:x86-64) -ENTRY(_start) - -PHDRS -{ - text PT_LOAD FILEHDR PHDRS FLAGS(5); - data PT_LOAD FLAGS(6); - stack PT_GNU_STACK FLAGS(6); -} - -SECTIONS -{ - . = USER_BASE + SIZEOF_HEADERS; - - .text : { *(.text .text.*) } :text - .rodata : { *(.rodata .rodata.*) } :text - - . = ALIGN(4K); - .data : { *(.data .data.*) } :data - .bss (NOLOAD) : { *(COMMON) *(.bss .bss.*) } :data - - /DISCARD/ : { *(.comment) *(.eh_frame*) *(.note*) } -} diff --git a/examples/typephp-os/user/runtime/README.md b/examples/typephp-os/user/runtime/README.md index 947e0c7c..a6f98a1d 100644 --- a/examples/typephp-os/user/runtime/README.md +++ b/examples/typephp-os/user/runtime/README.md @@ -1,8 +1,8 @@ # TypePHP userspace runtime -This directory contains the TypePHP-OS platform layer shared by all -tpc-generated userspace programs. The top-level Makefile compiles it, the -kernel ABI shims, and OpenLibm into: +This directory contains the TypePHP-OS platform layer shared by every Ring-3 +program, whether its application entry point is compiled from C or TypePHP. +The top-level Makefile compiles it, the kernel ABI shims, and OpenLibm into: ```text build/libtypephp-os.a @@ -22,18 +22,26 @@ link-paths: - ../../build link-libs: - typephp-os + - compiler-rt-builtins ``` -The archive owns the userspace `_start`, PHP Nano host entry, syscall bridge, -POSIX/libc and C++ ABI shims, buffered `dirent` implementation, and math implementation. Application projects -must not compile private copies of these files. This allows any number of -projects under `user/` to reuse one platform archive and keeps their generated -object directories independent. - -No compiler runtime library is currently required on x86-64; both the kernel -and Nano userspace links are audited for undefined symbols. Add selected -compiler-rt builtins only if generated code introduces an explicit helper -symbol in the future. +The archive owns the sole userspace `_start`, PHP Nano host entry, syscall +bridge, POSIX/libc and C++ ABI shims, buffered `dirent` implementation, and +math implementation. C programs link the same archive directly; the linker +extracts only the members they actually reference, so they do not pull in the +PHP Nano host or its larger compatibility layer. Application projects must not +compile private copies of these files. This allows any number of projects +under `user/` to reuse one platform archive and keeps their generated object +directories independent. + +Selected LLVM compiler-rt 128-bit integer helpers are built separately as +`build/libcompiler-rt-builtins.a`. Consumers link it after `typephp-os`. The +Ring-3 `builtins` command deliberately generates signed and unsigned +division/remainder helper calls and verifies the results at runtime. + +mlibc and Toybox are currently integration probes rather than runtime +dependencies. See `THIRDPARTY.md` for the tested boundary and the generated +Toybox ABI report. The archive and each consumer must use the same freestanding ABI flags and PHP Nano/PHPX feature definitions. Those shared settings are maintained in the diff --git a/examples/typephp-os/user/dirent.c b/examples/typephp-os/user/runtime/dirent.c similarity index 100% rename from examples/typephp-os/user/dirent.c rename to examples/typephp-os/user/runtime/dirent.c diff --git a/examples/typephp-os/user/fcntl.c b/examples/typephp-os/user/runtime/fcntl.c similarity index 100% rename from examples/typephp-os/user/fcntl.c rename to examples/typephp-os/user/runtime/fcntl.c diff --git a/examples/typephp-os/user/runtime/posix.c b/examples/typephp-os/user/runtime/posix.c index d69e9195..dc1236fa 100644 --- a/examples/typephp-os/user/runtime/posix.c +++ b/examples/typephp-os/user/runtime/posix.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -38,12 +39,26 @@ off_t lseek(int fd, off_t offset, int whence) int openat(int directory_fd, const char *path, int flags, ...) { - return (int) syscall(TYPEPHP_SYS_OPENAT, directory_fd, path, flags, 0); + int mode = 0; + if ((flags & O_CREAT) != 0) { + va_list arguments; + va_start(arguments, flags); + mode = va_arg(arguments, int); + va_end(arguments); + } + return (int) syscall(TYPEPHP_SYS_OPENAT, directory_fd, path, flags, mode); } int open(const char *path, int flags, ...) { - return openat(AT_FDCWD, path, flags, 0); + int mode = 0; + if ((flags & O_CREAT) != 0) { + va_list arguments; + va_start(arguments, flags); + mode = va_arg(arguments, int); + va_end(arguments); + } + return openat(AT_FDCWD, path, flags, mode); } int chdir(const char *path) @@ -66,6 +81,11 @@ int rmdir(const char *path) return (int) syscall(TYPEPHP_SYS_RMDIR, path); } +char *getcwd(char *buffer, size_t size) +{ + return syscall(TYPEPHP_SYS_GETCWD, buffer, size) < 0 ? NULL : buffer; +} + int brk(void *address) { return (void *) syscall(TYPEPHP_SYS_BRK, address) == address ? 0 : -1; @@ -237,3 +257,11 @@ gid_t getegid(void) { return (gid_t) syscall(TYPEPHP_SYS_GETEGID); } + +void _exit(int status) +{ + (void) syscall(TYPEPHP_SYS_EXIT_GROUP, status); + for (;;) { + __asm__ volatile("pause"); + } +} diff --git a/examples/typephp-os/user/syscall.S b/examples/typephp-os/user/syscall.S deleted file mode 100644 index d08b8e50..00000000 --- a/examples/typephp-os/user/syscall.S +++ /dev/null @@ -1,28 +0,0 @@ -.section .text -.code64 - -.global syscall -.type syscall, @function -syscall: - # Translate the System V function-call ABI used by glibc's syscall(2) - # wrapper into the Linux x86-64 syscall register ABI. The temporary - # kernel entry is int 0x80; replacing it with `syscall` will not change - # the C-visible contract. - mov %rdi, %rax - mov %rsi, %rdi - mov %rdx, %rsi - mov %rcx, %rdx - mov %r8, %r10 - mov %r9, %r8 - mov 8(%rsp), %r9 - int $0x80 - cmp $-4095, %rax - jb 1f - neg %eax - mov %eax, errno(%rip) - mov $-1, %rax -1: - ret -.size syscall, . - syscall - -.section .note.GNU-stack, "", @progbits