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.
master
James T. Martin 2022-09-06 23:16:23 -07:00
parent 57aa667000
commit 46640b6204
Signed by: james
GPG Key ID: D6FB2F9892F9B225
3 changed files with 22 additions and 85 deletions

View File

@ -1,96 +1,27 @@
#ifdef __linux__
#define _GNU_SOURCE
#endif
#include "io.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#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 <fcntl.h>
#include <libgen.h>
#ifdef __unix__
#include <sys/stat.h>
#include <unistd.h>
#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);

View File

@ -4,13 +4,12 @@
#include <stdint.h>
#include <stdio.h>
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);

View File

@ -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);