feat(kernel): implement errno location and error handling

- Replace global errno variable with __errno_location function
- Add complete strerror implementation for standard error codes
- Implement perror function for error message output
- Move strlen implementation to separate string.c file
- Update getcwd to use new errno macro instead of direct variable
- Mark getcwd as weak symbol to allow overriding
- Remove unimplemented strerror stub from assembly file
- Add 128-bit integer helpers from compiler-rt builtins
- Update third-party dependencies with detailed documentation
- Improve curl download scripts with silent
master
韩天峰 4 weeks ago
parent 78d6d2b67b
commit ce13f7d2c7
  1. 34
      examples/typephp-os/Makefile
  2. 17
      examples/typephp-os/README.md
  3. 16
      examples/typephp-os/ROADMAP.md
  4. 77
      examples/typephp-os/THIRDPARTY.md
  5. 47
      examples/typephp-os/kernel/core/abi/error.c
  6. 20
      examples/typephp-os/kernel/core/abi/libc.c
  7. 10
      examples/typephp-os/kernel/core/abi/string.c
  8. 1
      examples/typephp-os/kernel/core/abi/unimplemented.S
  9. 3
      examples/typephp-os/tools/fetch-compiler-rt-builtins.sh
  10. 2
      examples/typephp-os/tools/fetch-mlibc.sh
  11. 2
      examples/typephp-os/tools/fetch-openlibm.sh
  12. 2
      examples/typephp-os/tools/fetch-toybox.sh
  13. 2
      examples/typephp-os/tools/test-thirdparty.sh
  14. 1
      examples/typephp-os/tools/toybox-miniconfig
  15. 17
      examples/typephp-os/user/README.md
  16. 23
      examples/typephp-os/user/crt0.S
  17. 3
      examples/typephp-os/user/include/errno.h
  18. 295
      examples/typephp-os/user/libc.c
  19. 24
      examples/typephp-os/user/linker.ld
  20. 34
      examples/typephp-os/user/runtime/README.md
  21. 0
      examples/typephp-os/user/runtime/dirent.c
  22. 0
      examples/typephp-os/user/runtime/fcntl.c
  23. 32
      examples/typephp-os/user/runtime/posix.c
  24. 28
      examples/typephp-os/user/syscall.S

