933 lines
29 KiB
C
933 lines
29 KiB
C
// SPDX-FileCopyrightText: 2021 Alexander Sosedkin <monk@unboiled.info>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// syscalls (x86_64) ///////////////////////////////////////////////////////////
|
|
|
|
#ifndef INSIDE_NIX
|
|
#include "1-stage1/syscall.h"
|
|
#define TCC_SEED "/store/0-tcc-seed"
|
|
#define PROTOSRC "/protosrc"
|
|
#define RECIPES_STAGE1 "/recipes/1-stage1"
|
|
#define TMP_STAGE1 "/tmp/1-stage1"
|
|
#define STORE_PROTOBUSYBOX "/store/1-stage1/protobusybox"
|
|
#define STORE_PROTOMUSL "/store/1-stage1/protomusl"
|
|
#define STORE_TINYCC "/store/1-stage1/tinycc"
|
|
#define EXTRA_HELLO_ARGS
|
|
#else
|
|
#include "syscall.h"
|
|
#define EXTRA_HELLO_ARGS \
|
|
"-DRECIPES_STAGE1=\"" RECIPES_STAGE1"\"",
|
|
#endif
|
|
|
|
#define SYS_write 1
|
|
#define SYS_open 2
|
|
#define SYS_fork 57
|
|
#define SYS_execve 59
|
|
#define SYS_exit 60
|
|
#define SYS_wait4 61
|
|
#define SYS_getdents 78
|
|
#define SYS_mkdir 83
|
|
|
|
long write(int fd, const void* buf, long cnt) {
|
|
return __syscall3(SYS_write, fd, (long) buf, cnt);
|
|
}
|
|
|
|
int execve(const char* fname,
|
|
const char* const argv[], const char* const envp[]) {
|
|
return __syscall3(SYS_execve, (long) fname, (long) argv, (long) envp);
|
|
}
|
|
|
|
void exit(int code) { __syscall1(SYS_exit, code); };
|
|
|
|
int fork() { return __syscall0(SYS_fork); }
|
|
|
|
int wait4(int pid, int* status, int options, void* rusage) {
|
|
return __syscall4(
|
|
SYS_wait4,
|
|
pid, (long) status, options, (long) rusage
|
|
);
|
|
}
|
|
|
|
int mkdir(const char *pathname, unsigned mode) {
|
|
return __syscall2(SYS_mkdir, (long) pathname, mode);
|
|
}
|
|
|
|
int open(const char *pathname, int flags, int mode) {
|
|
return __syscall3(SYS_open, (long) pathname, flags, mode);
|
|
}
|
|
|
|
struct linux_dirent {
|
|
long d_ino;
|
|
long d_off;
|
|
unsigned short d_reclen;
|
|
char d_name[];
|
|
};
|
|
int getdents(unsigned int fd, struct linux_dirent *dirp,
|
|
unsigned int count) {
|
|
return __syscall3(SYS_getdents, fd, (long) dirp, count);
|
|
}
|
|
|
|
|
|
// random defines //////////////////////////////////////////////////////////////
|
|
|
|
#define NULL ((void*) 0)
|
|
#define STDOUT 1
|
|
#define STDERR 2
|
|
#define O_RDONLY 0
|
|
#define O_DIRECTORY 0200000
|
|
#define DT_REG 8
|
|
|
|
|
|
// basic QoL ///////////////////////////////////////////////////////////////////
|
|
|
|
unsigned strlen(const char* s) {
|
|
unsigned l;
|
|
for (l = 0; s[l]; l++);
|
|
return l;
|
|
}
|
|
|
|
int write_(int fd, const char* msg) {
|
|
return write(fd, msg, strlen(msg));
|
|
}
|
|
|
|
#define __quote(x) #x
|
|
#define _quote(x) __quote(x)
|
|
// real assert calls abort() -> 128 + SIGABRT = 134
|
|
#define assert(v) \
|
|
while (!(v)) { \
|
|
write_(STDERR, "Assertion "); \
|
|
write_(STDERR, _quote(v)); write_(STDERR, " failed at "); \
|
|
write_(STDERR, __FILE__); write_(STDERR, ":"); \
|
|
write_(STDERR, __func__); write_(STDERR, ":"); \
|
|
write_(STDERR, _quote(__LINE__)); write_(STDERR, "!\n"); \
|
|
exit(134); \
|
|
}
|
|
|
|
void err(const char* msg) { assert(write_(STDERR, msg) == strlen(msg)); }
|
|
|
|
void log_begin_line(const char* msg) {
|
|
assert(write_(STDOUT, "### 1-stage1.c: ") == 16);
|
|
assert(write_(STDOUT, msg) == strlen(msg));
|
|
}
|
|
void log_continue_line(const char* msg) {
|
|
assert(write_(STDOUT, msg) == strlen(msg));
|
|
}
|
|
void log_end_line() { assert(write_(STDOUT, "\n") == 1); }
|
|
void log(const char* msg) { log_begin_line(msg); log_end_line(); };
|
|
|
|
|
|
// more library function substitutes ///////////////////////////////////////////
|
|
|
|
void memset(char* ptr, int with, long len) {
|
|
long i;
|
|
for (i = 0; i < len; i++)
|
|
ptr[i] = with;
|
|
}
|
|
|
|
char* strcpy(char* dest, const char* src) {
|
|
while (*src)
|
|
*dest++ = *src++;
|
|
*dest = 0;
|
|
return dest;
|
|
}
|
|
|
|
int strcmp(const char* a, const char* b) {
|
|
for (; *a && *b; a++, b++)
|
|
if (*a != *b)
|
|
return (*a < *b) ? -1 : 1;
|
|
return !a && !b;
|
|
}
|
|
|
|
|
|
// my convenience functions: mkdir'ing /////////////////////////////////////////
|
|
|
|
|
|
void mkreqdirs(const char* path) {
|
|
char b[128], *p;
|
|
strcpy(b, path);
|
|
for (p = b + 1; *p; p++)
|
|
if (*p == '/') { *p = '\0'; mkdir(b, 0777); *p = '/'; }
|
|
}
|
|
void mkreqdirs_at(const char* at, const char* subpath) {
|
|
char b[128], *p;
|
|
p = strcpy(b, at);
|
|
p = strcpy(strcpy(strcpy(p, "/"), subpath), "/");
|
|
mkreqdirs(b);
|
|
}
|
|
#define mkdirs_at(at, args...) \
|
|
do { \
|
|
const char* const* p; \
|
|
for (p = (char*[]) { "/", ## args, NULL }; *p; p++) \
|
|
mkreqdirs_at(at, *p); \
|
|
} while (0)
|
|
|
|
|
|
// my convenience functions: fork + exec ///////////////////////////////////////
|
|
|
|
int run_(const char* cmd, const char* const args[], const char* const env[]) {
|
|
int pid, status, termsig;
|
|
if (pid = fork()) {
|
|
assert(wait4(pid, &status, 0, NULL) == pid);
|
|
termsig = status & 0x7f; // WTERMSIG
|
|
if (!termsig) {
|
|
return (status & 0xff00) >> 8; // WEXITSTATUS
|
|
} else {
|
|
err("child has been killed");
|
|
exit(termsig);
|
|
}
|
|
} else {
|
|
exit(execve(cmd, args, env));
|
|
}
|
|
return 0; // unreacheable
|
|
}
|
|
|
|
#define run(expected_retcode, first_arg, args...) \
|
|
do { \
|
|
const char const* __env[] = {NULL}; \
|
|
const char const* __args[] = {(first_arg), ##args, NULL}; \
|
|
int __i; \
|
|
log_begin_line("run() running: "); \
|
|
for(__i = 0; __args[__i]; __i++) { \
|
|
log_continue_line(__args[__i]); \
|
|
log_continue_line(" "); \
|
|
} \
|
|
log_end_line(); \
|
|
assert(run_(first_arg, __args, __env) == (expected_retcode)); \
|
|
} while (0)
|
|
#define run0(first_arg, args...) run(0, (first_arg), ## args)
|
|
|
|
|
|
// my convenience functions: dynamic args accumulation / command execution /////
|
|
|
|
struct args_accumulator {
|
|
char* pointers[4096];
|
|
char storage[262144];
|
|
char** ptr_curr;
|
|
char* char_curr;
|
|
};
|
|
void _aa_init(struct args_accumulator* aa) {
|
|
aa->char_curr = aa->storage;
|
|
aa->ptr_curr = aa->pointers;
|
|
*aa->ptr_curr = NULL;
|
|
}
|
|
void aa_append(struct args_accumulator* aa, const char* new_arg) {
|
|
*aa->ptr_curr = aa->char_curr;
|
|
*++aa->ptr_curr = NULL;
|
|
aa->char_curr = strcpy(aa->char_curr, new_arg);
|
|
aa->char_curr++;
|
|
}
|
|
void _aa_extend_from_arr(struct args_accumulator* aa, const char* const* p) {
|
|
for (; *p; p++)
|
|
aa_append(aa, *p);
|
|
}
|
|
void aa_extend_from(struct args_accumulator* aa, const void* from) {
|
|
// Cheat a little with void* to accept
|
|
// both struct args_accumulator* and null-terminated string arrays.
|
|
// Qualifiers could be stricter, but then declaring get cumbersome.
|
|
_aa_extend_from_arr(aa, (const char* const*) from);
|
|
}
|
|
#define aa_extend(aa_ptr, args...) \
|
|
_aa_extend_from_arr(aa_ptr, (const char*[]) { NULL, ## args, NULL } + 1)
|
|
#define aa_init(aa_ptr, args...) \
|
|
do { _aa_init(aa_ptr); aa_extend(aa_ptr, ## args); } while (0)
|
|
void aa_sort(struct args_accumulator* aa) {
|
|
int changes;
|
|
char **p, **n, *t;
|
|
if (!aa->pointers[0])
|
|
return;
|
|
if (!aa->pointers[1])
|
|
return;
|
|
do {
|
|
changes = 0;
|
|
for (p = aa->pointers, n = p + 1; *n; p++, n++) {
|
|
if (strcmp(*p, *n) > 0) {
|
|
t = *p; *p = *n; *n = t;
|
|
changes = 1;
|
|
}
|
|
}
|
|
} while (changes);
|
|
}
|
|
int aa_run(const struct args_accumulator* aa) {
|
|
int i;
|
|
log_begin_line("aa_run() running: ");
|
|
for (i = 0; aa->pointers[i]; i++) {
|
|
log_continue_line(aa->pointers[i]);
|
|
log_continue_line(" ");
|
|
}
|
|
log_end_line(STDOUT, "\n");
|
|
return run_(aa->pointers[0], aa->pointers, (char*[]) { NULL });
|
|
}
|
|
#define aa_run0(aa_ptr) do { assert(aa_run(aa_ptr) == 0); } while (0)
|
|
|
|
|
|
// my convenience functions: compiling whole directories worth of files ////////
|
|
|
|
_Bool is_compileable(char* fname) {
|
|
int i = 0;
|
|
while (fname[i])
|
|
i++;
|
|
if (i > 2)
|
|
if (fname[i - 2] == '.')
|
|
if (fname[i - 1] == 'c' || fname[i-1] == 's')
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void aa_extend_from_dir(struct args_accumulator* aa_out,
|
|
unsigned short keep_components, const char* dir_path) {
|
|
struct args_accumulator aa;
|
|
char d_buf[256];
|
|
char buf[256];
|
|
const char* prefix;
|
|
char* out_subpath;
|
|
struct linux_dirent* d;
|
|
int fd, r;
|
|
char d_type;
|
|
|
|
aa_init(&aa);
|
|
|
|
prefix = dir_path + strlen(dir_path);
|
|
while (keep_components) {
|
|
while (*prefix != '/')
|
|
prefix--;
|
|
keep_components--;
|
|
prefix += keep_components ? -1 : 1;
|
|
}
|
|
|
|
fd = open(dir_path, O_RDONLY | O_DIRECTORY, 0);
|
|
assert(fd != -1);
|
|
|
|
while (1) {
|
|
d = (struct linux_dirent*) d_buf;
|
|
r = getdents(fd, d, 256);
|
|
assert(r != -1);
|
|
if (!r)
|
|
break;
|
|
while ((char*) d - d_buf < r) {
|
|
d_type = *((char*) d + d->d_reclen - 1);
|
|
if ((d_type == DT_REG || !d_type)
|
|
&& is_compileable(d->d_name)) {
|
|
out_subpath = strcpy(buf, prefix);
|
|
out_subpath = strcpy(out_subpath, "/");
|
|
out_subpath = strcpy(out_subpath, d->d_name);
|
|
aa_append(&aa, buf);
|
|
}
|
|
d = (struct linux_dirent*) ((char*) d + d->d_reclen);
|
|
}
|
|
}
|
|
aa_sort(&aa); // iteration order isn't guaranteed, make stable
|
|
aa_extend_from(aa_out, &aa);
|
|
}
|
|
|
|
|
|
void mass_compile(const char* cc, const void* compile_args,
|
|
const char* in_dir_path, const void* fnames /* NULL=auto */,
|
|
const char* out_obj_dir_path, const char* out_lib_file_path) {
|
|
// qualifiers could've been stricter
|
|
// const void* could be struct args_accumulator*,
|
|
// NULL-terminated arrays or even just NULLs for fnames
|
|
struct args_accumulator aa, aa_link, aa_sources;
|
|
char in_file_path_buf[128], out_file_path_buf[128];
|
|
char* in_file_path;
|
|
char* out_file_path;
|
|
char** p;
|
|
|
|
aa_init(&aa_sources);
|
|
if (!fnames)
|
|
aa_extend_from_dir(&aa_sources, 0, in_dir_path);
|
|
else
|
|
aa_extend_from(&aa_sources, fnames);
|
|
|
|
mkreqdirs(out_lib_file_path);
|
|
aa_init(&aa_link, cc, "-ar", "rc", out_lib_file_path);
|
|
|
|
for (p = (char**) &aa_sources; *p; p++) {
|
|
in_file_path = strcpy(in_file_path_buf, in_dir_path);
|
|
in_file_path = strcpy(in_file_path, "/");
|
|
in_file_path = strcpy(in_file_path, *p);
|
|
|
|
out_file_path = strcpy(out_file_path_buf, out_obj_dir_path);
|
|
out_file_path = strcpy(out_file_path, "/");
|
|
out_file_path = strcpy(out_file_path, *p);
|
|
out_file_path = strcpy(out_file_path, ".o");
|
|
mkreqdirs(out_file_path_buf);
|
|
|
|
aa_init(&aa, cc);
|
|
aa_extend_from(&aa, compile_args);
|
|
aa_extend(&aa, "-c", in_file_path_buf, "-o", out_file_path_buf);
|
|
aa_run0(&aa);
|
|
|
|
aa_append(&aa_link, out_file_path_buf);
|
|
}
|
|
aa_run0(&aa_link);
|
|
}
|
|
|
|
|
|
// Kinda boring parts //////////////////////////////////////////////////////////
|
|
|
|
#define TCC_ARGS_NOSTD "-nostdlib", "-nostdinc", "-I", "/protosrc/tinycc/include"
|
|
|
|
|
|
void sanity_test() {
|
|
struct args_accumulator aa1, aa2;
|
|
|
|
log("sanity-testing run()...");
|
|
log("* testing run() -> retcode 0...");
|
|
run0(TCC_SEED, "--help");
|
|
log("* testing run() -> retcode 1...");
|
|
run(1, TCC_SEED, "-ar", "--help");
|
|
log("run() seems to work OK");
|
|
|
|
log("sanity-testing args accumulator...");
|
|
log("* testing aa_append, aa_extend, aa_sort and aa_run0...");
|
|
aa_init(&aa1);
|
|
aa_init(&aa2);
|
|
aa_append(&aa1, TCC_SEED);
|
|
aa_append(&aa2, "-ar");
|
|
aa_extend(&aa2, "help-must-precede-ar", "--help");
|
|
aa_sort(&aa2);
|
|
aa_extend_from(&aa1, &aa2);
|
|
assert(!strcmp(((char**) &aa1)[0], TCC_SEED));
|
|
assert(!strcmp(((char**) &aa1)[1], "--help"));
|
|
assert(!strcmp(((char**) &aa1)[2], "-ar"));
|
|
assert(!strcmp(((char**) &aa1)[3], "help-must-precede-ar"));
|
|
assert(NULL == ((char**) &aa1)[4]);
|
|
aa_run0(&aa1);
|
|
|
|
log("* testing aa_multi and aa_run for 1...");
|
|
aa_init(&aa1, TCC_SEED, "-ar", "--help");
|
|
assert(aa_run(&aa1) == 1);
|
|
}
|
|
|
|
|
|
// Interesting parts: libtcc1 //////////////////////////////////////////////////
|
|
|
|
|
|
void compile_libtcc1_1st_time_nostd(const char* cc) {
|
|
log("compiling our first libtcc1.a...");
|
|
mass_compile(cc, (char* []) { TCC_ARGS_NOSTD, "-DTCC_MUSL", NULL },
|
|
PROTOSRC"/tinycc/lib", (char* []) {
|
|
"libtcc1.c", "alloca.S",
|
|
"dsohandle.c", "stdatomic.c", "va_list.c",
|
|
0},
|
|
TMP_STAGE1"/tinycc/libtcc1",
|
|
STORE_TINYCC"/lib/libtcc1.a");
|
|
} // see also compile_libtcc1 far below
|
|
|
|
|
|
// Interesting parts: protomusl ////////////////////////////////////////////////
|
|
|
|
#define PROTOMUSL_EXTRA_CFLAGS \
|
|
"-std=c99", \
|
|
"-D_XOPEN_SOURCE=700"
|
|
#define PROTOMUSL_INTERNAL_INCLUDES \
|
|
"-I" PROTOSRC"/protomusl/src/include", \
|
|
"-I" PROTOSRC"/protomusl/arch/x86_64", \
|
|
"-I" PROTOSRC"/protomusl/host-generated/sed1", \
|
|
"-I" PROTOSRC"/protomusl/host-generated/sed2", \
|
|
"-I" PROTOSRC"/protomusl/arch/generic", \
|
|
"-I" PROTOSRC"/protomusl/src/internal", \
|
|
"-I" PROTOSRC"/protomusl/include"
|
|
#define PROTOMUSL_NOSTD_LDFLAGS_PRE \
|
|
"-static", \
|
|
STORE_PROTOMUSL"/lib/crt1.o", \
|
|
STORE_PROTOMUSL"/lib/crti.o"
|
|
#define PROTOMUSL_NOSTD_LDFLAGS_POST \
|
|
STORE_PROTOMUSL"/lib/libc.a", \
|
|
STORE_PROTOMUSL"/lib/crtn.o"
|
|
#define PROTOMUSL_INCLUDES \
|
|
"-I" PROTOSRC"/protomusl/include", \
|
|
"-I" PROTOSRC"/protomusl/arch/x86_64", \
|
|
"-I" PROTOSRC"/protomusl/arch/generic", \
|
|
"-I" PROTOSRC"/protomusl/host-generated/sed1", \
|
|
"-I" PROTOSRC"/protomusl/host-generated/sed2"
|
|
|
|
void compile_protomusl(const char* cc) {
|
|
struct args_accumulator aa;
|
|
aa_init(&aa);
|
|
|
|
log("compiling part of musl (protomusl)...");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/conf");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/ctype");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/dirent");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/env");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/errno");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/exit");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/fcntl");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/fenv");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/internal");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/ldso");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/legacy");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/linux");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/locale");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/malloc");
|
|
aa_extend_from_dir(&aa, 2, PROTOSRC"/protomusl/src/malloc/mallocng");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/math");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/misc");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/mman");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/multibyte");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/network");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/passwd");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/prng");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/process");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/regex");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/select");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/setjmp");
|
|
aa_extend_from_dir(&aa, 2, PROTOSRC"/protomusl/src/setjmp/x86_64");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/signal");
|
|
aa_extend_from_dir(&aa, 2, PROTOSRC"/protomusl/src/signal/x86_64");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/stat");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/stdio");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/stdlib");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/string");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/temp");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/termios");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/thread");
|
|
aa_extend_from_dir(&aa, 2, PROTOSRC"/protomusl/src/thread/x86_64");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/time");
|
|
aa_extend_from_dir(&aa, 1, PROTOSRC"/protomusl/src/unistd");
|
|
mass_compile(cc, (char*[]) {
|
|
TCC_ARGS_NOSTD,
|
|
PROTOMUSL_EXTRA_CFLAGS,
|
|
PROTOMUSL_INTERNAL_INCLUDES,
|
|
0},
|
|
PROTOSRC"/protomusl/src", &aa,
|
|
TMP_STAGE1"/protomusl",
|
|
STORE_PROTOMUSL"/lib/libc.a");
|
|
|
|
log("compiling crt bits of protomusl...");
|
|
run0(cc, TCC_ARGS_NOSTD, PROTOMUSL_INTERNAL_INCLUDES, "-DCRT",
|
|
"-c", PROTOSRC"/protomusl/crt/crt1.c",
|
|
"-o", STORE_PROTOMUSL"/lib/crt1.o");
|
|
run0(cc, TCC_ARGS_NOSTD, "-DCRT",
|
|
"-c", PROTOSRC"/protomusl/crt/crti.c",
|
|
"-o", STORE_PROTOMUSL"/lib/crti.o");
|
|
run0(cc, TCC_ARGS_NOSTD, "-DCRT",
|
|
"-c", PROTOSRC"/protomusl/crt/crtn.c",
|
|
"-o", STORE_PROTOMUSL"/lib/crtn.o");
|
|
}
|
|
|
|
void test_example_1st_time_nostd(const char* cc) {
|
|
log("linking an example (1st time)...");
|
|
run0(cc, TCC_ARGS_NOSTD, PROTOMUSL_INCLUDES,
|
|
PROTOMUSL_NOSTD_LDFLAGS_PRE,
|
|
RECIPES_STAGE1"/hello.c", EXTRA_HELLO_ARGS
|
|
PROTOMUSL_NOSTD_LDFLAGS_POST,
|
|
STORE_TINYCC"/lib/libtcc1.a",
|
|
"-o", TMP_STAGE1"/protomusl-hello");
|
|
|
|
log("executing an example...");
|
|
run(42, TMP_STAGE1"/protomusl-hello");
|
|
}
|
|
|
|
|
|
// Interesting parts: recompiling tcc //////////////////////////////////////////
|
|
|
|
void compile_libtcc1(const char* cc) {
|
|
log("recompiling libtcc1.a...");
|
|
mass_compile(cc, (char*[]) { "-DTCC_MUSL", PROTOMUSL_INCLUDES, 0},
|
|
PROTOSRC"/tinycc/lib", (char*[]) {
|
|
"libtcc1.c", "alloca.S",
|
|
"dsohandle.c", "stdatomic.c", "va_list.c",
|
|
// now we can compile more
|
|
"tcov.c", "alloca-bt.S",
|
|
// bcheck.c is excluded, as it references __FILE__
|
|
0},
|
|
TMP_STAGE1"/tinycc/libtcc1",
|
|
STORE_TINYCC"/lib/libtcc1.a");
|
|
}
|
|
|
|
#define TCC_CFLAGS \
|
|
"-I"PROTOSRC"/tinycc", \
|
|
"-I"PROTOSRC"/tinycc/include", \
|
|
"-I"TMP_STAGE1"/tinycc/gen", \
|
|
"-DTCC_VERSION=\"mob-af1abf1\"", \
|
|
"-DTCC_GITHASH=\"mob:af1abf1\"", \
|
|
"-DTCC_TARGET_X86_64", \
|
|
"-DTCC_MUSL", \
|
|
"-DONE_SOURCE=0", \
|
|
"-DCONFIG_TCCDIR=\""STORE_TINYCC"/lib\"", \
|
|
"-DCONFIG_TCC_SYSINCLUDEPATHS="\
|
|
"\""STORE_PROTOMUSL"/include\"", \
|
|
"-DCONFIG_TCC_LIBPATHS=\""STORE_PROTOMUSL"/lib\"", \
|
|
"-DCONFIG_TCC_CRTPREFIX=\""STORE_PROTOMUSL"/lib\"", \
|
|
"-DCONFIG_TCC_ELFINTERP=\"/sorry/not/yet\"", \
|
|
"-DCONFIG_TCC_PREDEFS=1"
|
|
|
|
void compile_tcc_1st_time_nostd(const char* cc) {
|
|
log("compiling tcc's conftest...");
|
|
mkdirs_at(TMP_STAGE1"/tinycc", "gen", "lib", "bin");
|
|
mkdirs_at(STORE_TINYCC"", "lib", "bin");
|
|
run0(cc, TCC_ARGS_NOSTD, PROTOMUSL_INCLUDES,
|
|
PROTOMUSL_NOSTD_LDFLAGS_PRE,
|
|
"-DC2STR", PROTOSRC"/tinycc/conftest.c",
|
|
PROTOMUSL_NOSTD_LDFLAGS_POST,
|
|
STORE_TINYCC"/lib/libtcc1.a",
|
|
"-o", TMP_STAGE1"/tinycc/conftest"
|
|
);
|
|
log("generating tccdefs_.h with conftest...");
|
|
run0(TMP_STAGE1"/tinycc/conftest",
|
|
PROTOSRC"/tinycc/include/tccdefs.h",
|
|
TMP_STAGE1"/tinycc/gen/tccdefs_.h");
|
|
|
|
log("compiling libtcc...");
|
|
mass_compile(cc, (char*[]) {
|
|
TCC_ARGS_NOSTD,
|
|
PROTOMUSL_INCLUDES,
|
|
TCC_CFLAGS,
|
|
0},
|
|
PROTOSRC"/tinycc", (char*[]) {
|
|
"libtcc.c", "tccpp.c", "tccgen.c", "tccelf.c",
|
|
"tccdbg.c", "tccasm.c", "tccrun.c",
|
|
"x86_64-gen.c", "x86_64-link.c", "i386-asm.c",
|
|
0},
|
|
TMP_STAGE1"/tinycc/libtcc",
|
|
STORE_TINYCC"/lib/libtcc.a");
|
|
run0(cc, TCC_ARGS_NOSTD, PROTOMUSL_INCLUDES, TCC_CFLAGS,
|
|
PROTOMUSL_NOSTD_LDFLAGS_PRE,
|
|
PROTOSRC"/tinycc/tcc.c",
|
|
STORE_TINYCC"/lib/libtcc.a",
|
|
PROTOMUSL_NOSTD_LDFLAGS_POST,
|
|
STORE_TINYCC"/lib/libtcc1.a",
|
|
"-o", STORE_TINYCC"/bin/tcc");
|
|
run0(STORE_TINYCC"/bin/tcc", "-print-search-dirs");
|
|
}
|
|
|
|
|
|
void compile_tcc(const char* cc) {
|
|
log("recompiling libtcc...");
|
|
mass_compile(cc, (char*[]) { PROTOMUSL_INCLUDES, TCC_CFLAGS, 0},
|
|
PROTOSRC"/tinycc", (char*[]) {
|
|
"libtcc.c", "tccpp.c", "tccgen.c", "tccelf.c",
|
|
"tccdbg.c", "tccasm.c", "tccrun.c",
|
|
"x86_64-gen.c", "x86_64-link.c", "i386-asm.c",
|
|
0},
|
|
TMP_STAGE1"/tinycc/libtcc",
|
|
STORE_TINYCC"/lib/libtcc.a");
|
|
run0(cc, PROTOMUSL_INCLUDES, TCC_CFLAGS, "-static",
|
|
PROTOSRC"/tinycc/tcc.c",
|
|
STORE_TINYCC"/lib/libtcc.a",
|
|
"-o", STORE_TINYCC"/bin/tcc");
|
|
}
|
|
|
|
void test_example_intermediate(const char* cc) {
|
|
log("linking an example (our tcc, includes not installed)...");
|
|
run0(cc, PROTOMUSL_INCLUDES, "-static",
|
|
RECIPES_STAGE1"/hello.c", EXTRA_HELLO_ARGS
|
|
"-o", TMP_STAGE1"/protomusl-hello");
|
|
|
|
log("executing an example...");
|
|
run(42, TMP_STAGE1"/protomusl-hello");
|
|
}
|
|
|
|
void test_example_final(const char* cc_wrapper) {
|
|
log("linking an example (wrapped tcc, includes installed)...");
|
|
run0(cc_wrapper, RECIPES_STAGE1"/hello.c", EXTRA_HELLO_ARGS
|
|
"-o", TMP_STAGE1"/protomusl-hello");
|
|
|
|
log("executing an example...");
|
|
run(42, TMP_STAGE1"/protomusl-hello");
|
|
}
|
|
|
|
|
|
// Interesting parts: hacky standalone busybox applets /////////////////////////
|
|
|
|
void compile_standalone_busybox_applets(const char* cc) {
|
|
log("compiling protolibbb...");
|
|
mass_compile(cc, (char*[]) {
|
|
PROTOMUSL_INCLUDES,
|
|
"-I"PROTOSRC"/protobusybox/include/",
|
|
"-I"PROTOSRC"/protobusybox/libbb/",
|
|
"-include", RECIPES_STAGE1"/protobusybox.h",
|
|
0},
|
|
PROTOSRC"/protobusybox/libbb", (char*[]) {
|
|
"ask_confirmation.c",
|
|
"auto_string.c",
|
|
"bb_cat.c",
|
|
"bb_getgroups.c",
|
|
"bb_pwd.c",
|
|
"bb_strtonum.c",
|
|
"compare_string_array.c",
|
|
"concat_path_file.c",
|
|
"concat_subpath_file.c",
|
|
"copy_file.c",
|
|
"copyfd.c",
|
|
"crc32.c",
|
|
"default_error_retval.c",
|
|
"dump.c",
|
|
"endofname.c",
|
|
"executable.c",
|
|
"fclose_nonstdin.c",
|
|
"fflush_stdout_and_exit.c",
|
|
"full_write.c",
|
|
"get_last_path_component.c",
|
|
"get_line_from_file.c",
|
|
"getopt32.c",
|
|
"inode_hash.c",
|
|
"isdirectory.c",
|
|
"isqrt.c",
|
|
"last_char_is.c",
|
|
"llist.c",
|
|
"make_directory.c",
|
|
"messages.c",
|
|
"mode_string.c",
|
|
"parse_mode.c",
|
|
"perror_msg.c",
|
|
"perror_nomsg_and_die.c",
|
|
"printable_string.c",
|
|
"process_escape_sequence.c",
|
|
"procps.c",
|
|
"ptr_to_globals.c",
|
|
"read.c",
|
|
"read_printf.c",
|
|
"recursive_action.c",
|
|
"remove_file.c",
|
|
"safe_poll.c",
|
|
"safe_strncpy.c",
|
|
"safe_write.c",
|
|
"signals.c",
|
|
"single_argv.c",
|
|
"skip_whitespace.c",
|
|
"sysconf.c",
|
|
"time.c",
|
|
"u_signal_names.c",
|
|
"verror_msg.c",
|
|
"vfork_daemon_rexec.c",
|
|
"wfopen.c",
|
|
"wfopen_input.c",
|
|
"xatonum.c",
|
|
"xfunc_die.c",
|
|
"xfuncs.c",
|
|
"xfuncs_printf.c",
|
|
"xgetcwd.c",
|
|
"xreadlink.c",
|
|
"xrealloc_vector.c",
|
|
"xregcomp.c",
|
|
0},
|
|
TMP_STAGE1"/protobusybox/libbb",
|
|
TMP_STAGE1"/protobusybox/libbb.a");
|
|
|
|
|
|
log("compiling standalone protobusybox applets...");
|
|
mkreqdirs(STORE_PROTOBUSYBOX"/bin/");
|
|
#define compile_applet(applet_name, files...) \
|
|
run0(cc, PROTOMUSL_INCLUDES, \
|
|
"-D__GNUC__=2", "-D__GNUC_MINOR__=7", \
|
|
"-static", \
|
|
"-I"PROTOSRC"/protobusybox/include", \
|
|
"-include", RECIPES_STAGE1"/protobusybox.h", \
|
|
"-DAPPLET_MAIN=" applet_name "_main", \
|
|
RECIPES_STAGE1"/protobusybox.c", \
|
|
## files, \
|
|
TMP_STAGE1"/protobusybox/libbb.a", \
|
|
"-o", STORE_PROTOBUSYBOX"/bin/" applet_name);
|
|
compile_applet("echo", PROTOSRC"/protobusybox/coreutils/echo.c")
|
|
run0(STORE_PROTOBUSYBOX"/bin/echo",
|
|
"Hello from protobusybox!");
|
|
|
|
compile_applet("ash",
|
|
PROTOSRC"/protobusybox/shell/shell_common.c",
|
|
PROTOSRC"/protobusybox/shell/ash_ptr_hack.c",
|
|
PROTOSRC"/protobusybox/shell/math.c",
|
|
PROTOSRC"/protobusybox/coreutils/printf.c",
|
|
PROTOSRC"/protobusybox/coreutils/test_ptr_hack.c",
|
|
PROTOSRC"/protobusybox/coreutils/test.c",
|
|
PROTOSRC"/protobusybox/shell/ash.c")
|
|
run(42, STORE_PROTOBUSYBOX"/bin/ash", "-c",
|
|
"printf 'Hello from ash!\n'; exit 42");
|
|
|
|
compile_applet("basename",
|
|
PROTOSRC"/protobusybox/coreutils/basename.c")
|
|
compile_applet("cat", PROTOSRC"/protobusybox/coreutils/cat.c")
|
|
compile_applet("chmod", PROTOSRC"/protobusybox/coreutils/chmod.c")
|
|
compile_applet("cp",
|
|
PROTOSRC"/protobusybox/coreutils/libcoreutils/cp_mv_stat.c",
|
|
PROTOSRC"/protobusybox/coreutils/cp.c");
|
|
compile_applet("cut", PROTOSRC"/protobusybox/coreutils/cut.c");
|
|
compile_applet("dirname", PROTOSRC"/protobusybox/coreutils/dirname.c")
|
|
compile_applet("env", PROTOSRC"/protobusybox/coreutils/env.c");
|
|
compile_applet("expr", PROTOSRC"/protobusybox/coreutils/expr.c");
|
|
compile_applet("head", PROTOSRC"/protobusybox/coreutils/head.c");
|
|
compile_applet("install", PROTOSRC"/protobusybox/coreutils/install.c");
|
|
compile_applet("ln", PROTOSRC"/protobusybox/coreutils/ln.c");
|
|
compile_applet("ls", PROTOSRC"/protobusybox/coreutils/ls.c");
|
|
compile_applet("mkdir", PROTOSRC"/protobusybox/coreutils/mkdir.c");
|
|
compile_applet("mktemp", PROTOSRC"/protobusybox/coreutils/mktemp.c");
|
|
compile_applet("mv",
|
|
PROTOSRC"/protobusybox/coreutils/libcoreutils/cp_mv_stat.c",
|
|
PROTOSRC"/protobusybox/coreutils/mv.c");
|
|
compile_applet("od", PROTOSRC"/protobusybox/coreutils/od.c");
|
|
compile_applet("pwd", PROTOSRC"/protobusybox/coreutils/pwd.c");
|
|
compile_applet("rm", PROTOSRC"/protobusybox/coreutils/rm.c");
|
|
compile_applet("rmdir", PROTOSRC"/protobusybox/coreutils/rmdir.c");
|
|
compile_applet("sleep", PROTOSRC"/protobusybox/coreutils/sleep.c");
|
|
compile_applet("sort", PROTOSRC"/protobusybox/coreutils/sort.c");
|
|
compile_applet("touch", PROTOSRC"/protobusybox/coreutils/touch.c");
|
|
compile_applet("tr", PROTOSRC"/protobusybox/coreutils/tr.c");
|
|
compile_applet("true", PROTOSRC"/protobusybox/coreutils/true.c");
|
|
compile_applet("uname", PROTOSRC"/protobusybox/coreutils/uname.c");
|
|
compile_applet("uniq", PROTOSRC"/protobusybox/coreutils/uniq.c");
|
|
compile_applet("wc", PROTOSRC"/protobusybox/coreutils/wc.c");
|
|
|
|
#define LIBARCHIVE PROTOSRC"/protobusybox/archival/libarchive"
|
|
compile_applet("tar",
|
|
LIBARCHIVE "/data_align.c",
|
|
LIBARCHIVE "/data_extract_all.c",
|
|
LIBARCHIVE "/data_extract_to_stdout.c",
|
|
LIBARCHIVE "/data_skip.c",
|
|
LIBARCHIVE "/decompress_bunzip2.c",
|
|
LIBARCHIVE "/decompress_gunzip.c",
|
|
LIBARCHIVE "/decompress_unxz.c",
|
|
LIBARCHIVE "/filter_accept_all.c",
|
|
LIBARCHIVE "/filter_accept_reject_list.c",
|
|
LIBARCHIVE "/find_list_entry.c",
|
|
LIBARCHIVE "/get_header_tar.c",
|
|
LIBARCHIVE "/header_list.c",
|
|
LIBARCHIVE "/header_skip.c",
|
|
LIBARCHIVE "/header_verbose_list.c",
|
|
LIBARCHIVE "/init_handle.c",
|
|
LIBARCHIVE "/open_transformer.c",
|
|
LIBARCHIVE "/seek_by_jump.c",
|
|
LIBARCHIVE "/seek_by_read.c",
|
|
LIBARCHIVE "/unsafe_prefix.c",
|
|
LIBARCHIVE "/unsafe_symlink_target.c",
|
|
PROTOSRC"/protobusybox/archival/chksum_and_xwrite_tar_header.c",
|
|
PROTOSRC"/protobusybox/archival/tar.c");
|
|
|
|
compile_applet("bzip2", PROTOSRC"/protobusybox/archival/bzip2.c",
|
|
PROTOSRC"/protobusybox/archival/bbunzip.c",
|
|
LIBARCHIVE "/decompress_bunzip2.c",
|
|
LIBARCHIVE "/decompress_gunzip.c",
|
|
LIBARCHIVE "/decompress_unxz.c",
|
|
LIBARCHIVE "/open_transformer.c"
|
|
);
|
|
|
|
compile_applet("awk", PROTOSRC"/protobusybox/editors/awk.c");
|
|
compile_applet("cmp", PROTOSRC"/protobusybox/editors/cmp.c");
|
|
compile_applet("diff", PROTOSRC"/protobusybox/editors/diff.c");
|
|
compile_applet("sed", PROTOSRC"/protobusybox/editors/sed.c");
|
|
|
|
compile_applet("grep", PROTOSRC"/protobusybox/findutils/grep.c");
|
|
compile_applet("find", PROTOSRC"/protobusybox/findutils/find.c");
|
|
compile_applet("xargs", PROTOSRC"/protobusybox/findutils/xargs.c");
|
|
}
|
|
|
|
|
|
// Little things we'll do now when we have a shell /////////////////////////////
|
|
|
|
void verify_tcc_stability(void) {
|
|
run0(STORE_PROTOBUSYBOX"/bin/cp",
|
|
STORE_TINYCC"/bin/tcc", TMP_STAGE1"/tcc-bak");
|
|
compile_tcc(STORE_TINYCC"/bin/tcc");
|
|
run0(STORE_PROTOBUSYBOX"/bin/diff",
|
|
STORE_TINYCC"/bin/tcc", TMP_STAGE1"/tcc-bak");
|
|
}
|
|
|
|
void tweak_output_in_store(void) {
|
|
run0(STORE_PROTOBUSYBOX"/bin/ash", "-uexvc",
|
|
":> "TMP_STAGE1"/empty.c\n"
|
|
STORE_TINYCC"/bin/tcc -c "TMP_STAGE1"/empty.c "
|
|
"-o "TMP_STAGE1"/empty.o\n"
|
|
STORE_TINYCC"/bin/tcc -ar "TMP_STAGE1"/empty.a "
|
|
TMP_STAGE1"/empty.o\n"
|
|
STORE_PROTOBUSYBOX"/bin/cp "TMP_STAGE1"/empty.a "
|
|
STORE_PROTOMUSL"/lib/libm.a\n"
|
|
STORE_PROTOBUSYBOX"/bin/cp "TMP_STAGE1"/empty.a "
|
|
STORE_PROTOMUSL"/lib/libpthread.a\n"
|
|
|
|
STORE_PROTOBUSYBOX"/bin/rm -rf "
|
|
STORE_PROTOMUSL"/include\n"
|
|
STORE_PROTOBUSYBOX"/bin/mkdir -p "
|
|
STORE_PROTOMUSL"/include/bits\n"
|
|
STORE_PROTOBUSYBOX"/bin/cp -rf "
|
|
PROTOSRC"/protomusl/host-generated/sed1/bits "
|
|
PROTOSRC"/protomusl/host-generated/sed2/bits "
|
|
PROTOSRC"/protomusl/arch/generic/* "
|
|
PROTOSRC"/protomusl/arch/x86_64/* "
|
|
PROTOSRC"/protomusl/include/* "
|
|
STORE_PROTOMUSL"/include/\n"
|
|
);
|
|
}
|
|
|
|
|
|
void wrap_tcc_tools(void) {
|
|
#define EXECTCC "#!"STORE_PROTOBUSYBOX"/bin/ash\n" \
|
|
"exec "STORE_TINYCC"/bin/tcc"
|
|
#define PASSTHROUGH "\\\"\\$@\\\"" // i.e., \"\$@\", i.e, "$@"
|
|
run0(STORE_PROTOBUSYBOX"/bin/ash", "-uexvc",
|
|
"PATH="STORE_PROTOBUSYBOX"/bin\n"
|
|
"mkdir -p "STORE_TINYCC"/wrappers\n"
|
|
"cd "STORE_TINYCC"/wrappers\n"
|
|
"_CPP_ARGS=\"-I"STORE_PROTOMUSL"/include\"\n"
|
|
"_LD_ARGS='-static'\n"
|
|
"echo -e \"" EXECTCC " $_LD_ARGS " PASSTHROUGH"\" > cc\n"
|
|
"echo -e \"" EXECTCC " -E $_CPP_ARGS " PASSTHROUGH"\" > cpp\n"
|
|
"echo -e \"" EXECTCC " $_LD_ARGS " PASSTHROUGH"\" > ld\n"
|
|
"echo -e \"" EXECTCC " -ar " PASSTHROUGH "\" > ar\n"
|
|
"chmod +x cc cpp ld ar\n"
|
|
);
|
|
}
|
|
|
|
|
|
// The main plot ///////////////////////////////////////////////////////////////
|
|
|
|
int _start() {
|
|
struct args_accumulator aa_cmd;
|
|
struct args_accumulator aa_link_objs;
|
|
|
|
log("hello from stage1!");
|
|
#ifdef INSIDE_NIX
|
|
log("executed inside nix");
|
|
log("TCC_SEED=" TCC_SEED);
|
|
log("PROTOSRC=" PROTOSRC);
|
|
log("RECIPES_STAGE1=" RECIPES_STAGE1);
|
|
log("TMP_STAGE1=" TMP_STAGE1);
|
|
log("STORE_PROTOBUSYBOX=" STORE_PROTOBUSYBOX);
|
|
log("STORE_PROTOMUSL=" STORE_PROTOMUSL);
|
|
log("STORE_TINYCC=" STORE_TINYCC);
|
|
#endif
|
|
|
|
log("creating directories...");
|
|
mkdirs_at("/",
|
|
TMP_STAGE1, STORE_PROTOBUSYBOX, STORE_PROTOMUSL, STORE_TINYCC);
|
|
|
|
sanity_test();
|
|
|
|
// starting with the seeded TCC
|
|
compile_libtcc1_1st_time_nostd(TCC_SEED);
|
|
compile_protomusl(TCC_SEED);
|
|
test_example_1st_time_nostd(TCC_SEED);
|
|
|
|
// build the first TCC that comes from our sources
|
|
compile_tcc_1st_time_nostd(TCC_SEED);
|
|
test_example_intermediate(STORE_TINYCC"/bin/tcc");
|
|
// rebuild everything with it
|
|
compile_libtcc1(STORE_TINYCC"/bin/tcc");
|
|
compile_protomusl(STORE_TINYCC"/bin/tcc");
|
|
test_example_intermediate(STORE_TINYCC"/bin/tcc");
|
|
|
|
// this is the final tcc we'll build, should not be worth repeating
|
|
compile_tcc(STORE_TINYCC"/bin/tcc");
|
|
// recompile everything else with the final tcc (could be an overkill)
|
|
compile_libtcc1(STORE_TINYCC"/bin/tcc");
|
|
compile_protomusl(STORE_TINYCC"/bin/tcc");
|
|
test_example_intermediate(STORE_TINYCC"/bin/tcc");
|
|
|
|
compile_standalone_busybox_applets(STORE_TINYCC"/bin/tcc");
|
|
|
|
verify_tcc_stability();
|
|
tweak_output_in_store();
|
|
wrap_tcc_tools();
|
|
test_example_final(STORE_TINYCC"/wrappers/cc");
|
|
|
|
log("done");
|
|
|
|
#ifndef CHAINLOAD
|
|
return 0;
|
|
#else
|
|
assert(execve(CHAINLOAD, (char*[]) {CHAINLOAD, 0}, NULL));
|
|
|
|
log("could not exec into next stages (ok when building with make)");
|
|
return 99;
|
|
#endif
|
|
}
|