aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--boot/build.c137
-rw-r--r--boot/catboot.c6
-rw-r--r--boot/common.c4
-rwxr-xr-xboot/scripts/embed.sh8
-rw-r--r--flake.nix3
6 files changed, 140 insertions, 23 deletions
diff --git a/Makefile b/Makefile
index bc091f0..046c3c1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,9 @@
MUSL_LIB ?= /usr/lib/musl
MUSL_DEV ?= /usr/include/musl
-DEFINES ?= -DMUSL_LIB=\"$(MUSL_LIB)\" -DMUSL_DEV=\"$(MUSL_DEV)\"
+DEFINES ?= -DCATBOOT_BACKEND_TCC -DMUSL_LIB=\"$(MUSL_LIB)\" -DMUSL_DEV=\"$(MUSL_DEV)\"
CFLAGS ?= -std=c99 -Wall -Werror -static -g -O0 $(DEFINES)
+LDFLAGS ?= -ltcc
.DEFAULT_GOAL := all
@@ -16,7 +17,7 @@ build/catskill: build $(SOURCES)
build/catboot: build $(BOOTSTRAP_SOURCES) boot/scripts/embed.sh
./boot/scripts/embed.sh $(BOOTSTRAP_SOURCES)
- $(CC) $(CFLAGS) -o ./build/catboot ./build/boot/catboot.c
+ $(CC) $(CFLAGS) -o ./build/catboot ./build/boot/catboot.c $(LDFLAGS)
build/catboot-test: build $(BOOTSTRAP_TEST_SOURCES)
$(CC) $(CFLAGS) -o ./build/catboot-test ./boot/tests/test.c
diff --git a/boot/build.c b/boot/build.c
index 1a1cb21..0c05372 100644
--- a/boot/build.c
+++ b/boot/build.c
@@ -99,9 +99,9 @@ write_build_file(struct String source, struct String input_path)
bool
copy_runtime_library(struct String build_path)
{
- const ascii core[] = CATSKILL_EMBED("./boot/runtime/core.c");
- const ascii runtime[] = CATSKILL_EMBED("./boot/runtime/runtime.c");
- const ascii stubs[] = CATSKILL_EMBED("./boot/runtime/stubs.c");
+ const ascii core[] = CATBOOT_EMBED("./boot/runtime/core.c");
+ const ascii runtime[] = CATBOOT_EMBED("./boot/runtime/runtime.c");
+ const ascii stubs[] = CATBOOT_EMBED("./boot/runtime/stubs.c");
struct String build_runtime_path = string_append_c_str(build_path, "/runtime/");
if (mkdir(string_c_str(build_runtime_path), 0700) == -1) {
@@ -166,17 +166,11 @@ struct Build_Result
struct String output_path;
};
+#ifdef CATBOOT_BACKEND_CLANG
struct Build_Result
-compile_using_backend(
+compile_using_backend_clang(
struct String build_path, struct String source_path, struct String output_path)
{
- log_debug("current backend: LLVM Clang (CLI)\n");
-
- // TODO: right now we always run the compiler from this source directory,
- // so we can refer to the runtime libraries by a relative path "./boot/runtime/",
- // but in the future we should embed these files into this compiler, and write
- // them out into the build directory.
-
// probably too many flags for our purposes, but we kind of want
// this simple bootstrapping output to be the eventual target
// of our self-hosted compiler stack, so we avoid anything fancy
@@ -246,6 +240,127 @@ compile_using_backend(
.output_path = output_path,
};
}
+#endif
+
+#ifdef CATBOOT_BACKEND_TCC
+
+#include <libtcc.h>
+
+#define BUILD_TCC_MAX_ERROR_LOG_LENGTH 65536
+
+static struct String_Buffer tcc_errors;
+bool tcc_errors_truncated;
+
+void
+build_tcc_on_error(void* something, const ascii* message)
+{
+ (void)something;
+ if (tcc_errors_truncated) return;
+
+ const ascii* log_prefix = ANSI_BOLD " tcc: " ANSI_NO_BOLD;
+ const ascii* log_suffix = "\n";
+
+ uint added_length = strlen(message) + strlen(log_prefix) + strlen(log_suffix);
+ uint free_space = string_buffer_capacity(&tcc_errors) - string_buffer_length(&tcc_errors);
+
+ if (free_space <= added_length) {
+ tcc_errors_truncated = true;
+ return;
+ }
+
+ string_buffer_append_c_str(&tcc_errors, log_prefix);
+ string_buffer_append_c_str(&tcc_errors, message);
+ string_buffer_append_c_str(&tcc_errors, log_suffix);
+}
+
+void
+build_tcc_reset_errors()
+{
+ if (tcc_errors.data == nil)
+ tcc_errors = string_buffer_new(BUILD_TCC_MAX_ERROR_LOG_LENGTH);
+ string_buffer_clear(&tcc_errors);
+ tcc_errors_truncated = false;
+}
+
+struct Build_Result
+compile_using_backend_tcc(
+ struct String build_path, struct String source_path, struct String output_path)
+{
+ TCCState* tcc = tcc_new();
+ if (!tcc) {
+ log_error("failed to initialize backend (TCC) compiler\n");
+ goto error;
+ }
+
+ build_tcc_reset_errors();
+ tcc_set_error_func(tcc, nil, build_tcc_on_error);
+
+ // NOTE: this has to be called prior to `tcc_set_output_type`, apparently setting the output
+ // to EXE pulls in some kind of default runtime which declares the _start & co. symbols before we can!
+ tcc_set_options(tcc, "-static -nostdlib -nostdinc");
+
+ // we only want a simple executable output.
+ // other options can create a dynamic library, or add the output
+ // to our memory to run directly, like for a just-in-time compiler, pretty neat!
+ tcc_set_output_type(tcc, TCC_OUTPUT_EXE);
+
+ // add necessary include paths
+ struct String runtime_path = string_append_c_str(build_path, "/runtime/");
+ tcc_add_include_path(tcc, string_c_str(runtime_path));
+
+ tcc_add_sysinclude_path(tcc, MUSL_DEV "/include");
+
+ // linking order, must be exact:
+ // crt1.o -> crti.o -> stubs.c -> source.c -> libc.a -> crtn.o
+ tcc_add_file(tcc, MUSL_LIB "/lib/crt1.o");
+ tcc_add_file(tcc, MUSL_LIB "/lib/crti.o");
+
+ struct String stubs_path = string_append_c_str(runtime_path, "stubs.c");
+ tcc_add_file(tcc, string_c_str(stubs_path));
+
+ if (tcc_add_file(tcc, string_c_str(source_path)) == -1) {
+ log_error(
+ "failed to add transpiled source to backend compiler (TCC), probably malformed?\n");
+ goto error;
+ }
+
+ tcc_add_file(tcc, MUSL_LIB "/lib/libc.a");
+ tcc_add_file(tcc, MUSL_LIB "/lib/crtn.o");
+
+ // now run the compilation, and output!
+ if (tcc_output_file(tcc, string_c_str(output_path)) == -1) {
+ log_error("error during backend (TCC) compilation!\n");
+ goto error;
+ }
+
+ tcc_delete(tcc);
+ return (struct Build_Result){ .success = true };
+
+error:
+ tcc_delete(tcc);
+ struct String log = string_buffer_to_string(&tcc_errors);
+ return (struct Build_Result){
+ .success = false,
+ .compiler_exit_code = -1,
+ .compiler_log = log,
+ };
+}
+#endif
+
+struct Build_Result
+compile_using_backend(
+ struct String build_path, struct String source_path, struct String output_path)
+{
+#if defined(CATBOOT_BACKEND_CLANG)
+ log_debug("current backend: LLVM Clang (CLI)\n");
+ return compile_using_backend_clang(build_path, source_path, output_path);
+#elif defined(CATBOOT_BACKEND_TCC)
+ log_debug("current backend: TCC (libtcc)\n");
+ return compile_using_backend_tcc(build_path, source_path, output_path);
+#else
+#error "no backend defined for compilation"
+#endif
+}
struct Build_Result
build_executable(struct String source, struct String output_path)
diff --git a/boot/catboot.c b/boot/catboot.c
index db100dd..e78a401 100644
--- a/boot/catboot.c
+++ b/boot/catboot.c
@@ -22,7 +22,7 @@
#include "catboot.h"
-#define VERSION "0.0.0"
+#define CATBOOT_VERSION "0.0.0"
struct String
read_file(const ascii* path)
@@ -66,7 +66,7 @@ version(void)
{
fprintf(
stderr,
- "catboot (catskill), version " VERSION "\n"
+ "catboot (catskill), version " CATBOOT_VERSION "\n"
"\n"
"This program's source code is subject to the terms of the Mozilla Public\n"
"License, v. 2.0. If a copy of the MPL was not distributed with this\n"
@@ -265,7 +265,7 @@ default_command(struct Command_Arguments* arguments)
if (!build_result.success) {
log_error("backend failure with exit code %ld\n", build_result.compiler_exit_code);
log_error(
- "compiler output:\n%.*s\n", (int)build_result.compiler_log.length,
+ "compiler output:\n%.*s", (int)build_result.compiler_log.length,
build_result.compiler_log.data);
return COMMAND_FAIL;
diff --git a/boot/common.c b/boot/common.c
index 125af86..2f9bdd0 100644
--- a/boot/common.c
+++ b/boot/common.c
@@ -53,11 +53,11 @@
#define NORETURN _Noreturn
// macro used as target for embedding binary files during build into a byte array.
-// usage: `const byte test_data[] = CATSKILL_EMBED(./files/test_data.txt);`
+// usage: `const byte test_data[] = CATBOOT_EMBED(./files/test_data.txt);`
// the array will be filled with hexadecimal values of the file contents,
// with a nil byte appended at the end.
// the size of the array can be retrieved through `strlen(array)`, or `ARRAY_SIZE(array)-1`.
-#define CATSKILL_EMBED(...) { 0 }
+#define CATBOOT_EMBED(...) { 0 }
// ansi escape codes for terminal color and style
#define ANSI(code) "\33[" code "m"
diff --git a/boot/scripts/embed.sh b/boot/scripts/embed.sh
index baf2e38..98a0c9d 100755
--- a/boot/scripts/embed.sh
+++ b/boot/scripts/embed.sh
@@ -2,7 +2,7 @@
# embed.sh
# a simple script to handle embedding binary file contents into invocations
-# of the CATSKILL_EMBED macro within catboot compiler source files.
+# of the CATBOOT_EMBED macro within catboot compiler source files.
# invoked with `./embed.sh source-1.c source-2.c header.h`
# outputs a file structure within the ./build/ directory matching the source
# directory, with the correct replacements done.
@@ -23,9 +23,9 @@ process_file() {
cp "$source_file" "$output_file"
- local embed_regex='CATSKILL_EMBED\("([^)]+)"\)' # const byte data[] = CATSKILL_EMBED("./file");
- local comment_regex='^\s*//' # // the macro CATSKILL_EMBED does...
- local define_regex='^\s*#define' # #define CATSKILL_EMBED {0}
+ local embed_regex='CATBOOT_EMBED\("([^)]+)"\)' # const byte data[] = CATBOOT_EMBED("./file");
+ local comment_regex='^\s*//' # // the macro CATBOOT_EMBED does...
+ local define_regex='^\s*#define' # #define CATBOOT_EMBED {0}
while IFS= read -r line; do
# skip lines which are just comments mentioning the macro and
diff --git a/flake.nix b/flake.nix
index 1d999f7..9c860eb 100644
--- a/flake.nix
+++ b/flake.nix
@@ -28,7 +28,8 @@
devShells = eachSystem (
{ system, pkgs }:
let
- inherit (pkgs) tinycc pkg-config gnumake lldb clang bintools xxd;
+ inherit (pkgs) pkgsStatic pkg-config gnumake lldb clang bintools xxd;
+ inherit (pkgsStatic) tinycc;
stdenv = pkgs.llvmPackages.stdenv;
mkShell = pkgs.mkShell.override { inherit stdenv; };