@ -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_PROGRAMS := sh $(USER_COMMANDS)
USER_SH_ELF := $(BUILD)/sh.elf USER_SH_ELF := $(BUILD)/sh.elf
USER_COMMAND_ELFS := $(addprefix $(BUILD)/,$(addsuffix .elf,$(USER_COMMANDS))) 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 NANO_USER_ELF := $(BUILD)/tnhello.elf
TYPEPHP_OS_LIBRARY := $(BUILD)/libtypephp-os.a TYPEPHP_OS_LIBRARY := $(BUILD)/libtypephp-os.a
TYPEPHP_OS_LIBRARY_OBJECT_DIR := $(BUILD)/libtypephp-os 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) 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_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)) 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) kernel/core/abi/libc.c $(OPENLIBM_SOURCES)
TYPEPHP_OS_LIBRARY_CXX_SOURCES := user/runtime/host.cc kernel/core/abi/cxx.cpp 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 \ 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 \ USER_CFLAGS := -m64 -O2 -ffreestanding -fno-builtin -fno-pic -fno-pie \
-fno-stack-protector -mno-red-zone -Wall -Wextra -Iuser/include -Ikernel/core/abi -fno-stack-protector -mno-red-zone -Wall -Wextra -Iuser/include -Ikernel/core/abi
USER_HEADERS := $(shell find user/include -type f) 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 \ 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 \ -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 \ -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) $(OBJECT_DIR)/%.o: user/%.c $(USER_HEADERS) kernel/core/abi/typephp_os_syscall.h | $(OBJECT_DIR)
gcc $(USER_CFLAGS) -c $< -o $@ 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 $(OPENLIBM_SOURCES): | thirdparty
$(COMPILER_RT_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) $(NANO_USER_ELF): $(NANO_USER_SOURCES) | thirdparty $(BUILD)
cd ../.. && ./bin/tpc.php --nano examples/typephp-os/user/nano/hello.yml 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) $(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 $@ $(filter %.o,$^) $(COMPILER_RT_LIBRARY) 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) $(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 $@ $(filter %.o,$^) $(COMPILER_RT_LIBRARY) ld $(USER_LDFLAGS) --defsym USER_BASE=0x41000000 -o $@ \
$(OBJECT_DIR)/$*.o $(TYPEPHP_OS_LIBRARY) $(COMPILER_RT_LIBRARY)
$(KERNEL): payload $(KERNEL): payload
objcopy -O binary $(PAYLOAD_ELF) $(PAYLOAD_BIN) objcopy -O binary $(PAYLOAD_ELF) $(PAYLOAD_BIN)

@ -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`. binutils, GNU make, `dosfstools`, `mtools`, and `qemu-system-x86_64`.
Third-party source is not committed. `make` invokes Third-party source is not committed. `make` invokes
[`tools/fetch-thirdparty.sh`](tools/fetch-thirdparty.sh), which downloads fixed [`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). [`THIRDPARTY.md`](THIRDPARTY.md).
From this directory, build and boot with: 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 - `build/typephp-os.img`: a persistent 32 MiB FAT16 disk image containing the
independently built shell and command executables; independently built shell and command executables;
- `build/sh.elf` and the command ELF files: independent ELF64 Ring-3 programs - `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 - `build/tnhello.elf`: a full TypePHP + PHPX + PHP Nano Ring-3 executable
generated by tpc from `user/nano/hello.php`. generated by tpc from `user/nano/hello.php`.
- `build/libtypephp-os.a`: the reusable userspace platform archive linked by - `build/libtypephp-os.a`: the sole reusable userspace platform archive linked
every tpc-generated TypePHP program with `-ltypephp-os`. 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: Run the automated serial-output smoke test with:
@ -96,6 +99,10 @@ Run the automated serial-output smoke test with:
make test 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 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 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` 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 - PHPX `Variant`, `Str`, `Array`, custom classes, and a typed
`std::vector<int>`; `std::vector<int>`;
- OpenLibm implementations of the ordinary double-precision math ABI; - 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()`; - RTC-derived UTC time exposed to userspace through `time()`;
- Linux-compatible `uname(2)` shared by the `uname` command and PHP's - Linux-compatible `uname(2)` shared by the `uname` command and PHP's
`php_uname()` implementation; `php_uname()` implementation;

@ -28,6 +28,10 @@ behavior.
`wait`, pipes, threads, scheduling signals, and job control will not be `wait`, pipes, threads, scheduling signals, and job control will not be
implemented. The shell may only launch one synchronous foreground ELF implemented. The shell may only launch one synchronous foreground ELF
through the private TypePHP-OS service. 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 ## Milestones
@ -81,9 +85,15 @@ behavior.
a Linux-style initial stack. A full PHP Nano/PHPX executable is now produced 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 by the ordinary tpc pipeline and runs in Ring 3 with working TypePHP
`argc/argv`; standard `php_uname()` reaches the userspace `uname()` ABI and `argc/argv`; standard `php_uname()` reaches the userspace `uname()` ABI and
reports TypePHP-OS. Shared TypePHP userspace support is compiled once into reports TypePHP-OS. All shared userspace startup, syscall, POSIX/libc,
`libtypephp-os.a`, allowing multiple tpc projects to link the same runtime C++ ABI, and math support is compiled once into `libtypephp-os.a`; both the
platform archive. Unavailable ABI functions remain explicit panic stubs. 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 Single-task identity calls report PID/TID 1 for the resident shell, 2 for
its synchronous foreground command, and root UID/GID. Linux-compatible its synchronous foreground command, and root UID/GID. Linux-compatible
wall/monotonic clock structures are exposed at the current one-second RTC wall/monotonic clock structures are exposed at the current one-second RTC

@ -6,8 +6,9 @@ third-party source dependency must instead be declared by a script under
- an immutable upstream version or commit; - an immutable upstream version or commit;
- the canonical download URL; - the canonical download URL;
- a SHA-256 checksum for the downloaded archive; - a SHA-256 checksum for every downloaded archive or individual source file;
- an explicit list of files copied into the build tree; - 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. - the upstream license file.
Run the common fetch entry point before compiling: Run the common fetch entry point before compiling:
@ -16,10 +17,20 @@ Run the common fetch entry point before compiling:
./tools/fetch-thirdparty.sh ./tools/fetch-thirdparty.sh
``` ```
`make` invokes this command automatically. A matching local version marker `make` invokes this command automatically. Each dependency has a dedicated
makes repeated invocations offline and effectively free. Adding another `fetch-*.sh` script, while `fetch-thirdparty.sh` is only the common entry point.
library must extend this same entry point or call a dedicated, equivalently A matching local version marker makes repeated invocations offline and
strict fetch script from it; third-party source must not be committed directly. 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 ## 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 Only the architecture/compatibility headers and C sources required by
TypePHP-OS's current x86_64 double-precision math ABI are installed. The TypePHP-OS's current x86_64 double-precision math ABI are installed. The
selection keeps unused implementations out of the freestanding payload. selection keeps unused implementations out of the freestanding payload.
## LLVM compiler-rt builtins
- Upstream: <https://github.com/llvm/llvm-project/tree/llvmorg-23.1.1/compiler-rt/lib/builtins>
- 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: <https://github.com/managarm/mlibc>
- 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: <https://codeberg.org/landley/toybox>
- 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.

@ -0,0 +1,47 @@
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
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);
}

@ -14,6 +14,7 @@
#include "typephp_os_abi.h" #include "typephp_os_abi.h"
#include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <locale.h> #include <locale.h>
#include <setjmp.h> #include <setjmp.h>
@ -35,7 +36,6 @@ enum { TYPEPHP_OS_ALIGNED_BLOCK_CAPACITY = 64 };
static uintptr_t arena_cursor; static uintptr_t arena_cursor;
static uintptr_t arena_end; static uintptr_t arena_end;
static int typephp_os_errno;
static typephp_os_aligned_block aligned_blocks[TYPEPHP_OS_ALIGNED_BLOCK_CAPACITY]; static typephp_os_aligned_block aligned_blocks[TYPEPHP_OS_ALIGNED_BLOCK_CAPACITY];
/* TypePHP OS has no hosted stdio object. These opaque values only satisfy /* TypePHP OS has no hosted stdio object. These opaque values only satisfy
@ -63,11 +63,6 @@ __attribute__((weak)) unsigned int getgid(void)
return 0; return 0;
} }
int *__errno_location(void)
{
return &typephp_os_errno;
}
void __assert_fail( void __assert_fail(
const char *assertion, const char *assertion,
const char *file, const char *file,
@ -195,15 +190,6 @@ int memcmp(const void *left, const void *right, size_t size)
return 0; 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) char *strdup(const char *string)
{ {
const size_t size = strlen(string) + 1; const size_t size = strlen(string) + 1;
@ -417,10 +403,10 @@ char *setlocale(int category, const char *locale)
return 0; return 0;
} }
char *getcwd(char *buffer, size_t size) __attribute__((weak)) char *getcwd(char *buffer, size_t size)
{ {
if (buffer == 0 || size < 2) { if (buffer == 0 || size < 2) {
typephp_os_errno = 34; /* ERANGE */ errno = ERANGE;
return 0; return 0;
} }
buffer[0] = '/'; buffer[0] = '/';

@ -0,0 +1,10 @@
#include <stddef.h>
size_t strlen(const char *string)
{
const char *end = string;
while (*end != '\0') {
++end;
}
return (size_t) (end - string);
}

@ -43,7 +43,6 @@ TYPEPHP_OS_UNIMPLEMENTED utime
TYPEPHP_OS_UNIMPLEMENTED mblen TYPEPHP_OS_UNIMPLEMENTED mblen
TYPEPHP_OS_UNIMPLEMENTED localtime_r TYPEPHP_OS_UNIMPLEMENTED localtime_r
TYPEPHP_OS_UNIMPLEMENTED strerror
TYPEPHP_OS_UNIMPLEMENTED strftime TYPEPHP_OS_UNIMPLEMENTED strftime
TYPEPHP_OS_UNIMPLEMENTED _Unwind_Resume TYPEPHP_OS_UNIMPLEMENTED _Unwind_Resume

@ -23,7 +23,8 @@ while read -r expected relative; do
[ -n "${relative}" ] || continue [ -n "${relative}" ] || continue
destination="${staging}/${relative}" destination="${staging}/${relative}"
mkdir -p "$(dirname -- "${destination}")" 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 - printf '%s %s\n' "${expected}" "${destination}" | sha256sum -c -
done < "${manifest}" done < "${manifest}"

@ -19,7 +19,7 @@ archive="${temporary}/mlibc.tar.gz"
staging="${temporary}/installed" staging="${temporary}/installed"
echo "Downloading mlibc v${version} (${commit})" 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" "https://github.com/managarm/mlibc/archive/refs/tags/v${version}.tar.gz"
printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c - printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c -
mkdir -p "${staging}" mkdir -p "${staging}"

@ -20,7 +20,7 @@ source_dir="${temporary}/openlibm-${version}"
staging="${temporary}/installed" staging="${temporary}/installed"
echo "Downloading OpenLibm v${version}" echo "Downloading OpenLibm v${version}"
curl -L --fail --retry 3 \ curl -L --fail --retry 3 --silent --show-error \
-o "${archive}" \ -o "${archive}" \
"https://github.com/JuliaMath/openlibm/archive/refs/tags/v${version}.tar.gz" "https://github.com/JuliaMath/openlibm/archive/refs/tags/v${version}.tar.gz"
echo "${archive_sha256} ${archive}" | sha256sum -c - echo "${archive_sha256} ${archive}" | sha256sum -c -

@ -19,7 +19,7 @@ archive="${temporary}/toybox.tar.gz"
staging="${temporary}/installed" staging="${temporary}/installed"
echo "Downloading Toybox ${version} (${commit})" 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" "https://landley.net/toybox/downloads/toybox-${version}.tar.gz"
printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c - printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c -
mkdir -p "${staging}" mkdir -p "${staging}"

@ -36,7 +36,7 @@ rm -rf "${mlibc_build}" "${mlibc_sysroot}"
-Dlinux_option=disabled \ -Dlinux_option=disabled \
-Dglibc_option=disabled \ -Dglibc_option=disabled \
-Dbsd_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) compiler_headers=$(gcc -print-file-name=include)
gcc -std=c11 -ffreestanding -fsyntax-only -nostdinc \ gcc -std=c11 -ffreestanding -fsyntax-only -nostdinc \
-isystem "${mlibc_sysroot}/usr/include" \ -isystem "${mlibc_sysroot}/usr/include" \

@ -1,6 +1,5 @@
CONFIG_TOYBOX=y CONFIG_TOYBOX=y
CONFIG_TOYBOX_HELP=y CONFIG_TOYBOX_HELP=y
# CONFIG_TOYBOX_FORK is not set
CONFIG_CAT=y CONFIG_CAT=y
CONFIG_DATE=y CONFIG_DATE=y
CONFIG_ECHO=y CONFIG_ECHO=y

@ -25,12 +25,15 @@ The following rules are normative for this directory:
it is not a public POSIX process-creation API. it is not a public POSIX process-creation API.
6. New kernel services should first consider the remaining interfaces needed 6. New kernel services should first consider the remaining interfaces needed
by a static libc and PHP Nano: memory mapping, files and directories, by a static libc and PHP Nano: memory mapping, files and directories,
clocks, TLS, terminal I/O, and single-task lifecycle. Socket support also clocks, TLS, and single-task lifecycle. Socket support remains out of
remains out of scope. scope. `ioctl` and `termios` are also intentionally deferred; basic console
7. Freestanding test programs remain libc-free only as a bootstrap constraint; `read`/`write` is the supported terminal contract.
it is not the final userspace programming model. 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`, `open`, `close`, `lseek`, `getcwd`, `chdir`, `mkdir`, `rmdir`, `unlink`,
`rename`, `stat`, `lstat`, `fstat`, `access`, `fsync`, `fdatasync`, `truncate`, `rename`, `stat`, `lstat`, `fstat`, `access`, `fsync`, `fdatasync`, `truncate`,
`ftruncate`, `time`, `gettimeofday`, `clock_gettime`, `clock_getres`, `uname`, `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 commands, it composes the complete PHP Nano and PHPX source manifests into an
independent ELF64 executable. Shared startup, host, POSIX, libc/C++ ABI, and independent ELF64 executable. Shared startup, host, POSIX, libc/C++ ABI, and
math support live under `runtime/` and are built once as `libtypephp-os.a`. math support live under `runtime/` and are built once as `libtypephp-os.a`.
Each TypePHP userspace project links that archive with `-ltypephp-os` instead Both the C commands and every TypePHP userspace project link that archive with
of recompiling the platform layer. The shared crt0 accepts the same Linux-style `-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 initial stack, so the TypePHP entry receives the real command-line `argc` and
`argv`. The standard extension's `php_uname()` remains implemented in the `argv`. The standard extension's `php_uname()` remains implemented in the
upstream `info.c`; the local `uname()` ABI reports `TypePHP-OS`. APIs whose upstream `info.c`; the local `uname()` ABI reports `TypePHP-OS`. APIs whose

