From 46640b6204cc130d38573d14b8bd00160a0ae6cd Mon Sep 17 00:00:00 2001 From: James Martin Date: Tue, 6 Sep 2022 23:16:23 -0700 Subject: [PATCH] Remove gratuitous platform-specific IO. I don't need some fancy atomic output file updating or posix_fadvise. I removed all platform-specific code except for a single `chmod`. That's not to say there's no advantage to atomically reading or writing files, but for this project, the first rule needs to be KISS. It's premature optimization and overengineering. --- src/io.c | 95 ++++++++++-------------------------------------------- src/io.h | 3 +- src/main.c | 9 +++--- 3 files changed, 22 insertions(+), 85 deletions(-) diff --git a/src/io.c b/src/io.c index bd274dc..314c6ea 100644 --- a/src/io.c +++ b/src/io.c @@ -1,96 +1,27 @@ -#ifdef __linux__ -#define _GNU_SOURCE -#endif - #include "io.h" #include #include #include -#ifdef __linux__ -// On Linux, we create a temporary output file using O_TMPFILE -// so that the output file is never in a corrupt or incomplete state. -// However, O_TMPFILE is not portable, so we only enable it on Linux. -#include -#include +#ifdef __unix__ #include -#include #endif static const char* outfile_name; -FILE* infile; -FILE* outfile; +static FILE* infile; +static FILE* outfile; // HACK: "here" tracking should be handled by the assembler, not IO. uint32_t here = 0; -#ifdef __linux__ void open_files(const char* infile_name, const char* outfile_name_) { outfile_name = outfile_name_; - // To avoid creating a corrupt or incomplete output file, - // we operate on a temporary file and atomically link it only once compilation has succeeded. - unlink(outfile_name); - - int infile_fd = open(infile_name, O_RDONLY); - if (infile_fd == -1) { - fprintf(stderr, "failed to open source file: %s\n", strerror(errno)); - exit(1); - } - off_t infile_len = lseek(infile_fd, 0, SEEK_END); - if (infile_len == (off_t) -1) { - fprintf(stderr, "failed to get length of source file: %s\n", strerror(errno)); - exit(1); - } - // There'll probably never be a source file large enough for this to make a difference, - // and I *certainly* haven't profiled, but... I've always wanted to use these syscalls. :) - posix_fadvise(infile_fd, 0, infile_len, POSIX_FADV_SEQUENTIAL); - posix_fadvise(infile_fd, 0, infile_len, POSIX_FADV_NOREUSE); - infile = fdopen(infile_fd, "rb"); - if (infile_fd == -1) { - fprintf(stderr, "failed to open source file fd as file handle: %s\n", strerror(errno)); - exit(1); - } - - int outfile_fd = open(dirname((char*) outfile_name), O_WRONLY | O_TMPFILE, S_IRWXU | S_IRWXG | S_IRWXO); - if (outfile_fd == -1) { - fprintf(stderr, "failed to create temporary output file: %s\n", strerror(errno)); - exit(1); - } - outfile = fdopen(outfile_fd, "wb"); - if (outfile == NULL) { - fprintf(stderr, "failed to open output file fd as file handle: %s\n", strerror(errno)); - exit(1); - } -} - -void close_files(void) { - if (fflush(outfile) != 0) { - fprintf(stderr, "failed to flush output file: %s\n", strerror(errno)); - exit(1); - } - - char outfile_tempname[20]; - snprintf(outfile_tempname, 20, "/proc/self/fd/%d", fileno(outfile)); - if (linkat(AT_FDCWD, outfile_tempname, AT_FDCWD, outfile_name, AT_SYMLINK_FOLLOW) == -1) { - fprintf(stderr, "failed to link output file into file system: %s\n", strerror(errno)); - exit(1); - } - - fclose(outfile); - fclose(infile); -} -#else -void open_files(const char* infile_name, const char* outfile_name) { infile = fopen(infile_name, "rb"); if (infile == NULL) { fprintf(stderr, "failed to open source file: %s\n", strerror(errno)); exit(1); } - // FIXME: portable temporary output file - // There is no way for us to mark the file as executable. - // Then again, if it's not Linux, that probably doesn't matter, - // because that's the only target platform we support. outfile = fopen(outfile_name, "wb"); if (outfile == NULL) { fprintf(stderr, "failed to open output file: %s\n", strerror(errno)); @@ -99,21 +30,29 @@ void open_files(const char* infile_name, const char* outfile_name) { } void close_files(void) { - // FIXME: set executable permission on unixen other than Linux - // (relevant for e.g. the BSDs, some of which can transparently - // emulate Linux executables.) + fclose(infile); if (fclose(outfile) != 0) { fprintf(stderr, "failed to close output file: %s\n", strerror(errno)); // NOTE: ideally we'd do this on any dirty exit // TODO: use portable tempfiles and then just copy the entire file at the end? if (remove(outfile_name) != 0) { - fprintf(stderr, "failed to remove output file, if it exists, it is corrupt: %s\n", strerror(errno)); + fprintf(stderr, "failed to remove output file; if it exists, it is corrupt: %s\n", strerror(errno)); } exit(1); } - fclose(infile); + + #ifdef __unix__ + chmod(outfile_name, 0777); + #endif +} + +void reserve(size_t len) { + if (fseek(outfile, len, SEEK_CUR) != 0) { + fprintf(stderr, "failed to reserve space in in output file: %s\n", strerror(errno)); + exit(1); + } + here += len; } -#endif void emit(const void* restrict ptr, size_t count) { fwrite(ptr, 1, count, outfile); diff --git a/src/io.h b/src/io.h index 3818171..3f5dc11 100644 --- a/src/io.h +++ b/src/io.h @@ -4,13 +4,12 @@ #include #include -extern FILE* outfile; -extern FILE* infile; extern uint32_t here; void open_files(const char* infile_name, const char* outfile_name); void close_files(void); +void reserve(size_t len); void emit(const void* ptr, size_t count); void emit_u8(uint8_t x); void emit_u32(uint32_t x); diff --git a/src/main.c b/src/main.c index f0f6601..8094d27 100644 --- a/src/main.c +++ b/src/main.c @@ -26,8 +26,7 @@ size_t compile(void) { } static void write_elf(uint64_t entry_point) { - uint64_t file_len = ftell(outfile); - fseek(outfile, 0, SEEK_SET); + uint64_t file_len = here; // Hardcoded ELF header for statically-linked position-independent executable. // Since we only support Linux amd64 static PIE, there's no need to abstract over this for now. @@ -39,7 +38,7 @@ static void write_elf(uint64_t entry_point) { 3, 0, 0x3E, 0, 1, 0, 0, 0, // dynamic executable, amd64, ELF version 1 again 0, 0, 0, 0, 0, 0, 0, 0, // PATCHME: entry point address 0x40, 0, 0, 0, 0, 0, 0, 0, // program header table offset (immediately after ELF) - 0, 0, 0, 0, 0, 0, 0, 0, // section header table offset (none) + 0, 0, 0, 0, 0, 0, 0, 0, // section eader table offset (none) 0, 0, 0, 0, 0x40, 0, 0x38, 0, // flags (none), header sizes 2, 0, 0, 0, 0, 0, 0, 0, // 2 segments, no sections @@ -67,7 +66,7 @@ static void write_elf(uint64_t entry_point) { memcpy(&elf_header[0x98], &file_len, sizeof(uint64_t)); memcpy(&elf_header[0x98 + sizeof(uint64_t)], &file_len, sizeof(uint64_t)); - emit(elf_header, ELF_HEADER_SIZE); + patch(0, elf_header, ELF_HEADER_SIZE); } int main(int argc, char** argv) { @@ -77,7 +76,7 @@ int main(int argc, char** argv) { } open_files(argv[2], argv[1]); - fseek(outfile, ELF_HEADER_SIZE, SEEK_SET); + reserve(ELF_HEADER_SIZE); size_t entry_point = compile(); write_elf((uint64_t) entry_point);