chibicc/main.c

386 lines
8.7 KiB
C
Raw Normal View History

2020-10-07 14:11:16 +03:00
#include "chibicc.h"
2019-08-03 09:36:43 +03:00
2020-09-25 17:18:32 +03:00
StringArray include_paths;
2020-08-20 15:37:02 +03:00
static bool opt_E;
2020-10-08 08:34:23 +03:00
static bool opt_S;
2020-09-19 14:36:26 +03:00
static bool opt_c;
2020-08-15 16:30:28 +03:00
static bool opt_cc1;
static bool opt_hash_hash_hash;
2020-05-08 14:44:25 +03:00
static char *opt_o;
2020-09-03 13:24:23 +03:00
char *base_file;
2020-08-18 03:16:57 +03:00
static char *output_file;
static StringArray input_paths;
2020-10-08 08:34:23 +03:00
static StringArray tmpfiles;
2020-05-08 14:44:25 +03:00
static void usage(int status) {
fprintf(stderr, "chibicc [ -o <path> ] <file>\n");
exit(status);
}
2020-08-18 03:16:57 +03:00
static bool take_arg(char *arg) {
2020-09-25 17:18:32 +03:00
char *x[] = {"-o", "-I"};
for (int i = 0; i < sizeof(x) / sizeof(*x); i++)
if (!strcmp(arg, x[i]))
return true;
return false;
2020-08-18 03:16:57 +03:00
}
2020-05-08 14:44:25 +03:00
static void parse_args(int argc, char **argv) {
2020-08-18 03:16:57 +03:00
// Make sure that all command line options that take an argument
// have an argument.
for (int i = 1; i < argc; i++)
if (take_arg(argv[i]))
if (!argv[++i])
usage(1);
2020-05-08 14:44:25 +03:00
for (int i = 1; i < argc; i++) {
2020-08-15 16:30:28 +03:00
if (!strcmp(argv[i], "-###")) {
opt_hash_hash_hash = true;
continue;
}
if (!strcmp(argv[i], "-cc1")) {
opt_cc1 = true;
continue;
}
2020-05-08 14:44:25 +03:00
if (!strcmp(argv[i], "--help"))
usage(0);
if (!strcmp(argv[i], "-o")) {
2020-08-18 03:16:57 +03:00
opt_o = argv[++i];
2020-05-08 14:44:25 +03:00
continue;
}
if (!strncmp(argv[i], "-o", 2)) {
opt_o = argv[i] + 2;
continue;
}
2020-10-08 08:34:23 +03:00
if (!strcmp(argv[i], "-S")) {
opt_S = true;
continue;
}
2020-09-19 14:36:26 +03:00
if (!strcmp(argv[i], "-c")) {
opt_c = true;
continue;
}
2020-08-20 15:37:02 +03:00
if (!strcmp(argv[i], "-E")) {
opt_E = true;
continue;
}
2020-09-25 17:18:32 +03:00
if (!strncmp(argv[i], "-I", 2)) {
strarray_push(&include_paths, argv[i] + 2);
continue;
}
2020-08-18 03:16:57 +03:00
if (!strcmp(argv[i], "-cc1-input")) {
base_file = argv[++i];
continue;
}
if (!strcmp(argv[i], "-cc1-output")) {
output_file = argv[++i];
continue;
}
2020-05-08 14:44:25 +03:00
if (argv[i][0] == '-' && argv[i][1] != '\0')
error("unknown argument: %s", argv[i]);
2020-08-18 03:16:57 +03:00
strarray_push(&input_paths, argv[i]);
2020-05-08 14:44:25 +03:00
}
2020-08-18 03:16:57 +03:00
if (input_paths.len == 0)
2020-05-08 14:44:25 +03:00
error("no input files");
}
static FILE *open_file(char *path) {
if (!path || strcmp(path, "-") == 0)
return stdout;
FILE *out = fopen(path, "w");
if (!out)
error("cannot open output file: %s: %s", path, strerror(errno));
return out;
}
2020-09-19 14:36:26 +03:00
static bool endswith(char *p, char *q) {
int len1 = strlen(p);
int len2 = strlen(q);
return (len1 >= len2) && !strcmp(p + len1 - len2, q);
}
2020-10-08 08:34:23 +03:00
// Replace file extension
static char *replace_extn(char *tmpl, char *extn) {
char *filename = basename(strdup(tmpl));
char *dot = strrchr(filename, '.');
if (dot)
*dot = '\0';
return format("%s%s", filename, extn);
}
static void cleanup(void) {
for (int i = 0; i < tmpfiles.len; i++)
unlink(tmpfiles.data[i]);
}
static char *create_tmpfile(void) {
char *path = strdup("/tmp/chibicc-XXXXXX");
int fd = mkstemp(path);
if (fd == -1)
error("mkstemp failed: %s", strerror(errno));
close(fd);
strarray_push(&tmpfiles, path);
return path;
}
2020-08-15 16:30:28 +03:00
static void run_subprocess(char **argv) {
// If -### is given, dump the subprocess's command line.
if (opt_hash_hash_hash) {
fprintf(stderr, "%s", argv[0]);
for (int i = 1; argv[i]; i++)
fprintf(stderr, " %s", argv[i]);
fprintf(stderr, "\n");
}
if (fork() == 0) {
// Child process. Run a new command.
execvp(argv[0], argv);
fprintf(stderr, "exec failed: %s: %s\n", argv[0], strerror(errno));
_exit(1);
}
2019-08-03 09:36:43 +03:00
2020-08-15 16:30:28 +03:00
// Wait for the child process to finish.
int status;
while (wait(&status) > 0);
if (status != 0)
exit(1);
}
2020-10-08 08:34:23 +03:00
static void run_cc1(int argc, char **argv, char *input, char *output) {
2020-08-15 16:30:28 +03:00
char **args = calloc(argc + 10, sizeof(char *));
memcpy(args, argv, argc * sizeof(char *));
args[argc++] = "-cc1";
2020-10-08 08:34:23 +03:00
2020-08-18 03:16:57 +03:00
if (input) {
args[argc++] = "-cc1-input";
2020-10-08 08:34:23 +03:00
args[argc++] = input;
2020-08-18 03:16:57 +03:00
}
2020-10-08 08:34:23 +03:00
if (output) {
2020-08-18 03:16:57 +03:00
args[argc++] = "-cc1-output";
2020-10-08 08:34:23 +03:00
args[argc++] = output;
}
2020-08-15 16:30:28 +03:00
run_subprocess(args);
}
2020-08-20 15:37:02 +03:00
// Print tokens to stdout. Used for -E.
static void print_tokens(Token *tok) {
FILE *out = open_file(opt_o ? opt_o : "-");
int line = 1;
for (; tok->kind != TK_EOF; tok = tok->next) {
if (line > 1 && tok->at_bol)
fprintf(out, "\n");
2020-08-18 04:45:03 +03:00
if (tok->has_space && !tok->at_bol)
fprintf(out, " ");
fprintf(out, "%.*s", tok->len, tok->loc);
2020-08-20 15:37:02 +03:00
line++;
}
fprintf(out, "\n");
}
2020-08-15 16:30:28 +03:00
static void cc1(void) {
// Tokenize and parse.
2020-08-18 03:16:57 +03:00
Token *tok = tokenize_file(base_file);
2020-09-03 13:24:23 +03:00
if (!tok)
error("%s: %s", base_file, strerror(errno));
2020-08-18 03:41:59 +03:00
tok = preprocess(tok);
2020-08-20 15:37:02 +03:00
// If -E is given, print out preprocessed C code as a result.
if (opt_E) {
print_tokens(tok);
return;
}
Obj *prog = parse(tok);
2020-10-07 14:12:19 +03:00
// Traverse the AST to emit assembly.
2020-08-18 03:16:57 +03:00
FILE *out = open_file(output_file);
2020-05-08 14:44:25 +03:00
codegen(prog, out);
2020-08-15 16:30:28 +03:00
}
2020-10-08 08:34:23 +03:00
static void assemble(char *input, char *output) {
char *cmd[] = {"as", "-c", input, "-o", output, NULL};
run_subprocess(cmd);
}
2020-09-19 14:36:26 +03:00
static char *find_file(char *pattern) {
char *path = NULL;
glob_t buf = {};
glob(pattern, 0, NULL, &buf);
if (buf.gl_pathc > 0)
path = strdup(buf.gl_pathv[buf.gl_pathc - 1]);
globfree(&buf);
return path;
}
// Returns true if a given file exists.
2020-08-30 12:57:20 +03:00
bool file_exists(char *path) {
2020-09-19 14:36:26 +03:00
struct stat st;
return !stat(path, &st);
}
static char *find_libpath(void) {
if (file_exists("/usr/lib/x86_64-linux-gnu/crti.o"))
return "/usr/lib/x86_64-linux-gnu";
if (file_exists("/usr/lib64/crti.o"))
return "/usr/lib64";
error("library path is not found");
}
static char *find_gcc_libpath(void) {
char *paths[] = {
"/usr/lib/gcc/x86_64-linux-gnu/*/crtbegin.o",
"/usr/lib/gcc/x86_64-pc-linux-gnu/*/crtbegin.o", // For Gentoo
"/usr/lib/gcc/x86_64-redhat-linux/*/crtbegin.o", // For Fedora
};
for (int i = 0; i < sizeof(paths) / sizeof(*paths); i++) {
char *path = find_file(paths[i]);
if (path)
return dirname(path);
}
error("gcc library path is not found");
}
static void run_linker(StringArray *inputs, char *output) {
StringArray arr = {};
strarray_push(&arr, "ld");
strarray_push(&arr, "-o");
strarray_push(&arr, output);
strarray_push(&arr, "-m");
strarray_push(&arr, "elf_x86_64");
strarray_push(&arr, "-dynamic-linker");
strarray_push(&arr, "/lib64/ld-linux-x86-64.so.2");
char *libpath = find_libpath();
char *gcc_libpath = find_gcc_libpath();
strarray_push(&arr, format("%s/crt1.o", libpath));
strarray_push(&arr, format("%s/crti.o", libpath));
strarray_push(&arr, format("%s/crtbegin.o", gcc_libpath));
strarray_push(&arr, format("-L%s", gcc_libpath));
strarray_push(&arr, format("-L%s", libpath));
strarray_push(&arr, format("-L%s/..", libpath));
strarray_push(&arr, "-L/usr/lib64");
strarray_push(&arr, "-L/lib64");
strarray_push(&arr, "-L/usr/lib/x86_64-linux-gnu");
strarray_push(&arr, "-L/usr/lib/x86_64-pc-linux-gnu");
strarray_push(&arr, "-L/usr/lib/x86_64-redhat-linux");
strarray_push(&arr, "-L/usr/lib");
strarray_push(&arr, "-L/lib");
for (int i = 0; i < inputs->len; i++)
strarray_push(&arr, inputs->data[i]);
strarray_push(&arr, "-lc");
strarray_push(&arr, "-lgcc");
strarray_push(&arr, "--as-needed");
strarray_push(&arr, "-lgcc_s");
strarray_push(&arr, "--no-as-needed");
strarray_push(&arr, format("%s/crtend.o", gcc_libpath));
strarray_push(&arr, format("%s/crtn.o", libpath));
strarray_push(&arr, NULL);
run_subprocess(arr.data);
}
2020-08-15 16:30:28 +03:00
int main(int argc, char **argv) {
2020-10-08 08:34:23 +03:00
atexit(cleanup);
2020-08-15 16:30:28 +03:00
parse_args(argc, argv);
if (opt_cc1) {
cc1();
return 0;
}
2020-08-20 15:37:02 +03:00
if (input_paths.len > 1 && opt_o && (opt_c || opt_S | opt_E))
error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files");
2020-09-19 14:36:26 +03:00
StringArray ld_args = {};
2020-08-18 03:16:57 +03:00
for (int i = 0; i < input_paths.len; i++) {
char *input = input_paths.data[i];
char *output;
if (opt_o)
output = opt_o;
else if (opt_S)
output = replace_extn(input, ".s");
else
output = replace_extn(input, ".o");
2020-09-19 14:36:26 +03:00
// Handle .o
if (endswith(input, ".o")) {
strarray_push(&ld_args, input);
continue;
}
// Handle .s
if (endswith(input, ".s")) {
if (!opt_S)
assemble(input, output);
continue;
}
// Handle .c
if (!endswith(input, ".c") && strcmp(input, "-"))
error("unknown file extension: %s", input);
2020-08-20 15:37:02 +03:00
// Just preprocess
if (opt_E) {
run_cc1(argc, argv, input, NULL);
continue;
}
// Compile
2020-08-18 03:16:57 +03:00
if (opt_S) {
run_cc1(argc, argv, input, output);
continue;
}
2020-09-19 14:36:26 +03:00
// Compile and assemble
if (opt_c) {
char *tmp = create_tmpfile();
run_cc1(argc, argv, input, tmp);
assemble(tmp, output);
continue;
}
// Compile, assemble and link
char *tmp1 = create_tmpfile();
char *tmp2 = create_tmpfile();
run_cc1(argc, argv, input, tmp1);
assemble(tmp1, tmp2);
strarray_push(&ld_args, tmp2);
continue;
2020-10-08 08:34:23 +03:00
}
2020-09-19 14:36:26 +03:00
if (ld_args.len > 0)
run_linker(&ld_args, opt_o ? opt_o : "a.out");
return 0;
}