@ -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

@ -1,7 +1,8 @@
#ifndef TYPEPHP_OS_USER_ERRNO_H #ifndef TYPEPHP_OS_USER_ERRNO_H
#define 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 ENOENT 2
#define EIO 5 #define EIO 5

@ -1,295 +0,0 @@
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
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");
}
}

@ -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*) }
}

@ -1,8 +1,8 @@
# TypePHP userspace runtime # TypePHP userspace runtime
This directory contains the TypePHP-OS platform layer shared by all This directory contains the TypePHP-OS platform layer shared by every Ring-3
tpc-generated userspace programs. The top-level Makefile compiles it, the program, whether its application entry point is compiled from C or TypePHP.
kernel ABI shims, and OpenLibm into: The top-level Makefile compiles it, the kernel ABI shims, and OpenLibm into:
```text ```text
build/libtypephp-os.a build/libtypephp-os.a
@ -22,18 +22,26 @@ link-paths:
- ../../build - ../../build
link-libs: link-libs:
- typephp-os - typephp-os
- compiler-rt-builtins
``` ```
The archive owns the userspace `_start`, PHP Nano host entry, syscall bridge, The archive owns the sole userspace `_start`, PHP Nano host entry, syscall
POSIX/libc and C++ ABI shims, buffered `dirent` implementation, and math implementation. Application projects bridge, POSIX/libc and C++ ABI shims, buffered `dirent` implementation, and
must not compile private copies of these files. This allows any number of math implementation. C programs link the same archive directly; the linker
projects under `user/` to reuse one platform archive and keeps their generated extracts only the members they actually reference, so they do not pull in the
object directories independent. PHP Nano host or its larger compatibility layer. Application projects must not
compile private copies of these files. This allows any number of projects
No compiler runtime library is currently required on x86-64; both the kernel under `user/` to reuse one platform archive and keeps their generated object
and Nano userspace links are audited for undefined symbols. Add selected directories independent.
compiler-rt builtins only if generated code introduces an explicit helper
symbol in the future. 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 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 PHP Nano/PHPX feature definitions. Those shared settings are maintained in the

@ -1,5 +1,6 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -38,12 +39,26 @@ off_t lseek(int fd, off_t offset, int whence)
int openat(int directory_fd, const char *path, int flags, ...) 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, ...) 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) int chdir(const char *path)
@ -66,6 +81,11 @@ int rmdir(const char *path)
return (int) syscall(TYPEPHP_SYS_RMDIR, 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) int brk(void *address)
{ {
return (void *) syscall(TYPEPHP_SYS_BRK, address) == address ? 0 : -1; 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); return (gid_t) syscall(TYPEPHP_SYS_GETEGID);
} }
void _exit(int status)
{
(void) syscall(TYPEPHP_SYS_EXIT_GROUP, status);
for (;;) {
__asm__ volatile("pause");
}
}

@ -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
Loading…
Cancel
Save