feat: POSIX libc surface, native binutils (as/ld/ar run on MontaukOS)
Grow the Montauk libc enough to cross-build binutils 2.43.1 with --host=x86_64-montauk. gas, ld, ar, nm, objcopy, objdump, readelf, ranlib, strip and friends now link as native ET_EXEC Montauk binaries (staged stripped in toolchain/native/, not yet shipped in the image). New libc surface: full Linux errno and signal sets, O_* flags with real O_EXCL/O_APPEND semantics, unlink/rmdir/dup/dup2/getpid/_exit, kill (SYS_KILL), fcntl, fileno/fdopen, putc/getchar/rewind, ctime/asctime, strtoll/strtoull/atoll, bsearch, mkstemp/mktemp, realpath (lexical, drive-prefix aware), mbstowcs/mblen, chmod/fchmod/ umask/utime no-ops (VFS has no modes or settable times), full struct stat with fake inodes, sscanf field widths (bounded conversions), SCN*/PRI* completions, wait/waitpid over SYS_WAITPID, and new headers sys/wait.h, sys/param.h, utime.h, wchar.h, memory.h. fork/exec/pipe are declared but fail with ENOSYS: binutils never spawns, and real process plumbing is the posix_spawn milestone, which needs kernel support (exit status reporting, fd redirection). TCC's montauk_compat.h shims (unlink, chmod, execvp, realpath, fdopen, strtoll, strtoull) are retired in favor of the libc versions. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -19,3 +19,4 @@ CLAUDE.md
|
||||
programs/lib/bearssl
|
||||
ramdisk.tar
|
||||
PLANS/.mcp.json
|
||||
toolchain/native/
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 7
|
||||
#define MONTAUK_BUILD_NUMBER 10
|
||||
|
||||
@@ -9,18 +9,48 @@ extern "C" {
|
||||
|
||||
extern int errno;
|
||||
|
||||
/* Linux errno numbering. */
|
||||
#define EPERM 1
|
||||
#define ENOENT 2
|
||||
#define ESRCH 3
|
||||
#define EINTR 4
|
||||
#define EIO 5
|
||||
#define ENXIO 6
|
||||
#define E2BIG 7
|
||||
#define ENOEXEC 8
|
||||
#define EBADF 9
|
||||
#define ECHILD 10
|
||||
#define EAGAIN 11
|
||||
#define ENOMEM 12
|
||||
#define EACCES 13
|
||||
#define EINVAL 22
|
||||
#define ERANGE 34
|
||||
#define ENOSYS 38
|
||||
#define EISDIR 21
|
||||
#define ENOTDIR 20
|
||||
#define EFAULT 14
|
||||
#define EBUSY 16
|
||||
#define EEXIST 17
|
||||
#define EBADF 9
|
||||
#define EPERM 1
|
||||
#define EXDEV 18
|
||||
#define ENODEV 19
|
||||
#define ENOTDIR 20
|
||||
#define EISDIR 21
|
||||
#define EINVAL 22
|
||||
#define ENFILE 23
|
||||
#define EMFILE 24
|
||||
#define ENOTTY 25
|
||||
#define EFBIG 27
|
||||
#define ENOSPC 28
|
||||
#define ESPIPE 29
|
||||
#define EROFS 30
|
||||
#define EMLINK 31
|
||||
#define EPIPE 32
|
||||
#define EDOM 33
|
||||
#define ERANGE 34
|
||||
#define EDEADLK 35
|
||||
#define ENAMETOOLONG 36
|
||||
#define ENOLCK 37
|
||||
#define ENOSYS 38
|
||||
#define ENOTEMPTY 39
|
||||
#define ELOOP 40
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#define EOVERFLOW 75
|
||||
#define ETIMEDOUT 110
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -10,10 +10,24 @@ extern "C" {
|
||||
#define O_RDONLY 0
|
||||
#define O_WRONLY 1
|
||||
#define O_RDWR 2
|
||||
#define O_ACCMODE 3
|
||||
#define O_CREAT 0x40
|
||||
#define O_EXCL 0x80
|
||||
#define O_TRUNC 0x200
|
||||
#define O_APPEND 0x400
|
||||
#define O_NONBLOCK 0x800
|
||||
#define O_CLOEXEC 0x80000
|
||||
|
||||
#define F_DUPFD 0
|
||||
#define F_GETFD 1
|
||||
#define F_SETFD 2
|
||||
#define F_GETFL 3
|
||||
#define F_SETFL 4
|
||||
|
||||
#define FD_CLOEXEC 1
|
||||
|
||||
int open(const char *path, int flags, ...);
|
||||
int fcntl(int fd, int cmd, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -23,5 +23,38 @@
|
||||
#define PRIX16 "X"
|
||||
#define PRIX32 "X"
|
||||
#define PRIX64 "lX"
|
||||
#define PRIo8 "o"
|
||||
#define PRIo16 "o"
|
||||
#define PRIo32 "o"
|
||||
#define PRIo64 "lo"
|
||||
|
||||
#define PRIdPTR "ld"
|
||||
#define PRIiPTR "li"
|
||||
#define PRIuPTR "lu"
|
||||
#define PRIxPTR "lx"
|
||||
#define PRIXPTR "lX"
|
||||
#define PRIdMAX "ld"
|
||||
#define PRIuMAX "lu"
|
||||
#define PRIxMAX "lx"
|
||||
|
||||
#define SCNd8 "d"
|
||||
#define SCNd16 "d"
|
||||
#define SCNd32 "d"
|
||||
#define SCNd64 "ld"
|
||||
#define SCNi8 "i"
|
||||
#define SCNi16 "i"
|
||||
#define SCNi32 "i"
|
||||
#define SCNi64 "li"
|
||||
#define SCNu8 "u"
|
||||
#define SCNu16 "u"
|
||||
#define SCNu32 "u"
|
||||
#define SCNu64 "lu"
|
||||
#define SCNx8 "x"
|
||||
#define SCNx16 "x"
|
||||
#define SCNx32 "x"
|
||||
#define SCNx64 "lx"
|
||||
#define SCNdPTR "ld"
|
||||
#define SCNuPTR "lu"
|
||||
#define SCNxPTR "lx"
|
||||
|
||||
#endif /* _LIBC_INTTYPES_H */
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef _LIBC_MEMORY_H
|
||||
#define _LIBC_MEMORY_H
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Traditional alias for string.h. */
|
||||
#include <string.h>
|
||||
|
||||
#endif /* _LIBC_MEMORY_H */
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -10,7 +12,26 @@ extern "C" {
|
||||
typedef int sig_atomic_t;
|
||||
typedef void (*sighandler_t)(int);
|
||||
|
||||
/* Linux signal numbering. Only SIGINT is ever delivered today; the
|
||||
rest exist so hosted code can name them. */
|
||||
#define SIGHUP 1
|
||||
#define SIGINT 2
|
||||
#define SIGQUIT 3
|
||||
#define SIGILL 4
|
||||
#define SIGTRAP 5
|
||||
#define SIGABRT 6
|
||||
#define SIGBUS 7
|
||||
#define SIGFPE 8
|
||||
#define SIGKILL 9
|
||||
#define SIGUSR1 10
|
||||
#define SIGSEGV 11
|
||||
#define SIGUSR2 12
|
||||
#define SIGPIPE 13
|
||||
#define SIGALRM 14
|
||||
#define SIGTERM 15
|
||||
#define SIGCHLD 17
|
||||
#define SIGCONT 18
|
||||
#define SIGSTOP 19
|
||||
|
||||
#define SIG_DFL ((sighandler_t)0)
|
||||
#define SIG_IGN ((sighandler_t)1)
|
||||
@@ -18,6 +39,7 @@ typedef void (*sighandler_t)(int);
|
||||
|
||||
sighandler_t signal(int sig, sighandler_t handler);
|
||||
int raise(int sig);
|
||||
int kill(pid_t pid, int sig);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -36,6 +36,12 @@ extern FILE *stdin;
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
|
||||
int putc(int c, FILE *stream);
|
||||
void rewind(FILE *stream);
|
||||
int getchar(void);
|
||||
int fileno(FILE *stream);
|
||||
FILE *fdopen(int fd, const char *mode);
|
||||
|
||||
int printf(const char *fmt, ...);
|
||||
int fprintf(FILE *stream, const char *fmt, ...);
|
||||
int sprintf(char *str, const char *fmt, ...);
|
||||
|
||||
@@ -50,9 +50,19 @@ int putenv(char *string);
|
||||
|
||||
void qsort(void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *));
|
||||
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *));
|
||||
|
||||
long strtol(const char *nptr, char **endptr, int base);
|
||||
unsigned long strtoul(const char *nptr, char **endptr, int base);
|
||||
long long strtoll(const char *nptr, char **endptr, int base);
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base);
|
||||
long long atoll(const char *nptr);
|
||||
int mkstemp(char *template_);
|
||||
char *mktemp(char *template_);
|
||||
char *realpath(const char *path, char *resolved);
|
||||
size_t mbstowcs(wchar_t *dst, const char *src, size_t n);
|
||||
int mblen(const char *s, size_t n);
|
||||
double strtod(const char *nptr, char **endptr);
|
||||
float strtof(const char *nptr, char **endptr);
|
||||
long double strtold(const char *nptr, char **endptr);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef _LIBC_SYS_PARAM_H
|
||||
#define _LIBC_SYS_PARAM_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
#define MAXPATHLEN 4096
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define howmany(x, y) (((x) + ((y) - 1)) / (y))
|
||||
#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||
|
||||
#endif /* _LIBC_SYS_PARAM_H */
|
||||
@@ -12,19 +12,54 @@ extern "C" {
|
||||
#define S_IFMT 0170000
|
||||
#define S_IFDIR 0040000
|
||||
#define S_IFREG 0100000
|
||||
#define S_IFCHR 0020000
|
||||
#define S_IFLNK 0120000
|
||||
|
||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
||||
#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
|
||||
#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
|
||||
#define S_ISFIFO(mode) (0)
|
||||
#define S_ISSOCK(mode) (0)
|
||||
#define S_ISBLK(mode) (0)
|
||||
|
||||
/* Permission bits are accepted and preserved by the API for POSIX
|
||||
compatibility, but the Montauk VFS does not store them. */
|
||||
#define S_IRWXU 0700
|
||||
#define S_IRUSR 0400
|
||||
#define S_IWUSR 0200
|
||||
#define S_IXUSR 0100
|
||||
#define S_IRWXG 0070
|
||||
#define S_IRGRP 0040
|
||||
#define S_IWGRP 0020
|
||||
#define S_IXGRP 0010
|
||||
#define S_IRWXO 0007
|
||||
#define S_IROTH 0004
|
||||
#define S_IWOTH 0002
|
||||
#define S_IXOTH 0001
|
||||
|
||||
struct stat {
|
||||
unsigned long st_dev;
|
||||
unsigned long st_ino;
|
||||
mode_t st_mode;
|
||||
unsigned long st_nlink;
|
||||
uid_t st_uid;
|
||||
gid_t st_gid;
|
||||
unsigned long st_size;
|
||||
unsigned long st_blksize;
|
||||
unsigned long st_blocks;
|
||||
time_t st_atime;
|
||||
time_t st_mtime;
|
||||
time_t st_ctime;
|
||||
};
|
||||
|
||||
int mkdir(const char *path, unsigned int mode);
|
||||
int stat(const char *path, struct stat *buf);
|
||||
int fstat(int fd, struct stat *buf);
|
||||
int lstat(const char *path, struct stat *buf);
|
||||
int chmod(const char *path, mode_t mode);
|
||||
int fchmod(int fd, mode_t mode);
|
||||
mode_t umask(mode_t mask);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef _LIBC_SYS_WAIT_H
|
||||
#define _LIBC_SYS_WAIT_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Status decoding. The Montauk kernel does not yet report exit codes
|
||||
through SYS_WAITPID, so waited children always decode as exit 0. */
|
||||
#define WIFEXITED(s) (((s) & 0x7F) == 0)
|
||||
#define WEXITSTATUS(s) (((s) >> 8) & 0xFF)
|
||||
#define WIFSIGNALED(s) (0)
|
||||
#define WTERMSIG(s) (0)
|
||||
#define WIFSTOPPED(s) (0)
|
||||
#define WSTOPSIG(s) (0)
|
||||
|
||||
#define WNOHANG 1
|
||||
#define WUNTRACED 2
|
||||
|
||||
pid_t wait(int *status);
|
||||
pid_t waitpid(pid_t pid, int *status, int options);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBC_SYS_WAIT_H */
|
||||
@@ -34,6 +34,9 @@ struct tm *localtime(const time_t *timer);
|
||||
time_t mktime(struct tm *tm);
|
||||
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
|
||||
|
||||
char *asctime(const struct tm *tm);
|
||||
char *ctime(const time_t *timep);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define STDIN_FILENO 0
|
||||
#define STDOUT_FILENO 1
|
||||
#define STDERR_FILENO 2
|
||||
|
||||
#define F_OK 0
|
||||
#define X_OK 1
|
||||
#define W_OK 2
|
||||
@@ -22,6 +27,19 @@ int chdir(const char *path);
|
||||
char *getcwd(char *buf, size_t size);
|
||||
int access(const char *path, int mode);
|
||||
int isatty(int fd);
|
||||
int unlink(const char *path);
|
||||
int rmdir(const char *path);
|
||||
int dup(int fd);
|
||||
int dup2(int oldfd, int newfd);
|
||||
pid_t getpid(void);
|
||||
void _exit(int status) __attribute__((noreturn));
|
||||
pid_t fork(void);
|
||||
int execv(const char *path, char *const argv[]);
|
||||
int execve(const char *path, char *const argv[], char *const envp[]);
|
||||
int execvp(const char *file, char *const argv[]);
|
||||
int pipe(int fds[2]);
|
||||
|
||||
extern char **environ;
|
||||
|
||||
unsigned int sleep(unsigned int seconds);
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef _LIBC_UTIME_H
|
||||
#define _LIBC_UTIME_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct utimbuf {
|
||||
time_t actime;
|
||||
time_t modtime;
|
||||
};
|
||||
|
||||
/* Accepted for POSIX compatibility; the Montauk VFS does not support
|
||||
setting file times. */
|
||||
int utime(const char *path, const struct utimbuf *times);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBC_UTIME_H */
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef _LIBC_WCHAR_H
|
||||
#define _LIBC_WCHAR_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef unsigned int wint_t;
|
||||
|
||||
#define WEOF ((wint_t)-1)
|
||||
|
||||
/* Byte-oriented C locale only: one byte, one character. */
|
||||
size_t mbstowcs(wchar_t *dst, const char *src, size_t n);
|
||||
int mblen(const char *s, size_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBC_WCHAR_H */
|
||||
+521
-4
@@ -20,6 +20,7 @@
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <utime.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -110,6 +111,9 @@ static inline long _zos_syscall4(long nr, long a1, long a2, long a3, long a4) {
|
||||
#define SYS_FRENAME 94
|
||||
#define SYS_GETCWD 95
|
||||
#define SYS_CHDIR 96
|
||||
#define SYS_GETPID 3
|
||||
#define SYS_DUPHANDLE 98
|
||||
#define SYS_KILL 62
|
||||
|
||||
/* ========================================================================
|
||||
errno
|
||||
@@ -2035,8 +2039,12 @@ int sscanf(const char *str, const char *fmt, ...) {
|
||||
while (*fmt) {
|
||||
if (*fmt == '%') {
|
||||
fmt++;
|
||||
int width = 0;
|
||||
while (isdigit((unsigned char)*fmt))
|
||||
width = width * 10 + (*fmt++ - '0');
|
||||
int is_long = 0;
|
||||
if (*fmt == 'l') { is_long = 1; fmt++; }
|
||||
if (*fmt == 'l') { fmt++; } /* ll behaves as l on LP64 */
|
||||
|
||||
if (*fmt == 'd' || *fmt == 'i') {
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
@@ -2045,7 +2053,8 @@ int sscanf(const char *str, const char *fmt, ...) {
|
||||
else if (*s == '+') s++;
|
||||
if (!isdigit((unsigned char)*s)) goto done;
|
||||
long val = 0;
|
||||
while (isdigit((unsigned char)*s))
|
||||
int n = 0;
|
||||
while (isdigit((unsigned char)*s) && (width == 0 || n++ < width))
|
||||
val = val * 10 + (*s++ - '0');
|
||||
if (neg) val = -val;
|
||||
if (is_long) *va_arg(ap, long *) = val;
|
||||
@@ -2055,7 +2064,8 @@ int sscanf(const char *str, const char *fmt, ...) {
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
if (!isdigit((unsigned char)*s)) goto done;
|
||||
unsigned long val = 0;
|
||||
while (isdigit((unsigned char)*s))
|
||||
int n = 0;
|
||||
while (isdigit((unsigned char)*s) && (width == 0 || n++ < width))
|
||||
val = val * 10 + (unsigned long)(*s++ - '0');
|
||||
if (is_long) *va_arg(ap, unsigned long *) = val;
|
||||
else *va_arg(ap, unsigned int *) = (unsigned int)val;
|
||||
@@ -2065,7 +2075,8 @@ int sscanf(const char *str, const char *fmt, ...) {
|
||||
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2;
|
||||
if (!isxdigit((unsigned char)*s)) goto done;
|
||||
unsigned long val = 0;
|
||||
while (isxdigit((unsigned char)*s)) {
|
||||
int n = 0;
|
||||
while (isxdigit((unsigned char)*s) && (width == 0 || n++ < width)) {
|
||||
int d;
|
||||
if (*s >= '0' && *s <= '9') d = *s - '0';
|
||||
else if (*s >= 'a' && *s <= 'f') d = *s - 'a' + 10;
|
||||
@@ -2080,7 +2091,9 @@ int sscanf(const char *str, const char *fmt, ...) {
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
char *dst = va_arg(ap, char *);
|
||||
if (*s == '\0') goto done;
|
||||
while (*s && !isspace((unsigned char)*s))
|
||||
int n = 0;
|
||||
while (*s && !isspace((unsigned char)*s)
|
||||
&& (width == 0 || n++ < width))
|
||||
*dst++ = *s++;
|
||||
*dst = '\0';
|
||||
matched++;
|
||||
@@ -2152,6 +2165,10 @@ int open(const char *path, int flags, ...) {
|
||||
if (wants_create) {
|
||||
h = (int)_zos_syscall1(SYS_FCREATE, (long)path);
|
||||
}
|
||||
} else if (wants_create && (flags & O_EXCL)) {
|
||||
_zos_syscall1(SYS_CLOSE, (long)h);
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
} else if (wants_trunc) {
|
||||
_zos_syscall1(SYS_CLOSE, (long)h);
|
||||
_zos_syscall1(SYS_FDELETE, (long)path);
|
||||
@@ -2160,6 +2177,9 @@ int open(const char *path, int flags, ...) {
|
||||
|
||||
if (h >= 0 && h < _FD_POS_MAX) {
|
||||
_fd_pos[h] = 0;
|
||||
if (flags & O_APPEND) {
|
||||
_fd_pos[h] = (unsigned long)_zos_syscall1(SYS_GETSIZE, (long)h);
|
||||
}
|
||||
}
|
||||
if (h < 0) {
|
||||
errno = ENOENT;
|
||||
@@ -2267,6 +2287,16 @@ int stat(const char *path, struct stat *buf) {
|
||||
buf->st_mode = S_IFREG;
|
||||
buf->st_size = (unsigned long)_zos_syscall1(SYS_GETSIZE, (long)h);
|
||||
_zos_syscall1(SYS_CLOSE, (long)h);
|
||||
/* No inodes in the VFS API: derive a stable id from the path
|
||||
so that distinct paths compare as distinct files. */
|
||||
unsigned long ino = 5381;
|
||||
for (const char *p = path; *p; p++) {
|
||||
ino = ino * 33 + (unsigned char)*p;
|
||||
}
|
||||
buf->st_ino = ino | 1;
|
||||
buf->st_nlink = 1;
|
||||
buf->st_blksize = 4096;
|
||||
buf->st_blocks = (buf->st_size + 511) / 512;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2288,6 +2318,12 @@ int fstat(int fd, struct stat *buf) {
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
buf->st_mode = S_IFREG;
|
||||
buf->st_size = (unsigned long)_zos_syscall1(SYS_GETSIZE, (long)fd);
|
||||
/* Handles do not map back to paths, so fake a per-handle inode in a
|
||||
range disjoint from the path hashes used by stat(). */
|
||||
buf->st_ino = 0x4000000000000000UL + (unsigned long)fd;
|
||||
buf->st_nlink = 1;
|
||||
buf->st_blksize = 4096;
|
||||
buf->st_blocks = (buf->st_size + 511) / 512;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3110,3 +3146,484 @@ unsigned int sleep(unsigned int seconds) {
|
||||
_zos_syscall1(SYS_SLEEP_MS, (long)seconds * 1000L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ========================================================================
|
||||
POSIX additions for hosted programs (native binutils/gcc ports)
|
||||
======================================================================== */
|
||||
|
||||
/* Byte-oriented C locale: every byte is one character, nothing is an
|
||||
invalid sequence. */
|
||||
void rewind(FILE *stream) {
|
||||
if (stream == NULL || stream->is_std) {
|
||||
return;
|
||||
}
|
||||
stream->pos = 0;
|
||||
stream->eof = 0;
|
||||
stream->error = 0;
|
||||
stream->ungetc_buf = -1;
|
||||
}
|
||||
|
||||
char *asctime(const struct tm *tm) {
|
||||
static char buf[26];
|
||||
if (tm == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y\n", tm);
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *ctime(const time_t *timep) {
|
||||
if (timep == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return asctime(localtime(timep));
|
||||
}
|
||||
|
||||
int fchmod(int fd, mode_t mode) {
|
||||
(void)fd; (void)mode;
|
||||
return 0; /* no permission bits in the VFS */
|
||||
}
|
||||
|
||||
int utime(const char *path, const struct utimbuf *times) {
|
||||
(void)times;
|
||||
if (path == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return 0; /* file times cannot be set through the VFS */
|
||||
}
|
||||
|
||||
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *)) {
|
||||
size_t lo = 0;
|
||||
size_t hi = nmemb;
|
||||
while (lo < hi) {
|
||||
size_t mid = lo + (hi - lo) / 2;
|
||||
const void *elem = (const char *)base + mid * size;
|
||||
int c = compar(key, elem);
|
||||
if (c == 0) {
|
||||
return (void *)elem;
|
||||
}
|
||||
if (c < 0) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t mbstowcs(wchar_t *dst, const char *src, size_t n) {
|
||||
if (src == NULL) {
|
||||
errno = EINVAL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
if (dst == NULL) {
|
||||
return strlen(src);
|
||||
}
|
||||
size_t i = 0;
|
||||
for (; i < n && src[i]; i++) {
|
||||
dst[i] = (wchar_t)(unsigned char)src[i];
|
||||
}
|
||||
if (i < n) {
|
||||
dst[i] = 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int mblen(const char *s, size_t n) {
|
||||
if (s == NULL) return 0;
|
||||
if (n == 0) return -1;
|
||||
return (*s != '\0') ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Absolute paths on Montauk start with a drive prefix ("0:/...""). A
|
||||
path is made absolute against the cwd, then . and .. segments are
|
||||
folded out. No symlinks exist, so this is purely lexical. */
|
||||
char *realpath(const char *path, char *resolved) {
|
||||
if (path == NULL) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char abs[1024];
|
||||
int has_drive = 0;
|
||||
for (const char *p = path; *p; p++) {
|
||||
if (*p == ':') { has_drive = 1; break; }
|
||||
if (*p == '/') break;
|
||||
}
|
||||
if (has_drive) {
|
||||
strncpy(abs, path, sizeof(abs) - 1);
|
||||
abs[sizeof(abs) - 1] = '\0';
|
||||
} else {
|
||||
if (getcwd(abs, sizeof(abs)) == NULL) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
size_t cl = strlen(abs);
|
||||
if (cl > 0 && abs[cl - 1] != '/' && cl + 1 < sizeof(abs)) {
|
||||
abs[cl++] = '/';
|
||||
abs[cl] = '\0';
|
||||
}
|
||||
strncpy(abs + cl, path, sizeof(abs) - cl - 1);
|
||||
abs[sizeof(abs) - 1] = '\0';
|
||||
}
|
||||
|
||||
/* Fold . and .. segments in place, keeping the drive prefix. */
|
||||
char *body = strchr(abs, ':');
|
||||
body = body ? body + 1 : abs; /* points at "/..." */
|
||||
char out[1024];
|
||||
size_t o = 0;
|
||||
const char *p = body;
|
||||
while (*p) {
|
||||
while (*p == '/') p++;
|
||||
if (*p == '\0') break;
|
||||
const char *seg = p;
|
||||
while (*p && *p != '/') p++;
|
||||
size_t seglen = (size_t)(p - seg);
|
||||
if (seglen == 1 && seg[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
if (seglen == 2 && seg[0] == '.' && seg[1] == '.') {
|
||||
while (o > 0 && out[--o] != '/') { }
|
||||
continue;
|
||||
}
|
||||
if (o + seglen + 2 >= sizeof(out)) {
|
||||
errno = ENAMETOOLONG;
|
||||
return NULL;
|
||||
}
|
||||
out[o++] = '/';
|
||||
memcpy(out + o, seg, seglen);
|
||||
o += seglen;
|
||||
}
|
||||
if (o == 0) {
|
||||
out[o++] = '/';
|
||||
}
|
||||
out[o] = '\0';
|
||||
|
||||
size_t prefix = (size_t)(body - abs);
|
||||
size_t total = prefix + o + 1;
|
||||
if (resolved == NULL) {
|
||||
resolved = (char *)malloc(total);
|
||||
if (resolved == NULL) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
memcpy(resolved, abs, prefix);
|
||||
memcpy(resolved + prefix, out, o + 1);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
int putc(int c, FILE *stream) {
|
||||
return fputc(c, stream);
|
||||
}
|
||||
|
||||
int getchar(void) {
|
||||
return fgetc(stdin);
|
||||
}
|
||||
|
||||
int unlink(const char *path) {
|
||||
if (path == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if ((int)_zos_syscall1(SYS_FDELETE, (long)path) < 0) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rmdir(const char *path) {
|
||||
return unlink(path);
|
||||
}
|
||||
|
||||
/* Handle validity is probed via SYS_DUPHANDLE since the kernel has no
|
||||
direct query. Flag state is not tracked per handle; F_GETFL reports
|
||||
O_RDWR and the set commands succeed as no-ops. */
|
||||
int fcntl(int fd, int cmd, ...) {
|
||||
switch (cmd) {
|
||||
case F_GETFD:
|
||||
case F_GETFL: {
|
||||
int probe = (int)_zos_syscall1(SYS_DUPHANDLE, (long)fd);
|
||||
if (probe < 0) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
_zos_syscall1(SYS_CLOSE, (long)probe);
|
||||
return (cmd == F_GETFL) ? O_RDWR : 0;
|
||||
}
|
||||
case F_SETFD:
|
||||
case F_SETFL:
|
||||
return 0;
|
||||
case F_DUPFD:
|
||||
return dup(fd);
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
pid_t getpid(void) {
|
||||
return (pid_t)_zos_syscall0(SYS_GETPID);
|
||||
}
|
||||
|
||||
/* True dup2 needs kernel support for duplicating into a chosen slot.
|
||||
Best effort: freeing newfd first makes the kernel's lowest-free-slot
|
||||
allocation usually land there. The oldfd == newfd form (the common
|
||||
validity probe) is handled exactly. */
|
||||
int dup2(int oldfd, int newfd) {
|
||||
if (oldfd < 0 || newfd < 0) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (oldfd == newfd) {
|
||||
int probe = (int)_zos_syscall1(SYS_DUPHANDLE, (long)oldfd);
|
||||
if (probe < 0) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
_zos_syscall1(SYS_CLOSE, (long)probe);
|
||||
return newfd;
|
||||
}
|
||||
_zos_syscall1(SYS_CLOSE, (long)newfd);
|
||||
int h = (int)_zos_syscall1(SYS_DUPHANDLE, (long)oldfd);
|
||||
if (h < 0) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (h != newfd) {
|
||||
_zos_syscall1(SYS_CLOSE, (long)h);
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
if (h < _FD_POS_MAX) {
|
||||
_fd_pos[h] = (oldfd < _FD_POS_MAX) ? _fd_pos[oldfd] : 0;
|
||||
}
|
||||
return newfd;
|
||||
}
|
||||
|
||||
/* SYS_WAITPID blocks until the child exits but does not report its
|
||||
exit code yet, so successful waits decode as exit status 0. */
|
||||
pid_t waitpid(pid_t pid, int *status, int options) {
|
||||
(void)options;
|
||||
if (pid <= 0) {
|
||||
errno = ECHILD; /* no process groups / wait-any support */
|
||||
return -1;
|
||||
}
|
||||
_zos_syscall1(SYS_WAITPID, (long)pid);
|
||||
if (status != NULL) {
|
||||
*status = 0;
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
void _exit(int status) {
|
||||
_zos_syscall1(SYS_EXIT, (long)status);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
/* SYS_KILL terminates the target outright; the signal number is
|
||||
accepted for API shape but not delivered as a handler. */
|
||||
int kill(pid_t pid, int sig) {
|
||||
(void)sig;
|
||||
if ((int)_zos_syscall1(SYS_KILL, (long)pid) < 0) {
|
||||
errno = ESRCH;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The libc environment lives in name/value slots (getenv/setenv), not
|
||||
NAME=VALUE strings, and exec* is unimplemented, so nothing can
|
||||
consume a populated environ yet. An empty, valid vector satisfies
|
||||
POSIX-shaped code until the posix_spawn work builds the real one. */
|
||||
static char *_environ_empty[] = { NULL };
|
||||
char **environ = _environ_empty;
|
||||
|
||||
/* No fork/exec process model on Montauk (spawn-style only, see
|
||||
SYS_SPAWN). These exist so POSIX code links; they fail cleanly at
|
||||
runtime. A posix_spawn implementation is the planned real path. */
|
||||
pid_t fork(void) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int execv(const char *path, char *const argv[]) {
|
||||
(void)path; (void)argv;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int execve(const char *path, char *const argv[], char *const envp[]) {
|
||||
(void)path; (void)argv; (void)envp;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int execvp(const char *file, char *const argv[]) {
|
||||
(void)file; (void)argv;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Byte pipes exist in the kernel (SYS_STREAM_CREATE), but the libc
|
||||
read()/write() paths are positional file calls; wiring stream
|
||||
handles into the fd space is future work alongside posix_spawn. */
|
||||
int pipe(int fds[2]) {
|
||||
(void)fds;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid_t wait(int *status) {
|
||||
(void)status;
|
||||
errno = ECHILD; /* wait-any needs kernel support */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dup(int fd) {
|
||||
int h = (int)_zos_syscall1(SYS_DUPHANDLE, (long)fd);
|
||||
if (h < 0) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (h < _FD_POS_MAX) {
|
||||
_fd_pos[h] = (fd >= 0 && fd < _FD_POS_MAX) ? _fd_pos[fd] : 0;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
int fileno(FILE *stream) {
|
||||
if (stream == NULL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (stream->is_std) {
|
||||
return stream->is_std - 1; /* stdin 0, stdout 1, stderr 2 */
|
||||
}
|
||||
return stream->handle;
|
||||
}
|
||||
|
||||
FILE *fdopen(int fd, const char *mode) {
|
||||
if (fd < 0 || mode == NULL) {
|
||||
errno = EBADF;
|
||||
return NULL;
|
||||
}
|
||||
FILE *f = (FILE *)malloc(sizeof(FILE));
|
||||
if (f == NULL) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
f->handle = fd;
|
||||
f->pos = (fd < _FD_POS_MAX) ? _fd_pos[fd] : 0;
|
||||
f->size = (unsigned long)_zos_syscall1(SYS_GETSIZE, (long)fd);
|
||||
f->eof = 0;
|
||||
f->error = 0;
|
||||
f->is_std = 0;
|
||||
f->ungetc_buf = -1;
|
||||
if (mode[0] == 'a') {
|
||||
f->pos = f->size;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/* long is 64-bit on x86_64-montauk, so the ll variants are aliases. */
|
||||
long long strtoll(const char *nptr, char **endptr, int base) {
|
||||
return (long long)strtol(nptr, endptr, base);
|
||||
}
|
||||
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base) {
|
||||
return (unsigned long long)strtoul(nptr, endptr, base);
|
||||
}
|
||||
|
||||
long long atoll(const char *nptr) {
|
||||
return (long long)atol(nptr);
|
||||
}
|
||||
|
||||
/* The Montauk VFS does not store permission bits; accept and ignore. */
|
||||
int chmod(const char *path, mode_t mode) {
|
||||
(void)mode;
|
||||
if (path == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static mode_t _umask_cur = 022;
|
||||
|
||||
mode_t umask(mode_t mask) {
|
||||
mode_t old = _umask_cur;
|
||||
_umask_cur = mask & 0777;
|
||||
return old;
|
||||
}
|
||||
|
||||
int lstat(const char *path, struct stat *buf) {
|
||||
return stat(path, buf); /* no symlinks in the Montauk VFS */
|
||||
}
|
||||
|
||||
char *mktemp(char *template_) {
|
||||
if (template_ == NULL) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
size_t len = strlen(template_);
|
||||
if (len < 6 || strcmp(template_ + len - 6, "XXXXXX") != 0) {
|
||||
errno = EINVAL;
|
||||
if (len > 0) template_[0] = '\0';
|
||||
return template_;
|
||||
}
|
||||
static const char cs[] =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
unsigned long seed = (unsigned long)_zos_syscall0(SYS_GETMILLISECONDS)
|
||||
^ ((unsigned long)getpid() << 16);
|
||||
for (int attempt = 0; attempt < 128; attempt++) {
|
||||
unsigned long v = seed + (unsigned long)attempt * 7919UL;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
template_[len - 6 + i] = cs[v % (sizeof(cs) - 1)];
|
||||
v /= sizeof(cs) - 1;
|
||||
}
|
||||
int h = (int)_zos_syscall1(SYS_OPEN, (long)template_);
|
||||
if (h < 0) {
|
||||
return template_; /* name is free */
|
||||
}
|
||||
_zos_syscall1(SYS_CLOSE, (long)h);
|
||||
}
|
||||
template_[0] = '\0';
|
||||
errno = EEXIST;
|
||||
return template_;
|
||||
}
|
||||
|
||||
int mkstemp(char *template_) {
|
||||
if (template_ == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
size_t len = strlen(template_);
|
||||
if (len < 6 || strcmp(template_ + len - 6, "XXXXXX") != 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
static const char cs[] =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
unsigned long seed = (unsigned long)_zos_syscall0(SYS_GETMILLISECONDS)
|
||||
^ ((unsigned long)getpid() << 16);
|
||||
for (int attempt = 0; attempt < 128; attempt++) {
|
||||
unsigned long v = seed + (unsigned long)attempt * 7919UL;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
template_[len - 6 + i] = cs[v % (sizeof(cs) - 1)];
|
||||
v /= sizeof(cs) - 1;
|
||||
}
|
||||
int fd = open(template_, O_RDWR | O_CREAT | O_EXCL);
|
||||
if (fd >= 0) {
|
||||
return fd;
|
||||
}
|
||||
if (errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,83 +2,20 @@
|
||||
* montauk_compat.h
|
||||
* POSIX compatibility shims for TCC on MontaukOS
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*
|
||||
* Historical note: this header used to carry static fallbacks for
|
||||
* unlink/chmod/execvp/realpath/fdopen before the Montauk libc grew
|
||||
* them (native binutils port). It is now a thin passthrough.
|
||||
*/
|
||||
|
||||
#ifndef MONTAUK_COMPAT_H
|
||||
#define MONTAUK_COMPAT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* getcwd is provided by libc now */
|
||||
char *getcwd(char *buf, size_t size);
|
||||
|
||||
/* chmod - no-op on MontaukOS */
|
||||
static inline int chmod(const char *path, unsigned int mode) {
|
||||
(void)path; (void)mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close (provided by libc) */
|
||||
int close(int fd);
|
||||
|
||||
/* lseek - provided by libc, tracks fd positions */
|
||||
long lseek(int fd, long offset, int whence);
|
||||
|
||||
/* realpath - just return a copy of the path */
|
||||
static inline char *realpath(const char *path, char *resolved) {
|
||||
if (path == NULL) return NULL;
|
||||
if (resolved == NULL) {
|
||||
size_t len = 0;
|
||||
const char *p = path;
|
||||
while (*p++) len++;
|
||||
resolved = (char*)malloc(len + 1);
|
||||
if (!resolved) return NULL;
|
||||
}
|
||||
{
|
||||
const char *s = path;
|
||||
char *d = resolved;
|
||||
while ((*d++ = *s++));
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/* dlfcn stubs not needed -- TCC_IS_NATIVE is disabled for MontaukOS */
|
||||
|
||||
/* ====================================================================
|
||||
ssize_t (used in tccelf.c)
|
||||
==================================================================== */
|
||||
|
||||
#ifndef _SSIZE_T_DEFINED
|
||||
#define _SSIZE_T_DEFINED
|
||||
typedef long ssize_t;
|
||||
#endif
|
||||
|
||||
/* ====================================================================
|
||||
POSIX I/O (provided by libc)
|
||||
==================================================================== */
|
||||
|
||||
int read(int fd, void *buf, size_t count);
|
||||
int write(int fd, const void *buf, size_t count);
|
||||
|
||||
/* unlink = remove */
|
||||
static inline int unlink(const char *path) {
|
||||
return remove(path);
|
||||
}
|
||||
|
||||
/* fdopen - MontaukOS doesn't have fd-to-FILE conversion.
|
||||
TCC uses fdopen after creating a file with open().
|
||||
We return NULL and let TCC fall back to error path,
|
||||
or we can use a workaround. */
|
||||
FILE *fdopen(int fd, const char *mode);
|
||||
|
||||
/* ====================================================================
|
||||
Process stubs
|
||||
==================================================================== */
|
||||
|
||||
static inline int execvp(const char *file, char *const argv[]) {
|
||||
(void)file; (void)argv;
|
||||
return -1;
|
||||
}
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#endif /* MONTAUK_COMPAT_H */
|
||||
|
||||
@@ -40,40 +40,8 @@ static inline long _sys2(long nr, long a1, long a2) {
|
||||
#define SYS_GETSIZE 8
|
||||
#define SYS_GETARGS 25
|
||||
|
||||
/* ====================================================================
|
||||
fdopen - wrap a raw fd in a FILE (needed by tccelf.c)
|
||||
==================================================================== */
|
||||
|
||||
FILE *fdopen(int fd, const char *mode) {
|
||||
(void)mode;
|
||||
if (fd < 0) return NULL;
|
||||
|
||||
unsigned long fileSize = (unsigned long)_sys1(SYS_GETSIZE, (long)fd);
|
||||
|
||||
FILE *f = (FILE *)malloc(sizeof(FILE));
|
||||
if (f == NULL) return NULL;
|
||||
|
||||
f->handle = fd;
|
||||
f->pos = 0;
|
||||
f->size = fileSize;
|
||||
f->eof = 0;
|
||||
f->error = 0;
|
||||
f->is_std = 0;
|
||||
f->ungetc_buf = -1;
|
||||
return f;
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
strtoll / strtoull (needed by TCC, not in MontaukOS libc)
|
||||
==================================================================== */
|
||||
|
||||
long long strtoll(const char *nptr, char **endptr, int base) {
|
||||
return (long long)strtol(nptr, endptr, base);
|
||||
}
|
||||
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base) {
|
||||
return (unsigned long long)strtoul(nptr, endptr, base);
|
||||
}
|
||||
/* fdopen, strtoll and strtoull used to be defined here; they moved
|
||||
into the Montauk libc with the native binutils port. */
|
||||
|
||||
/* ====================================================================
|
||||
_start entry point
|
||||
|
||||
@@ -61,3 +61,36 @@ toolchain/
|
||||
local/ # install prefix (ignored)
|
||||
sysroot/ # generated target sysroot (ignored)
|
||||
```
|
||||
|
||||
## Native binutils (runs on MontaukOS)
|
||||
|
||||
Binutils can be cross-compiled to run *on* MontaukOS itself
|
||||
(`--host=x86_64-montauk`), the first step toward a self-hosted GCC:
|
||||
|
||||
```bash
|
||||
mkdir -p toolchain/build/binutils-native && cd toolchain/build/binutils-native
|
||||
export PATH=$PWD/../../local/bin:$PATH
|
||||
../../src/binutils-2.43.1/configure \
|
||||
--build=x86_64-pc-linux-gnu --host=x86_64-montauk \
|
||||
--target=x86_64-montauk --prefix=/usr \
|
||||
--disable-nls --disable-werror --disable-gprofng --disable-gold \
|
||||
--disable-plugins --disable-shared --enable-static
|
||||
make -j$(nproc) all-gas all-ld all-binutils
|
||||
make install-strip-gas install-strip-ld install-strip-binutils \
|
||||
DESTDIR=$PWD/../../native
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Build only `all-gas all-ld all-binutils`. gprof needs fscanf with
|
||||
%[] scansets (not in the Montauk libc); gold and gprofng are
|
||||
disabled outright.
|
||||
- The libc gained a large POSIX surface for this port (fd functions,
|
||||
full errno/signal sets, struct stat, sys/wait.h, utime.h, wchar.h,
|
||||
sys/param.h, memory.h, bsearch, sscanf field widths, ...). pex-style
|
||||
process spawning (fork/exec/pipe) is stubbed to fail with ENOSYS:
|
||||
the tools themselves never spawn, and real process plumbing is the
|
||||
posix_spawn milestone that needs kernel support (exit codes in
|
||||
SYS_WAITPID, fd redirection wiring).
|
||||
- The staged tree in toolchain/native/ is not yet integrated into the
|
||||
OS image; that needs a target-side sysroot layout decision (where
|
||||
headers, libc.a and crt objects live on 0:/).
|
||||
|
||||
Reference in New Issue
Block a user