lang-bootstrap/06/recipes/1-stage1.c
2025-04-19 08:57:21 +01:00

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
}