From f25dc5cc8d76b209df3fac5d01be7b66c7d7aa77 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Mon, 12 May 2025 16:02:07 -0700 Subject: [PATCH] pack export table into obj and input back to the linker --- src/linker/lnk.c | 169 +++++++--- src/linker/lnk.h | 6 +- src/linker/lnk_config.c | 69 +--- src/linker/lnk_config.h | 20 +- src/linker/lnk_export_table.c | 591 +++++++++++++++++++-------------- src/linker/lnk_export_table.h | 62 ++-- src/linker/lnk_section_table.c | 8 +- src/linker/lnk_section_table.h | 3 +- 8 files changed, 525 insertions(+), 403 deletions(-) diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 9eeff7bb..2fa3e468 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -869,6 +869,48 @@ lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path) } } +internal void +lnk_push_export(Arena *arena, HashTable *export_ht, LNK_ExportParseList *export_list, String8List *include_symbol_list, LNK_ExportParse export_parse) +{ + LNK_ExportParseNode *exp_n = 0; + String8 export_name = lnk_name_from_export_parse(&export_parse); + hash_table_search_string_raw(export_ht, export_name, &exp_n); + + if (exp_n == 0) { + // make sure export is defined + if (!export_parse.is_forwarder) { + str8_list_push(arena, include_symbol_list, export_parse.name); + } + + // push new export + exp_n = lnk_export_parse_list_push(arena, export_list, export_parse); + + hash_table_push_string_raw(arena, export_ht, export_name, exp_n); + } else { + B32 is_ambiguous = 1; + LNK_ExportParse *extant_export = &exp_n->data; + + if (extant_export->alias.size && export_parse.alias.size && !str8_match(extant_export->alias, export_parse.alias, 0)) { + goto report; + } + + if (extant_export->ordinal != export_parse.ordinal) { + goto report; + } + + is_ambiguous = 0; + + if (extant_export->alias.size == 0 && export_parse.alias.size != 0) { + extant_export->alias = export_parse.alias; + } + + report:; + if (is_ambiguous) { + lnk_error_with_loc(LNK_Error_IllExport, export_parse.obj_path, export_parse.lib_path, "ambiguous symbol export %S", export_parse.name); + } + } +} + internal LNK_InputObjList lnk_push_linker_symbols(Arena *arena, LNK_Config *config) { @@ -963,13 +1005,13 @@ lnk_queue_lib_member_input(Arena *arena, PathStyle path_style, LNK_SymbolLib *sy } internal int -lnk_section_contrib_is_before(void *raw_a, void *raw_b) +lnk_section_contrib_ptr_is_before(void *raw_a, void *raw_b) { // Grouped Sections (PE Format) // "All contributions with the same object-section name are allocated contiguously in the image, // and the blocks of contributions are sorted in lexical order by object-section name." - LNK_SectionContrib *a = raw_a; - LNK_SectionContrib *b = raw_b; + LNK_SectionContrib *a = *(LNK_SectionContrib **)raw_a; + LNK_SectionContrib *b = *(LNK_SectionContrib **)raw_b; int cmp; @@ -1220,7 +1262,6 @@ internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, - LNK_ExportTable *exptab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, @@ -2007,7 +2048,7 @@ lnk_pdata_is_before_x8664(void *raw_a, void *raw_b) } internal String8 -lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, LNK_ObjList obj_list) +lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ObjList obj_list) { Temp scratch = scratch_begin(arena->v, arena->count); @@ -2182,18 +2223,18 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S COFF_SectionFlags flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; LNK_Section *sect = lnk_section_table_search(sectab, sect_name, flags); + String8Node *data_n = push_array(sect->arena, String8Node, 1); + data_n->string = sect_data; + // fill out contrib LNK_SectionContribChunk *sc_chunk = sect->contribs.first; LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(sc_chunk, 1); sc->align = sc_align; + sc->data_list = data_n; sc->u.obj_idx = obj_idx; sc->u.sort_idx_size = (U16)sect_sort_idx.size; sc->u.sort_idx = sect_sort_idx.str; - String8Node *data_n = push_array(sect->arena, String8Node, 1); - data_n->string = sect_data; - SLLStackPush(sc->data_list, data_n); - sect_map[obj_idx][sect_idx] = sc; } } @@ -2242,7 +2283,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { for (LNK_SectionContribChunk *sc_chunk = sect_n->data.contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { Assert(sc_chunk->count == sc_chunk->cap); - radsort(sc_chunk->v, sc_chunk->count, lnk_section_contrib_is_before); + radsort(sc_chunk->v, sc_chunk->count, lnk_section_contrib_ptr_is_before); } } @@ -2279,7 +2320,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S LNK_Section *sect = §_n->data; for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { - sc_chunk->v[sc_idx].u.sect_idx = sect->sect_idx; + sc_chunk->v[sc_idx]->u.sect_idx = sect->sect_idx; } } } @@ -2417,11 +2458,11 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S if (obj->header.is_big_obj) { COFF_Symbol32 *symbol32 = symbol.raw_symbol; symbol32->section_number = safe_cast_u32(sc->u.sect_idx + 1); - symbol32->value = safe_cast_u32(sc->u.off); + symbol32->value = safe_cast_u32(sc->u.off + symbol32->value); } else { COFF_Symbol16 *symbol16 = symbol.raw_symbol; symbol16->section_number = safe_cast_u16(sc->u.sect_idx + 1); - symbol16->value = safe_cast_u32(sc->u.off); + symbol16->value = safe_cast_u32(sc->u.off + symbol16->value); } } } @@ -2789,7 +2830,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S U64 prev_sc_opl = 0; for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { - LNK_SectionContrib *sc = &sc_chunk->v[sc_idx]; + LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; // fill align bytes Assert(sc->u.off >= prev_sc_opl); @@ -2961,7 +3002,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S for (LNK_SectionContribChunk *sc_chunk = tls_sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { - LNK_SectionContrib *sc = &sc_chunk->v[sc_idx]; + LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; tls_align = Max(tls_align, sc->align); } } @@ -3080,7 +3121,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S } internal String8List -lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportTable *exptab) +lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportParseList export_list) { ProfBeginFunction(); Temp scratch = scratch_begin(&arena, 1); @@ -3094,20 +3135,24 @@ lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time String8 null_thunk_data_obj = lnk_build_null_thunk_data_obj(scratch.arena, dll_name, time_stamp, machine); COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + + // push import table nulls coff_lib_writer_push_obj(lib_writer, dll_name, import_entry_obj); coff_lib_writer_push_obj(lib_writer, dll_name, null_import_descriptor_obj); coff_lib_writer_push_obj(lib_writer, dll_name, null_thunk_data_obj); - KeyValuePair *raw_export_arr = key_value_pairs_from_hash_table(scratch.arena, exptab->name_export_ht); - for (U64 i = 0; i < exptab->name_export_ht->count; ++i) { - LNK_Export *exp = raw_export_arr[i].value_raw; - if (exp->name.size) { - coff_lib_writer_push_export_by_name(lib_writer, machine, time_stamp, dll_name, exp->type, exp->name, safe_cast_u16(exp->id)); - } else { + // push exports + for (LNK_ExportParseNode *exp_n = export_list.first; exp_n != 0; exp_n = exp_n->next) { + LNK_ExportParse *exp = &exp_n->data; + if (exp->is_noname_present) { coff_lib_writer_push_export_by_ordinal(lib_writer, machine, time_stamp, dll_name, exp->type, exp->ordinal); + } else { + String8 name = lnk_name_from_export_parse(exp); + coff_lib_writer_push_export_by_name(lib_writer, machine, time_stamp, dll_name, exp->type, name, exp->hint); } } + // serialize lib String8List lib = coff_lib_writer_serialize(arena, lib_writer, COFF_TimeStamp_Max, 0, /* emit second member: */ 1); coff_lib_writer_release(&lib_writer); @@ -3284,7 +3329,7 @@ lnk_build_rad_chunk_map(Arena *arena, String8 image_data, U64 thread_count, LNK_ for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { Temp temp = temp_begin(scratch.arena); - LNK_SectionContrib *sc = &sc_chunk->v[sc_idx]; + LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; U64 file_off = image_section_table[sc->u.sect_idx]->foff + sc->u.off; U64 virt_off = image_section_table[sc->u.sect_idx]->voff + sc->u.off; @@ -3499,6 +3544,7 @@ lnk_run(int argc, char **argv) String8List input_manifest_path_list = str8_list_copy(scratch.arena, &config->input_list[LNK_Input_Manifest]); String8List manifest_dep_list = str8_list_copy(scratch.arena, &config->manifest_dependency_list); LNK_ExportParseList export_symbol_list = config->export_symbol_list; + HashTable *export_ht = hash_table_init(scratch.arena, max_U16/2); LNK_AltNameList alt_name_list = config->alt_name_list; LNK_InputObjList input_obj_list = {0}; LNK_InputImportList input_import_list = {0}; @@ -3520,7 +3566,6 @@ lnk_run(int argc, char **argv) LNK_SectionTable *sectab = 0; LNK_ImportTable *imptab_static = lnk_import_table_alloc(0); LNK_ImportTable *imptab_delayed = lnk_import_table_alloc(config->import_table_flags); - LNK_ExportTable *exptab = lnk_export_table_alloc(); LNK_ObjList obj_list = {0}; LNK_LibList lib_index[LNK_InputSource_Count] = {0}; Arena *ht_arena = arena_alloc(); @@ -3570,10 +3615,17 @@ lnk_run(int argc, char **argv) // lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".xdata"), str8_lit_comp(".rdata") }); lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".tls"), str8_lit_comp(".data") }); - lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".edata"), str8_lit_comp(".rdata") }); + //lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".edata"), str8_lit_comp(".rdata") }); lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".idata"), str8_lit_comp(".rdata") }); lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".didat"), str8_lit_comp(".didat") }); lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".RAD_LINKER_DEBUG_DIR"), str8_lit_comp(".rdata") }); + + // + // Push config exports + // + for (LNK_ExportParseNode *exp_n = config->export_symbol_list.first; exp_n != 0; exp_n = exp_n->next) { + lnk_push_export(scratch.arena, export_ht, &export_symbol_list, &include_symbol_list, exp_n->data); + } ProfBegin("Image"); // :EndImage lnk_timer_begin(LNK_Timer_Image); @@ -3792,14 +3844,11 @@ lnk_run(int argc, char **argv) // /EXPORT { - LNK_ExportParseList obj_exports = {0}; for (LNK_Directive *dir = directive_info.v[LNK_CmdSwitch_Export].first; dir != 0; dir = dir->next) { - lnk_parse_export_directive(scratch.arena, &obj_exports, dir->value_list, obj->path, obj->lib_path); + LNK_ExportParse export_parse = {0}; + lnk_parse_export_directive_ex(scratch.arena, dir->value_list, obj->path, obj->lib_path, &export_parse); + lnk_push_export(scratch.arena, export_ht, &export_symbol_list, &include_symbol_list, export_parse); } - for (LNK_ExportParse *exp = obj_exports.first; exp != 0; exp = exp->next) { - str8_list_push(scratch.arena, &include_symbol_list, exp->name); - } - lnk_export_parse_list_concat_in_place(&export_symbol_list, &obj_exports); } // /INCLUDESYMBOL @@ -4145,7 +4194,7 @@ lnk_run(int argc, char **argv) case State_ReportUnresolvedSymbols: { // report unresolved symbols for (LNK_SymbolNode *node = unresolved_undef_list.first; node != 0; node = node->next) { - lnk_error(LNK_Error_UnresolvedSymbol, "unresolved symbol %S", node->data->name); + lnk_error_obj(LNK_Error_UnresolvedSymbol, node->data->u.undef.obj, "unresolved symbol %S", node->data->name); } if (unresolved_undef_list.count) { goto exit; @@ -4179,14 +4228,52 @@ lnk_run(int argc, char **argv) if (export_symbol_list.count) { ProfBegin("Build Export Table"); - ProfBeginV("Push Exports [Count %u]", export_symbol_list.count); - for (LNK_ExportParse *exp_parse = export_symbol_list.first; exp_parse != 0; exp_parse = exp_parse->next) { - lnk_export_table_push_export(exptab, symtab, exp_parse); - } - ProfEnd(); + LNK_ExportParseList resolved_exports = {0}; + for (LNK_ExportParseNode *exp_n = export_symbol_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { + exp_n_next = exp_n->next; + LNK_ExportParse *exp = &exp_n->data; - LNK_InputObjList export_objs = lnk_export_table_serialize(scratch.arena, exptab, str8_skip_last_slash(config->image_name), config->machine); - lnk_input_obj_list_concat_in_place(&input_obj_list, &export_objs); + if (!exp->is_forwarder) { + // filter out unresolved exports + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, exp_n->data.name); + if (symbol == 0) { + lnk_error_with_loc(LNK_Warning_IllExport, exp->obj_path, exp->lib_path, "unresolved export symbol %S\n", exp->name); + continue; + } + + // check export type + switch (exp->type) { + case COFF_ImportHeader_Code: { + COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + B32 is_export_data = !COFF_SymbolType_IsFunc(defn.type); + if (is_export_data) { + lnk_error_with_loc(LNK_Warning_IllExport, exp->obj_path, exp->lib_path, "export \"%S\" is DATA but has type CODE", exp->name); + } + } break; + case COFF_ImportHeader_Data: { + COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + B32 is_export_code = COFF_SymbolType_IsFunc(defn.type); + if (is_export_code) { + lnk_error_with_loc(LNK_Warning_IllExport, exp->obj_path, exp->lib_path, "export \"%S\" is CODE but has type DATA", exp->name); + } + } break; + case COFF_ImportHeader_Const: { + lnk_not_implemented("TODO: COFF_ImportHeader_Const"); + } break; + default: { InvalidPath; } break; + } + } + + // push resolved export + lnk_export_parse_list_push_node(&resolved_exports, exp_n); + } + + String8 edata_obj = lnk_make_edata_obj(scratch.arena, symtab, str8_skip_last_slash(config->image_name), config->machine, resolved_exports); + + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->path = str8_lit("* Exports *"); + input->dedup_id = input->path; + input->data = edata_obj; ProfEnd(); } @@ -4309,7 +4396,7 @@ lnk_run(int argc, char **argv) } break; case State_BuildImage: { // build image - image_data = lnk_build_win32_image(tp_arena, tp, config, symtab, exptab, obj_list); + image_data = lnk_build_win32_image(tp_arena, tp, config, symtab, obj_list); // write image to disk in a background thread { @@ -4343,7 +4430,7 @@ lnk_run(int argc, char **argv) case State_BuildImpLib: { ProfBegin("Build Imp Lib"); lnk_timer_begin(LNK_Timer_Lib); - String8List lib_list = lnk_build_import_lib(tp_arena->v[0], config->machine, config->time_stamp, config->image_name, exptab); + String8List lib_list = lnk_build_import_lib(tp_arena->v[0], config->machine, config->time_stamp, config->image_name, export_symbol_list); lnk_write_data_list_to_file_path(config->imp_lib_name, str8_zero(), lib_list); lnk_timer_end(LNK_Timer_Lib); ProfEnd(); diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 2f2ef39f..5bf62c45 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -187,14 +187,14 @@ internal void lnk_queue_lib_member_input(Arena *arena, PathStyle pat // --- Win32 Image ------------------------------------------------------------- -internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag); +internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag); internal String8List lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_temp, LNK_Config *config, U64 objs_count, LNK_Obj **objs); internal String8List lnk_build_win32_image_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sect_arr, U64 expected_image_header_size); -internal String8 lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, LNK_ObjList obj_list); +internal String8 lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ObjList obj_list); // --- Import Lib -------------------------------------------------------------- -internal String8List lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportTable *exptab); +internal String8List lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportParseList export_list); // --- Logger ------------------------------------------------------------------ diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index a6e76e38..756b3b01 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -801,72 +801,13 @@ lnk_parse_alt_name_directive_list(Arena *arena, String8List list, LNK_AltNameLis return 0; } -internal void -lnk_export_parse_list_concat_in_place(LNK_ExportParseList *list, LNK_ExportParseList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal LNK_ExportParse * -lnk_parse_export_directive(Arena *arena, LNK_ExportParseList *list, String8List value_list, String8 obj_path, String8 lib_path) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - LNK_ExportParse *parse = 0; - - // parse directive - String8 name = str8_zero(); - String8 alias = str8_zero(); - String8 type = coff_string_from_import_header_type(COFF_ImportHeader_Code); - if (value_list.node_count > 0) { - String8List dir_split = str8_split_by_string_chars(scratch.arena, value_list.first->string, str8_lit("="), 0); - B32 is_export_valid = value_list.node_count <= 2 && value_list.node_count > 0; - if (is_export_valid) { - if (dir_split.node_count > 0) { - name = dir_split.last->string; - } - if (dir_split.node_count == 2) { - alias = dir_split.first->string; - } - if (value_list.node_count == 2) { - type = value_list.last->string; - } - } - } - - // prase error check - if (name.size == 0) { - String8 dir = str8_list_join(scratch.arena, &value_list, 0); - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "invalid export directive \"%S\"", dir); - goto exit; - } - - parse = push_array_no_zero(arena, LNK_ExportParse, 1); - parse->next = 0; - parse->name = name; - parse->alias = alias; - parse->type = type; - - SLLQueuePush(list->first, list->last, parse); - ++list->count; - -exit:; - - scratch_end(scratch); - ProfEnd(); - return parse; -} - internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data) { LNK_MergeDirectiveNode *node = push_array_no_zero(arena, LNK_MergeDirectiveNode, 1); - node->data = data; - node->next = 0; - + node->data = data; SLLQueuePush(list->first, list->last, node); - ++list->count; - + list->count += 1; return node; } @@ -1121,8 +1062,10 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Export: { - String8List value_strings_copy = str8_list_copy(arena, &value_strings); - lnk_parse_export_directive(arena, &config->export_symbol_list, value_strings_copy, obj_path, lib_path); + LNK_ExportParse export_parse = {0}; + if (lnk_parse_export_directive_ex(arena, value_strings, obj_path, lib_path, &export_parse)) { + lnk_export_parse_list_push(arena, &config->export_symbol_list, export_parse); + } } break; case LNK_CmdSwitch_FastFail: { diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index ad0160a3..8e883fb8 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -238,21 +238,6 @@ typedef struct LNK_AltNameList String8List to_list; } LNK_AltNameList; -typedef struct LNK_ExportParse -{ - String8 name; - String8 alias; - String8 type; - struct LNK_ExportParse *next; -} LNK_ExportParse; - -typedef struct LNK_ExportParseList -{ - U64 count; - LNK_ExportParse *first; - LNK_ExportParse *last; -} LNK_ExportParseList; - typedef struct LNK_MergeDirective { String8 src; @@ -573,10 +558,9 @@ internal void lnk_alt_name_list_concat_in_place(LNK_AltNameList *list, LNK_ internal B32 lnk_parse_alt_name_directive (Arena *arena, String8 input, LNK_AltNameList *list_out); internal String8 * lnk_parse_alt_name_directive_list(Arena *arena, String8List list, LNK_AltNameList *list_out); -internal LNK_ExportParse * lnk_parse_export_directive(Arena *arena, LNK_ExportParseList *list, String8List value_list, String8 obj_path, String8 lib_path); - internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data); -internal B32 lnk_parse_merge_directive(String8 string, LNK_MergeDirective *out); + +internal B32 lnk_parse_merge_directive(String8 string, LNK_MergeDirective *parse_out); //////////////////////////////// diff --git a/src/linker/lnk_export_table.c b/src/linker/lnk_export_table.c index f2081e08..62081894 100644 --- a/src/linker/lnk_export_table.c +++ b/src/linker/lnk_export_table.c @@ -1,303 +1,406 @@ // Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -int -lnk_export_name_compar(const void *a_, const void *b_) +internal String8 +lnk_name_from_export_parse(LNK_ExportParse *exp) { - const LNK_Export *a = (const LNK_Export *)a_; - const LNK_Export *b = (const LNK_Export *)b_; - return str8_compar_case_sensitive(&a->name, &b->name); + String8 name; + if (exp->is_forwarder) { + name = exp->alias; + } else if (exp->alias.size) { + name = exp->alias; + } else { + name = exp->name; + } + return name; } -int -lnk_export_ordinal_compar(const void *a_, const void *b_) -{ - const LNK_Export *a = (const LNK_Export *)a_; - const LNK_Export *b = (const LNK_Export *)b_; - int cmp = u16_compar(&a->ordinal, &b->ordinal); - return cmp; -} - -internal LNK_ExportTable * -lnk_export_table_alloc(void) +internal B32 +lnk_parse_export_directive_ex(Arena *arena, String8List directive, String8 obj_path, String8 lib_path, LNK_ExportParse *export_out) { ProfBeginFunction(); - Arena *arena = arena_alloc(); + Temp scratch = scratch_begin(&arena, 1); + B32 is_parsed = 0; - LNK_ExportTable *exptab = push_array(arena, LNK_ExportTable, 1); - exptab->arena = arena; - exptab->voff_size = sizeof(U32); - exptab->max_ordinal = max_U16; - exptab->is_ordinal_used = push_array(arena, B8, exptab->max_ordinal); - exptab->name_export_ht = hash_table_init(arena, 0x10000); - exptab->noname_export_ht = hash_table_init(arena, 0x100); + // parse "alias=name" + String8 name = {0}; + String8 alias = {0}; + String8List flags = {0}; + { + String8List alias_name_split = str8_split_by_string_chars(scratch.arena, directive.first->string, str8_lit("="), 0); + if (alias_name_split.node_count == 2) { + alias = alias_name_split.first->string; + name = alias_name_split.last->string; + } else if (alias_name_split.node_count == 1) { + name = alias_name_split.first->string; + } else { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); + goto exit; + } + flags = directive; + str8_list_pop_front(&flags); + } + + // discard alias to itself + if (str8_match(name, alias, 0)) { + alias = str8_zero(); + } + + // does directive have ordinal? + U16 ordinal16 = 0; + String8 ordinal = {0}; + String8 noname_flag = {0}; + if (str8_match(str8_prefix(str8_list_first(&flags), 1), str8_lit("@"), 0)) { + // parse ordinal + ordinal = str8_skip(str8_list_pop_front(&flags)->string, 1); + if (str8_is_integer(ordinal, 10)) { + U64 ordinal64 = u64_from_str8(ordinal, 10); + if (ordinal64 <= max_U16) { + ordinal16 = (U16)ordinal64; + } else { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "ordinal value must fit into 16-bit integer, \"/EXPORT:%S\"", d); + goto exit; + } + } else { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); + goto exit; + } + + // detect NONAME flag + if (str8_match(str8_list_first(&flags), str8_lit("NONAME"), StringMatchFlag_CaseInsensitive)) { + noname_flag = str8_list_pop_front(&flags)->string; + } + } + + // detect PRIVATE flag + String8 private_flag = {0}; + if (str8_match(str8_list_first(&flags), str8_lit("PRIVATE"), StringMatchFlag_CaseInsensitive)) { + private_flag = str8_list_pop_front(&flags)->string; + } + + // parse export type + COFF_ImportType type = COFF_ImportHeader_Code; + if (flags.node_count) { + type = coff_import_header_type_from_string(str8_list_pop_front(&flags)->string); + if (type == COFF_ImportType_Invalid) { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); + goto exit; + } + } + + // are there leftover nodes? + if (flags.node_count != 0) { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); + goto exit; + } + + // fill out export + export_out->obj_path = obj_path; + export_out->lib_path = lib_path; + export_out->name = push_str8_copy(arena, name); + export_out->alias = push_str8_copy(arena, alias); + export_out->type = type; + export_out->ordinal = ordinal16; + export_out->is_ordinal_assigned = ordinal.size > 0; + export_out->is_noname_present = noname_flag.size > 0; + export_out->is_private = private_flag.size > 0; + export_out->is_forwarder = str8_find_needle(name, 0, str8_lit("."), 0) < name.size; + + is_parsed = 1; + +exit:; + scratch_end(scratch); ProfEnd(); - return exptab; + return is_parsed; +} + +internal B32 +lnk_parse_export_directive(Arena *arena, String8 directive, String8 obj_path, String8 lib_path, LNK_ExportParse *export_out) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List split_directive = str8_split_by_string_chars(scratch.arena, directive, str8_lit(","), 0); + B32 is_parsed = lnk_parse_export_directive_ex(arena, split_directive, obj_path, lib_path, export_out); + scratch_end(scratch); + return is_parsed; +} + +internal LNK_ExportParsePtrArray +lnk_array_from_export_list(Arena *arena, LNK_ExportParseList list) +{ + LNK_ExportParsePtrArray result = {0}; + result.v = push_array_no_zero(arena, LNK_ExportParse *, list.count); + for (LNK_ExportParseNode *exp = list.first; exp != 0; exp = exp->next) { + result.v[result.count++] = &exp->data; + } + return result; } internal void -lnk_export_table_release(LNK_ExportTable **exptab_ptr) +lnk_export_parse_list_push_node(LNK_ExportParseList *list, LNK_ExportParseNode *node) { - ProfBeginFunction(); - arena_release((*exptab_ptr)->arena); - *exptab_ptr = NULL; - ProfEnd(); + SLLQueuePush(list->first, list->last, node); + list->count += 1; } -internal LNK_Export * -lnk_export_table_search(LNK_ExportTable *exptab, String8 name) +internal LNK_ExportParseNode * +lnk_export_parse_list_push(Arena *arena, LNK_ExportParseList *list, LNK_ExportParse data) { - KeyValuePair *kv = hash_table_search_string(exptab->name_export_ht, name); - if (kv) { - return kv->value_raw; - } - return 0; + LNK_ExportParseNode *node = push_array(arena, LNK_ExportParseNode, 1); + node->data = data; + lnk_export_parse_list_push_node(list, node); + return node; } -internal LNK_Export * -lnk_export_table_push_export(LNK_ExportTable *exptab, LNK_SymbolTable *symtab, LNK_ExportParse *exp_parse) +internal void +lnk_export_parse_list_concat_in_place(LNK_ExportParseList *list, LNK_ExportParseList *to_concat) { - LNK_Export *exp = 0; - - // get export symbol - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, exp_parse->name); - if (symbol == 0) { - lnk_error(LNK_Warning_IllExport, "symbol \"%S\" for export doesn't exist", exp_parse->name); - goto exit; - } - if (symbol->type != LNK_Symbol_Defined) { - lnk_error(LNK_Warning_IllExport, "unable to resolve symbol \"%S\" for export", exp_parse->name); - goto exit; - } - - // NOTE: It is possible to export a global variable as CODE - // with following snippet: - // int global_bar = 0; - // #pragma comment(linker, "/export:global_bar") - // for some reason MSVC and LLD don't check symbol type and default - // to CODE instead of DATA. But if you try export global variable with: - // #pragma comment(linker, "/export:global_bar,CODE") - // MSVC and LLD issue an error. For compatibility sake we do the same thing too. - COFF_ImportType type = coff_import_header_type_from_string(exp_parse->type); - switch (type) { - case COFF_ImportHeader_Code: { - COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); - B32 is_export_data = !COFF_SymbolType_IsFunc(defn.type); - if (is_export_data) { - lnk_error(LNK_Error_IllExport, "export \"%S\" is DATA but has specifier CODE", exp_parse->name); - } - } break; - case COFF_ImportHeader_Data: { - COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); - B32 is_export_code = COFF_SymbolType_IsFunc(defn.type); - if (is_export_code) { - lnk_error(LNK_Error_IllExport, "export \"%S\" is CODE but has specifier DATA", exp_parse->name); - } - } break; - case COFF_ImportHeader_Const: { - lnk_not_implemented("TODO: COFF_ImportHeader_Const"); - } break; - default: { - if (exp_parse->type.size) { - lnk_error(LNK_Error_IllExport, "invalid type \"%S\" for export \"%S\"", exp_parse->type, exp_parse->name); - } - } break; - } - - // error check multiple def - exp = lnk_export_table_search(exptab, exp_parse->alias); - if (exp) { - if (exp->type != type) { - lnk_error(LNK_Warning_IllExport, "trying to rexport symbol \"%S\"", exp_parse->alias); - } - goto exit; - } - exp = lnk_export_table_search(exptab, exp_parse->name); - if (exp) { - if (exp->type != type) { - lnk_error(LNK_Warning_IllExport, "multiple export definition for \"%S\"", exp_parse->name); - } - goto exit; - } - - - // find free ordinal - U16 ordinal; - for (ordinal = 0; ordinal < exptab->max_ordinal; ++ordinal) { - if (!exptab->is_ordinal_used[ordinal]) { - exptab->is_ordinal_used[ordinal] = 1; - break; - } - } - - // ordinal alloc error check - if (ordinal >= exptab->max_ordinal) { - lnk_error(LNK_Error_OutOfExportOrdinals, "reached export limit of %u, discarding export %S", exptab->max_ordinal, exp_parse->name); - goto exit; - } - - - // fill out export - exp = push_array_no_zero(exptab->arena, LNK_Export, 1); - exp->next = 0; - exp->name = push_str8_copy(exptab->arena, exp_parse->alias.size > 0 ? exp_parse->alias : exp_parse->name); - exp->symbol = symbol; - exp->id = exptab->name_export_ht->count; - exp->ordinal = ordinal; - exp->type = type; - exp->is_private = 0; // exports through directives are public - - hash_table_push_string_raw(exptab->arena, exptab->name_export_ht, exp->name, exp); - - exit:; - return exp; + SLLConcatInPlace(list, to_concat); } -internal LNK_ExportArray -lnk_export_array_from_list(Arena *arena, LNK_ExportList list) +internal int +lnk_named_export_is_before(void *raw_a, void *raw_b) { - ProfBeginFunction(); - LNK_ExportArray arr; - arr.count = 0; - arr.v = push_array_no_zero(arena, LNK_Export, list.count); - for (LNK_Export *exp = list.first; exp != NULL; exp = exp->next) { - arr.v[arr.count++] = *exp; - } - ProfEnd(); - return arr; + LNK_ExportParse *a = *(LNK_ExportParse **)raw_a; + LNK_ExportParse *b = *(LNK_ExportParse **)raw_b; + int cmp = str8_compar_case_sensitive(&a->name, &b->name); + return cmp < 0; } -internal LNK_InputObjList -lnk_export_table_serialize(Arena *arena, LNK_ExportTable *exptab, String8 image_name, COFF_MachineType machine) +internal int +lnk_ordinal_export_is_before(void *raw_a, void *raw_b) +{ + LNK_ExportParse *a = raw_a; + LNK_ExportParse *b = raw_b; + return a->ordinal < b->ordinal; +} + +internal String8 +lnk_make_edata_obj(Arena *arena, + LNK_SymbolTable *symtab, + String8 image_name, + COFF_MachineType machine, + LNK_ExportParseList export_list) { Temp scratch = scratch_begin(&arena, 1); - // compute ordinal bounds - U64 ordinal_low; - for (ordinal_low = 0; ordinal_low < exptab->max_ordinal; ++ordinal_low) { - if (exptab->is_ordinal_used[ordinal_low]) { - break; + // compute max ordinal and used ordinal flag array + U64 ordinal_low = max_U64; + B8 *is_ordinal_used = push_array(arena, B8, max_U16); + for (LNK_ExportParseNode *exp_n = export_list.first; exp_n != 0; exp_n = exp_n->next) { + LNK_ExportParse *exp = &exp_n->data; + if (exp->is_ordinal_assigned) { + ordinal_low = Min(ordinal_low, exp->ordinal); + is_ordinal_used[exp->ordinal] = 1; } } - U64 ordinal_high; - for (ordinal_high = exptab->max_ordinal - 1; ordinal_high > 0; --ordinal_high) { - if (exptab->is_ordinal_used[ordinal_high]) { - break; + + LNK_ExportParsePtrArray named_exports = {0}; + LNK_ExportParsePtrArray noname_exports = {0}; + LNK_ExportParsePtrArray forwarder_exports = {0}; + { + // group exports based on flags + LNK_ExportParseList named_exports_list = {0}; + LNK_ExportParseList noname_exports_list = {0}; + LNK_ExportParseList forwarder_exports_list = {0}; + for (LNK_ExportParseNode *exp_n = export_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { + exp_n_next = exp_n->next; + if (exp_n->data.is_forwarder) { + lnk_export_parse_list_push_node(&forwarder_exports_list, exp_n); + } else if (exp_n->data.is_noname_present) { + AssertAlways(exp_n->data.is_ordinal_assigned); + lnk_export_parse_list_push_node(&noname_exports_list, exp_n); + } else { + lnk_export_parse_list_push_node(&named_exports_list, exp_n); + } + } + + // list -> array + named_exports = lnk_array_from_export_list(scratch.arena, named_exports_list); + noname_exports = lnk_array_from_export_list(scratch.arena, noname_exports_list); + forwarder_exports = lnk_array_from_export_list(scratch.arena, forwarder_exports_list); + + // sort exports + radsort(named_exports.v, named_exports.count, lnk_named_export_is_before); + radsort(noname_exports.v, noname_exports.count, lnk_ordinal_export_is_before); + radsort(forwarder_exports.v, forwarder_exports.count, lnk_named_export_is_before); + + MemoryZeroStruct(&export_list); + lnk_export_parse_list_concat_in_place(&export_list, &named_exports_list); + lnk_export_parse_list_concat_in_place(&export_list, &forwarder_exports_list); + lnk_export_parse_list_concat_in_place(&export_list, &noname_exports_list); + } + + // assign omitted ordinals + { + U16 last_ordinal = ordinal_low; + for (U64 exp_idx = 0; exp_idx < named_exports.count; exp_idx += 1) { + LNK_ExportParse *exp = named_exports.v[exp_idx]; + if (!exp->is_ordinal_assigned) { + for (; last_ordinal < max_U16 && is_ordinal_used[last_ordinal] != 0; last_ordinal += 1); + exp->ordinal = last_ordinal; + exp->is_ordinal_assigned = 1; + is_ordinal_used[last_ordinal] = 1; + } + } + for (U64 exp_idx = 0; exp_idx < forwarder_exports.count; exp_idx += 1) { + LNK_ExportParse *exp = forwarder_exports.v[exp_idx]; + if (!exp->is_ordinal_assigned) { + for (; last_ordinal < max_U16 && is_ordinal_used[last_ordinal] != 0; last_ordinal += 1); + exp->ordinal = last_ordinal; + exp->is_ordinal_assigned = 1; + is_ordinal_used[last_ordinal] = 1; + } + } + for (U64 exp_idx = 0; exp_idx < noname_exports.count; exp_idx += 1) { + LNK_ExportParse *exp = noname_exports.v[exp_idx]; + if (!exp->is_ordinal_assigned) { + exp->ordinal = last_ordinal; + exp->is_ordinal_assigned = 1; + is_ordinal_used[last_ordinal] = 1; + } } } - + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, machine); - // fill out export table header - PE_ExportTableHeader *header = push_array(obj_writer->arena, PE_ExportTableHeader, 1); - header->ordinal_base = safe_cast_u16(ordinal_low + 1); - header->export_address_table_count = safe_cast_u32(exptab->name_export_ht->count + exptab->noname_export_ht->count); - header->name_pointer_table_count = safe_cast_u32(exptab->name_export_ht->count); - - - // make iamge name c-string - String8 image_name_cstr = push_cstr(obj_writer->arena, image_name); - // push sections - COFF_ObjSection *header_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$1"), LNK_EDATA_SECTION_FLAGS, str8_struct(header)); - COFF_ObjSection *voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$2"), LNK_EDATA_SECTION_FLAGS, str8_zero()); - COFF_ObjSection *name_voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$3"), LNK_EDATA_SECTION_FLAGS, str8_zero()); - COFF_ObjSection *ordinal_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$4"), LNK_EDATA_SECTION_FLAGS, str8_zero()); - COFF_ObjSection *string_buffer_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$5"), LNK_EDATA_SECTION_FLAGS, str8_zero()); - COFF_ObjSection *image_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$6"), LNK_EDATA_SECTION_FLAGS, image_name_cstr); + COFF_ObjSection *voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$2"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero()); + COFF_ObjSection *name_voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$3"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero()); + COFF_ObjSection *ordinal_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$4"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); + COFF_ObjSection *string_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$5"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); + COFF_ObjSection *image_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$6"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, push_cstr(obj_writer->arena, image_name)); - // push symbols - COFF_ObjSymbol *image_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_NAME_VOFF"), 0, image_name_sect); - COFF_ObjSymbol *address_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_ADDRESS_TABLE_VOFF"), 0, voff_table_sect); - COFF_ObjSymbol *name_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_NAME_POINTER_VOFF"), 0, name_voff_table_sect); - COFF_ObjSymbol *ordinal_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_ORDINAL_TABLE_VOFF"), 0, ordinal_table_sect); + ProfBegin("Virtual Offset Table"); + { + B8 *is_ordinal_bound = push_array(scratch.arena, B8, max_U16); + LNK_ExportParsePtrArray *all_exports[] = { &named_exports, &forwarder_exports, &noname_exports }; - // patch export table header - switch (machine) { - case COFF_MachineType_Unknown: break; - case COFF_MachineType_X64: { - coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_voff), image_name_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, export_address_table_voff), address_table_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_pointer_table_voff), name_table_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, ordinal_table_voff), ordinal_table_symbol, COFF_Reloc_X64_Addr32Nb); - } break; - default: { NotImplemented; } break; - } - - - B8 *is_ordinal_bound = push_array(scratch.arena, B8, exptab->max_ordinal); - HashTable *exp_ht_arr[] = { exptab->name_export_ht, exptab->noname_export_ht }; - - for (U64 ht_idx = 0; ht_idx < ArrayCount(exp_ht_arr); ht_idx += 1) { - HashTable *ht = exp_ht_arr[ht_idx]; - - KeyValuePair *kv_arr = key_value_pairs_from_hash_table(scratch.arena, exptab->name_export_ht); - - // named exports must be lexically sorted - if (ht_idx == 0) { - sort_key_value_pairs_as_string_sensitive(kv_arr, exptab->name_export_ht->count); - } - - for (U64 i = 0; i < ht->count; i += 1) { - LNK_Export *exp = kv_arr[i].value_raw; - - { - U64 export_name_offset = string_buffer_sect->data.total_size; - String8 export_name_cstr = push_cstr(obj_writer->arena, exp->name); - str8_list_push(obj_writer->arena, &string_buffer_sect->data, export_name_cstr); - - String8 export_name_symbol_name = push_str8f(obj_writer->arena, "EXPORT.%S", exp->name); - COFF_ObjSymbol *export_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, export_name_symbol_name, export_name_offset, string_buffer_sect); - - U64 export_name_voff_offset = name_voff_table_sect->data.total_size; - U64 export_name_voff_size = sizeof(U32); - U8 *export_name_voff = push_array(obj_writer->arena, U8, export_name_voff_size); - str8_list_push(obj_writer->arena, &name_voff_table_sect->data, str8_array(export_name_voff, export_name_voff_size)); - - switch (machine) { - case COFF_MachineType_Unknown: break; - case COFF_MachineType_X64: { coff_obj_writer_section_push_reloc(obj_writer, name_voff_table_sect, export_name_voff_offset, export_name_symbol, COFF_Reloc_X64_Addr32Nb); } break; - default: { NotImplemented; } break; - } - } - - { - U16 *ordinal = push_array(obj_writer->arena, U16, 1); - *ordinal = exp->ordinal - ordinal_low; - str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal)); - - if ( ! is_ordinal_bound[exp->ordinal]) { + for (U64 arr_idx = 0; arr_idx < ArrayCount(all_exports); arr_idx += 1) { + for (U64 exp_idx = 0; exp_idx < all_exports[arr_idx]->count; exp_idx += 1) { + LNK_ExportParse *exp = all_exports[arr_idx]->v[exp_idx]; + if (is_ordinal_bound[exp->ordinal] == 0) { + // alloc only one slot per ordinal, so it's possible to map ordinal to a virtual offset is_ordinal_bound[exp->ordinal] = 1; + // create slot for the ordinal virtual offset U64 voff_offset = voff_table_sect->data.total_size; U32 *voff = push_array(obj_writer->arena, U32, 1); str8_list_push(obj_writer->arena, &voff_table_sect->data, str8_struct(voff)); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, exp->name); - switch (machine) { - case COFF_MachineType_Unknown: break; - case COFF_MachineType_X64: { coff_obj_writer_section_push_reloc(obj_writer, voff_table_sect, voff_offset, symbol, COFF_Reloc_X64_Addr32Nb); } break; - default: { NotImplemented; } break; + COFF_ObjSymbol *exp_symbol; + if (exp->is_forwarder) { + U64 forwarder_name_offset = string_table_sect->data.total_size; + String8 forwarder_name_cstr = push_cstr(obj_writer->arena, exp->name); + str8_list_push(obj_writer->arena, &string_table_sect->data, forwarder_name_cstr); + // symbol to the name string + exp_symbol = coff_obj_writer_push_symbol_static(obj_writer, exp->name, forwarder_name_offset, string_table_sect); + } else { + // function or global var symbol + exp_symbol = coff_obj_writer_push_symbol_undef(obj_writer, exp->name); } + + U16 ordinal_nb = exp->ordinal - ordinal_low; + coff_obj_writer_section_push_reloc(obj_writer, voff_table_sect, ordinal_nb*sizeof(U32), exp_symbol, coff_virt_off_reloc_from_machine(machine)); } } } } + ProfEnd(); + ProfBegin("Named & Forwarder Exports"); + { + LNK_ExportParsePtrArray *exports_with_names[] = { &named_exports, &forwarder_exports }; + + // assign hints + for (U64 arr_idx = 0, hint = 0; arr_idx < ArrayCount(exports_with_names); arr_idx += 1) { + for (U64 exp_idx = 0; exp_idx < exports_with_names[arr_idx]->count; exp_idx += 1, hint += 1) { + LNK_ExportParse *exp = exports_with_names[arr_idx]->v[exp_idx]; + exp->hint = hint; + } + } + + for (U64 arr_idx = 0; arr_idx < ArrayCount(exports_with_names); arr_idx += 1) { + LNK_ExportParsePtrArray *exports = exports_with_names[arr_idx]; + for (U64 exp_idx = 0; exp_idx < exports->count; exp_idx += 1) { + LNK_ExportParse *exp = exports->v[exp_idx]; + + String8 name = lnk_name_from_export_parse(exp); + + // store symbol name string + U64 export_name_offset = string_table_sect->data.total_size; + String8 export_name_cstr = push_cstr(obj_writer->arena, name); + str8_list_push(obj_writer->arena, &string_table_sect->data, export_name_cstr); + + // create symbol for the name string + String8 export_name_symbol_name = push_str8f(obj_writer->arena, "RAD_NAME:%S", name); + COFF_ObjSymbol *export_name_symbol = coff_obj_writer_push_symbol_extern(obj_writer, export_name_symbol_name, export_name_offset, string_table_sect); + + // create slot for export virtual offset + U64 export_name_voff_offset = name_voff_table_sect->data.total_size; + U8 *export_name_voff = push_array(obj_writer->arena, U8, sizeof(U32)); + str8_list_push(obj_writer->arena, &name_voff_table_sect->data, str8_array(export_name_voff, sizeof(U32))); + + // write string's virtual offset + coff_obj_writer_section_push_reloc(obj_writer, name_voff_table_sect, export_name_voff_offset, export_name_symbol, coff_virt_off_reloc_from_machine(machine)); + + // create and store export's ordinal + U16 *ordinal = push_array(obj_writer->arena, U16, 1); + *ordinal = exp->ordinal - ordinal_low; + str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal)); + } + } + } + ProfEnd(); + + ProfBegin("NONAME Exports"); + { + for (U64 exp_idx = 0; exp_idx < noname_exports.count; exp_idx += 1) { + // create and store export's ordinal + LNK_ExportParse *exp = noname_exports.v[exp_idx]; + U16 *ordinal = push_array(obj_writer->arena, U16, 1); + *ordinal = exp->ordinal - ordinal_low; + str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal)); + } + } + ProfEnd(); + + // fill out export table header + PE_ExportTableHeader *header = push_array(obj_writer->arena, PE_ExportTableHeader, 1); + header->time_stamp = COFF_TimeStamp_Max; + header->ordinal_base = safe_cast_u16(ordinal_low); + header->export_address_table_count = safe_cast_u32(voff_table_sect->data.node_count); + header->name_pointer_table_count = safe_cast_u32(name_voff_table_sect->data.node_count); + + // push header field's symbols + COFF_ObjSymbol *image_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_NAME_VOFF"), 0, image_name_sect); + COFF_ObjSymbol *address_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_ADDRESS_TABLE_VOFF"), 0, voff_table_sect); + COFF_ObjSymbol *name_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_NAME_POINTER_VOFF"), 0, name_voff_table_sect); + COFF_ObjSymbol *ordinal_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_ORDINAL_TABLE_VOFF"), 0, ordinal_table_sect); + + // push export table header section + COFF_ObjSection *header_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$1"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_struct(header)); + coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_HEADER"), 0, header_sect); + + // patch export table header + COFF_RelocType virt_off_reloc_type = coff_virt_off_reloc_from_machine(machine); + coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_voff), image_name_symbol, virt_off_reloc_type); + coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, export_address_table_voff), address_table_symbol, virt_off_reloc_type); + coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_pointer_table_voff), name_table_symbol, virt_off_reloc_type); + coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, ordinal_table_voff), ordinal_table_symbol, virt_off_reloc_type); String8 obj = coff_obj_writer_serialize(arena, obj_writer); coff_obj_writer_release(&obj_writer); - LNK_InputObjList result = {0}; - LNK_InputObj *input = lnk_input_obj_list_push(arena, &result); - input->path = str8_lit("* Exports *"); - input->dedup_id = input->path; - input->data = obj; + os_write_data_to_file_path(str8_lit("foo.obj"), obj); scratch_end(scratch); - return result; + return obj; } - diff --git a/src/linker/lnk_export_table.h b/src/linker/lnk_export_table.h index 57533918..f3c5b9ec 100644 --- a/src/linker/lnk_export_table.h +++ b/src/linker/lnk_export_table.h @@ -3,42 +3,44 @@ #pragma once -typedef struct LNK_Export +typedef struct LNK_ExportParse { - struct LNK_Export *next; - String8 name; - struct LNK_Symbol *symbol; - U32 id; - U16 ordinal; - COFF_ImportType type; - B32 is_private; -} LNK_Export; + String8 obj_path; + String8 lib_path; + String8 name; + String8 alias; + COFF_ImportType type; + U16 ordinal; + U16 hint; + B32 is_ordinal_assigned; + B32 is_noname_present; + B32 is_private; + B32 is_forwarder; +} LNK_ExportParse; -typedef struct LNK_ExportList +typedef struct LNK_ExportParseNode { - U64 count; - LNK_Export *first; - LNK_Export *last; -} LNK_ExportList; + LNK_ExportParse data; + struct LNK_ExportParseNode *next; +} LNK_ExportParseNode; -typedef struct LNK_ExportArray +typedef struct LNK_ExportParseList { - U64 count; - LNK_Export *v; -} LNK_ExportArray; + U64 count; + LNK_ExportParseNode *first; + LNK_ExportParseNode *last; +} LNK_ExportParseList; -typedef struct LNK_ExportTable +typedef struct LNK_ExportParsePtrArray { - Arena *arena; - HashTable *name_export_ht; - HashTable *noname_export_ht; - U64 voff_size; - U64 max_ordinal; - B8 *is_ordinal_used; -} LNK_ExportTable; + U64 count; + LNK_ExportParse **v; +} LNK_ExportParsePtrArray; -internal LNK_ExportTable * lnk_export_table_alloc(void); -internal void lnk_export_table_release(LNK_ExportTable **exptab_ptr); -internal LNK_Export * lnk_export_table_search(LNK_ExportTable *exptab, String8 name); -internal LNK_InputObjList lnk_export_table_serialize(Arena *arena, LNK_ExportTable *exptab, String8 image_name, COFF_MachineType machine); +//////////////////////////////// + +internal B32 lnk_parse_export_directive_ex(Arena *arena, String8List directive, String8 obj_path, String8 lib_path, LNK_ExportParse *export_out); +internal B32 lnk_parse_export_directive(Arena *arena, String8 directive, String8 obj_path, String8 lib_path, LNK_ExportParse *parse_out); +internal LNK_ExportParsePtrArray lnk_array_from_export_list(Arena *arena, LNK_ExportParseList list); +internal LNK_ExportParseNode * lnk_export_parse_list_push(Arena *arena, LNK_ExportParseList *list, LNK_ExportParse data); diff --git a/src/linker/lnk_section_table.c b/src/linker/lnk_section_table.c index 529a25bf..e1f855c6 100644 --- a/src/linker/lnk_section_table.c +++ b/src/linker/lnk_section_table.c @@ -35,7 +35,7 @@ internal LNK_SectionContrib * lnk_section_contrib_chunk_push(LNK_SectionContribChunk *chunk, U64 count) { Assert(chunk->count + count <= chunk->cap); - LNK_SectionContrib *result = &chunk->v[chunk->count]; + LNK_SectionContrib *result = chunk->v[chunk->count]; chunk->count += count; return result; } @@ -46,7 +46,9 @@ lnk_section_contrib_chunk_list_push_chunk(Arena *arena, LNK_SectionContribChunkL LNK_SectionContribChunk *chunk = push_array(arena, LNK_SectionContribChunk, 1); chunk->count = 0; chunk->cap = cap; - chunk->v = push_array(arena, LNK_SectionContrib, cap); + chunk->v = push_array(arena, LNK_SectionContrib *, cap); + chunk->v2 = push_array(arena, LNK_SectionContrib, cap); + for (U64 i = 0; i < cap; i += 1) { chunk->v[i] = &chunk->v2[i]; } SLLQueuePush(list->first, list->last, chunk); list->chunk_count += 1; return chunk; @@ -317,7 +319,7 @@ lnk_finalize_section_layout(LNK_SectionTable *sectab, LNK_Section *sect, U64 fil U64 cursor = 0; for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { - LNK_SectionContrib *sc = &sc_chunk->v[sc_idx]; + LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; cursor = AlignPow2(cursor, sc->align); diff --git a/src/linker/lnk_section_table.h b/src/linker/lnk_section_table.h index 8884e5d0..d468728d 100644 --- a/src/linker/lnk_section_table.h +++ b/src/linker/lnk_section_table.h @@ -38,7 +38,8 @@ typedef struct LNK_SectionContribChunk struct LNK_SectionContribChunk *next; U64 count; U64 cap; - LNK_SectionContrib *v; + LNK_SectionContrib **v; + LNK_SectionContrib *v2; } LNK_SectionContribChunk; typedef struct LNK_SectionContribChunkList