From 3771ed4ee78ce33848256c2d7fcd00dcb31ac771 Mon Sep 17 00:00:00 2001 From: sfja Date: Thu, 17 Apr 2025 13:27:20 +0200 Subject: [PATCH] extract object header --- link/common.c | 163 +++++++++++--------------------------------------- link/header.c | 133 ++++++++++++++++++++++++++++++++++++++++ link/header.h | 34 +++++++++++ link/kern.c | 35 ++++++----- link/link.h | 37 +++--------- link/main.c | 10 ++-- link/ref.c | 43 +++++++++++++ link/ref.h | 24 ++++++++ link/sym.c | 110 ++++++++++++++++++++++++++++++++++ link/sym.h | 33 ++++++++++ 10 files changed, 443 insertions(+), 179 deletions(-) create mode 100644 link/header.c create mode 100644 link/header.h create mode 100644 link/ref.c create mode 100644 link/ref.h create mode 100644 link/sym.c create mode 100644 link/sym.h diff --git a/link/common.c b/link/common.c index 798c082..6e1704a 100644 --- a/link/common.c +++ b/link/common.c @@ -1,9 +1,44 @@ #include "link.h" +#include #include #include #include #include +char* link_strdup(const char* str) +{ + size_t len = strlen(str); + char* val = calloc(len + 1, sizeof(char)); + strncpy(val, str, len); + return val; +} + +FILE* open_output_file(const char* filename) +{ + FILE* fp = fopen(filename, "wb"); + if (!fp) { + fprintf(stderr, + "error: could not open output file '%s' for writing: %s\n", + filename, + strerror(errno)); + return NULL; + } + return fp; +} + +FILE* open_input_file(const char* filename) +{ + FILE* fp = fopen(filename, "rb"); + if (!fp) { + fprintf(stderr, + "error: could not open input file '%s' for reading: %s\n", + filename, + strerror(errno)); + return NULL; + } + return fp; +} + const char* file_iter_next(FileIter* iter) { while (iter->files) { @@ -21,131 +56,3 @@ const char* file_iter_next(FileIter* iter) } return NULL; } - -void header_destroy(Header* header) -{ - for (size_t i = 0; i < header->global_syms_size; ++i) { - free(header->global_syms[i].ident); - } - free(header->global_syms); - for (size_t i = 0; i < header->extern_syms_size; ++i) { - free(header->extern_syms[i].ident); - } - free(header->extern_syms); - free(header->extern_refs); -} - -int header_read_from(Header* header, FILE* fp, const char* filename) -{ - int res = 0; - - uint16_t ident[6] = { 0 }; - if (fread(ident, sizeof(uint16_t), 5, fp) != 5) { - res = 1; - goto leave_1; - } - if (strcmp((char*)ident, "vc3-object") != 0) { - res = 2; - goto leave_1; - } - uint16_t header_size; - if (fread(&header_size, sizeof(uint16_t), 1, fp) != 1) { - res = 3; - goto leave_1; - } - // We want header size in words, not bytes. - header_size /= 2; - - fseek(fp, 0, SEEK_SET); - - uint16_t* data = malloc(sizeof(uint16_t) * header_size); - if (fread(data, sizeof(uint16_t), header_size, fp) != header_size) { - res = 4; - goto leave_2; - } - - size_t dc = 6; - - uint16_t global_syms_size = data[dc++]; - HeaderGlobalSym* global_syms - = malloc(sizeof(HeaderGlobalSym) * global_syms_size); - for (size_t i = 0; i < global_syms_size; ++i) { - uint16_t offset = data[dc++]; - uint16_t ident_len = data[dc++]; - char* ident = calloc(ident_len + 1, sizeof(char)); - strncpy(ident, (char*)&data[dc], ident_len); - dc += (ident_len + 1) / 2; - global_syms[i] = (HeaderGlobalSym) { offset, ident }; - } - - uint16_t extern_syms_size = data[dc++]; - HeaderExternSym* extern_syms - = malloc(sizeof(HeaderExternSym) * extern_syms_size); - for (size_t i = 0; i < extern_syms_size; ++i) { - uint16_t id = data[dc++]; - uint16_t ident_len = data[dc++]; - char* ident = calloc(ident_len + 1, sizeof(char)); - strncpy(ident, (char*)&data[dc], ident_len); - dc += (ident_len + 1) / 2; - extern_syms[i] = (HeaderExternSym) { id, ident }; - } - - uint16_t extern_refs_size = data[dc++]; - HeaderExternRef* extern_refs - = malloc(sizeof(HeaderExternRef) * extern_refs_size); - for (size_t i = 0; i < extern_refs_size; ++i) { - uint16_t id = data[dc++]; - uint16_t offset = data[dc++]; - extern_refs[i] = (HeaderExternRef) { id, offset }; - } - - uint16_t program_size = data[dc++]; - // We want program size in words too. - program_size /= 2; - - *header = (Header) { - header_size, - global_syms_size, - global_syms, - extern_syms_size, - extern_syms, - extern_refs_size, - extern_refs, - program_size, - }; - - res = 0; -leave_2: - free(data); -leave_1: - if (res != 0) { - fprintf(stderr, "error: malformed object file '%s'\n", filename); - printf("res = %d\n", res); - } - return res; -} - -void header_print(const Header* header) -{ - printf("globals:\n"); - printf("address symbol\n"); - printf("-----------------------\n"); - for (size_t i = 0; i < header->global_syms_size; ++i) { - HeaderGlobalSym* sym = &header->global_syms[i]; - printf("%04x, %s\n", sym->offset, sym->ident); - } - printf("\nexterns:\n"); - printf("id symbol\n"); - printf("--------------------\n"); - for (size_t i = 0; i < header->extern_syms_size; ++i) { - HeaderExternSym* sym = &header->extern_syms[i]; - printf("%-3d %s\n", sym->id, sym->ident); - } - printf("\nextern uses:\n"); - printf("id address\n"); - printf("-----------\n"); - for (size_t i = 0; i < header->extern_refs_size; ++i) { - HeaderExternRef* ref = &header->extern_refs[i]; - printf("%3d %04x\n", ref->id, ref->offset); - } -} diff --git a/link/header.c b/link/header.c new file mode 100644 index 0000000..3bf3665 --- /dev/null +++ b/link/header.c @@ -0,0 +1,133 @@ +#include "header.h" +#include +#include +#include +#include + +void header_destroy(Header* header) +{ + for (size_t i = 0; i < header->global_syms_size; ++i) { + free(header->global_syms[i].ident); + } + free(header->global_syms); + for (size_t i = 0; i < header->extern_syms_size; ++i) { + free(header->extern_syms[i].ident); + } + free(header->extern_syms); + free(header->extern_refs); +} + +int header_read_from(Header* header, FILE* fp, const char* filename) +{ + int res = 0; + + uint16_t ident[6] = { 0 }; + if (fread(ident, sizeof(uint16_t), 5, fp) != 5) { + res = 1; + goto leave_1; + } + if (strcmp((char*)ident, "vc3-object") != 0) { + res = 2; + goto leave_1; + } + uint16_t header_size; + if (fread(&header_size, sizeof(uint16_t), 1, fp) != 1) { + res = 3; + goto leave_1; + } + // We want header size in words, not bytes. + header_size /= 2; + + fseek(fp, 0, SEEK_SET); + + uint16_t* data = malloc(sizeof(uint16_t) * header_size); + if (fread(data, sizeof(uint16_t), header_size, fp) != header_size) { + res = 4; + goto leave_2; + } + + size_t dc = 6; + + uint16_t global_syms_size = data[dc++]; + HeaderGlobalSym* global_syms + = malloc(sizeof(HeaderGlobalSym) * global_syms_size); + for (size_t i = 0; i < global_syms_size; ++i) { + uint16_t offset = data[dc++]; + uint16_t ident_len = data[dc++]; + char* ident = calloc(ident_len + 1, sizeof(char)); + strncpy(ident, (char*)&data[dc], ident_len); + dc += (size_t)(ident_len + 1) / 2; + global_syms[i] = (HeaderGlobalSym) { offset, ident }; + } + + uint16_t extern_syms_size = data[dc++]; + HeaderExternSym* extern_syms + = malloc(sizeof(HeaderExternSym) * extern_syms_size); + for (size_t i = 0; i < extern_syms_size; ++i) { + uint16_t id = data[dc++]; + uint16_t ident_len = data[dc++]; + char* ident = calloc(ident_len + 1, sizeof(char)); + strncpy(ident, (char*)&data[dc], ident_len); + dc += (size_t)(ident_len + 1) / 2; + extern_syms[i] = (HeaderExternSym) { id, ident }; + } + + uint16_t extern_refs_size = data[dc++]; + HeaderExternRef* extern_refs + = malloc(sizeof(HeaderExternRef) * extern_refs_size); + for (size_t i = 0; i < extern_refs_size; ++i) { + uint16_t id = data[dc++]; + uint16_t offset = data[dc++]; + extern_refs[i] = (HeaderExternRef) { id, offset }; + } + + uint16_t program_size = data[dc++]; + // We want program size in words too. + program_size /= 2; + + *header = (Header) { + header_size, + global_syms_size, + global_syms, + extern_syms_size, + extern_syms, + extern_refs_size, + extern_refs, + program_size, + }; + + res = 0; +leave_2: + free(data); +leave_1: + if (res != 0) { + fprintf(stderr, "error: malformed object file '%s'\n", filename); + printf("res = %d\n", res); + } + return res; +} + +void header_print(const Header* header) +{ + printf("globals:\n"); + printf("address symbol\n"); + printf("-----------------------\n"); + for (size_t i = 0; i < header->global_syms_size; ++i) { + HeaderGlobalSym* sym = &header->global_syms[i]; + printf("%04x, %s\n", sym->offset, sym->ident); + } + printf("\nexterns:\n"); + printf("id symbol\n"); + printf("--------------------\n"); + for (size_t i = 0; i < header->extern_syms_size; ++i) { + HeaderExternSym* sym = &header->extern_syms[i]; + printf("%-3d %s\n", sym->id, sym->ident); + } + printf("\nextern uses:\n"); + printf("id address\n"); + printf("-----------\n"); + for (size_t i = 0; i < header->extern_refs_size; ++i) { + HeaderExternRef* ref = &header->extern_refs[i]; + printf("%3d %04x\n", ref->id, ref->offset); + } +} diff --git a/link/header.h b/link/header.h new file mode 100644 index 0000000..def8ecd --- /dev/null +++ b/link/header.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +typedef struct { + uint16_t offset; + char* ident; +} HeaderGlobalSym; + +typedef struct { + uint16_t id; + char* ident; +} HeaderExternSym; + +typedef struct { + uint16_t id; + uint16_t offset; +} HeaderExternRef; + +typedef struct { + uint16_t header_size; + uint16_t global_syms_size; + HeaderGlobalSym* global_syms; + uint16_t extern_syms_size; + HeaderExternSym* extern_syms; + uint16_t extern_refs_size; + HeaderExternRef* extern_refs; + uint16_t program_size; +} Header; + +void header_destroy(Header* header); +int header_read_from(Header* header, FILE* fp, const char* filename); +void header_print(const Header* header); diff --git a/link/kern.c b/link/kern.c index 76c1afb..ee6a1d1 100644 --- a/link/kern.c +++ b/link/kern.c @@ -1,5 +1,6 @@ +#include "header.h" #include "link.h" -#include +#include "sym.h" #include #include @@ -7,12 +8,15 @@ int link_kern(const Args* args, FileIter input_files) { int res = 0; - FILE* output_fp = fopen(args->output_file, "wb"); + SymTable sym_table; + sym_table_construct(&sym_table); + SymTable* syms = &sym_table; + + uint16_t offset = 0; + + FILE* output_fp = open_output_file(args->output_file); if (!output_fp) { - fprintf(stderr, - "could not open output file '%s' for writing: %s\n", - args->output_file, - strerror(errno)); + res = -1; goto leave_0; } @@ -21,22 +25,20 @@ int link_kern(const Args* args, FileIter input_files) const char* input_filename = file_iter_next(&input_files); while (input_filename != NULL) { - input_fp = fopen(input_filename, "rb"); + input_fp = open_input_file(input_filename); if (!input_fp) { - fprintf(stderr, - "could not open input file '%s' for reading: %s\n", - input_filename, - strerror(errno)); - goto leave_1; + res = -1; + goto leave_0; } - int read_res = header_read_from(&header, input_fp, input_filename); - if (read_res != 0) { - res = -1; + res = header_read_from(&header, input_fp, input_filename); + if (res != 0) goto leave_1; - } + header_print(&header); + sym_table_walk_header(syms, &header, offset, input_filename); + fclose(input_fp); input_fp = NULL; @@ -50,6 +52,7 @@ leave_1: if (header.header_size != 0) header_destroy(&header); fclose(output_fp); + sym_table_destroy(&sym_table); leave_0: return res; } diff --git a/link/link.h b/link/link.h index 42b44fc..987b978 100644 --- a/link/link.h +++ b/link/link.h @@ -1,7 +1,14 @@ #pragma once +#include #include #include + +char* link_strdup(const char* str); + +FILE* open_output_file(const char* filename); +FILE* open_input_file(const char* filename); + typedef enum { Profile_Linked, Profile_Shared, @@ -24,34 +31,4 @@ typedef struct { const char* file_iter_next(FileIter* iter); -typedef struct { - uint16_t offset; - char* ident; -} HeaderGlobalSym; - -typedef struct { - uint16_t id; - char* ident; -} HeaderExternSym; - -typedef struct { - uint16_t id; - uint16_t offset; -} HeaderExternRef; - -typedef struct { - uint16_t header_size; - uint16_t global_syms_size; - HeaderGlobalSym* global_syms; - uint16_t extern_syms_size; - HeaderExternSym* extern_syms; - uint16_t extern_refs_size; - HeaderExternRef* extern_refs; - uint16_t program_size; -} Header; - -void header_destroy(Header* header); -int header_read_from(Header* header, FILE* fp, const char* filename); -void header_print(const Header* header); - int link_kern(const Args* args, FileIter input_files); diff --git a/link/main.c b/link/main.c index 061d936..82a4213 100644 --- a/link/main.c +++ b/link/main.c @@ -32,14 +32,14 @@ static inline Args parse_args(int argc, char** argv) if (strcmp(argv[i], "-o") == 0) { i += 1; if (i >= argc) { - fprintf(stderr, "no filename given to -o\n"); + fprintf(stderr, "error: no filename given to -o\n"); exit(1); } output_file = argv[i]; } else if (strcmp(argv[i], "-p") == 0) { i += 1; if (i >= argc) { - fprintf(stderr, "no profile given to -p\n"); + fprintf(stderr, "error: no profile given to -p\n"); exit(1); } @@ -53,8 +53,8 @@ static inline Args parse_args(int argc, char** argv) } } if (!found) { - fprintf(stderr, "invalid profile '%s'\n", argv[i]); - fprintf(stderr, "valid profiles: linked, shared, kern\n"); + fprintf(stderr, "error: invalid profile '%s'\n", argv[i]); + fprintf(stderr, "info: valid profiles: linked, shared, kern\n"); exit(1); } } else { @@ -62,7 +62,7 @@ static inline Args parse_args(int argc, char** argv) } } if (input_files == 0) { - fprintf(stderr, "no input files specified\n"); + fprintf(stderr, "error: no input files specified\n"); exit(1); } if (output_file == NULL) { diff --git a/link/ref.c b/link/ref.c new file mode 100644 index 0000000..612bd5d --- /dev/null +++ b/link/ref.c @@ -0,0 +1,43 @@ +#include "ref.h" +#include "sym.h" +#include +#include + +void ref_queue_construct(RefQueue* queue) +{ + const size_t start_capacity = 128; + *queue = (RefQueue) { + .data = malloc(sizeof(Ref) * start_capacity), + .capacity = start_capacity, + .back = 0, + .front = 0, + }; +} + +void ref_queue_destroy(RefQueue* queue) +{ + free(queue->data); +} + +void ref_queue_push(RefQueue* queue, const Sym* sym, uint16_t offset) +{ + if (queue->front + 1 > queue->capacity) { + queue->capacity *= 2; + queue->data = realloc(queue->data, sizeof(Ref) * queue->capacity); + } + queue->data[queue->front++] = (Ref) { sym, offset }; +} + +Ref ref_queue_pop(RefQueue* queue) +{ + if (queue->back == queue->front) { + fprintf(stderr, "panic: queue underflow\n"); + exit(1); + } + return queue->data[queue->back++]; +} + +size_t ref_queue_size(const RefQueue* queue) +{ + return queue->front - queue->back; +} diff --git a/link/ref.h b/link/ref.h new file mode 100644 index 0000000..e6f72b1 --- /dev/null +++ b/link/ref.h @@ -0,0 +1,24 @@ +#pragma once + +#include "sym.h" +#include + +typedef struct { + const Sym* sym; + uint16_t offset; +} Ref; + +typedef struct RefQueueChunk RefQueueChunk; + +typedef struct { + Ref* data; + size_t capacity; + size_t back; + size_t front; +} RefQueue; + +void ref_queue_construct(RefQueue* queue); +void ref_queue_destroy(RefQueue* queue); +void ref_queue_push(RefQueue* queue, const Sym* sym, uint16_t offset); +Ref ref_queue_pop(RefQueue* queue); +size_t ref_queue_size(const RefQueue* queue); diff --git a/link/sym.c b/link/sym.c new file mode 100644 index 0000000..f3a57f2 --- /dev/null +++ b/link/sym.c @@ -0,0 +1,110 @@ +#include "sym.h" +#include "header.h" +#include "link.h" +#include +#include +#include +#include + +void sym_destroy(Sym* sym) +{ + free(sym->ident); +} + +void sym_table_construct(SymTable* table) +{ + const size_t syms_capacity = 16; + *table = (SymTable) { + .syms = malloc(sizeof(SymTable) * syms_capacity), + .syms_capacity = syms_capacity, + .syms_size = 0, + }; +} + +void sym_table_destroy(SymTable* table) +{ + for (size_t i = 0; i < table->syms_size; ++i) { + sym_destroy(&table->syms[i]); + } + free(table->syms); +} + +Sym* sym_table_find(SymTable* table, const char* ident) +{ + for (size_t i = 0; i < table->syms_size; ++i) { + Sym* sym = &table->syms[i]; + if (strcmp(sym->ident, ident) == 0) { + return sym; + } + } + return NULL; +} + +static inline void sym_table_add(SymTable* table, + char* ident, + const char* filename, + uint16_t offset, + bool resolved) +{ + if (table->syms_size + 1 > table->syms_capacity) { + table->syms_capacity *= 2; + table->syms + = realloc(table->syms, sizeof(SymTable) * table->syms_capacity); + } + table->syms[table->syms_size++] + = (Sym) { ident, filename, offset, resolved }; +} + +void sym_table_add_unresolved( + SymTable* table, char* ident, const char* filename) +{ + sym_table_add(table, ident, filename, 0, false); +} + +void sym_table_add_resolved( + SymTable* table, char* ident, const char* filename, uint16_t offset) +{ + sym_table_add(table, ident, filename, offset, true); +} + +int sym_table_walk_header( + SymTable* syms, Header* header, uint16_t offset, const char* input_filename) +{ + for (size_t i = 0; i < header->global_syms_size; ++i) { + const HeaderGlobalSym* global_sym = &header->global_syms[i]; + Sym* sym = sym_table_find(syms, global_sym->ident); + + if (!sym) { + sym_table_add_resolved(syms, + link_strdup(global_sym->ident), + input_filename, + offset + global_sym->offset); + } else if (!sym->resolved) { + sym->offset = offset + global_sym->offset; + sym->resolved = true; + } else { + + fprintf(stderr, + "error: duplicate symbol '%s' defined in %s\n", + global_sym->ident, + input_filename); + fprintf(stderr, + "info: symbol '%s' originally defined in %s\n", + sym->ident, + sym->filename); + return -1; + } + } + + for (size_t i = 0; i < header->extern_syms_size; ++i) { + const HeaderExternSym* extern_sym = &header->extern_syms[i]; + Sym* sym = sym_table_find(syms, extern_sym->ident); + + if (!sym) { + sym_table_add_unresolved( + syms, link_strdup(extern_sym->ident), input_filename); + } + } + + return 0; +} diff --git a/link/sym.h b/link/sym.h new file mode 100644 index 0000000..105a4ac --- /dev/null +++ b/link/sym.h @@ -0,0 +1,33 @@ +#pragma once + +#include "header.h" +#include +#include +#include + +typedef struct { + char* ident; + const char* filename; + uint16_t offset; + bool resolved; +} Sym; + +void sym_destroy(Sym* sym); + +typedef struct { + Sym* syms; + size_t syms_capacity; + size_t syms_size; +} SymTable; + +void sym_table_construct(SymTable* table); +void sym_table_destroy(SymTable* table); +Sym* sym_table_find(SymTable* table, const char* ident); +void sym_table_add_unresolved( + SymTable* table, char* ident, const char* filename); +void sym_table_add_resolved( + SymTable* table, char* ident, const char* filename, uint16_t offset); +int sym_table_walk_header(SymTable* syms, + Header* header, + uint16_t offset, + const char* input_filename);