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;