From 1aac27095e74d2f0ed07f2c1704a2f52beaa9313 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Mon, 1 Sep 2025 14:39:19 -0700 Subject: [PATCH] WIP: reorder input and library search order to match MSVC behavior --- src/linker/hash_table.c | 63 +- src/linker/hash_table.h | 1 + src/linker/linker.natvis | 34 + src/linker/lnk.c | 2352 ++++++++++++++++----------------- src/linker/lnk.h | 152 ++- src/linker/lnk_config.c | 152 ++- src/linker/lnk_config.h | 42 +- src/linker/lnk_input.c | 175 --- src/linker/lnk_input.h | 77 -- src/linker/lnk_lib.c | 49 +- src/linker/lnk_lib.h | 22 +- src/linker/lnk_obj.c | 91 +- src/linker/lnk_obj.h | 45 +- src/linker/lnk_symbol_table.c | 13 +- src/linker/lnk_symbol_table.h | 1 + 15 files changed, 1652 insertions(+), 1617 deletions(-) delete mode 100644 src/linker/lnk_input.c delete mode 100644 src/linker/lnk_input.h diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index e8c3a3c0..4a4c462f 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -211,11 +211,18 @@ hash_table_search_u64(HashTable *ht, U64 key_u64) return 0; } -internal void * -hash_table_search_u64_raw(HashTable *ht, U64 key_u64) +internal KeyValuePair * +hash_table_search_raw(HashTable *ht, void *key) { - KeyValuePair *kv = hash_table_search_u64(ht, key_u64); - return kv ? kv->value_raw : 0; + U64 hash = hash_table_hasher(str8_struct(&key)); + U64 ibucket = hash % ht->cap; + BucketList *bucket = ht->buckets + ibucket; + for (BucketNode *n = bucket->first; n != 0; n = n->next) { + if (n->v.key_raw == key) { + return &n->v; + } + } + return 0; } internal KeyValuePair * @@ -230,13 +237,6 @@ hash_table_search_path(HashTable *ht, String8 path) return result; } -internal void * -hash_table_search_path_raw(HashTable *ht, String8 path) -{ - KeyValuePair *kv = hash_table_search_path(ht, path); - return kv ? kv->value_raw : 0; -} - internal B32 hash_table_search_path_u64(HashTable *ht, String8 key, U64 *value_out) { @@ -250,6 +250,27 @@ hash_table_search_path_u64(HashTable *ht, String8 key, U64 *value_out) return 0; } +internal void * +hash_table_search_u64_raw(HashTable *ht, U64 key_u64) +{ + KeyValuePair *kv = hash_table_search_u64(ht, key_u64); + return kv ? kv->value_raw : 0; +} + +internal void * +hash_table_search_path_raw(HashTable *ht, String8 path) +{ + KeyValuePair *kv = hash_table_search_path(ht, path); + return kv ? kv->value_raw : 0; +} + +internal void * +hash_table_search_raw_raw(HashTable *ht, void *key) +{ + KeyValuePair *kv = hash_table_search_raw(ht, key); + return kv ? kv->value_raw : 0; +} + internal B32 hash_table_search_string_u64(HashTable *ht, String8 key, U64 *value_out) { @@ -283,6 +304,13 @@ hash_table_push_u32_u32(Arena *arena, HashTable *ht, U32 key, U32 value) return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_u32 = key, .value_u32 = value }); } +internal BucketNode * +hash_table_push_raw_raw(Arena *arena, HashTable *ht, void *key, void *value) +{ + U64 hash = hash_table_hasher(str8_struct(&key)); + return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_raw = key, .value_raw = value }); +} + internal B32 hash_table_search_string_string(HashTable *ht, String8 key, String8 *value_out) { @@ -381,6 +409,19 @@ key_value_pairs_from_hash_table(Arena *arena, HashTable *ht) return pairs; } +internal void * +keys_from_hash_table_raw(Arena *arena, HashTable *ht) +{ + void **result = push_array(arena, void *, ht->count); + for (U64 bucket_idx = 0, cursor = 0; bucket_idx < ht->cap; ++bucket_idx) { + for (BucketNode *n = ht->buckets[bucket_idx].first; n != 0; n = n->next) { + Assert(cursor < ht->count); + result[cursor++] = n->v.key_raw; + } + } + return result; +} + internal void * values_from_hash_table_raw(Arena *arena, HashTable *ht) { diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index 0eb3bf67..d7458adc 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -87,6 +87,7 @@ internal U64 * keys_from_hash_table_u64 (Arena *arena, HashTable internal String8 keys_from_hash_table_str8 (Arena *arena, HashTable *ht); internal KeyValuePair * key_value_pairs_from_hash_table(Arena *arena, HashTable *ht); +internal void * keys_from_hash_table_raw(Arena *arena, HashTable *ht); internal void * values_from_hash_table_raw(Arena *arena, HashTable *ht); internal void sort_key_value_pairs_as_u32(KeyValuePair *pairs, U64 count); diff --git a/src/linker/linker.natvis b/src/linker/linker.natvis index 8414241e..9c69c117 100644 --- a/src/linker/linker.natvis +++ b/src/linker/linker.natvis @@ -17,6 +17,8 @@ + + {{count={count} first={first} last={last} }} @@ -92,6 +94,38 @@ + + + count + + + + + + idx = 0 + + + node->v[idx] + idx += 1 + + node = node->next + + + + + + + {count,cap,v} + + count + cap + + count + v + + + + diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 75dfd025..3f9ce0e0 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -99,7 +99,6 @@ #include "lnk_timer.h" #include "lnk_io.h" #include "lnk_cmd_line.h" -#include "lnk_input.h" #include "lnk_config.h" #include "lnk_symbol_table.h" #include "lnk_section_table.h" @@ -114,7 +113,6 @@ #include "lnk_timer.c" #include "lnk_io.c" #include "lnk_cmd_line.c" -#include "lnk_input.c" #include "lnk_config.c" #include "lnk_symbol_table.c" #include "lnk_section_table.c" @@ -212,7 +210,7 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) } // init config - LNK_Config *config = lnk_config_from_cmd_line(arena, raw_cmd_line, cmd_line); + LNK_Config *config = lnk_config_from_cmd_line(raw_cmd_line, cmd_line); #if PROFILE_TELEMETRY { @@ -375,7 +373,7 @@ lnk_merge_manifest_files(String8 mt_path, String8 out_name, String8List manifest for (String8Node *man_node = manifest_path_list.first; man_node != 0; man_node = man_node->next) { - // resolve relativ path inputs + // resolve relative inputs String8 full_path = path_absolute_dst_from_relative_dst_src(scratch.arena, man_node->string, work_dir); // normalize slashes @@ -467,7 +465,7 @@ lnk_make_null_obj(Arena *arena) // push import stub { - COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("RAD_IMPORT_STUB_NULL"), 0, COFF_SymStorageClass_Static); + COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_IMPORT_STUB), 0, COFF_SymStorageClass_Static); coff_obj_writer_push_symbol_weak(obj_writer, str8_lit(LNK_IMPORT_STUB), COFF_WeakExt_AntiDependency, tag); } @@ -919,114 +917,1148 @@ lnk_make_linker_obj(Arena *arena, LNK_Config *config) } internal String8 -lnk_get_lib_name(String8 path) +lnk_make_obj_with_undefined_symbols(Arena *arena, String8List symbol_names) { - static String8 LIB_EXT = str8_lit_comp(".LIB"); - - // strip path - String8 name = str8_skip_last_slash(path); - - // strip extension - String8 name_ext = str8_postfix(name, LIB_EXT.size); - if (str8_match(name_ext, LIB_EXT, StringMatchFlag_CaseInsensitive)) { - name = str8_chop(name, LIB_EXT.size); + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); + for (String8Node *name_n = symbol_names.first; name_n != 0; name_n = name_n->next) { + coff_obj_writer_push_symbol_undef(obj_writer, name_n->string); } - - return name; -} - -internal B32 -lnk_is_lib_disallowed(HashTable *disallow_lib_ht, String8 path) -{ - String8 lib_name = lnk_get_lib_name(path); - return hash_table_search_path(disallow_lib_ht, lib_name) != 0; -} - -internal B32 -lnk_is_lib_loaded(HashTable *loaded_lib_ht, String8 path) -{ - KeyValuePair *is_loaded = hash_table_search_path(loaded_lib_ht, path); - return is_loaded != 0; + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); + return obj; } internal void -lnk_push_disallow_lib(Arena *arena, HashTable *disallow_lib_ht, String8 path) +lnk_input_list_push_node(LNK_InputList *list, LNK_Input *node) { - String8 lib_name = lnk_get_lib_name(path); - hash_table_push_path_u64(arena, disallow_lib_ht, lib_name, 0); + SLLQueuePush(list->first, list->last, node); + list->count += 1; } internal void -lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path) +lnk_input_list_concat_in_place(LNK_InputList *list, LNK_InputList *to_concat) { - if (!hash_table_search_path(loaded_lib_ht, path)) { - String8 path_copy = push_str8_copy(arena, path); - hash_table_push_path_u64(arena, loaded_lib_ht, path_copy, 0); + SLLConcatInPlace(list, to_concat); +} + +internal LNK_InputPtrArray +lnk_array_from_input_list(Arena *arena, LNK_InputList list) +{ + LNK_InputPtrArray result = {0}; + result.v = push_array(arena, LNK_Input *, list.count); + for (LNK_Input *node = list.first; node != 0; node = node->next, result.count += 1) { + result.v[result.count] = node; } + return result; } -internal void -lnk_queue_lib_member_for_input(Arena *arena, - LNK_Config *config, - LNK_Symbol *pull_in_ref, - LNK_Lib *lib, - U32 member_idx, - LNK_InputImportList *input_import_list, - LNK_InputObjList *input_obj_list) +internal LNK_Inputer * +lnk_inputer_init(void) { - B32 was_member_queued = ins_atomic_u8_eval_assign(&lib->was_member_queued[member_idx], 1); - if (!was_member_queued) { - U64 member_offset = lib->member_offsets[member_idx]; + Arena *arena = arena_alloc(); + LNK_Inputer *inputer = push_array(arena, LNK_Inputer, 1); + inputer->arena = arena; + inputer->objs_ht = hash_table_init(arena, 0x20000); + inputer->libs_ht = hash_table_init(arena, 0x1000); + inputer->missing_lib_ht = hash_table_init(arena, 0x100); + return inputer; +} - // compose input index so that members are laid out in the image - // in the order of undefined symbols appearing in objs, - // mimicking serial discovery - U64 input_idx = Compose64Bit(pull_in_ref->defined.obj->input_idx, pull_in_ref->defined.symbol_idx); +internal LNK_Input * +lnk_input_push(Arena *arena, LNK_InputList *list, String8 path, String8 data) +{ + LNK_Input *node = push_array(arena, LNK_Input, 1); + node->path = path; + node->data = data; + lnk_input_list_push_node(list, node); + return node; +} - // parse member - COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, member_offset); - COFF_DataType member_type = coff_data_type_from_data(member_info.data); +internal LNK_Input * +lnk_inputer_push_linkgen(Arena *arena, LNK_InputList *list, String8 data, String8 path) +{ + LNK_Input *input = lnk_input_push(arena, list, data, path); + input->exclude_from_debug_info = 1; + return input; +} - switch (member_type) { - case COFF_DataType_Null: break; - case COFF_DataType_Import: { - LNK_InputImportNode *input = lnk_input_import_list_push(arena, input_import_list); - input->data.coff_import = member_info.data; - input->data.input_idx = input_idx; - } break; - case COFF_DataType_BigObj: - case COFF_DataType_Obj: { - String8 obj_path = coff_parse_long_name(lib->long_names, member_info.header.name); +internal LNK_Input * +lnk_inputer_push_thin(Arena *arena, LNK_InputList *list, HashTable *ht, String8 full_path) +{ + Temp scratch = scratch_begin(&arena, 1); + LNK_Input *input = hash_table_search_path_raw(ht, full_path); + if (input == 0) { + input = lnk_input_push(arena, list, full_path, str8_zero()); + input->path = push_str8_copy(arena, full_path); + input->is_thin = 1; - // obj path in thin archive has slash appended which screws up - // file lookup on disk; it couble be there to enable paths to symbols - // but we don't use this feature - String8 slash = str8_lit("/"); - if (str8_ends_with(obj_path, slash, 0)) { - obj_path = str8_chop(obj_path, slash.size); - } + hash_table_push_path_raw(arena, ht, full_path, input); + } + scratch_end(scratch); + return input; +} - // obj path in thin archive is relative to directory with archive - B32 is_thin = lib->type == COFF_Archive_Thin; - if (is_thin) { - Temp scratch = scratch_begin(&arena, 1); - String8List obj_path_list = {0}; - str8_list_push(scratch.arena, &obj_path_list, str8_chop_last_slash(lib->path)); - str8_list_push(scratch.arena, &obj_path_list, obj_path); - obj_path = str8_path_list_join_by_style(arena, &obj_path_list, config->path_style); - scratch_end(scratch); - } +internal LNK_Input * +lnk_inputer_push_obj(LNK_Inputer *inputer, LNK_LibMemberRef *trigger, String8 path, String8 data) +{ + lnk_log(LNK_Log_InputObj, "Input Obj: %S", path); + LNK_Input *input = lnk_input_push(inputer->arena, &inputer->new_objs, path, data); + input->trigger = trigger; + return input; +} - LNK_InputObj *input = lnk_input_obj_list_push(arena, input_obj_list); - input->is_thin = is_thin; - input->dedup_id = push_str8f(arena, "%S/%S", lib->path, obj_path); - input->path = obj_path; - input->data = member_info.data; - input->lib = lib; - input->input_idx = input_idx; - } break; +internal LNK_Input * +lnk_inputer_push_obj_linkgen(LNK_Inputer *inputer, LNK_LibMemberRef *trigger, String8 path, String8 data) +{ + lnk_log(LNK_Log_InputObj, "Input Obj: %S", path); + LNK_Input *input = lnk_inputer_push_linkgen(inputer->arena, &inputer->new_objs, path, data); + input->trigger = trigger; + return input; +} + +internal LNK_Input * +lnk_inputer_push_obj_thin(LNK_Inputer *inputer, LNK_LibMemberRef *trigger, String8 path) +{ + lnk_log(LNK_Log_InputObj, "Input Obj: %S", path); + Temp scratch = scratch_begin(0,0); + String8 full_path = os_full_path_from_path(scratch.arena, path); + LNK_Input *input = lnk_inputer_push_thin(inputer->arena, &inputer->new_objs, inputer->objs_ht, full_path); + input->trigger = trigger; + scratch_end(scratch); + return input; +} + +internal LNK_Input * +lnk_inputer_push_lib(LNK_Inputer *inputer, LNK_InputSourceType input_source, String8 path, String8 data) +{ + lnk_log(LNK_Log_InputLib, "Input Lib: %S", path); + return lnk_input_push(inputer->arena, &inputer->new_libs[input_source], path, data); +} + +internal LNK_Input * +lnk_inputer_push_lib_linkgen(LNK_Inputer *inputer, LNK_InputSourceType input_source, String8 path, String8 data) +{ + lnk_log(LNK_Log_InputLib, "Input Lib: %S", path); + return lnk_input_push(inputer->arena, &inputer->new_libs[input_source], path, data); +} + +internal LNK_Input * +lnk_input_from_path(HashTable *load_ht, String8 path) +{ + LNK_Input *input = hash_table_search_path_raw(load_ht, path); + if (input == 0) { + Temp scratch = scratch_begin(0, 0); + String8 full_path = os_full_path_from_path(scratch.arena, path); + input = hash_table_search_path_raw(load_ht, full_path); + scratch_end(scratch); + } + return input; +} + +internal LNK_Input * +lnk_inputer_push_lib_thin(LNK_Inputer *inputer, LNK_Config *config, LNK_InputSourceType input_source, String8 path) +{ + Temp scratch = scratch_begin(0,0); + + LNK_Input *input = 0; + + // default libraries may omit extension + if (input_source == LNK_InputSource_Default || input_source == LNK_InputSource_Obj) { + if (!str8_ends_with(path, str8_lit(".lib"), StringMatchFlag_CaseInsensitive)) { + path = push_str8f(scratch.arena, "%S.lib", path); + } + if (lnk_is_lib_disallowed(config, path)) { + goto exit; } } + + // was library already loaded? + input = hash_table_search_path_raw(inputer->libs_ht, path); + if (input) { + goto exit; + } + + // search disk for library + String8List matches = lnk_file_search(scratch.arena, config->lib_dir_list, path); + + // warn about missing library + if (matches.node_count == 0) { + KeyValuePair *was_reported = hash_table_search_path(inputer->missing_lib_ht, path); + if (was_reported == 0) { + hash_table_push_path_u64(inputer->arena, inputer->missing_lib_ht, path, 0); + lnk_error(LNK_Warning_FileNotFound, "unable to find library `%S`", path); + } + goto exit; + } + + String8 first_match = matches.first->string; + input = hash_table_search_path_raw(inputer->libs_ht, first_match); + if (input) { + goto exit; + } + + // warn about multiple matches + if (matches.node_count > 1) { + lnk_error(LNK_Warning_MultipleLibMatch, "multiple libraries match `%S` (picking first match)", path); + lnk_supplement_error_list(matches); + } + + lnk_log(LNK_Log_InputLib, "Input Lib: %S", first_match); + input = lnk_inputer_push_thin(inputer->arena, &inputer->new_libs[input_source], inputer->libs_ht, first_match); + + exit:; + scratch_end(scratch); + return input; +} + +internal B32 +lnk_inputer_has_items(LNK_Inputer *inputer) +{ + if (inputer->new_objs.count > 0) { + return 1; + } + + for EachIndex(i, ArrayCount(inputer->new_libs)) { + if (inputer->new_libs[i].count > 0) { + return 1; + } + } + + return 0; +} + +internal LNK_InputPtrArray +lnk_inputer_flush(Arena *arena, TP_Context *tp, LNK_Inputer *inputer, LNK_IO_Flags io_flags, LNK_InputList *all_inputs, LNK_InputList *new_inputs) +{ + Temp scratch = scratch_begin(&arena, 1); + + ProfBegin("Gather Thin Inputs"); + U64 thin_inputs_count = 0; + for (LNK_Input *node = new_inputs->first; node != 0; node = node->next) { + if (node->is_thin) { + thin_inputs_count += 1; + } + } + LNK_Input **thin_inputs = push_array(scratch.arena, LNK_Input *, thin_inputs_count); + U64 thin_idx = 0; + for (LNK_Input *node = new_inputs->first; node != 0; node = node->next) { + if (node->is_thin) { + thin_inputs[thin_idx++] = node; + } + } + String8Array thin_input_paths = {0}; + thin_input_paths.count = thin_inputs_count; + thin_input_paths.v = push_array(scratch.arena, String8, thin_inputs_count); + for EachIndex(i, thin_inputs_count) { + thin_input_paths.v[i] = thin_inputs[i]->path; + } + ProfEnd(); + + ProfBegin("Load Inputs From Disk"); + String8Array thin_input_datas = lnk_read_data_from_file_path_parallel(tp, inputer->arena, io_flags, thin_input_paths); + for EachIndex(thin_input_idx, thin_inputs_count) { + thin_inputs[thin_input_idx]->has_disk_read_failed = thin_input_datas.v[thin_input_idx].size == 0; + thin_inputs[thin_input_idx]->data = thin_input_datas.v[thin_input_idx]; + } + ProfEnd(); + + ProfBegin("Disk Read Check"); + for EachIndex(i, thin_inputs_count) { + if (thin_inputs[i]->has_disk_read_failed) { + lnk_error(LNK_Error_InvalidPath, "unable to find file \"%S\"", thin_inputs[i]->path); + } + } + ProfEnd(); + + LNK_InputPtrArray result = lnk_array_from_input_list(arena, *new_inputs); + + lnk_input_list_concat_in_place(all_inputs, new_inputs); + + scratch_end(scratch); + return result; +} + +internal void +lnk_lib_member_ref_list_push_node(LNK_LibMemberRefList *list, LNK_LibMemberRef *node) +{ + SLLQueuePush(list->first, list->last, node); + list->count += 1; +} + +internal int +lnk_lib_member_ref_is_before(void *raw_a, void *raw_b) +{ + LNK_LibMemberRef **a = raw_a, **b = raw_b; + LNK_Symbol *a_pull_in_ref = (*a)->lib->was_member_linked[(*a)->member_idx]; + LNK_Symbol *b_pull_in_ref = (*b)->lib->was_member_linked[(*b)->member_idx]; + return lnk_symbol_defined_is_before(a_pull_in_ref, b_pull_in_ref); +} + +internal LNK_LibMemberRef ** +lnk_array_from_lib_member_list(Arena *arena, LNK_LibMemberRefList list) +{ + LNK_LibMemberRef **result = push_array(arena, LNK_LibMemberRef *, list.count); + U64 idx = 0; + for (LNK_LibMemberRef *node = list.first; node != 0; node = node->next, idx += 1) { + result[idx] = node; + } + return result; +} + +internal LNK_ObjNode * +lnk_load_objs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, U64 *objs_count_out) +{ + ProfBegin("Input Objs [Count %llu]", inputer->new_objs.count); + Temp scratch = scratch_begin(arena->v, arena->count); + + // load obj inputer from disk + LNK_InputPtrArray new_input_objs = lnk_inputer_flush(arena->v[0], tp, inputer, config->io_flags, &inputer->objs, &inputer->new_objs); + + if (lnk_get_log_status(LNK_Log_InputObj) && new_input_objs.count) { + U64 input_size = 0; + for EachIndex(i, new_input_objs.count) { input_size += new_input_objs.v[i]->data.size; } + lnk_log(LNK_Log_InputObj, "[ Obj Input Size %M ]", input_size); + } + + LNK_ObjNode *new_objs = lnk_obj_from_input_many(tp, arena, config->machine, new_input_objs.count, new_input_objs.v); + + // if machine type was unspecified on the command line, derive it from obj file + if (config->machine == COFF_MachineType_Unknown) { + for EachIndex(obj_idx, new_input_objs.count) { + if (new_objs[obj_idx].data.header.machine != COFF_MachineType_Unknown) { + config->machine = new_objs[obj_idx].data.header.machine; + break; + } + } + } + + ProfBegin("Apply Directives"); + for EachIndex(obj_idx, new_input_objs.count) { + LNK_Obj *obj = &new_objs[obj_idx].data; + String8List raw_directives = lnk_raw_directives_from_obj(scratch.arena, obj); + LNK_DirectiveInfo directive_info = lnk_directive_info_from_raw_directives(scratch.arena, obj, raw_directives); + for EachIndex(i, ArrayCount(directive_info.v)) { + for (LNK_Directive *dir = directive_info.v[i].first; dir != 0; dir = dir->next) { + lnk_apply_cmd_option_to_config(config, dir->id, dir->value_list, obj); + } + } + } + ProfEnd(); + + if (objs_count_out) { + *objs_count_out = new_input_objs.count; + } + + scratch_end(scratch); + ProfEnd(); + return new_objs; +} + +internal void +lnk_load_libs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_Link *link) +{ + for EachIndex(input_source, LNK_InputSource_Count) { + ProfBegin("Input Libs [Count %llu]", inputer->new_libs[i].count); + + LNK_InputPtrArray new_input_libs = lnk_inputer_flush(arena->v[0], tp, inputer, config->io_flags, &inputer->libs, &inputer->new_libs[input_source]); + + if (lnk_get_log_status(LNK_Log_InputLib) && new_input_libs.count) { + U64 input_size = 0; + for EachIndex(i, new_input_libs.count) { input_size += new_input_libs.v[i]->data.size; } + lnk_log(LNK_Log_InputObj, "[ Lib Input Size %M ]", input_size); + } + + lnk_lib_list_push_parallel(tp, arena, &link->libs, new_input_libs.count, new_input_libs.v); + + ProfEnd(); + } +} + +internal void +lnk_load_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link) +{ + Temp scratch = scratch_begin(arena->v, arena->count); + + U64 obj_id_base = link->objs.count; + + U64 objs_count = 0; + LNK_ObjNode *objs = lnk_load_objs(tp, arena, config, inputer, symtab, link, &objs_count); + lnk_obj_list_push_node_many(&link->objs, objs_count, objs); + + // handle /INCLUDE + { + // group include symbols by obj + HashTable *ht = hash_table_init(scratch.arena, 64); + for (LNK_IncludeSymbolNode *node = config->include_symbol_list.first; node != 0; node = node->next) { + LNK_IncludeSymbol *include_symbol = &node->v; + + // skip, include symbol is already in the global symbol table + if (lnk_symbol_table_search(symtab, include_symbol->name)) { + continue; + } + + // was obj already seen? + String8List *include_name_list = hash_table_search_raw_raw(ht, include_symbol->obj); + + if (include_name_list == 0) { + // push entry for new obj + include_name_list = push_array(scratch.arena, String8List, 1); + hash_table_push_raw_raw(scratch.arena, ht, include_symbol->obj, include_name_list); + } + + // append include symbol to obj's name list + str8_list_push(scratch.arena, include_name_list, include_symbol->name); + } + + LNK_Obj **objs_with_includes = keys_from_hash_table_raw(scratch.arena, ht); + String8List **include_names = values_from_hash_table_raw(scratch.arena, ht); + for EachIndex(i, ht->count) { + LNK_Obj *obj_with_includes = objs_with_includes[i]; + String8 include_obj_path = obj_with_includes ? obj_with_includes->path : str8_lit("RADLINK"); + String8 include_obj_data = lnk_make_obj_with_undefined_symbols(arena->v[0], *include_names[i]); + lnk_inputer_push_obj_linkgen(inputer, obj_with_includes ? obj_with_includes->trigger_symbol : 0, include_obj_path, include_obj_data); + + U64 include_obj_count = 0; + LNK_ObjNode *include_obj = lnk_load_objs(tp, arena, config, inputer, symtab, link, &include_obj_count); + AssertAlways(include_obj_count == 1); + + if (obj_with_includes) { + DLLInsert(link->objs.first, link->objs.last, obj_with_includes->node, include_obj); + link->objs.count += 1; + } else { + lnk_obj_list_push_node(&link->objs, include_obj); + } + } + } + + // assign input indices to objs + { + U64 node_idx = 0; + for (LNK_ObjNode *node = objs; node != 0; node = node->next, node_idx += 1) { + node->data.input_idx = obj_id_base + node_idx; + } + } + + // input indices on objs are finalized, push external symbols to the global symbol table + { + U64 new_objs_count = 0; + for (LNK_ObjNode *node = objs; node != 0; node = node->next) { new_objs_count += 1; } + + LNK_Obj **new_objs = push_array(scratch.arena, LNK_Obj *, new_objs_count); + { + U64 node_idx = 0; + for (LNK_ObjNode *node = objs; node != 0; node = node->next) { + new_objs[node_idx++] = &node->data; + } + } + + lnk_push_obj_symbols(tp, arena, symtab, new_objs_count, new_objs); + } + + // input default libraries + for (; *link->last_default_lib; link->last_default_lib = &(*link->last_default_lib)->next) { + lnk_inputer_push_lib_thin(inputer, config, LNK_InputSource_Default, (*link->last_default_lib)->string); + } + + // input libraries referenced in objs + for (; *link->last_obj_lib; link->last_obj_lib = &(*link->last_obj_lib)->next) { + lnk_inputer_push_lib_thin(inputer, config, LNK_InputSource_Obj, (*link->last_obj_lib)->string); + } + + // load new libs + lnk_load_libs(tp, arena, config, inputer, link); + + scratch_end(scratch); +} + +internal void +lnk_resolve_entry_point(LNK_Config *config, LNK_SymbolTable *symtab, LNK_Link *link) +{ + // loop over all possible subsystems and entry point names and pick + // subsystem that has a defined entry point symbol + if (config->entry_point_name.size == 0) { + PE_WindowsSubsystem subsys_first = config->subsystem; + PE_WindowsSubsystem subsys_last = config->subsystem == PE_WindowsSubsystem_UNKNOWN ? PE_WindowsSubsystem_COUNT : config->subsystem+1; + LNK_Symbol *entry_point_symbol = 0; + for (U64 subsys_idx = subsys_first; subsys_idx < subsys_last; subsys_idx += 1) { + String8Array entry_points = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); + for EachIndex(i, entry_points.count) { + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, entry_points.v[i]); + if (symbol) { + config->subsystem = subsys_idx; + config->entry_point_name = entry_points.v[i]; + goto found_entry_and_subsystem; + } + } + } +found_entry_and_subsystem:; + } + + // search for entry point in libs + if (config->entry_point_name.size == 0 && config->subsystem != PE_WindowsSubsystem_UNKNOWN) { + String8Array entry_points = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics); + for EachIndex(entry_idx, entry_points.count) { + for (LNK_LibNode *lib_n = link->libs.first; lib_n != 0; lib_n = lib_n->next) { + if (lnk_search_lib(&lib_n->data, entry_points.v[entry_idx], 0)) { + config->entry_point_name = entry_points.v[entry_idx]; + goto found_entry_in_libs; + } + } + } +found_entry_in_libs:; + } + + // infer subsystem from entry point name + if (config->entry_point_name.size != 0 && config->subsystem == PE_WindowsSubsystem_UNKNOWN) { + for EachIndex(subsys_idx, PE_WindowsSubsystem_COUNT) { + String8Array entry_points = pe_get_entry_point_names(config->machine, subsys_idx, config->file_characteristics); + for EachIndex(i, entry_points.count) { + if (str8_match(entry_points.v[i], config->entry_point_name, 0)) { + config->subsystem = subsys_idx; + goto subsystem_inferred_from_entry; + } + } + } +subsystem_inferred_from_entry:; + } + + // do we have an entry point name? + if (config->entry_point_name.size) { + // redirect user entry to appropriate CRT entry + String8 crt_entry_point_name = msvcrt_ctr_entry_from_user_entry(config->entry_point_name); + config->entry_point_name = crt_entry_point_name.size ? crt_entry_point_name : config->entry_point_name; + + // generate undefined symbol for entry point + lnk_include_symbol(config, config->entry_point_name, 0); + + // do we have a subsystem? + if (config->subsystem != PE_WindowsSubsystem_UNKNOWN) { + // if subsystem version not specified set default values + if (config->subsystem_ver.major == 0 && config->subsystem_ver.minor == 0) { + config->subsystem_ver = lnk_get_default_subsystem_version(config->subsystem, config->machine); + } + + // check subsystem version against allowed min version + Version min_subsystem_ver = lnk_get_min_subsystem_version(config->subsystem, config->machine); + if (version_compar(config->subsystem_ver, min_subsystem_ver) < 0) { + lnk_error(LNK_Error_Cmdl, "subsystem version %I64u.%I64u can't be lower than %I64u.%I64u", + config->subsystem_ver.major, config->subsystem_ver.minor, min_subsystem_ver.major, min_subsystem_ver.minor); + } + + // by default terminal server is enabled for windows and console applications + if (~config->flags & LNK_ConfigFlag_NoTsAware && ~config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) { + if (config->subsystem == PE_WindowsSubsystem_WINDOWS_GUI || config->subsystem == PE_WindowsSubsystem_WINDOWS_CUI) { + config->dll_characteristics |= PE_DllCharacteristic_TERMINAL_SERVER_AWARE; + } + } + + // entry point found! + link->try_to_resolve_entry_point = 0; + } else { + lnk_error(LNK_Error_NoSubsystem, "unknown subsystem, please use /SUBSYSTEM to set subsytem type you need"); + } + } +} + +internal void +lnk_queue_lib_member(Arena *arena, LNK_LibMemberRefList *queued_members, LNK_Symbol *trigger_symbol, LNK_Lib *lib, U32 member_idx) +{ + if (lnk_flag_member_as_queued(lib, member_idx, trigger_symbol)) { + LNK_LibMemberRef *member_ref = push_array(arena, LNK_LibMemberRef, 1); + member_ref->lib = lib; + member_ref->member_idx = member_idx; + + lnk_lib_member_ref_list_push_node(queued_members, member_ref); + + trigger_symbol->is_lib_member_linked = 1; + } +} + +internal void +lnk_link_inputs_(TP_Context *tp, + TP_Arena *arena, + LNK_Config *config, + LNK_Inputer *inputer, + LNK_SymbolTable *symtab, + LNK_Link *link, + LNK_ImportTables *imps, + B32 search_anti_deps) +{ + Temp scratch = scratch_begin(arena->v, arena->count); + + lnk_load_inputs(tp, arena, config, inputer, symtab, link); + + for (U64 resolved_members_count = 0; ; resolved_members_count = 0) { + if (link->try_to_resolve_entry_point) { + lnk_resolve_entry_point(config, symtab, link); + } + + for (LNK_LibNode *lib_n = link->libs.first; lib_n != 0; lib_n = lib_n->next) { + do { + LNK_LibMemberRefList queued_members = {0}; + for EachIndex(worker_id, symtab->arena->count) { + for (LNK_SymbolHashTrieChunk *c = symtab->chunks[worker_id].first; c != 0; c = c->next) { + for EachIndex(i, c->count) { + LNK_Symbol *symbol = c->v[i].symbol; + if (symbol->is_lib_member_linked) { continue; } + + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); + if (symbol_interp == COFF_SymbolValueInterp_Undefined) { + U32 member_idx; + if (lnk_search_lib(&lib_n->data, symbol->name, &member_idx)) { + lnk_queue_lib_member(arena->v[0], &queued_members, symbol, &lib_n->data, member_idx); + } + } else if (symbol_interp == COFF_SymbolValueInterp_Weak) { + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol->defined.obj->header.is_big_obj); + if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { + U32 member_idx; + if (lnk_search_lib(&lib_n->data, symbol->name, &member_idx)) { + lnk_queue_lib_member(arena->v[0], &queued_members, symbol, &lib_n->data, member_idx); + } + } else if (search_anti_deps && weak_ext->characteristics == COFF_WeakExt_AntiDependency) { + LNK_SymbolDefined dep_symbol = lnk_resolve_weak_symbol(symtab, symbol->defined); + COFF_ParsedSymbol dep_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dep_symbol.obj, dep_symbol.symbol_idx); + COFF_SymbolValueInterpType dep_interp = coff_interp_from_parsed_symbol(dep_parsed); + if (dep_interp == COFF_SymbolValueInterp_Weak) { + U32 member_idx; + if (lnk_search_lib(&lib_n->data, symbol_parsed.name, &member_idx)) { + lnk_queue_lib_member(scratch.arena, &queued_members, symbol, &lib_n->data, member_idx); + } + } + } + } + } + } + } + + LNK_LibMemberRef **member_refs = lnk_array_from_lib_member_list(scratch.arena, queued_members); + radsort(member_refs, queued_members.count, lnk_lib_member_ref_is_before); + + // load lib member refs + for EachIndex(i, queued_members.count) { + LNK_LibMemberRef *member_ref = member_refs[i]; + LNK_Lib *lib = member_ref->lib; + U32 member_offset = lib->member_offsets[member_ref->member_idx]; + + // parse member + COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, member_offset); + COFF_DataType member_type = coff_data_type_from_data(member_info.data); + + switch (member_type) { + case COFF_DataType_Import: { + COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(member_info.data); + + // import machine compat check + if (import_header.machine != config->machine) { + lnk_error(LNK_Error_IncompatibleMachine, "symbol %S pulled in import with incompatible machine %S (expected %S)", + import_header.func_name, + coff_string_from_machine_type(import_header.machine), + coff_string_from_machine_type(config->machine)); + break; + } + + if (str8_match(import_header.func_name, str8_lit("LoadLibraryA"), 0)) { + int x = 0; + } + + // skip duplicate import inputer + if (hash_table_search_string_raw(imps->import_stub_ht, import_header.func_name, 0)) { break; } + hash_table_push_string_raw(imps->arena, imps->import_stub_ht, import_header.func_name, 0); + + // create import stubs (later replaced with acutal imports generated by linker) + LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, str8_lit(LNK_IMPORT_STUB)); + LNK_Symbol *thunk_symbol = lnk_make_defined_symbol(symtab->arena->v[0], import_header.func_name, import_stub->defined.obj, import_stub->defined.symbol_idx); + LNK_Symbol *imp_symbol = lnk_make_defined_symbol(symtab->arena->v[0], push_str8f(symtab->arena->v[0], "__imp_%S", import_header.func_name), import_stub->defined.obj, import_stub->defined.symbol_idx); + lnk_symbol_table_push(symtab, thunk_symbol); + lnk_symbol_table_push(symtab, imp_symbol); + + // search DLL symbol list + HashTable *imports_ht = lnk_is_dll_delay_load(config, import_header.dll_name) ? imps->delayed_imports : imps->static_imports; + String8List *import_symbols = hash_table_search_path_raw(imports_ht, import_header.dll_name); + if (import_symbols == 0) { + import_symbols = push_array(imps->arena, String8List, 1); + hash_table_push_path_raw(imps->arena, imports_ht, import_header.dll_name, import_symbols); + } + + // push symbol + str8_list_push(imps->arena, import_symbols, member_info.data); + } break; + case COFF_DataType_BigObj: + case COFF_DataType_Obj: { + String8 obj_path = coff_parse_long_name(lib->long_names, member_info.header.name); + + // obj path in thin archive has slash appended which screws up + // file lookup on disk; it couble be there to enable paths to symbols + // but we don't use this feature + String8 slash = str8_lit("/"); + if (str8_ends_with(obj_path, slash, 0)) { + obj_path = str8_chop(obj_path, slash.size); + } + + B32 is_thin = lib->type == COFF_Archive_Thin; + if (is_thin) { + + // obj path in thin archive is relative to directory with archive + String8List obj_path_list = {0}; + str8_list_push(scratch.arena, &obj_path_list, str8_chop_last_slash(lib->path)); + str8_list_push(scratch.arena, &obj_path_list, obj_path); + obj_path = str8_path_list_join_by_style(inputer->arena, &obj_path_list, config->path_style); + + lnk_inputer_push_obj_thin(inputer, member_ref, obj_path); + } else { + lnk_inputer_push_obj(inputer, member_ref, obj_path, member_info.data); + } + } break; + case COFF_DataType_Null: break; + default: { InvalidPath; } break; + } + } + + lnk_load_inputs(tp, arena, config, inputer, symtab, link); + + resolved_members_count += queued_members.count; + } while (lnk_inputer_has_items(inputer)); + } + + if (resolved_members_count == 0) { break; } + } + + scratch_end(scratch); +} + +internal void +lnk_link_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, LNK_ImportTables *imps) +{ + lnk_link_inputs_(tp, arena, config, inputer, symtab, link, imps, 0); + + // input /ALTERNATENAME + { + // replace undefined symbols that have an alternate name with a weak symbol + for (LNK_AltNameNode *alt_name_n = config->alt_name_list.first; alt_name_n != 0; alt_name_n = alt_name_n->next) { + LNK_SymbolHashTrie *symbol_ht = lnk_symbol_table_search_(symtab, alt_name_n->data.from); + if (symbol_ht) { + COFF_SymbolValueInterpType interp = lnk_interp_from_symbol(symbol_ht->symbol); + if (interp == COFF_SymbolValueInterp_Undefined) { + // clear out slot so weak symbol can replace undefined symbol (general rule is + // weak symbol is not allowed to replace undefined) + symbol_ht->symbol = 0; + + // make obj with alternamte name symbol + String8 alt_name_obj_data; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); + COFF_ObjSymbol *from_symbol = coff_obj_writer_push_symbol_weak(obj_writer, alt_name_n->data.from, COFF_WeakExt_SearchLibrary, 0); + COFF_ObjSymbol *to_symbol = coff_obj_writer_push_symbol_weak(obj_writer, alt_name_n->data.to, COFF_WeakExt_AntiDependency, from_symbol); + coff_obj_writer_set_default_symbol(from_symbol, to_symbol); + alt_name_obj_data = coff_obj_writer_serialize(arena->v[0], obj_writer); + coff_obj_writer_release(&obj_writer); + } + + // input alt name obj + LNK_Obj *obj_with_alt_name = alt_name_n->data.obj; + String8 alt_name_obj_path = obj_with_alt_name ? obj_with_alt_name->path : str8_lit("RADLINK"); + lnk_inputer_push_obj_linkgen(inputer, 0, alt_name_obj_path, alt_name_obj_data); + } + } + } + } + + // + // include delay load helper + // + if (config->machine != COFF_MachineType_Unknown && config->delay_load_helper_name.size == 0 && config->delay_load_dll_list.node_count) { + config->delay_load_helper_name = mscrt_delay_load_helper_name_from_machine(config->machine); + if (config->delay_load_helper_name.size) { + lnk_include_symbol(config, config->delay_load_helper_name, 0); + } + } + + lnk_link_inputs_(tp, arena, config, inputer, symtab, link, imps, 1); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_replace_weak_with_default_symbol_task) +{ + LNK_ReplaceWeakSymbolsWithDefaultSymbolTask *task = raw_task; + LNK_SymbolTable *symtab = task->symtab; + LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id]; + for EachIndex(i, chunk->count) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); + if (symbol_interp == COFF_SymbolValueInterp_Weak) { + LNK_SymbolDefined resolve = lnk_resolve_weak_symbol(symtab, symbol->defined); + COFF_ParsedSymbol resolve_parsed = lnk_parsed_symbol_from_coff_symbol_idx(resolve.obj, resolve.symbol_idx); + COFF_SymbolValueInterpType resolve_interp = coff_interp_from_parsed_symbol(resolve_parsed); + if (resolve_interp == COFF_SymbolValueInterp_Weak) { + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(resolve_parsed, symbol->defined.obj->header.is_big_obj); + if (symbol->defined.obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol_parsed.raw_symbol; + symbol32->section_number = COFF_Symbol_UndefinedSection; + symbol32->value = 0; + symbol32->storage_class = COFF_SymStorageClass_External; + } else { + COFF_Symbol16 *symbol16 = symbol_parsed.raw_symbol; + symbol16->section_number = COFF_Symbol_UndefinedSection; + symbol16->value = 0; + symbol16->storage_class = COFF_SymStorageClass_External; + } + } else { + symbol->defined = resolve; + } + } + } +} + +internal LNK_Link * +lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab) +{ + Temp scratch = scratch_begin(arena->v, arena->count); + + // + // init link context + // + LNK_Link *link = push_array(arena->v[0], LNK_Link, 1); + link->last_include = &config->include_symbol_list.first; + link->last_default_lib = &config->input_default_lib_list.first; + link->last_obj_lib = &config->input_obj_lib_list.first; + link->last_cmd_lib = &config->input_list[LNK_Input_Lib].first; + link->try_to_resolve_entry_point = 1; + + // + // init import hash tables + // + LNK_ImportTables *imps = 0; + { + Arena *imps_arena = arena_alloc(); + imps = push_array(imps_arena, LNK_ImportTables, 1); + imps->arena = imps_arena; + imps->static_imports = hash_table_init(imps->arena, 0x1000); + imps->delayed_imports = hash_table_init(imps->arena, 0x1000); + imps->import_stub_ht = hash_table_init(imps->arena, 0x10000); + } + + // input :null_obj + String8 null_obj = lnk_make_null_obj(inputer->arena); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Null *"), null_obj); + + // input objs on command line + for (String8Node *obj_path = config->input_list[LNK_Input_Obj].first; obj_path != 0; obj_path = obj_path->next) { + lnk_inputer_push_obj_thin(inputer, 0, obj_path->string); + } + + // input libs from command line + for (; *link->last_cmd_lib; link->last_cmd_lib = &(*link->last_cmd_lib)->next) { + lnk_inputer_push_lib_thin(inputer, config, LNK_InputSource_CmdLine, (*link->last_cmd_lib)->string); + } + + // link inputer + lnk_link_inputs(tp, arena, config, inputer, symtab, link, imps); + + // TODO: need to know under which condition to include load config + //lnk_include_symbol(config, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME), 0); + + { + ProfBegin("Push Linker Symbols"); + String8 linker_symbols_obj = lnk_make_linker_obj(arena->v[0], config); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Linker Symbols *"), linker_symbols_obj); + ProfEnd(); + } + + // make and input delayed imports + { + HashTable *delayed_imports = imps->delayed_imports; + if (delayed_imports->count) { + ProfBegin("Build Delay Import Table"); + + COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; + B32 emit_biat = config->import_table_emit_biat == LNK_SwitchState_Yes; + B32 emit_uiat = config->import_table_emit_uiat == LNK_SwitchState_Yes; + String8 *dll_names = keys_from_hash_table_string(scratch.arena, delayed_imports); + String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, delayed_imports); + + for EachIndex(dll_idx, delayed_imports->count) { + String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); + String8 import_obj = pe_make_import_dll_obj_delayed(arena->v[0], time_stamp, config->machine, dll_names[dll_idx], config->delay_load_helper_name, import_debug_symbols, *dll_import_headers[dll_idx], emit_biat, emit_uiat); + lnk_inputer_push_obj(inputer, 0, dll_names[dll_idx], import_obj); + } + + String8 linker_debug_symbols = lnk_make_linker_debug_symbols(arena->v[0], config->machine); + String8 null_desc_obj = pe_make_null_import_descriptor_delayed(arena->v[0], time_stamp, config->machine, linker_debug_symbols); + String8 null_thunk_obj = pe_make_null_thunk_data_obj_delayed(arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); + lnk_inputer_push_obj(inputer, 0, str8_lit("* Delayed Null Import Descriptor *"), null_desc_obj); + lnk_inputer_push_obj(inputer, 0, str8_lit("* Delayed Null Thunk Data *"), null_thunk_obj); + + ProfEnd(); + } + } + + // make and input static imports + { + HashTable *static_imports = imps->static_imports; + if (static_imports->count) { + ProfBegin("Build Static Import Table"); + + COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; + String8 *dll_names = keys_from_hash_table_string(scratch.arena, static_imports); + String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, static_imports); + for EachIndex(dll_idx, static_imports->count) { + String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); + String8 import_obj = pe_make_import_dll_obj_static(arena->v[0], time_stamp, config->machine, dll_names[dll_idx], import_debug_symbols, *dll_import_headers[dll_idx]); + lnk_inputer_push_obj(inputer, 0, dll_names[dll_idx], import_obj); + } + + String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); + String8 null_desc_obj = pe_make_null_import_descriptor_obj(arena->v[0], time_stamp, config->machine, linker_debug_symbols); + String8 null_thunk_obj = pe_make_null_thunk_data_obj(arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Null Import Descriptor *"), null_desc_obj); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Null Thunk Data *"), null_thunk_obj); + + ProfEnd(); + } + } + + if (config->export_symbol_list.count) { + ProfBegin("Build Export Table"); + + PE_ExportParseList resolved_exports = {0}; + for (PE_ExportParseNode *exp_n = config->export_symbol_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { + exp_n_next = exp_n->next; + PE_ExportParse *exp = &exp_n->data; + + if (str8_match(exp->name, config->entry_point_name, 0)) { + lnk_error_with_loc(LNK_Warning_TryingToExportEntryPoint, exp->obj_path, exp->lib_path, "exported entry point \"%S\"", exp->name); + } + if (str8_match(exp->alias, config->entry_point_name, 0)) { + lnk_error_with_loc(LNK_Warning_TryingToExportEntryPoint, exp->obj_path, exp->lib_path, "alias exports entry point \"%S=%S\"", exp->name, exp->alias); + continue; + } + + if (!exp->is_forwarder) { + // filter out unresolved exports + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, 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; + } + } + + // push resolved export + pe_export_parse_list_push_node(&resolved_exports, exp_n); + } + + PE_FinalizedExports finalized_exports = pe_finalize_export_list(scratch.arena, resolved_exports); + String8 edata_obj = pe_make_edata_obj(arena->v[0], str8_skip_last_slash(config->image_name), COFF_TimeStamp_Max, config->machine, finalized_exports); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Exports *"), edata_obj); + + ProfEnd(); + } + + { + String8List res_data_list = {0}; + String8List res_path_list = {0}; + + // do we have manifest deps passed through pragma alone? + LNK_ManifestOpt manifest_opt = config->manifest_opt; + if (config->manifest_dependency_list.node_count > 0 && manifest_opt == LNK_ManifestOpt_Null) { + manifest_opt = LNK_ManifestOpt_Embed; + } + + switch (manifest_opt) { + case LNK_ManifestOpt_Embed: { + ProfBegin("Embed Manifest"); + // TODO: currently we convert manifest to res and parse res again, this unnecessary instead push manifest + // resource to the tree directly + String8 manifest_data = lnk_manifest_from_inputs(scratch.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, config->input_list[LNK_Input_Manifest], config->manifest_dependency_list); + String8 manifest_res = pe_make_manifest_resource(scratch.arena, *config->manifest_resource_id, manifest_data); + str8_list_push(scratch.arena, &res_data_list, manifest_res); + str8_list_push(scratch.arena, &res_path_list, str8_lit("* Manifest *")); + ProfEnd(); + } break; + case LNK_ManifestOpt_WriteToFile: { + ProfBeginDynamic("Write Manifest To: %.*s", str8_varg(config->manifest_name)); + Temp temp = temp_begin(scratch.arena); + String8 manifest_data = lnk_manifest_from_inputs(temp.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, config->input_list[LNK_Input_Manifest], config->manifest_dependency_list); + lnk_write_data_to_file_path(config->manifest_name, str8_zero(), manifest_data); + temp_end(temp); + ProfEnd(); + } break; + case LNK_ManifestOpt_Null: { + Assert(config->input_list[LNK_Input_Manifest].node_count == 0); + Assert(config->manifest_dependency_list.node_count == 0); + } break; + case LNK_ManifestOpt_No: { + // omit manifest generation + } break; + } + + ProfBegin("Load .res files from disk"); + for (String8Node *node = config->input_list[LNK_Input_Res].first; node != 0; node = node->next) { + String8 res_data = lnk_read_data_from_file_path(scratch.arena, config->io_flags, node->string); + if (res_data.size > 0) { + if (pe_is_res(res_data)) { + str8_list_push(scratch.arena, &res_data_list, res_data); + String8 stable_res_path = lnk_make_full_path(scratch.arena, config->path_style, config->work_dir, node->string); + str8_list_push(scratch.arena, &res_path_list, stable_res_path); + } else { + lnk_error(LNK_Error_LoadRes, "file is not of RES format: %S", node->string); + } + } else { + lnk_error(LNK_Error_LoadRes, "unable to open res file: %S", node->string); + } + } + ProfEnd(); + + if (res_data_list.node_count > 0) { + ProfBegin("Build * Resources *"); + String8 obj_name = str8_lit("* Resources *"); + String8 obj_data = lnk_make_res_obj(arena->v[0], res_data_list, res_path_list, config->machine, config->time_stamp, config->work_dir, config->path_style, obj_name); + lnk_inputer_push_obj_linkgen(inputer, 0, obj_name, obj_data); + ProfEnd(); + } + } + + if (lnk_do_debug_info(config)) { + { + ProfBegin("Build * Linker * Obj"); + String8 obj_name = str8_lit("* Linker *"); + String8 raw_cmd_line = str8_list_join(scratch.arena, &config->raw_cmd_line, &(StringJoin){ str8_lit_comp(""), str8_lit_comp(" "), str8_lit_comp("") }); + String8 obj_data = lnk_make_linker_coff_obj(arena->v[0], config->time_stamp, config->machine, config->work_dir, config->image_name, config->pdb_name, raw_cmd_line, obj_name); + lnk_inputer_push_obj_linkgen(inputer, 0, obj_name, obj_data); + ProfEnd(); + } + + ProfBegin("Build * Debug Directories *"); + if (config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null) { + String8 pdb_dir_obj = pe_make_debug_directory_pdb_obj(arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->pdb_alt_path); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Debug Directory PDB *"), pdb_dir_obj); + } + if (config->rad_debug == LNK_SwitchState_Yes) { + String8 rdi_dir_obj = pe_make_debug_directory_rdi_obj(arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->rad_debug_alt_path); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Debug Directory RDI *"), rdi_dir_obj); + } + ProfEnd(); + } + + // + // link linker made objs + // + lnk_link_inputs(tp, arena, config, inputer, symtab, link, imps); + + // + // warn about unused delayloads + // + if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { + for (String8Node *dll_name_n = config->delay_load_dll_list.first; dll_name_n != 0; dll_name_n = dll_name_n->next) { + if (!hash_table_search_path_raw(imps->delayed_imports, dll_name_n->string)) { + lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", dll_name_n->string); + } + } + } + + // + // report undefined symbols + // + { + U64 chunks_count = 0; + LNK_SymbolHashTrieChunk **chunks = lnk_array_from_symbol_hash_trie_chunk_list(scratch.arena, symtab->chunks, symtab->arena->count, &chunks_count); + + ProfBegin("Replace Unresolved Weak Symbols With Defualt Symbol"); + { + LNK_ReplaceWeakSymbolsWithDefaultSymbolTask task = { .symtab = symtab, .chunks = chunks }; + tp_for_parallel(tp, 0, chunks_count, lnk_replace_weak_with_default_symbol_task, &task); +#if BUILD_DEBUG + for EachIndex(chunk_idx, chunks_count) { + LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; + for EachIndex(i, chunk->count) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol); + AssertAlways(symbol_interp != COFF_SymbolValueInterp_Weak); + } + } +#endif + } + ProfEnd(); + + + if (config->entry_point_name.size == 0) { + lnk_error(LNK_Error_EntryPoint, "unable to find entry point symbol"); + } + + ProfBegin("Report Unresolved Symbols"); + { + U64 count = 0; + for EachIndex(chunk_idx, chunks_count) { + LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; + for EachIndex(i, chunk->count) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol); + if (symbol_interp == COFF_SymbolValueInterp_Undefined) { + count += 1; + } + } + } + + U64 cursor = 0; + LNK_Symbol **unresolved = push_array(scratch.arena, LNK_Symbol *, count); + for EachIndex(chunk_idx, chunks_count) { + LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; + for EachIndex(i, chunk->count) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol); + if (symbol_interp == COFF_SymbolValueInterp_Undefined) { + unresolved[cursor++] = chunk->v[i].symbol; + } + } + } + + radsort(unresolved, count, lnk_symbol_defined_ptr_is_before); + + for EachIndex(i, count) { + LNK_Symbol *symbol = unresolved[i]; + lnk_error_obj(LNK_Error_UnresolvedSymbol, symbol->defined.obj, "unresolved symbol %S", symbol->name); + } + + // TODO: /FORCE + if (count) { + lnk_exit(LNK_Error_UnresolvedSymbol); + } + } + ProfEnd(); + } + + // + // discard COMDAT sections that are not referenced + // + if (config->opt_ref == LNK_SwitchState_Yes) { + lnk_opt_ref(tp, symtab, config, link->objs); + } + + // + // infer minimal padding size for functions from the target machine + // + if (config->machine != COFF_MachineType_Unknown && config->infer_function_pad_min) { + config->function_pad_min = lnk_get_default_function_pad_min(config->machine); + config->infer_function_pad_min = 0; + } + + // + // log + // + if (lnk_get_log_status(LNK_Log_InputObj)) { + U64 total_input_size = 0; + for (LNK_ObjNode *obj_n = link->objs.first; obj_n != 0; obj_n = obj_n->next) { total_input_size += obj_n->data.data.size; } + lnk_log(LNK_Log_InputObj, "[Total Obj Input Size %M]", total_input_size); + } + if (lnk_get_log_status(LNK_Log_InputLib)) { + U64 total_input_size = 0; + for (LNK_LibNode *lib_n = link->libs.first; lib_n != 0; lib_n = lib_n->next) { total_input_size += lib_n->data.data.size; } + lnk_log(LNK_Log_InputLib, "[Total Lib Input Size %M]", total_input_size); + } + + scratch_end(scratch); + return link; } internal void @@ -1053,17 +2085,15 @@ lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj // define roots // { - String8List roots = str8_list_copy(scratch.arena, &config->include_symbol_list); - // tls LNK_Symbol *tls_symbol = lnk_symbol_table_searchf(symtab, MSCRT_TLS_SYMBOL_NAME); if (tls_symbol) { - str8_list_pushf(scratch.arena, &roots, MSCRT_TLS_SYMBOL_NAME); + lnk_include_symbol(config, str8_lit(MSCRT_TLS_SYMBOL_NAME), 0); } // push tasks for each root symbol - for (String8Node *root_n = roots.first; root_n != 0; root_n = root_n->next) { - LNK_Symbol *root = lnk_symbol_table_search(symtab, root_n->string); + for (LNK_IncludeSymbolNode *root_n = config->include_symbol_list.first; root_n != 0; root_n = root_n->next) { + LNK_Symbol *root = lnk_symbol_table_search(symtab, root_n->v.name); struct Task *t = push_array(scratch.arena, struct Task, 1); t->obj = root->defined.obj; @@ -1193,10 +2223,12 @@ lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj ProfBegin("Remove Unreachable Sections"); for (LNK_ObjNode *obj_n = objs.first; obj_n != 0; obj_n = obj_n->next) { LNK_Obj *obj = &obj_n->data; + for EachIndex(sect_idx, obj->header.section_count_no_null) { U32 section_number = sect_idx+1; COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number); + if (lnk_is_coff_section_debug(obj, sect_idx)) { continue; } // remove unreferenced sections @@ -1225,1058 +2257,6 @@ lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj ProfEnd(); } -internal -THREAD_POOL_TASK_FUNC(lnk_replace_weak_with_default_symbol_task) -{ - LNK_ReplaceWeakSymbolsWithDefaultSymbolTask *task = raw_task; - LNK_SymbolTable *symtab = task->symtab; - LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id]; - for EachIndex(i, chunk->count) { - LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); - if (symbol_interp == COFF_SymbolValueInterp_Weak) { - LNK_SymbolDefined resolve = lnk_resolve_weak_symbol(symtab, symbol->defined); - COFF_ParsedSymbol resolve_parsed = lnk_parsed_symbol_from_coff_symbol_idx(resolve.obj, resolve.symbol_idx); - COFF_SymbolValueInterpType resolve_interp = coff_interp_from_parsed_symbol(resolve_parsed); - if (resolve_interp == COFF_SymbolValueInterp_Weak) { - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(resolve_parsed, symbol->defined.obj->header.is_big_obj); - if (symbol->defined.obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = symbol_parsed.raw_symbol; - symbol32->section_number = COFF_Symbol_UndefinedSection; - symbol32->value = 0; - symbol32->storage_class = COFF_SymStorageClass_External; - } else { - COFF_Symbol16 *symbol16 = symbol_parsed.raw_symbol; - symbol16->section_number = COFF_Symbol_UndefinedSection; - symbol16->value = 0; - symbol16->storage_class = COFF_SymStorageClass_External; - } - } else { - symbol->defined = resolve; - } - } - } -} - -internal LNK_LinkContext -lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) -{ - enum State { - State_Null, - State_InputDisallowLibs, - State_InputImports, - State_InputInclude, - State_InputObjs, - State_InputLibs, - State_InputDelayLoadDlls, - State_InputLinkerObjs, - State_SearchUndefinedAndWeak, - State_SearchWeakAntiDep, - State_SearchEntryPoint, - }; - struct StateNode { struct StateNode *next; enum State state; }; - struct StateList { U64 count; struct StateNode *first; struct StateNode *last; }; -#define state_list_push(a, l, s) do { \ - struct StateNode *node = push_array(a, struct StateNode, 1); \ - node->state = s; \ - SLLQueuePush(l.first, l.last, node); \ - l.count += 1; \ -} while (0) -#define state_list_pop(l) (l).first->state; SLLQueuePop((l).first, (l).last); (l).count -= 1 - typedef enum { - SearchFlag_UndefinedAndWeak = (1 << 0), - SearchFlag_WeakAntiDep = (1 << 1), - SearchFlag_All = (SearchFlag_UndefinedAndWeak|SearchFlag_WeakAntiDep) - } SearchFlags; - - ProfBeginFunction(); - Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); - - // - // state - // - LNK_SymbolTable *symtab = lnk_symbol_table_init(tp_arena); - String8Node **last_include_symbol = &config->include_symbol_list.first; - String8Node **last_disallow_lib = &config->disallow_lib_list.first; - String8Node **last_delay_load_dll = &config->delay_load_dll_list.first; - LNK_InputObjList input_obj_list = {0}; - LNK_InputImportList input_import_list = {0}; - LNK_InputLib **input_libs[LNK_InputSource_Count] = { &config->input_list[LNK_Input_Lib].first, &config->input_default_lib_list.first, &config->input_obj_lib_list.first }; - LNK_ObjList obj_list = {0}; - LNK_LibList lib_index[LNK_InputSource_Count] = {0}; - U64 entry_point_search_attempts = 0; - SearchFlags search_flags = SearchFlag_All; - B32 input_linker_objs = 1; - HashTable *static_imports = hash_table_init(scratch.arena, 512); - HashTable *delayed_imports = hash_table_init(scratch.arena, 512); - HashTable *import_stub_ht = hash_table_init(scratch.arena, 0x1000); - // TODO: move disallow_lib, loaded_lib, missing_lib, and loaded_obj to config - Arena *ht_arena = arena_alloc(); - HashTable *disallow_lib_ht = hash_table_init(scratch.arena, 0x100); - HashTable *loaded_lib_ht = hash_table_init(scratch.arena, 0x100); - HashTable *missing_lib_ht = hash_table_init(scratch.arena, 0x100); - HashTable *loaded_obj_ht = hash_table_init(scratch.arena, 0x4000); - - // TODO: this should happen in the config before state machine setup - { - // input :null_obj - LNK_InputObj *null_obj_input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - null_obj_input->exclude_from_debug_info = 1; - null_obj_input->path = str8_lit("* Null Obj *"); - null_obj_input->dedup_id = null_obj_input->path; - null_obj_input->data = lnk_make_null_obj(tp_arena->v[0]); - - // input command line objs - LNK_InputObjList cmd_line_obj_inputs = lnk_input_obj_list_from_string_list(scratch.arena, config->input_list[LNK_Input_Obj]); - lnk_input_obj_list_concat_in_place(&input_obj_list, &cmd_line_obj_inputs); - } - - // TODO: MSVC does not need this flag to include load config - if (config->guard_flags != LNK_Guard_None) { - // TODO: config_refactor - String8List value_strings = {0}; - str8_list_push(scratch.arena, &value_strings, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME)); - lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0); - } - - // - // run states - // - struct StateList state_list = {0}; - for (;;) { - for (; state_list.count > 0; ) { - enum State state = state_list_pop(state_list); - switch (state) { - case State_Null: break; - - case State_InputDisallowLibs: { - ProfBegin("Input /disallowlib"); - for (; *last_disallow_lib; last_disallow_lib = &(*last_disallow_lib)->next) { - if ( ! lnk_is_lib_disallowed(disallow_lib_ht, (*last_disallow_lib)->string)) { - lnk_push_disallow_lib(scratch.arena, disallow_lib_ht, (*last_disallow_lib)->string); - } - } - ProfEnd(); - } break; - case State_InputImports: { - ProfBegin("Input Imports"); - for (LNK_InputImportNode *input = input_import_list.first; input != 0; input = input->next) { - COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(input->data.coff_import); - - // import machine compat check - if (import_header.machine != config->machine) { - lnk_error(LNK_Error_IncompatibleMachine, "symbol %S pulled in import with incompatible machine %S (expected %S)", - import_header.func_name, - coff_string_from_machine_type(import_header.machine), - coff_string_from_machine_type(config->machine)); - continue; - } - - // skip duplicate import inputs - if (hash_table_search_string_raw(import_stub_ht, import_header.func_name, 0)) { continue; } - hash_table_push_string_raw(ht_arena, import_stub_ht, import_header.func_name, 0); - - // create import stubs (later replaced with acutal imports generated by linker) - LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, str8_lit(LNK_IMPORT_STUB)); - LNK_Symbol *thunk_symbol = lnk_make_defined_symbol(symtab->arena->v[0], import_header.func_name, import_stub->defined.obj, import_stub->defined.symbol_idx); - LNK_Symbol *imp_symbol = lnk_make_defined_symbol(symtab->arena->v[0], push_str8f(scratch.arena, "__imp_%S", import_header.func_name), import_stub->defined.obj, import_stub->defined.symbol_idx); - lnk_symbol_table_push(symtab, thunk_symbol); - lnk_symbol_table_push(symtab, imp_symbol); - - // pick imports hash table - HashTable *imports_ht = lnk_is_dll_delay_load(config, import_header.dll_name) ? delayed_imports : static_imports; - - // search DLL symbol list - String8List *import_symbols = hash_table_search_path_raw(imports_ht, import_header.dll_name); - if (import_symbols == 0) { - import_symbols = push_array(scratch.arena, String8List, 1); - hash_table_push_path_raw(scratch.arena, imports_ht, import_header.dll_name, import_symbols); - } - - // push symbol - str8_list_push(scratch.arena, import_symbols, input->data.coff_import); - } - - search_flags = SearchFlag_All; - - // reset input - MemoryZeroStruct(&input_import_list); - - ProfEnd(); - } break; - case State_InputInclude: { - ProfBegin("Input Include"); - - // push a relocation which references an undefined include symbol - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".radinc$"), 0, str8_zero()); - for (; *last_include_symbol; last_include_symbol = &(*last_include_symbol)->next) { - COFF_ObjSymbol *include_symbol = coff_obj_writer_push_symbol_undef(obj_writer, (*last_include_symbol)->string); - coff_obj_writer_section_push_reloc(obj_writer, sect, 0, include_symbol, 0); - } - - // input obj with includes - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->exclude_from_debug_info = 1; - input->path = str8_lit("* INCLUDE SYMBOLS *"); - input->dedup_id = push_str8f(scratch.arena, "%S %llu", input->path, obj_list.count); - input->data = coff_obj_writer_serialize(tp_arena->v[0], obj_writer); - - coff_obj_writer_release(&obj_writer); - - ProfEnd(); - } break; - case State_InputObjs: { - ProfBegin("Input Objs [Count %llu]", input_obj_list.count); - - ProfBegin("Collect Obj Paths"); - LNK_InputObjList unique_obj_input_list = {0}; - for (LNK_InputObj *input = input_obj_list.first, *next; input != 0; input = next) { - next = input->next; - - B32 was_obj_loaded = hash_table_search_path_u64(loaded_obj_ht, input->dedup_id, 0); - if (was_obj_loaded) { continue; } - - if (input->is_thin) { - String8 full_path = input->dedup_id.size ? os_full_path_from_path(scratch.arena, input->dedup_id) : str8_zero(); - B32 was_full_path_used = hash_table_search_path_u64(loaded_obj_ht, full_path, 0); - if (was_full_path_used) { continue; } - if (!str8_match(input->dedup_id, full_path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive)) { - hash_table_push_path_u64(scratch.arena, loaded_obj_ht, full_path, 0); - } - } - - hash_table_push_path_u64(scratch.arena, loaded_obj_ht, input->dedup_id, 0); - - lnk_input_obj_list_push_node(&unique_obj_input_list, input); - - lnk_log(LNK_Log_InputObj, "Input Obj: %S", input->path); - } - ProfEnd(); - - ProfBegin("Load Objs From Disk"); - U64 thin_inputs_count = 0; - LNK_InputObj **thin_inputs = lnk_thin_array_from_input_obj_list(scratch.arena, unique_obj_input_list, &thin_inputs_count); - String8Array thin_input_paths = lnk_path_array_from_input_obj_array(scratch.arena, thin_inputs, thin_inputs_count); - String8Array thin_input_datas = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], config->io_flags, thin_input_paths); - for EachIndex(thin_input_idx, thin_inputs_count) { - thin_inputs[thin_input_idx]->has_disk_read_failed = thin_input_datas.v[thin_input_idx].size == 0; - thin_inputs[thin_input_idx]->data = thin_input_datas.v[thin_input_idx]; - } - ProfEnd(); - - ProfBegin("Disk Read Check"); - LNK_InputObj **input_obj_arr = lnk_array_from_input_obj_list(scratch.arena, unique_obj_input_list); - for EachIndex(input_idx, unique_obj_input_list.count) { - if (input_obj_arr[input_idx]->has_disk_read_failed) { - lnk_error(LNK_Error_InvalidPath, "unable to find obj \"%S\"", input_obj_arr[input_idx]->path); - } - } - ProfEnd(); - - if (lnk_get_log_status(LNK_Log_InputObj)) { - U64 input_size = 0; - for EachIndex(i, unique_obj_input_list.count) { input_size += input_obj_arr[i]->data.size; } - lnk_log(LNK_Log_InputObj, "[ Obj Input Size %M ]", input_size); - } - - LNK_ObjNodeArray obj_node_arr = lnk_obj_list_push_parallel(tp, tp_arena, &obj_list, config->machine, unique_obj_input_list.count, input_obj_arr); - - // if the machine was omitted on the command line, derive machine from obj - if (config->machine == COFF_MachineType_Unknown) { - for EachIndex(obj_idx, obj_node_arr.count) { - if (obj_node_arr.v[obj_idx].data.header.machine != COFF_MachineType_Unknown) { - config->machine = obj_node_arr.v[obj_idx].data.header.machine; - break; - } - } - } - - ProfBegin("Apply Directives"); - for EachIndex(obj_idx, obj_node_arr.count) { - LNK_Obj *obj = &obj_node_arr.v[obj_idx].data; - String8List raw_directives = lnk_raw_directives_from_obj(scratch.arena, obj); - LNK_DirectiveInfo directive_info = lnk_directive_info_from_raw_directives(scratch.arena, obj, raw_directives); - for EachIndex(i, ArrayCount(directive_info.v)) { - for (LNK_Directive *dir = directive_info.v[i].first; dir != 0; dir = dir->next) { - lnk_apply_cmd_option_to_config(tp_arena->v[0], config, dir->id, dir->value_list, obj); - } - } - } - ProfEnd(); - - // input extern symbols from each obj to the symbol table - lnk_input_obj_symbols(tp, tp_arena, symtab, obj_node_arr); - - // schedule symbol input - search_flags = SearchFlag_All; - - // reset input objs - MemoryZeroStruct(&input_obj_list); - - // replace undefined symbols that have an alternate name with a weak symbol - for (LNK_AltNameNode *alt_name_n = config->alt_name_list.first; alt_name_n != 0; alt_name_n = alt_name_n->next) { - LNK_SymbolHashTrie *symbol_ht = lnk_symbol_table_search_(symtab, alt_name_n->data.from); - if (symbol_ht) { - COFF_SymbolValueInterpType interp = lnk_interp_from_symbol(symbol_ht->symbol); - if (interp == COFF_SymbolValueInterp_Undefined) { - // clear out slot so weak symbol can replace undefined symbol - symbol_ht->symbol = 0; - - // make obj with alternamte name symbol - String8 alt_name_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); - COFF_ObjSymbol *from_symbol = coff_obj_writer_push_symbol_weak(obj_writer, alt_name_n->data.from, COFF_WeakExt_SearchLibrary, 0); - COFF_ObjSymbol *to_symbol = coff_obj_writer_push_symbol_weak(obj_writer, alt_name_n->data.to, COFF_WeakExt_AntiDependency, from_symbol); - coff_obj_writer_set_default_symbol(from_symbol, to_symbol); - alt_name_obj = coff_obj_writer_serialize(tp_arena->v[0], obj_writer); - coff_obj_writer_release(&obj_writer); - } - - // input alt name obj - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = alt_name_n->data.obj ? alt_name_n->data.obj->path : str8_lit("RADLINK"); - input->exclude_from_debug_info = 1; - input->dedup_id = push_str8f(scratch.arena, "* ALTERNATE NAME FOR %S=%S %u *", alt_name_n->data.from, alt_name_n->data.to, obj_list.count); - input->data = alt_name_obj; - input->lib = alt_name_n->data.obj ? alt_name_n->data.obj->lib : 0; - } - } - } - } - - ProfEnd(); - } break; - case State_InputLibs: { - ProfBegin("Input Libs"); - - // input libs from command line only - U64 input_source_opl = config->no_default_libs ? LNK_InputSource_Default: LNK_InputSource_Count; - for EachIndex(input_source, input_source_opl) { - ProfBeginV("Input Source %S", lnk_string_from_input_source(input_source)); - - Temp temp = temp_begin(scratch.arena); - LNK_InputLibList unique_input_lib_list = {0}; - - ProfBegin("Collect unique input libs"); - for (; *input_libs[input_source] != 0; input_libs[input_source] = &(*input_libs[input_source])->next) { - String8 path = (*input_libs[input_source])->string; - - if (input_source == LNK_InputSource_Default || input_source == LNK_InputSource_Obj) { - if (!str8_ends_with(path, str8_lit(".lib"), StringMatchFlag_CaseInsensitive)) { - path = push_str8f(temp.arena, "%S.lib", path); - } - if (lnk_is_lib_disallowed(disallow_lib_ht, path)) { - continue; - } - } - - if (lnk_is_lib_loaded(loaded_lib_ht, path)) { - continue; - } - - // search disk for library - String8List match_list = lnk_file_search(temp.arena, config->lib_dir_list, path); - - // warn about missing lib - if (match_list.node_count == 0) { - KeyValuePair *was_reported = hash_table_search_path(missing_lib_ht, path); - if (was_reported == 0) { - hash_table_push_path_u64(ht_arena, missing_lib_ht, path, 0); - lnk_error(LNK_Warning_FileNotFound, "unable to find library `%S`", path); - } - continue; - } - - // pick first match - String8 full_path = str8_list_first(&match_list); - - if (lnk_is_lib_loaded(loaded_lib_ht, full_path)) { - continue; - } - - // warn about multiple matches - if (match_list.node_count > 1) { - lnk_error(LNK_Warning_MultipleLibMatch, "multiple libs match `%S` (picking first match)", path); - lnk_supplement_error_list(match_list); - } - - // push library for loading - str8_list_push(temp.arena, &unique_input_lib_list, full_path); - - // save paths for future checks - lnk_push_loaded_lib(ht_arena, loaded_lib_ht, path); - lnk_push_loaded_lib(ht_arena, loaded_lib_ht, full_path); - - lnk_log(LNK_Log_InputLib, "Input Lib: %S", full_path); - } - ProfEnd(); - - ProfBegin("Disk Read Libs"); - String8Array paths = str8_array_from_list(temp.arena, &unique_input_lib_list); - String8Array datas = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], config->io_flags, paths); - ProfEnd(); - - ProfBegin("Lib Init"); - LNK_LibNodeArray libs = lnk_lib_list_push_parallel(tp, tp_arena, &lib_index[input_source], datas, paths); - ProfEnd(); - - if (lnk_get_log_status(LNK_Log_InputLib)) { - if (libs.count > 0) { - U64 input_size = 0; - for EachIndex(i, libs.count) { input_size += libs.v[i]->data.data.size; } - lnk_log(LNK_Log_InputObj, "[ Lib Input Size %M ]", input_size); - } - } - - temp_end(temp); - ProfEnd(); - } - - // schedule symbol input - search_flags = SearchFlag_All; - - ProfEnd(); - } break; - case State_InputDelayLoadDlls: { - ProfBegin("Input Delay Load Dlls"); - - // skip input delay load dlls - for (; *last_delay_load_dll; last_delay_load_dll = &(*last_delay_load_dll)->next); - - // establish delay load helper name - config->delay_load_helper_name = mscrt_delay_load_helper_name_from_machine(config->machine); - - // TODO: config_refactor - String8List value_strings = {0}; - str8_list_push(scratch.arena, &value_strings, config->delay_load_helper_name); - lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0); - - ProfEnd(); - } break; - case State_SearchUndefinedAndWeak: { - ProfBegin("Search Undefined And Weak"); - LNK_InputImportList new_imports = {0}; - LNK_InputObjList new_objs = {0}; - for EachIndex(i, ArrayCount(lib_index)) { - for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) { - for EachIndex(worker_id, symtab->arena->count) { - for (LNK_SymbolHashTrieChunk *c = symtab->chunks[worker_id].first; c != 0; c = c->next) { - for EachIndex(i, c->count) { - LNK_Symbol *symbol = c->v[i].symbol; - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); - if (symbol_interp == COFF_SymbolValueInterp_Undefined) { - U32 member_idx; - if (lnk_search_lib(&lib_n->data, symbol->name, &member_idx)) { - lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &new_imports, &new_objs); - } - } else if (symbol_interp == COFF_SymbolValueInterp_Weak) { - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol->defined.obj->header.is_big_obj); - if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { - U32 member_idx; - if (lnk_search_lib(&lib_n->data, symbol->name, &member_idx)) { - lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &new_imports, &new_objs); - } - } - } - } - } - } - if (new_imports.count || new_objs.count) { - goto end_search_undefined; - } - } - } - end_search_undefined:; - - { - LNK_InputImportNode **import_nodes = lnk_input_import_arr_from_list(scratch.arena, new_imports); - radsort(import_nodes, new_imports.count, lnk_input_import_is_before); - new_imports = lnk_list_from_input_import_arr(import_nodes, new_imports.count); - - LNK_InputObj **obj_nodes = lnk_array_from_input_obj_list(scratch.arena, new_objs); - radsort(obj_nodes, new_objs.count, lnk_input_obj_compar_is_before); - new_objs = lnk_list_from_input_obj_arr(obj_nodes, new_objs.count); - - lnk_input_import_list_concat_in_place(&input_import_list, &new_imports); - lnk_input_obj_list_concat_in_place(&input_obj_list, &new_objs); - } - - ProfEnd(); - } break; - case State_SearchWeakAntiDep: { - ProfBegin("Search Weak AntiDep"); - LNK_InputImportList new_imports = {0}; - LNK_InputObjList new_objs = {0}; - for EachIndex(i, ArrayCount(lib_index)) { - for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) { - for EachIndex(worker_id, symtab->arena->count) { - for (LNK_SymbolHashTrieChunk *c = symtab->chunks[worker_id].first; c != 0; c = c->next) { - for EachIndex(i, c->count) { - LNK_Symbol *symbol = c->v[i].symbol; - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); - if (symbol_interp == COFF_SymbolValueInterp_Weak) { - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol->defined.obj->header.is_big_obj); - if (weak_ext->characteristics == COFF_WeakExt_AntiDependency) { - LNK_SymbolDefined dep_symbol = lnk_resolve_weak_symbol(symtab, symbol->defined); - COFF_ParsedSymbol dep_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dep_symbol.obj, dep_symbol.symbol_idx); - COFF_SymbolValueInterpType dep_interp = coff_interp_from_parsed_symbol(dep_parsed); - if (dep_interp == COFF_SymbolValueInterp_Weak) { - U32 member_idx; - if (lnk_search_lib(&lib_n->data, symbol_parsed.name, &member_idx)) { - lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &new_imports, &new_objs); - } - } - } - } - } - } - } - if (new_imports.count || new_objs.count) { - goto end_search_antidep; - } - } - } - end_search_antidep:; - - { - LNK_InputImportNode **import_nodes = lnk_input_import_arr_from_list(scratch.arena, new_imports); - radsort(import_nodes, new_imports.count, lnk_input_import_is_before); - new_imports = lnk_list_from_input_import_arr(import_nodes, new_imports.count); - - LNK_InputObj **obj_nodes = lnk_array_from_input_obj_list(scratch.arena, new_objs); - radsort(obj_nodes, new_objs.count, lnk_input_obj_compar_is_before); - new_objs = lnk_list_from_input_obj_arr(obj_nodes, new_objs.count); - - lnk_input_import_list_concat_in_place(&input_import_list, &new_imports); - lnk_input_obj_list_concat_in_place(&input_obj_list, &new_objs); - } - - ProfEnd(); - } break; - case State_SearchEntryPoint: { - ProfBegin("Search Entry Point"); - - // loop over all possible subsystems and entry point names and pick - // subsystem that has a defined entry point symbol - if (config->entry_point_name.size == 0) { - PE_WindowsSubsystem subsys_first = config->subsystem; - PE_WindowsSubsystem subsys_last = config->subsystem == PE_WindowsSubsystem_UNKNOWN ? PE_WindowsSubsystem_COUNT : config->subsystem+1; - LNK_Symbol *entry_point_symbol = 0; - for (U64 subsys_idx = subsys_first; subsys_idx < subsys_last; subsys_idx += 1) { - String8Array entry_points = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); - for EachIndex(i, entry_points.count) { - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, entry_points.v[i]); - if (symbol) { - config->subsystem = subsys_idx; - config->entry_point_name = entry_points.v[i]; - goto found_entry_and_subsystem; - } - } - } - found_entry_and_subsystem:; - } - - // search for entry point in libs - if (config->entry_point_name.size == 0 && config->subsystem != PE_WindowsSubsystem_UNKNOWN) { - String8Array entry_points = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics); - for EachIndex(entry_idx, entry_points.count) { - for EachIndex(i, ArrayCount(lib_index)) { - for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) { - if (lnk_search_lib(&lib_n->data, entry_points.v[entry_idx], 0)) { - config->entry_point_name = entry_points.v[entry_idx]; - goto found_entry_in_libs; - } - } - } - } - found_entry_in_libs:; - } - - // infer subsystem from entry point name - if (config->entry_point_name.size != 0 && config->subsystem == PE_WindowsSubsystem_UNKNOWN) { - for EachIndex(subsys_idx, PE_WindowsSubsystem_COUNT) { - String8Array entry_points = pe_get_entry_point_names(config->machine, subsys_idx, config->file_characteristics); - for EachIndex(i, entry_points.count) { - if (str8_match(entry_points.v[i], config->entry_point_name, 0)) { - config->subsystem = subsys_idx; - goto subsystem_inferred_from_entry; - } - } - } - subsystem_inferred_from_entry:; - } - - // do we have an entry point name? - if (config->entry_point_name.size) { - // redirect user entry to appropriate CRT entry - String8 crt_entry_point_name = msvcrt_ctr_entry_from_user_entry(config->entry_point_name); - config->entry_point_name = crt_entry_point_name.size ? crt_entry_point_name : config->entry_point_name; - - // generate undefined symbol for entry point - // - // TODO: config_refactor - String8List value_strings = {0}; - str8_list_push(scratch.arena, &value_strings, config->entry_point_name); - lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0); - - // do we have a subsystem? - if (config->subsystem != PE_WindowsSubsystem_UNKNOWN) { - // if subsystem version not specified set default values - if (config->subsystem_ver.major == 0 && config->subsystem_ver.minor == 0) { - config->subsystem_ver = lnk_get_default_subsystem_version(config->subsystem, config->machine); - } - - // check subsystem version against allowed min version - Version min_subsystem_ver = lnk_get_min_subsystem_version(config->subsystem, config->machine); - if (version_compar(config->subsystem_ver, min_subsystem_ver) < 0) { - lnk_error(LNK_Error_Cmdl, "subsystem version %I64u.%I64u can't be lower than %I64u.%I64u", - config->subsystem_ver.major, config->subsystem_ver.minor, min_subsystem_ver.major, min_subsystem_ver.minor); - } - - // by default terminal server is enabled for windows and console applications - if (~config->flags & LNK_ConfigFlag_NoTsAware && ~config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) { - if (config->subsystem == PE_WindowsSubsystem_WINDOWS_GUI || config->subsystem == PE_WindowsSubsystem_WINDOWS_CUI) { - config->dll_characteristics |= PE_DllCharacteristic_TERMINAL_SERVER_AWARE; - } - } - } else { - lnk_error(LNK_Error_NoSubsystem, "unknown subsystem, please use /SUBSYSTEM to set subsytem type you need"); - } - } - - ProfEnd(); - } break; - case State_InputLinkerObjs: { - { - ProfBegin("Push Linker Symbols"); - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* Linker Symbols *"); - input->exclude_from_debug_info = 1; - input->dedup_id = input->path; - input->data = lnk_make_linker_obj(tp_arena->v[0], config); - ProfEnd(); - } - - // warn about unused delayloads - if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { - for (String8Node *dll_name_n = config->delay_load_dll_list.first; dll_name_n != 0; dll_name_n = dll_name_n->next) { - if (!hash_table_search_path_raw(delayed_imports, dll_name_n->string)) { - lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", dll_name_n->string); - } - } - } - - // make and input delayed imports - if (delayed_imports->count) { - ProfBegin("Build Delay Import Table"); - - COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; - B32 emit_biat = config->import_table_emit_biat == LNK_SwitchState_Yes; - B32 emit_uiat = config->import_table_emit_uiat == LNK_SwitchState_Yes; - String8 *dll_names = keys_from_hash_table_string(scratch.arena, delayed_imports); - String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, delayed_imports); - - for EachIndex(dll_idx, delayed_imports->count) { - String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = obj_list.count; - input->data = pe_make_import_dll_obj_delayed(tp_arena->v[0], time_stamp, config->machine, dll_names[dll_idx], config->delay_load_helper_name, import_debug_symbols, *dll_import_headers[dll_idx], emit_biat, emit_uiat); - input->path = dll_names[dll_idx]; - input->dedup_id = input->path; - } - String8 linker_debug_symbols = lnk_make_linker_debug_symbols(tp_arena->v[0], config->machine); - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = obj_list.count; - input->exclude_from_debug_info = 1; - input->data = pe_make_null_import_descriptor_delayed(tp_arena->v[0], time_stamp, config->machine, linker_debug_symbols); - input->path = str8_lit("* Delayed Null Import Descriptor *"); - input->dedup_id = input->path; - } - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = obj_list.count; - input->exclude_from_debug_info = 1; - input->data = pe_make_null_thunk_data_obj_delayed(tp_arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); - input->path = str8_lit("* Delayed Null Thunk Data *"); - input->dedup_id = input->path; - } - - ProfEnd(); - } - - // make and input static imports - if (static_imports->count) { - ProfBegin("Build Static Import Table"); - - COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; - String8 *dll_names = keys_from_hash_table_string(scratch.arena, static_imports); - String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, static_imports); - for EachIndex(dll_idx, static_imports->count) { - String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = obj_list.count; - input->data = pe_make_import_dll_obj_static(tp_arena->v[0], time_stamp, config->machine, dll_names[dll_idx], import_debug_symbols, *dll_import_headers[dll_idx]); - input->path = dll_names[dll_idx]; - input->dedup_id = dll_names[dll_idx]; - } - String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = obj_list.count; - input->exclude_from_debug_info = 1; - input->data = pe_make_null_import_descriptor_obj(tp_arena->v[0], time_stamp, config->machine, linker_debug_symbols); - input->path = str8_lit("* Null Import Descriptor *"); - input->dedup_id = input->path; - } - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = obj_list.count; - input->exclude_from_debug_info = 1; - input->data = pe_make_null_thunk_data_obj(tp_arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); - input->path = str8_lit("* Null Thunk Data *"); - input->dedup_id = input->path; - } - - ProfEnd(); - } - - if (config->export_symbol_list.count) { - ProfBegin("Build Export Table"); - - PE_ExportParseList resolved_exports = {0}; - for (PE_ExportParseNode *exp_n = config->export_symbol_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { - exp_n_next = exp_n->next; - PE_ExportParse *exp = &exp_n->data; - - if (str8_match(exp->name, config->entry_point_name, 0)) { - lnk_error_with_loc(LNK_Warning_TryingToExportEntryPoint, exp->obj_path, exp->lib_path, "exported entry point \"%S\"", exp->name); - } - if (str8_match(exp->alias, config->entry_point_name, 0)) { - lnk_error_with_loc(LNK_Warning_TryingToExportEntryPoint, exp->obj_path, exp->lib_path, "alias exports entry point \"%S=%S\"", exp->name, exp->alias); - continue; - } - - if (!exp->is_forwarder) { - // filter out unresolved exports - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, 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; - } - } - - // push resolved export - pe_export_parse_list_push_node(&resolved_exports, exp_n); - } - - PE_FinalizedExports finalized_exports = pe_finalize_export_list(scratch.arena, resolved_exports); - String8 edata_obj = pe_make_edata_obj(tp_arena->v[0], str8_skip_last_slash(config->image_name), COFF_TimeStamp_Max, config->machine, finalized_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(); - } - - { - String8List res_data_list = {0}; - String8List res_path_list = {0}; - - // do we have manifest deps passed through pragma alone? - LNK_ManifestOpt manifest_opt = config->manifest_opt; - if (config->manifest_dependency_list.node_count > 0 && manifest_opt == LNK_ManifestOpt_Null) { - manifest_opt = LNK_ManifestOpt_Embed; - } - - switch (manifest_opt) { - case LNK_ManifestOpt_Embed: { - ProfBegin("Embed Manifest"); - // TODO: currently we convert manifest to res and parse res again, this unnecessary instead push manifest - // resource to the tree directly - String8 manifest_data = lnk_manifest_from_inputs(scratch.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, config->input_list[LNK_Input_Manifest], config->manifest_dependency_list); - String8 manifest_res = pe_make_manifest_resource(scratch.arena, *config->manifest_resource_id, manifest_data); - str8_list_push(scratch.arena, &res_data_list, manifest_res); - str8_list_push(scratch.arena, &res_path_list, str8_lit("* Manifest *")); - ProfEnd(); - } break; - case LNK_ManifestOpt_WriteToFile: { - ProfBeginDynamic("Write Manifest To: %.*s", str8_varg(config->manifest_name)); - Temp temp = temp_begin(scratch.arena); - String8 manifest_data = lnk_manifest_from_inputs(temp.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, config->input_list[LNK_Input_Manifest], config->manifest_dependency_list); - lnk_write_data_to_file_path(config->manifest_name, str8_zero(), manifest_data); - temp_end(temp); - ProfEnd(); - } break; - case LNK_ManifestOpt_Null: { - Assert(config->input_list[LNK_Input_Manifest].node_count == 0); - Assert(config->manifest_dependency_list.node_count == 0); - } break; - case LNK_ManifestOpt_No: { - // omit manifest generation - } break; - } - - ProfBegin("Load .res files from disk"); - for (String8Node *node = config->input_list[LNK_Input_Res].first; node != 0; node = node->next) { - String8 res_data = lnk_read_data_from_file_path(scratch.arena, config->io_flags, node->string); - if (res_data.size > 0) { - if (pe_is_res(res_data)) { - str8_list_push(scratch.arena, &res_data_list, res_data); - String8 stable_res_path = lnk_make_full_path(scratch.arena, config->path_style, config->work_dir, node->string); - str8_list_push(scratch.arena, &res_path_list, stable_res_path); - } else { - lnk_error(LNK_Error_LoadRes, "file is not of RES format: %S", node->string); - } - } else { - lnk_error(LNK_Error_LoadRes, "unable to open res file: %S", node->string); - } - } - ProfEnd(); - - if (res_data_list.node_count > 0) { - ProfBegin("Build * Resources *"); - - String8 obj_name = str8_lit("* Resources *"); - String8 obj_data = lnk_make_res_obj(tp_arena->v[0], - res_data_list, - res_path_list, - config->machine, - config->time_stamp, - config->work_dir, - config->path_style, - obj_name); - - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->dedup_id = obj_name; - input->path = obj_name; - input->data = obj_data; - - ProfEnd(); - } - } - - if (lnk_do_debug_info(config)) { - { - ProfBegin("Build * Linker * Obj"); - - String8 obj_name = str8_lit("* Linker *"); - String8 raw_cmd_line = str8_list_join(scratch.arena, &config->raw_cmd_line, &(StringJoin){ str8_lit_comp(""), str8_lit_comp(" "), str8_lit_comp("") }); - String8 obj_data = lnk_make_linker_coff_obj(tp_arena->v[0], config->time_stamp, config->machine, config->work_dir, config->image_name, config->pdb_name, raw_cmd_line, obj_name); - - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->dedup_id = obj_name; - input->path = obj_name; - input->data = obj_data; - - ProfEnd(); - } - - { - ProfBegin("Build * Debug Directories *"); - if (config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null) { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->exclude_from_debug_info = 1; - input->path = str8_lit("* Debug Directory PDB *"); - input->dedup_id = input->path; - input->data = pe_make_debug_directory_pdb_obj(tp_arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->pdb_alt_path); - } - if (config->rad_debug == LNK_SwitchState_Yes) { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->exclude_from_debug_info = 1; - input->path = str8_lit("* Debug Directory RDI *"); - input->dedup_id = input->path; - input->data = pe_make_debug_directory_rdi_obj(tp_arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->rad_debug_alt_path); - } - ProfEnd(); - } - } - } break; - } - } - - if (*last_disallow_lib) { - state_list_push(scratch.arena, state_list, State_InputDisallowLibs); - continue; - } - if (input_import_list.count) { - state_list_push(scratch.arena, state_list, State_InputImports); - continue; - } - if (*last_include_symbol) { - state_list_push(scratch.arena, state_list, State_InputInclude); - continue; - } - { - B32 have_pending_lib_inputs = 0; - for EachIndex(i, ArrayCount(input_libs)) { - if (*input_libs[i] != 0) { - have_pending_lib_inputs = 1; - break; - } - } - if (have_pending_lib_inputs) { - state_list_push(scratch.arena, state_list, State_InputLibs); - continue; - } - } - if (input_obj_list.count) { - state_list_push(scratch.arena, state_list, State_InputObjs); - continue; - } - if (*last_delay_load_dll) { - state_list_push(scratch.arena, state_list, State_InputDelayLoadDlls); - continue; - } - if (search_flags & SearchFlag_UndefinedAndWeak) { - search_flags &= ~SearchFlag_UndefinedAndWeak; - state_list_push(scratch.arena, state_list, State_SearchUndefinedAndWeak); - continue; - } - if (search_flags & SearchFlag_WeakAntiDep) { - search_flags &= ~SearchFlag_WeakAntiDep; - state_list_push(scratch.arena, state_list, State_SearchWeakAntiDep); - continue; - } - if (entry_point_search_attempts == 0) { - state_list_push(scratch.arena, state_list, State_SearchEntryPoint); - entry_point_search_attempts += 1; - continue; - } - if (input_linker_objs) { - input_linker_objs = 0; - state_list_push(scratch.arena, state_list, State_InputLinkerObjs); - continue; - } - - break; - } - - { - U64 chunks_count = 0; - LNK_SymbolHashTrieChunk **chunks = lnk_array_from_symbol_hash_trie_chunk_list(scratch.arena, symtab->chunks, symtab->arena->count, &chunks_count); - - ProfBegin("Replace Unresolved Weak Symbols With Defualt Symbol"); - { - LNK_ReplaceWeakSymbolsWithDefaultSymbolTask task = { .symtab = symtab, .chunks = chunks }; - tp_for_parallel(tp, 0, chunks_count, lnk_replace_weak_with_default_symbol_task, &task); -#if BUILD_DEBUG - for EachIndex(chunk_idx, chunks_count) { - LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; - for EachIndex(i, chunk->count) { - LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol); - AssertAlways(symbol_interp != COFF_SymbolValueInterp_Weak); - } - } -#endif - } - ProfEnd(); - - - if (config->entry_point_name.size == 0) { - lnk_error(LNK_Error_EntryPoint, "unable to find entry point symbol"); - } - - ProfBegin("Report Unresolved Symbols"); - { - U64 count = 0; - for EachIndex(chunk_idx, chunks_count) { - LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; - for EachIndex(i, chunk->count) { - LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol); - if (symbol_interp == COFF_SymbolValueInterp_Undefined) { - count += 1; - } - } - } - - U64 cursor = 0; - LNK_Symbol **unresolved = push_array(scratch.arena, LNK_Symbol *, count); - for EachIndex(chunk_idx, chunks_count) { - LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; - for EachIndex(i, chunk->count) { - LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol); - if (symbol_interp == COFF_SymbolValueInterp_Undefined) { - unresolved[cursor++] = chunk->v[i].symbol; - } - } - } - - radsort(unresolved, count, lnk_symbol_defined_ptr_is_before); - - for EachIndex(i, count) { - LNK_Symbol *symbol = unresolved[i]; - lnk_error_obj(LNK_Error_UnresolvedSymbol, symbol->defined.obj, "unresolved symbol %S", symbol->name); - } - - // TODO: /FORCE - if (count) { - lnk_exit(LNK_Error_UnresolvedSymbol); - } - } - ProfEnd(); - } - - // - // discard COMDAT sections that are not referenced - // - if (config->opt_ref == LNK_SwitchState_Yes) { - lnk_opt_ref(tp, symtab, config, obj_list); - } - - // - // infer minimal padding size for functions from the target machine - // - if (config->machine != COFF_MachineType_Unknown && config->infer_function_pad_min) { - config->function_pad_min = lnk_get_default_function_pad_min(config->machine); - config->infer_function_pad_min = 0; - } - - // - // log - // - if (lnk_get_log_status(LNK_Log_InputObj)) { - U64 total_input_size = 0; - for (LNK_ObjNode *obj_n = obj_list.first; obj_n != 0; obj_n = obj_n->next) { total_input_size += obj_n->data.data.size; } - lnk_log(LNK_Log_InputObj, "[Total Obj Input Size %M]", total_input_size); - } - if (lnk_get_log_status(LNK_Log_InputLib)) { - U64 total_input_size = 0; - for EachIndex(i, ArrayCount(lib_index)) { - LNK_LibList list = lib_index[i]; - for (LNK_LibNode *lib_n = list.first; lib_n != 0; lib_n = lib_n->next) { total_input_size += lib_n->data.data.size; } - } - lnk_log(LNK_Log_InputLib, "[Total Lib Input Size %M]", total_input_size); - } - - // - // fill out link context - // - LNK_LinkContext link_ctx = {0}; - link_ctx.symtab = symtab; - link_ctx.objs_count = obj_list.count; - link_ctx.objs = lnk_array_from_obj_list(tp_arena->v[0], obj_list); - MemoryCopyTyped(&link_ctx.lib_index[0], &lib_index[0], ArrayCount(lib_index)); - - ProfEnd(); - scratch_end(scratch); - return link_ctx; - -#undef state_list_push -#undef state_list_pop -} - internal B32 lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_SymbolDefined symbol, LNK_SymbolDefined *symbol_out) { @@ -2769,6 +2749,10 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) String8 symbol_table = str8_substr(obj->data, obj_header.symbol_table_range); String8 string_table = str8_substr(obj->data, obj_header.string_table_range); + U32 closest_sect = 0; + U32 closest_reloc = 0; + U32 closest_foff = max_U32; + for EachIndex(sect_idx, obj_header.section_count_no_null) { COFF_SectionHeader *section_header = §ion_table[sect_idx]; @@ -2917,15 +2901,17 @@ THREAD_POOL_TASK_FUNC(lnk_flag_hotpatch_contribs_task) U64 obj_idx = task_id; LNK_Obj *obj = task->objs[obj_idx]; - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) { - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); - LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; - if (sc != task->null_sc) { - sc->hotpatch = !!(section_header->flags & COFF_SectionFlag_CntCode); + if (obj->hotpatch) { + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) { + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; + if (sc != task->null_sc) { + sc->hotpatch = !!(section_header->flags & COFF_SectionFlag_CntCode); + } } } } @@ -3859,6 +3845,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT lnk_section_table_push(sectab, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS); lnk_section_table_push(sectab, str8_lit(".data" ), PE_DATA_SECTION_FLAGS ); lnk_section_table_push(sectab, str8_lit(".bss" ), PE_BSS_SECTION_FLAGS ); + lnk_section_table_push(sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS); LNK_Section *common_block_sect = lnk_section_table_search(sectab, str8_lit(".bss"), PE_BSS_SECTION_FLAGS); LNK_BuildImageTask task = { @@ -4577,7 +4564,7 @@ lnk_pair_u32_nearest_section(PairU32 *arr, U64 count, LNK_Obj **objs, U32 voff) } internal String8List -lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs_count, LNK_Obj **objs, LNK_LibList lib_index[LNK_InputSource_Count], LNK_SectionTable *sectab) +lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs_count, LNK_Obj **objs, U64 libs_count, LNK_Lib **libs, LNK_SectionTable *sectab) { ProfBeginFunction(); Temp scratch = scratch_begin(&arena, 1); @@ -4687,12 +4674,10 @@ lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs str8_list_pushf(arena, &map, "\n"); ProfBegin("LIBS"); - for (U64 input_source = 0; input_source < LNK_InputSource_Count; ++input_source) { - if (lib_index[input_source].count) { - str8_list_pushf(arena, &map, "# LIBS (%S)\n", lnk_string_from_input_source(input_source)); - for (LNK_LibNode *lib_n = lib_index[input_source].first; lib_n != 0; lib_n = lib_n->next) { - str8_list_pushf(arena, &map, "%S\n", lib_n->data.path); - } + if (libs_count) { + str8_list_pushf(arena, &map, "# LIBS\n"); + for EachIndex(i, libs_count) { + str8_list_pushf(arena, &map, "%S\n", libs[i]->path); } } ProfEnd(); @@ -4752,14 +4737,29 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) Temp scratch = scratch_begin(arena->v, arena->count); // - // Link Inputs + // Input Context // - LNK_LinkContext link_ctx = lnk_build_link_context(tp, arena, config); + LNK_Inputer *inputer = lnk_inputer_init(); // - // Image + // Symbol Table // - LNK_ImageContext image_ctx = lnk_build_image(arena, tp, config, link_ctx.symtab, link_ctx.objs_count, link_ctx.objs); + LNK_SymbolTable *symtab = lnk_symbol_table_init(arena); + + // + // Link Image + // + LNK_Link *link = lnk_link_image(tp, arena, config, inputer, symtab); + + U64 objs_count = link->objs.count; + U64 libs_count = link->libs.count; + LNK_Obj **objs = lnk_array_from_obj_list(scratch.arena, link->objs); + LNK_Lib **libs = lnk_array_from_lib_list(scratch.arena, link->libs); + + // + // Layout Image + // + LNK_ImageContext image_ctx = lnk_build_image(arena, tp, config, symtab, objs_count, objs); // Write image in the background LNK_WriteThreadContext *image_write_ctx = push_array(scratch.arena, LNK_WriteThreadContext, 1); @@ -4772,7 +4772,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) // RAD Map // if (config->rad_chunk_map == LNK_SwitchState_Yes) { - String8List rad_map = lnk_build_rad_map(scratch.arena, image_ctx.image_data, config, link_ctx.objs_count, link_ctx.objs, link_ctx.lib_index, image_ctx.sectab); + String8List rad_map = lnk_build_rad_map(scratch.arena, image_ctx.image_data, config, objs_count, objs, libs_count, libs, image_ctx.sectab); lnk_write_data_list_to_file_path(config->rad_chunk_map_name, config->temp_rad_chunk_map_name, rad_map); } @@ -4796,18 +4796,18 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) ProfBegin("Debug Info"); lnk_timer_begin(LNK_Timer_Debug); - U64 objs_count = 0; - LNK_Obj **objs = push_array(scratch.arena, LNK_Obj *, link_ctx.objs_count); - for EachIndex(obj_idx, link_ctx.objs_count) { - LNK_Obj *obj = link_ctx.objs[obj_idx]; + U64 debug_info_objs_count = 0; + LNK_Obj **debug_info_objs = push_array(scratch.arena, LNK_Obj *, objs_count); + for EachIndex(obj_idx, objs_count) { + LNK_Obj *obj = objs[obj_idx]; if (obj->exclude_from_debug_info) { continue; } - objs[objs_count++] = obj; + debug_info_objs[debug_info_objs_count++] = obj; } // // CodeView // - LNK_CodeViewInput input = lnk_make_code_view_input(tp, arena, config->io_flags, config->lib_dir_list, objs_count, objs); + LNK_CodeViewInput input = lnk_make_code_view_input(tp, arena, config->io_flags, config->lib_dir_list, debug_info_objs_count, debug_info_objs); CV_DebugT *types = lnk_import_types(tp, arena, &input); // @@ -4851,7 +4851,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) arena, image_ctx.image_data, config, - link_ctx.symtab, + symtab, input.count, input.obj_arr, input.debug_s_arr, diff --git a/src/linker/lnk.h b/src/linker/lnk.h index e15252a0..f26c5f24 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -3,21 +3,99 @@ #pragma once -// --- Link -------------------------------------------------------------------- +// --- Input ------------------------------------------------------------------- + +typedef struct LNK_LibMemberRef +{ + LNK_Lib *lib; + U32 member_idx; + + struct LNK_LibMemberRef *next; +} LNK_LibMemberRef; + +typedef struct LNK_LibMemberRefList +{ + U64 count; + LNK_LibMemberRef *first; + LNK_LibMemberRef *last; +} LNK_LibMemberRefList; + +typedef enum +{ + LNK_InputSource_CmdLine, // specified on command line + LNK_InputSource_Default, // specified through defaultlib switch + LNK_InputSource_Obj, // refrenced from objects + LNK_InputSource_Count +} LNK_InputSourceType; + +typedef struct LNK_Input +{ + String8 path; + String8 data; + B32 disallow; + B32 is_thin; + B32 has_disk_read_failed; + B32 exclude_from_debug_info; + LNK_LibMemberRef *trigger; + void *loaded_input; + + struct LNK_Input *next; +} LNK_Input; + +typedef struct LNK_InputList +{ + U64 count; + LNK_Input *first; + LNK_Input *last; +} LNK_InputList; + +typedef struct LNK_InputPtrArray +{ + U64 count; + LNK_Input **v; +} LNK_InputPtrArray; + +typedef struct LNK_Inputer +{ + Arena *arena; + + LNK_InputList objs; + HashTable *objs_ht; + LNK_InputList new_objs; + + HashTable *libs_ht; + HashTable *missing_lib_ht; + LNK_InputList libs; + LNK_InputList new_libs[LNK_InputSource_Count]; +} LNK_Inputer; + +// --- Image Link ------------------------------------------------------------- #define LNK_IMPORT_STUB "*** RAD_IMPORT_STUB ***" #define LNK_NULL_SYMBOL "*** RAD_NULL_SYMBOL ***" + #define LNK_SECTION_FLAG_IS_LIVE (1 << 0) -typedef struct LNK_LinkContext +typedef struct LNK_ImportTables { - LNK_SymbolTable *symtab; - U64 objs_count; - LNK_Obj **objs; - LNK_LibList lib_index[LNK_InputSource_Count]; -} LNK_LinkContext; + Arena *arena; + HashTable *static_imports; + HashTable *delayed_imports; + HashTable *import_stub_ht; +} LNK_ImportTables; -// -- Image -------------------------------------------------------------------- +typedef struct LNK_Link +{ + LNK_ObjList objs; + LNK_LibList libs; + LNK_IncludeSymbolNode **last_include; + String8Node **last_cmd_lib; + String8Node **last_default_lib; + String8Node **last_obj_lib; + B32 try_to_resolve_entry_point; +} LNK_Link; + +// -- Image Layout ------------------------------------------------------------ #define LNK_REMOVED_SECTION_NUMBER_32 (U32)-3 #define LNK_REMOVED_SECTION_NUMBER_16 (U16)-3 @@ -135,24 +213,6 @@ typedef struct B32 is_large_addr_aware; } LNK_ObjBaseRelocTask; -typedef struct -{ - LNK_InputObjList input_obj_list; - U64 input_imports_count; - LNK_InputImport *input_imports; - LNK_InputImportList input_import_list; - LNK_SymbolList unresolved_symbol_list; -} LNK_SymbolFinderResult; - -typedef struct -{ - PathStyle path_style; - LNK_SymbolTable *symtab; - LNK_SymbolNodeArray lookup_node_arr; - LNK_SymbolFinderResult *result_arr; - Rng1U64 *range_arr; -} LNK_SymbolFinder; - typedef struct { String8 image_data; @@ -203,17 +263,43 @@ internal String8 lnk_make_null_obj(Arena *arena); internal String8 lnk_make_res_obj(Arena *arena, String8List res_file_list, String8List res_path_list, COFF_MachineType machine, U32 time_stamp, String8 work_dir, PathStyle system_path_style, String8 obj_name); internal String8 lnk_make_linker_obj(Arena *arena, LNK_Config *config); +// --- Inputer ----------------------------------------------------------------- + +internal void lnk_input_list_push_node(LNK_InputList *list, LNK_Input *node); +internal void lnk_input_list_concat_in_place(LNK_InputList *list, LNK_InputList *to_concat); +internal LNK_InputPtrArray lnk_array_from_input_list(Arena *arena, LNK_InputList list); + +internal LNK_Inputer * lnk_inputer_init(void); + +internal LNK_Input * lnk_input_push(Arena *arena, LNK_InputList *list, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_linkgen(Arena *arena, LNK_InputList *list, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_thin(Arena *arena, LNK_InputList *list, HashTable *ht, String8 full_path); + +internal LNK_Input * lnk_inputer_push_obj(LNK_Inputer *inputer, LNK_LibMemberRef *trigger, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_obj_linkgen(LNK_Inputer *inputer, LNK_LibMemberRef *trigger, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_obj_thin(LNK_Inputer *inputer, LNK_LibMemberRef *trigger, String8 path); + +internal LNK_Input * lnk_inputer_push_lib(LNK_Inputer *inputer, LNK_InputSourceType input_source, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_lib_linkgen(LNK_Inputer *inputer, LNK_InputSourceType input_source, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_lib_thin(LNK_Inputer *inputer, LNK_Config *config, LNK_InputSourceType input_source, String8 lib_path); + +internal B32 lnk_inputer_has_items(LNK_Inputer *inputer); +internal LNK_InputPtrArray lnk_inputer_flush(Arena *arena, TP_Context *tp, LNK_Inputer *inputer, LNK_IO_Flags io_flags, LNK_InputList *all_inputs, LNK_InputList *new_inputs); + // --- Link Context ------------------------------------------------------------ -internal String8 lnk_get_lib_name(String8 path); -internal B32 lnk_is_lib_disallowed(HashTable *disallow_lib_ht, String8 path); -internal B32 lnk_is_lib_loaded(HashTable *loaded_lib_ht, String8 lib_path); -internal void lnk_push_disallow_lib(Arena *arena, HashTable *disallow_lib_ht, String8 path); -internal void lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path); +internal void lnk_lib_member_ref_list_push_node(LNK_LibMemberRefList *list, LNK_LibMemberRef *node); +internal int lnk_lib_member_ref_is_before(void *raw_a, void *raw_b); +internal LNK_LibMemberRef ** lnk_array_from_lib_member_list(Arena *arena, LNK_LibMemberRefList list); -internal void lnk_queue_lib_member_for_input(Arena *arena, LNK_Config *config, LNK_Symbol *pull_in_ref, LNK_Lib *lib, U32 member_idx, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list); +internal LNK_ObjNode * lnk_load_objs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, U64 *objs_count_out); +internal void lnk_load_libs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_Link *link); +internal void lnk_link_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, LNK_ImportTables *imps); +internal LNK_Link * lnk_link_image (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab); -internal LNK_LinkContext lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config); +// --- Optimizations ----------------------------------------------------------- + +internal void lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_ObjList objs); // --- Win32 Image ------------------------------------------------------------- diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 6bedf521..aa5ab473 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -944,6 +944,57 @@ lnk_is_dll_delay_load(LNK_Config *config, String8 dll_name) return hash_table_search_path_u64(config->delay_load_ht, dll_name, 0); } +internal String8 +lnk_get_lib_name(String8 path) +{ + static String8 LIB_EXT = str8_lit_comp(".LIB"); + + // strip path + String8 name = str8_skip_last_slash(path); + + // strip extension + String8 name_ext = str8_postfix(name, LIB_EXT.size); + if (str8_match(name_ext, LIB_EXT, StringMatchFlag_CaseInsensitive)) { + name = str8_chop(name, LIB_EXT.size); + } + + return name; +} + +internal void +lnk_push_disallow_lib(LNK_Config *config, String8 path) +{ + String8 lib_name = lnk_get_lib_name(path); + hash_table_push_path_u64(config->arena, config->disallow_lib_ht, lib_name, 0); +} + +internal B32 +lnk_is_lib_disallowed(LNK_Config *config, String8 path) +{ + String8 lib_name = lnk_get_lib_name(path); + return hash_table_search_path(config->disallow_lib_ht, lib_name) != 0; +} + +internal void +lnk_include_symbol(LNK_Config *config, String8 name, LNK_Obj *obj) +{ + // is this a duplicate symbol? + if (hash_table_search_string_raw(config->include_symbol_ht, name, 0)) { + return; + } + + name = push_str8_copy(config->arena, name); + + LNK_IncludeSymbolNode *node = push_array(config->arena, LNK_IncludeSymbolNode, 1); + node->v.name = name; + node->v.obj = obj; + + SLLQueuePush(config->include_symbol_list.first, config->include_symbol_list.last, node); + config->include_symbol_list.count += 1; + + hash_table_push_string_raw(config->arena, config->include_symbol_ht, name, node); +} + internal void lnk_print_build_info() { @@ -1068,9 +1119,9 @@ lnk_unwrap_rsp(Arena *arena, String8List arg_list) } internal void -lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_name, String8List value_strings, LNK_Obj *obj) +lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List value_strings, LNK_Obj *obj) { - Temp scratch = scratch_begin(&arena,1); + Temp scratch = scratch_begin(&config->arena, 1); LNK_CmdSwitchType cmd_switch = lnk_cmd_switch_type_from_string(cmd_name); @@ -1111,12 +1162,12 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam lnk_error_obj(LNK_Error_AlternateNameConflict, obj, "conflicting alternative name: existing '%S=%S' vs. new '%S=%S'", alt_name.from, to_extant, alt_name.from, alt_name.to); } } else { - hash_table_push_string_string(arena, config->alt_name_ht, alt_name.from, alt_name.to); + hash_table_push_string_string(config->arena, config->alt_name_ht, alt_name.from, alt_name.to); - alt_name.from = push_str8_copy(arena, alt_name.from); - alt_name.to = push_str8_copy(arena, alt_name.to); + alt_name.from = push_str8_copy(config->arena, alt_name.from); + alt_name.to = push_str8_copy(config->arena, alt_name.to); - LNK_AltNameNode *alt_name_n = push_array(arena, LNK_AltNameNode, 1); + LNK_AltNameNode *alt_name_n = push_array(config->arena, LNK_AltNameNode, 1); alt_name_n->data = alt_name; SLLQueuePush(config->alt_name_list.first, config->alt_name_list.last, alt_name_n); @@ -1182,7 +1233,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_DefaultLib: { - String8List default_lib_list = str8_list_copy(arena, &value_strings); + String8List default_lib_list = str8_list_copy(config->arena, &value_strings); if (obj) { str8_list_concat_in_place(&config->input_obj_lib_list, &default_lib_list); } else { @@ -1208,9 +1259,9 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam case LNK_CmdSwitch_DelayLoad: { for (String8Node *name_n = value_strings.first; name_n != 0; name_n = name_n->next) { if (hash_table_search_path_u64(config->delay_load_ht, name_n->string, 0)) { continue; } - String8 name = push_str8_copy(arena, name_n->string); - hash_table_push_path_u64(arena, config->delay_load_ht, name, 0); - str8_list_push(arena, &config->delay_load_dll_list, name); + String8 name = push_str8_copy(config->arena, name_n->string); + hash_table_push_path_u64(config->arena, config->delay_load_ht, name, 0); + str8_list_push(config->arena, &config->delay_load_dll_list, name); } } break; @@ -1228,7 +1279,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam case LNK_CmdSwitch_Entry: { String8 new_entry_point_name = {0}; - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &new_entry_point_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &new_entry_point_name); if (config->entry_point_name.size) { lnk_error_cmd_switch(LNK_Warning_Cmdl, obj, cmd_switch, "unable to redefine entry point \"%S\" to \"%S\"", config->entry_point_name, new_entry_point_name); @@ -1240,7 +1291,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam case LNK_CmdSwitch_Export: { PE_ExportParse export_parse = {0}; - if (lnk_parse_export_directive_ex(arena, value_strings, obj, &export_parse)) { + if (lnk_parse_export_directive_ex(config->arena, value_strings, obj, &export_parse)) { PE_ExportParseNode *exp_n = 0; String8 export_name = pe_name_from_export_parse(&export_parse); hash_table_search_string_raw(config->export_ht, export_name, &exp_n); @@ -1248,13 +1299,13 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (exp_n == 0) { // make sure export is defined if (!export_parse.is_forwarder) { - str8_list_push(arena, &config->include_symbol_list, export_parse.name); + lnk_include_symbol(config, export_parse.name, 0); } // push new export - exp_n = pe_export_parse_list_push(arena, &config->export_symbol_list, export_parse); + exp_n = pe_export_parse_list_push(config->arena, &config->export_symbol_list, export_parse); - hash_table_push_string_raw(arena, config->export_ht, export_name, exp_n); + hash_table_push_string_raw(config->arena, config->export_ht, export_name, exp_n); } else { B32 is_ambiguous = 1; PE_ExportParse *extant_export = &exp_n->data; @@ -1352,19 +1403,12 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_ImpLib: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->imp_lib_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->imp_lib_name); } break; case LNK_CmdSwitch_Include: { for (String8Node *value_n = value_strings.first; value_n != 0; value_n = value_n->next) { - // is this a duplicate symbol? - if (hash_table_search_string_raw(config->include_symbol_ht, value_n->string, 0)) { - continue; - } - - String8 include_symbol = push_str8_copy(arena, value_n->string); - hash_table_push_string_raw(arena, config->include_symbol_ht, include_symbol, 0); - str8_list_push(arena, &config->include_symbol_list, include_symbol); + lnk_include_symbol(config, value_n->string, obj); } } break; @@ -1386,7 +1430,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_LibPath: { - String8List lib_dir_list = str8_list_copy(arena, &value_strings); + String8List lib_dir_list = str8_list_copy(config->arena, &value_strings); for (String8Node *dir_n = lib_dir_list.first; dir_n != 0; dir_n = dir_n->next) { if (!os_folder_path_exists(dir_n->string)) { String8 full_path = os_full_path_from_path(scratch.arena, dir_n->string); @@ -1426,7 +1470,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (res_id_arr.count == 2) { U64 resource_id; if (try_u64_from_str8_c_rules(res_id_arr.v[1], &resource_id)) { - config->manifest_resource_id = push_u64(arena, resource_id); + config->manifest_resource_id = push_u64(config->arena, resource_id); } else { lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse resource_id \"%S\"", res_id_arr.v[1]); } @@ -1455,7 +1499,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_ManifestDependency: { - String8List manifest_dependency_list = str8_list_copy(arena, &value_strings); + String8List manifest_dependency_list = str8_list_copy(config->arena, &value_strings); str8_list_concat_in_place(&config->manifest_dependency_list, &manifest_dependency_list); if (config->manifest_opt == LNK_ManifestOpt_Null) { @@ -1464,7 +1508,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_ManifestFile: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->manifest_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->manifest_name); } break; case LNK_CmdSwitch_ManifestInput: { @@ -1488,7 +1532,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam str8_match_lit("'requireAdministrator'", level, 0)) { // manifest level was parsed! config->manifest_uac = 1; - config->manifest_level = push_str8_copy(arena, level); + config->manifest_level = push_str8_copy(config->arena, level); if (param_arr.count > 1) { String8 ui_access_param = param_arr.v[1]; String8List ui_access_list = str8_split_by_string_chars(scratch.arena, ui_access_param, str8_lit("="), 0); @@ -1497,7 +1541,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (str8_match_lit("'true'", ui_access, 0) || str8_match_lit("'false'", ui_access, 0)) { // ui access was parsed! - config->manifest_ui_access = push_str8_copy(arena, ui_access); + config->manifest_ui_access = push_str8_copy(config->arena, ui_access); } else { lnk_error_invalid_uac_ui_access_param(LNK_Error_Cmdl, obj, cmd_switch, ui_access_param); } @@ -1531,9 +1575,9 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (value_strings.node_count == 1) { LNK_MergeDirective merge = {0}; if (lnk_parse_merge_directive(value_strings.first->string, obj, &merge)) { - merge.src = push_str8_copy(arena, merge.src); - merge.dst = push_str8_copy(arena, merge.dst); - lnk_merge_directive_list_push(arena, &config->merge_list, merge); + merge.src = push_str8_copy(config->arena, merge.src); + merge.dst = push_str8_copy(config->arena, merge.dst); + lnk_merge_directive_list_push(config->arena, &config->merge_list, merge); } } else { lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters %d", value_strings.node_count); @@ -1549,7 +1593,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } } - String8List natvis_list = str8_list_copy(arena, &value_strings); + String8List natvis_list = str8_list_copy(config->arena, &value_strings); str8_list_concat_in_place(&config->natvis_list, &natvis_list); } break; @@ -1558,8 +1602,13 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (value_strings.node_count == 0) { config->no_default_libs = 1; } else { - String8List no_default_lib_list = str8_list_copy(arena, &value_strings); - str8_list_concat_in_place(&config->disallow_lib_list, &no_default_lib_list); + for (String8Node *lib_n = value_strings.first; lib_n != 0; lib_n = lib_n->next) { + String8 lib_name = lnk_get_lib_name(lib_n->string); + if (hash_table_search_path_raw(config->disallow_lib_ht, lib_name)) { + continue; + } + hash_table_push_path_raw(config->arena, config->disallow_lib_ht, lib_name, 0); + } } } break; @@ -1614,16 +1663,16 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Out: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->image_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->image_name); } break; case LNK_CmdSwitch_Pdb: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->pdb_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->pdb_name); } break; case LNK_CmdSwitch_PdbAltPath: { // see :PdbAltPath - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->pdb_alt_path); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->pdb_alt_path); } break; case LNK_CmdSwitch_PdbPageSize: { @@ -1723,7 +1772,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Rad_Map: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->rad_chunk_map_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->rad_chunk_map_name); config->rad_chunk_map = LNK_SwitchState_Yes; } break; @@ -1737,11 +1786,11 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam case LNK_CmdSwitch_Rad_DebugName: { // :Rad_DebugAltPath - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->rad_debug_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->rad_debug_name); } break; case LNK_CmdSwitch_Rad_DebugAltPath: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->rad_debug_alt_path); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->rad_debug_alt_path); } break; case LNK_CmdSwitch_Rad_DelayBind: { @@ -1839,7 +1888,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Rad_MtPath: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->mt_path); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->mt_path); } break; case LNK_CmdSwitch_Rad_OsVer: { @@ -1880,7 +1929,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Rad_PdbHashTypeNameMap: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->pdb_hash_type_name_map); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->pdb_hash_type_name_map); } break; case LNK_CmdSwitch_Rad_PdbHashTypeNameLength: { @@ -1890,8 +1939,8 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam case LNK_CmdSwitch_Rad_RemoveSection: { String8 sect_name = {0}; if (lnk_cmd_switch_parse_string(obj, cmd_switch, value_strings, §_name)) { - sect_name = push_str8_copy(arena, sect_name); - str8_list_push(arena, &config->remove_sections, sect_name); + sect_name = push_str8_copy(config->arena, sect_name); + str8_list_push(config->arena, &config->remove_sections, sect_name); } } break; @@ -1991,12 +2040,14 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } internal LNK_Config * -lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd_line) +lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line) { ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); + Temp scratch = scratch_begin(0, 0); - LNK_Config *config = push_array(arena, LNK_Config, 1); + Arena *arena = arena_alloc(); + LNK_Config *config = push_array(arena, LNK_Config, 1); + config->arena = arena; config->raw_cmd_line = str8_list_copy(arena, &raw_cmd_line); config->work_dir = os_get_current_path(arena); config->build_imp_lib = 1; @@ -2012,10 +2063,11 @@ lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd config->alt_name_ht = hash_table_init(arena, 0x100); config->include_symbol_ht = hash_table_init(arena, 0x100); config->delay_load_ht = hash_table_init(arena, 0x100); + config->disallow_lib_ht = hash_table_init(arena, 0x100); // process command line switches for (LNK_CmdOption *cmd = cmd_line.first_option; cmd != 0; cmd = cmd->next) { - lnk_apply_cmd_option_to_config(arena, config, cmd->string, cmd->value_strings, 0); + lnk_apply_cmd_option_to_config(config, cmd->string, cmd->value_strings, 0); } // :manifest_input diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index bc0bce05..41b3e7f7 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -249,6 +249,25 @@ typedef enum LNK_ManifestOpt_No, } LNK_ManifestOpt; +typedef struct LNK_IncludeSymbol +{ + String8 name; + struct LNK_Obj *obj; +} LNK_IncludeSymbol; + +typedef struct LNK_IncludeSymbolNode +{ + struct LNK_IncludeSymbolNode *next; + LNK_IncludeSymbol v; +} LNK_IncludeSymbolNode; + +typedef struct LNK_IncludeSymbolList +{ + U64 count; + LNK_IncludeSymbolNode *first; + LNK_IncludeSymbolNode *last; +} LNK_IncludeSymbolList; + typedef struct LNK_AltName { String8 from; @@ -304,6 +323,7 @@ typedef enum typedef struct LNK_Config { + Arena *arena; LNK_ConfigFlags flags; LNK_DebugMode debug_mode; LNK_SwitchState opt_ref; @@ -362,7 +382,6 @@ typedef struct LNK_Config String8List input_list[LNK_Input_Count]; String8List input_obj_lib_list; String8List input_default_lib_list; - String8List disallow_lib_list; String8List delay_load_dll_list; String8List natvis_list; String8 manifest_name; @@ -375,7 +394,7 @@ typedef struct LNK_Config String8 rad_chunk_map_name; String8 rad_debug_name; String8 rad_debug_alt_path; - String8List include_symbol_list; + LNK_IncludeSymbolList include_symbol_list; LNK_AltNameList alt_name_list; LNK_MergeDirectiveList merge_list; U64 symbol_table_cap_defined; @@ -397,6 +416,7 @@ typedef struct LNK_Config HashTable *alt_name_ht; HashTable *include_symbol_ht; HashTable *delay_load_ht; + HashTable *disallow_lib_ht; } LNK_Config; // --- MSVC Error Codes -------------------------------------------------------- @@ -573,20 +593,26 @@ internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LN // --- Getters ----------------------------------------------------------------- -internal String8 lnk_get_image_name(LNK_Config *config); -internal U64 lnk_get_default_function_pad_min(COFF_MachineType machine); -internal U64 lnk_get_base_addr(LNK_Config *config); +internal String8 lnk_get_image_name (LNK_Config *config); +internal U64 lnk_get_default_function_pad_min (COFF_MachineType machine); +internal U64 lnk_get_base_addr (LNK_Config *config); internal Version lnk_get_default_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine); -internal Version lnk_get_min_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine); +internal Version lnk_get_min_subsystem_version (PE_WindowsSubsystem subsystem, COFF_MachineType machine); internal B32 lnk_do_debug_info (LNK_Config *config); internal B32 lnk_is_thread_pool_shared(LNK_Config *config); internal B32 lnk_is_section_removed (LNK_Config *config, String8 section_name); internal B32 lnk_is_dll_delay_load (LNK_Config *config, String8 dll_name); +internal String8 lnk_get_lib_name (String8 path); +internal void lnk_push_disallow_lib(LNK_Config *config, String8 path); +internal B32 lnk_is_lib_disallowed(LNK_Config *config, String8 path); + +internal void lnk_include_symbol(LNK_Config *config, String8 name, struct LNK_Obj *obj); + // --- Config ------------------------------------------------------------------ -internal void lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 name, String8List value_list, struct LNK_Obj *obj); +internal void lnk_apply_cmd_option_to_config(LNK_Config *config, String8 name, String8List value_list, struct LNK_Obj *obj); -internal LNK_Config * lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd_line); +internal LNK_Config * lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line); diff --git a/src/linker/lnk_input.c b/src/linker/lnk_input.c deleted file mode 100644 index 16f8deef..00000000 --- a/src/linker/lnk_input.c +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2025 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal void -lnk_error_input_obj(LNK_ErrorCode code, LNK_InputObj *input, char *fmt, ...) -{ - va_list args; va_start(args, fmt); - lnk_error_with_loc_fv(code, input->path, input->lib ? input->lib->path : str8_zero(), fmt, args); - va_end(args); -} - -internal String8 -lnk_string_from_input_source(LNK_InputSourceType input_source) -{ - String8 result = str8_zero(); - switch (input_source) { - case LNK_InputSource_CmdLine: result = str8_lit("CmdLine"); break; - case LNK_InputSource_Default: result = str8_lit("Default"); break; - case LNK_InputSource_Obj: result = str8_lit("Obj"); break; - default: InvalidPath; - } - return result; -} - -internal void -lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node) -{ - SLLQueuePush(list->first, list->last, node); - ++list->count; -} - -internal LNK_InputObj * -lnk_input_obj_list_push(Arena *arena, LNK_InputObjList *list) -{ - LNK_InputObj *node = push_array(arena, LNK_InputObj, 1); - lnk_input_obj_list_push_node(list, node); - return node; -} - -internal void -lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal LNK_InputObj ** -lnk_array_from_input_obj_list(Arena *arena, LNK_InputObjList list) -{ - LNK_InputObj **result = push_array_no_zero(arena, LNK_InputObj *, list.count); - U64 i = 0; - for (LNK_InputObj *n = list.first; n != 0; n = n->next, ++i) { - Assert(i < list.count); - result[i] = n; - } - return result; -} - -internal LNK_InputObj ** -lnk_thin_array_from_input_obj_list(Arena *arena, LNK_InputObjList list, U64 *count_out) -{ - for (LNK_InputObj *input = list.first; input != 0; input = input->next) { - if (input->is_thin) { *count_out += 1; } - } - LNK_InputObj **thin_inputs = push_array(arena, LNK_InputObj *, *count_out); - U64 input_idx = 0; - for (LNK_InputObj *input = list.first; input != 0; input = input->next) { - if (input->is_thin) { thin_inputs[input_idx++] = input; } - } - return thin_inputs; -} - -internal String8Array -lnk_path_array_from_input_obj_array(Arena *arena, LNK_InputObj **arr, U64 count) -{ - String8Array paths = {0}; - paths.count = count; - paths.v = push_array(arena, String8, count); - for (U64 i = 0; i < count; i += 1) { - paths.v[i] = arr[i]->path; - } - return paths; -} - -internal int -lnk_input_obj_compar(const void *raw_a, const void *raw_b) -{ - LNK_InputObj * const *a = raw_a, * const *b = raw_b; - return u64_compar(&(*a)->input_idx, &(*b)->input_idx); -} - -internal int -lnk_input_obj_compar_is_before(void *raw_a, void *raw_b) -{ - LNK_InputObj **a = raw_a, **b = raw_b; - return lnk_input_obj_compar(a, b) < 0; -} - -internal LNK_InputObjList -lnk_list_from_input_obj_arr(LNK_InputObj **arr, U64 count) -{ - LNK_InputObjList list = {0}; - for (U64 i = 0; i < count; ++i) { - SLLQueuePush(list.first, list.last, arr[i]); - ++list.count; - } - return list; -} - -internal LNK_InputObjList -lnk_input_obj_list_from_string_list(Arena *arena, String8List list) -{ - LNK_InputObjList input_list = {0}; - for (String8Node *path = list.first; path != 0; path = path->next) { - LNK_InputObj *input = lnk_input_obj_list_push(arena, &input_list); - input->is_thin = 1; - input->dedup_id = path->string; - input->path = path->string; - } - return input_list; -} - -internal LNK_InputImportNode * -lnk_input_import_list_push(Arena *arena, LNK_InputImportList *list) -{ - LNK_InputImportNode *node = push_array(arena, LNK_InputImportNode, 1); - SLLQueuePush(list->first, list->last, node); - list->count += 1; - return node; -} - -internal void -lnk_input_import_list_concat_in_place(LNK_InputImportList *list, LNK_InputImportList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal LNK_InputImportNode ** -lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list) -{ - LNK_InputImportNode **result = push_array_no_zero(arena, LNK_InputImportNode *, list.count); - U64 idx = 0; - for (LNK_InputImportNode *node = list.first; node != 0; node = node->next) { - Assert(idx < list.count); - result[idx++] = node; - } - return result; -} - -internal LNK_InputImportList -lnk_list_from_input_import_arr(LNK_InputImportNode **arr, U64 count) -{ - LNK_InputImportList list = {0}; - for (U64 i = 0; i < count; i += 1) { - SLLQueuePush(list.first, list.last, arr[i]); - list.count += 1; - } - return list; -} - -int -lnk_input_import_is_before(void *raw_a, void *raw_b) -{ - LNK_InputImport *a = *(LNK_InputImport **)raw_a; - LNK_InputImport *b = *(LNK_InputImport **)raw_b; - return a->input_idx < b->input_idx; -} - -int -lnk_input_import_node_compar(const void *raw_a, const void *raw_b) -{ - LNK_InputImportNode * const *a = raw_a; - LNK_InputImportNode * const *b = raw_b; - return u64_compar(&(*a)->data.input_idx, &(*b)->data.input_idx); -} - diff --git a/src/linker/lnk_input.h b/src/linker/lnk_input.h deleted file mode 100644 index a8869d37..00000000 --- a/src/linker/lnk_input.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2025 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -typedef enum -{ - LNK_InputSource_CmdLine, // specified on command line - LNK_InputSource_Default, // specified through defaultlib switch - LNK_InputSource_Obj, // refrenced from objects - LNK_InputSource_Count -} LNK_InputSourceType; - -typedef String8Node LNK_InputLib; -typedef String8List LNK_InputLibList; - -typedef struct LNK_InputImport -{ - String8 coff_import; - U64 input_idx; - struct LNK_Lib *lib; -} LNK_InputImport; - -typedef struct LNK_InputImportNode -{ - struct LNK_InputImportNode *next; - LNK_InputImport data; -} LNK_InputImportNode; - -typedef struct LNK_InputImportList -{ - U64 count; - LNK_InputImportNode *first; - LNK_InputImportNode *last; -} LNK_InputImportList; - -typedef struct LNK_InputObj -{ - String8 path; - String8 dedup_id; - String8 data; - B8 is_thin; - B8 exclude_from_debug_info; - B8 has_disk_read_failed; - struct LNK_Lib *lib; - U64 input_idx; - struct LNK_InputObj *next; -} LNK_InputObj; - -typedef struct LNK_InputObjList -{ - U64 count; - LNK_InputObj *first; - LNK_InputObj *last; -} LNK_InputObjList; - -//////////////////////////////// - -internal void lnk_error_input_obj(LNK_ErrorCode code, LNK_InputObj *input, char *fmt, ...); - -internal String8 lnk_string_from_input_source(LNK_InputSourceType input_source); - -internal void lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node); -internal LNK_InputObj * lnk_input_obj_list_push(Arena *arena, LNK_InputObjList *list); -internal void lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat); - -internal LNK_InputObj ** lnk_array_from_input_obj_list(Arena *arena, LNK_InputObjList list); -internal LNK_InputObj ** lnk_thin_array_from_input_obj_list(Arena *arena, LNK_InputObjList list, U64 *count_out); -internal String8Array lnk_path_array_from_input_obj_array(Arena *arena, LNK_InputObj **arr, U64 count); -internal LNK_InputObjList lnk_list_from_input_obj_arr(LNK_InputObj **arr, U64 count); -internal LNK_InputObjList lnk_input_obj_list_from_string_list(Arena *arena, String8List list); - -internal LNK_InputImportNode * lnk_input_import_list_push(Arena *arena, LNK_InputImportList *list); -internal void lnk_input_import_list_concat_in_place(LNK_InputImportList *list, LNK_InputImportList *to_concat); -internal LNK_InputImportNode ** lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list); -internal LNK_InputImportList lnk_list_from_input_import_arr(LNK_InputImportNode **arr, U64 count); - diff --git a/src/linker/lnk_lib.c b/src/linker/lnk_lib.c index d620f2cb..6002046b 100644 --- a/src/linker/lnk_lib.c +++ b/src/linker/lnk_lib.c @@ -10,8 +10,7 @@ lnk_lib_node_is_before(void *a, void *b) internal int lnk_lib_node_ptr_is_before(void *raw_a, void *raw_b) { - LNK_LibNode **a = raw_a, **b = raw_b; - return lnk_lib_node_is_before(*a, *b); + return raw_a < raw_b; } internal B32 @@ -117,7 +116,7 @@ lnk_lib_from_data(Arena *arena, String8 data, String8 path, LNK_Lib *lib_out) lib_out->symbol_count = symbol_count; lib_out->member_offsets = member_offsets; lib_out->symbol_indices = symbol_indices; - lib_out->was_member_queued = push_array(arena, B8, member_count); + lib_out->was_member_linked = push_array(arena, LNK_Symbol *, member_count); lib_out->symbol_names = symbol_names; lib_out->long_names = parse.long_names; @@ -128,13 +127,13 @@ lnk_lib_from_data(Arena *arena, String8 data, String8 path, LNK_Lib *lib_out) internal THREAD_POOL_TASK_FUNC(lnk_lib_initer) { - LNK_LibIniter *task = raw_task; + LNK_LibIniter *task = raw_task; + LNK_Input *input = task->inputs[task_id]; U64 lib_node_idx = ins_atomic_u64_inc_eval(&task->next_free_lib_idx)-1; LNK_LibNode *lib_node = &task->free_libs[lib_node_idx]; - lib_node->data.input_idx = task_id; - B32 is_valid_lib = lnk_lib_from_data(arena, task->data_arr[task_id], task->path_arr[task_id], &lib_node->data); + B32 is_valid_lib = lnk_lib_from_data(arena, input->data, input->path, &lib_node->data); if (is_valid_lib) { U64 valid_lib_idx = ins_atomic_u64_inc_eval(&task->valid_libs_count)-1; task->valid_libs[valid_lib_idx] = lib_node; @@ -144,6 +143,17 @@ THREAD_POOL_TASK_FUNC(lnk_lib_initer) } } +internal LNK_Lib ** +lnk_array_from_lib_list(Arena *arena, LNK_LibList list) +{ + LNK_Lib **arr = push_array_no_zero(arena, LNK_Lib *, list.count); + U64 idx = 0; + for (LNK_LibNode *node = list.first; node != 0; node = node->next, ++idx) { + arr[idx] = &node->data; + } + return arr; +} + internal void lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node) { @@ -152,32 +162,31 @@ lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node) } internal LNK_LibNodeArray -lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, String8Array data_arr, String8Array path_arr) +lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, U64 inputs_count, LNK_Input **inputs) { Temp scratch = scratch_begin(arena->v, arena->count); - Assert(data_arr.count == path_arr.count); - U64 lib_count = data_arr.count; + U64 lib_id_base = list->count; // parse libs in parallel LNK_LibIniter task = {0}; - task.free_libs = push_array(arena->v[0], LNK_LibNode, lib_count); - task.valid_libs = push_array(scratch.arena, LNK_LibNode *, lib_count); - task.invalid_libs = push_array(scratch.arena, LNK_LibNode *, lib_count); - task.data_arr = data_arr.v; - task.path_arr = path_arr.v; - tp_for_parallel(tp, arena, lib_count, lnk_lib_initer, &task); + task.free_libs = push_array(arena->v[0], LNK_LibNode, inputs_count); + task.valid_libs = push_array(scratch.arena, LNK_LibNode *, inputs_count); + task.invalid_libs = push_array(scratch.arena, LNK_LibNode *, inputs_count); + task.inputs = inputs; + tp_for_parallel(tp, arena, inputs_count, lnk_lib_initer, &task); // report invalid libs radsort(task.invalid_libs, task.invalid_libs_count, lnk_lib_node_ptr_is_before); for EachIndex(i, task.invalid_libs_count) { U64 input_idx = task.invalid_libs[i]->data.input_idx; - lnk_error(LNK_Error_InvalidLib, "%S: failed to parse library", path_arr.v[input_idx]); + lnk_error(LNK_Error_InvalidLib, "%S: failed to parse library", inputs[input_idx]->path); } // push parsed libs radsort(task.valid_libs, task.valid_libs_count, lnk_lib_node_ptr_is_before); for EachIndex(i, task.valid_libs_count) { + task.valid_libs[i]->data.input_idx = lib_id_base + i; lnk_lib_list_push_node(list, task.valid_libs[i]); } @@ -187,6 +196,14 @@ lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, S return result; } +internal B32 +lnk_flag_member_as_queued(LNK_Lib *lib, U32 member_idx, LNK_Symbol *trigger) +{ + LNK_Symbol *slot = ins_atomic_ptr_eval_cond_assign(&lib->was_member_linked[member_idx], trigger, 0); + B32 is_first_queue_attempt = (slot == 0); + return is_first_queue_attempt; +} + internal B32 lnk_search_lib(LNK_Lib *lib, String8 symbol_name, U32 *member_idx_out) { diff --git a/src/linker/lnk_lib.h b/src/linker/lnk_lib.h index b585d302..e266dbc7 100644 --- a/src/linker/lnk_lib.h +++ b/src/linker/lnk_lib.h @@ -11,7 +11,7 @@ typedef struct LNK_Lib U32 symbol_count; U32 *member_offsets; U16 *symbol_indices; - B8 *was_member_queued; + LNK_Symbol **was_member_linked; String8Array symbol_names; String8 long_names; U64 input_idx; @@ -40,14 +40,13 @@ typedef struct LNK_LibList typedef struct { - String8 *data_arr; - String8 *path_arr; - U64 next_free_lib_idx; - U64 valid_libs_count; - U64 invalid_libs_count; - LNK_LibNode *free_libs; - LNK_LibNode **valid_libs; - LNK_LibNode **invalid_libs; + struct LNK_Input **inputs; + U64 next_free_lib_idx; + U64 valid_libs_count; + U64 invalid_libs_count; + LNK_LibNode *free_libs; + LNK_LibNode **valid_libs; + LNK_LibNode **invalid_libs; } LNK_LibIniter; // ----------------------------------------------------------------------------- @@ -56,8 +55,11 @@ internal int lnk_lib_node_is_before(void *a, void *b); internal int lnk_lib_node_ptr_is_before(void *raw_a, void *raw_b); internal B32 lnk_lib_from_data(Arena *arena, String8 data, String8 path, LNK_Lib *lib_out); +internal LNK_Lib ** lnk_array_from_lib_list(Arena *arena, LNK_LibList list); internal void lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node); -internal LNK_LibNodeArray lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, String8Array data_arr, String8Array path_arr); +internal LNK_LibNodeArray lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, U64 inputs_count, struct LNK_Input **inputs); + +internal B32 lnk_flag_member_as_queued(LNK_Lib *lib, U32 member_idx, LNK_Symbol *trigger); internal B32 lnk_search_lib(LNK_Lib *lib, String8 symbol_name, U32 *member_idx_out); diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index bb15bcc2..734da57e 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -11,6 +11,14 @@ lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...) va_end(args); } +internal void +lnk_error_input_obj(LNK_ErrorCode code, LNK_Input *input, char *fmt, ...) +{ + va_list args; va_start(args, fmt); + lnk_error_with_loc_fv(code, input->path, input->trigger->lib ? input->trigger->lib->path : str8_zero(), fmt, args); + va_end(args); +} + internal LNK_Obj ** lnk_array_from_obj_list(Arena *arena, LNK_ObjList list) { @@ -26,9 +34,8 @@ internal THREAD_POOL_TASK_FUNC(lnk_obj_initer) { LNK_ObjIniter *task = raw_task; - LNK_InputObj *input = task->inputs[task_id]; - LNK_Obj *obj = &task->objs.v[task_id].data; - U64 obj_idx = task->obj_id_base + task_id; + LNK_Input *input = task->inputs[task_id]; + LNK_Obj *obj = &task->objs[task_id].data; ProfBeginV("Init Obj [%S%s%S]", input->lib_path, (input->lib_path.size ? ": " : 0), input->path); @@ -281,58 +288,60 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) // fill out obj obj->data = input->data; obj->path = push_str8_copy(arena, input->path); - obj->lib = input->lib; - obj->input_idx = obj_idx; + obj->lib = input->trigger ? input->trigger->lib : 0; obj->header = header; obj->comdats = comdats; obj->exclude_from_debug_info = input->exclude_from_debug_info; obj->hotpatch = hotpatch; obj->associated_sections = associated_sections; + obj->node = &task->objs[task_id]; + obj->trigger_symbol = input->trigger; ProfEnd(); } -internal LNK_ObjNodeArray -lnk_obj_list_push_parallel(TP_Context *tp, - TP_Arena *arena, - LNK_ObjList *list, - COFF_MachineType machine, - U64 input_count, - LNK_InputObj **inputs) +internal LNK_ObjNode * +lnk_obj_from_input_many(TP_Context *tp, TP_Arena *arena, COFF_MachineType machine, U64 inputs_count, LNK_Input **inputs) { - ProfBeginFunction(); - - // store base id - U64 obj_id_base = list->count; - - // reserve obj nodes - LNK_ObjNodeArray objs = {0}; - if (input_count > 0) { - objs.count = input_count; - objs.v = push_array(arena->v[0], LNK_ObjNode, input_count); - for (LNK_ObjNode *ptr = objs.v, *opl = objs.v + input_count; ptr < opl; ++ptr) { - SLLQueuePush(list->first, list->last, ptr); - } - list->count += input_count; + LNK_ObjNode *objs = 0; + if (inputs_count) { + objs = push_array(arena->v[0], LNK_ObjNode, inputs_count); + tp_for_parallel(tp, arena, inputs_count, lnk_obj_initer, &(LNK_ObjIniter){ .inputs = inputs, .objs = objs, .machine = machine }); } - - // fill out & run task - LNK_ObjIniter task = {0}; - task.inputs = inputs; - task.obj_id_base = obj_id_base; - task.objs = objs; - task.machine = machine; - tp_for_parallel(tp, arena, input_count, lnk_obj_initer, &task); - - ProfEnd(); return objs; } +internal LNK_ObjNode * +lnk_obj_from_input(Arena *arena, COFF_MachineType machine, LNK_Input *input) +{ + Temp scratch = scratch_begin(&arena, 1); + TP_Context *tp = tp_alloc(scratch.arena, 1, 1, str8_zero()); + TP_Arena tp_arena = { .count = 1, .v = &arena }; + LNK_ObjNode *result = lnk_obj_from_input_many(tp, &tp_arena, machine, 1, &input); + scratch_end(scratch); + return result; +} + +internal void +lnk_obj_list_push_node_many(LNK_ObjList *list, U64 count, LNK_ObjNode *nodes) +{ + for EachIndex(i, count) { + DLLPushBack(list->first, list->last, &nodes[i]); + } + list->count += count; +} + +internal void +lnk_obj_list_push_node(LNK_ObjList *list, LNK_ObjNode *node) +{ + lnk_obj_list_push_node_many(list, 1, node); +} + internal THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table) { LNK_InputCoffSymbolTable *task = raw_task; - LNK_Obj *obj = &task->objs.v[task_id].data; + LNK_Obj *obj = task->objs[task_id]; COFF_ParsedSymbol symbol = {0}; for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); @@ -400,17 +409,17 @@ internal THREAD_POOL_TASK_FUNC(lnk_assign_comdat_symlinks_task) { LNK_InputCoffSymbolTable *task = raw_task; - LNK_Obj *obj = &task->objs.v[task_id].data; + LNK_Obj *obj = task->objs[task_id]; obj->symlinks = lnk_symlinks_from_obj(arena, task->symtab, obj); } internal void -lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs) +lnk_push_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs) { ProfBeginFunction(); LNK_InputCoffSymbolTable task = { .symtab = symtab, .objs = objs }; - tp_for_parallel(tp, arena, objs.count, lnk_input_coff_symbol_table, &task); - tp_for_parallel(tp, arena, objs.count, lnk_assign_comdat_symlinks_task, &task); + tp_for_parallel(tp, arena, objs_count, lnk_input_coff_symbol_table, &task); + tp_for_parallel(tp, arena, objs_count, lnk_assign_comdat_symlinks_task, &task); ProfEnd(); } diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index 52ac822e..5ce71547 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -7,21 +7,25 @@ typedef struct LNK_Obj { - String8 data; - String8 path; - struct LNK_Lib *lib; - U32 input_idx; - COFF_FileHeaderInfo header; - U32 *comdats; - B8 hotpatch; - B8 exclude_from_debug_info; - U32Node **associated_sections; - LNK_SymbolHashTrie **symlinks; + String8 path; + String8 data; + struct LNK_Lib *lib; + struct LNK_LibMemberRef *trigger_symbol; + U32 input_idx; + COFF_FileHeaderInfo header; + U32 *comdats; + B8 hotpatch; + B8 exclude_from_debug_info; + U32Node **associated_sections; + LNK_SymbolHashTrie **symlinks; + + struct LNK_ObjNode *node; } LNK_Obj; typedef struct LNK_ObjNode { struct LNK_ObjNode *next; + struct LNK_ObjNode *prev; LNK_Obj data; } LNK_ObjNode; @@ -63,16 +67,16 @@ typedef struct LNK_DirectiveInfo typedef struct { - LNK_InputObj **inputs; - LNK_ObjNodeArray objs; - U64 obj_id_base; - U32 machine; + struct LNK_Input **inputs; + LNK_ObjNode *objs; + U64 obj_id_base; + U32 machine; } LNK_ObjIniter; typedef struct { - LNK_SymbolTable *symtab; - LNK_ObjNodeArray objs; + LNK_SymbolTable *symtab; + LNK_Obj **objs; } LNK_InputCoffSymbolTable; typedef struct @@ -86,12 +90,15 @@ typedef struct // --- Error ------------------------------------------------------------------- internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...); +internal void lnk_error_input_obj(LNK_ErrorCode code, struct LNK_Input *input, char *fmt, ...); // --- Input ------------------------------------------------------------------- -internal LNK_Obj ** lnk_array_from_obj_list(Arena *arena, LNK_ObjList list); -internal LNK_ObjNodeArray lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *tp_arena, LNK_ObjList *obj_list, COFF_MachineType machine, U64 input_count, LNK_InputObj **inputs); -internal void lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs); +internal LNK_Obj ** lnk_array_from_obj_list(Arena *arena, LNK_ObjList list); +internal void lnk_obj_list_push_node_many(LNK_ObjList *list, U64 count, LNK_ObjNode *nodes); +internal void lnk_obj_list_push_node(LNK_ObjList *list, LNK_ObjNode *node); + +internal void lnk_inputer_push_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, U64 objs_count, LNK_ObjNode *objs); // --- Metadata ---------------------------------------------------------------- diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index 47e709c4..161ac01d 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -15,7 +15,18 @@ internal B32 lnk_symbol_defined_is_before(void *raw_a, void *raw_b) { LNK_Symbol *a = raw_a, *b = raw_b; - return a->defined.obj->input_idx < b->defined.obj->input_idx; + + + U32 a_lib_input_idx = a->defined.obj->lib ? a->defined.obj->lib->input_idx : 0; + U32 b_lib_input_idx = b->defined.obj->lib ? b->defined.obj->lib->input_idx : 0; + + if (a_lib_input_idx == b_lib_input_idx) { + if (a->defined.obj->input_idx == b->defined.obj->input_idx) { + return a->defined.symbol_idx < b->defined.symbol_idx; + } + return a->defined.obj->input_idx < b->defined.obj->input_idx; + } + return a_lib_input_idx < b_lib_input_idx; } internal B32 diff --git a/src/linker/lnk_symbol_table.h b/src/linker/lnk_symbol_table.h index b87fa44b..c789a270 100644 --- a/src/linker/lnk_symbol_table.h +++ b/src/linker/lnk_symbol_table.h @@ -14,6 +14,7 @@ typedef struct LNK_SymbolDefined typedef struct LNK_Symbol { String8 name; + B8 is_lib_member_linked; LNK_SymbolDefined defined; } LNK_Symbol;