diff --git a/src/linker/base_ext/base_arena.c b/src/linker/base_ext/base_arena.c index 5b6ca528..79471dfb 100644 --- a/src/linker/base_ext/base_arena.c +++ b/src/linker/base_ext/base_arena.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal String8 diff --git a/src/linker/base_ext/base_arena.h b/src/linker/base_ext/base_arena.h index dbafb624..cb7f00ee 100644 --- a/src/linker/base_ext/base_arena.h +++ b/src/linker/base_ext/base_arena.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/base_ext/base_arrays.c b/src/linker/base_ext/base_arrays.c index 423ab684..040f220d 100644 --- a/src/linker/base_ext/base_arrays.c +++ b/src/linker/base_ext/base_arrays.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal U64 diff --git a/src/linker/base_ext/base_arrays.h b/src/linker/base_ext/base_arrays.h index e1cd052d..b2c45bd5 100644 --- a/src/linker/base_ext/base_arrays.h +++ b/src/linker/base_ext/base_arrays.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/base_ext/base_bit_array.c b/src/linker/base_ext/base_bit_array.c index 2e213294..2ab94b77 100644 --- a/src/linker/base_ext/base_bit_array.c +++ b/src/linker/base_ext/base_bit_array.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal U32Array diff --git a/src/linker/base_ext/base_bit_array.h b/src/linker/base_ext/base_bit_array.h index c9428957..76e19f87 100644 --- a/src/linker/base_ext/base_bit_array.h +++ b/src/linker/base_ext/base_bit_array.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/base_ext/base_blake3.c b/src/linker/base_ext/base_blake3.c index 9f522068..ca29e21a 100644 --- a/src/linker/base_ext/base_blake3.c +++ b/src/linker/base_ext/base_blake3.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #if defined(__clang__) diff --git a/src/linker/base_ext/base_blake3.h b/src/linker/base_ext/base_blake3.h index 4489e1d5..72e8986f 100644 --- a/src/linker/base_ext/base_blake3.h +++ b/src/linker/base_ext/base_blake3.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/base_ext/base_blake3_asm.c b/src/linker/base_ext/base_blake3_asm.c index d02a4380..1b91df31 100644 --- a/src/linker/base_ext/base_blake3_asm.c +++ b/src/linker/base_ext/base_blake3_asm.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #include "../third_party_ext/blake3/blake3_portable.c" diff --git a/src/linker/base_ext/base_blake3_asm.h b/src/linker/base_ext/base_blake3_asm.h index 94cdf267..e39c3b41 100644 --- a/src/linker/base_ext/base_blake3_asm.h +++ b/src/linker/base_ext/base_blake3_asm.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #ifndef BASE_BLAKE3_H diff --git a/src/linker/base_ext/base_core.c b/src/linker/base_ext/base_core.c index 2f44ddd5..3239be28 100644 --- a/src/linker/base_ext/base_core.c +++ b/src/linker/base_ext/base_core.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal U16 diff --git a/src/linker/base_ext/base_core.h b/src/linker/base_ext/base_core.h index 2f850d3b..deb18e8c 100644 --- a/src/linker/base_ext/base_core.h +++ b/src/linker/base_ext/base_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/base_ext/base_crc32.c b/src/linker/base_ext/base_crc32.c index ee5a95aa..28b90da6 100644 --- a/src/linker/base_ext/base_crc32.c +++ b/src/linker/base_ext/base_crc32.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal U32 diff --git a/src/linker/base_ext/base_crc32.h b/src/linker/base_ext/base_crc32.h index a7bb209d..e6d55bae 100644 --- a/src/linker/base_ext/base_crc32.h +++ b/src/linker/base_ext/base_crc32.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/base_ext/base_inc.c b/src/linker/base_ext/base_inc.c index c90891e9..c3886302 100644 --- a/src/linker/base_ext/base_inc.c +++ b/src/linker/base_ext/base_inc.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #include "base_core.c" diff --git a/src/linker/base_ext/base_inc.h b/src/linker/base_ext/base_inc.h index c06090ff..67081bf6 100644 --- a/src/linker/base_ext/base_inc.h +++ b/src/linker/base_ext/base_inc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/base_ext/base_md5.c b/src/linker/base_ext/base_md5.c index 326c466b..38ffac51 100644 --- a/src/linker/base_ext/base_md5.c +++ b/src/linker/base_ext/base_md5.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal MD5Hash diff --git a/src/linker/base_ext/base_md5.h b/src/linker/base_ext/base_md5.h index b4593dd4..b435fe58 100644 --- a/src/linker/base_ext/base_md5.h +++ b/src/linker/base_ext/base_md5.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/base_ext/base_strings.c b/src/linker/base_ext/base_strings.c index 83b1d81d..fe465f5e 100644 --- a/src/linker/base_ext/base_strings.c +++ b/src/linker/base_ext/base_strings.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal String8Node * diff --git a/src/linker/base_ext/base_strings.h b/src/linker/base_ext/base_strings.h index ce683cfe..b469cccf 100644 --- a/src/linker/base_ext/base_strings.h +++ b/src/linker/base_ext/base_strings.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/codeview_ext/codeview.c b/src/linker/codeview_ext/codeview.c index f09ff50f..b932a4be 100644 --- a/src/linker/codeview_ext/codeview.c +++ b/src/linker/codeview_ext/codeview.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// diff --git a/src/linker/codeview_ext/codeview.h b/src/linker/codeview_ext/codeview.h index a4bddb1c..d4b4ac4e 100644 --- a/src/linker/codeview_ext/codeview.h +++ b/src/linker/codeview_ext/codeview.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index 4f9f8033..bdc32ac3 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal void diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index df557997..cc65cbf5 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/lnk.c b/src/linker/lnk.c index cb2731d5..975922b1 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// @@ -116,15 +116,14 @@ #include "lnk_timer.h" #include "lnk_io.h" #include "lnk_cmd_line.h" +#include "lnk_input.h" +#include "lnk_import_table.h" +#include "lnk_export_table.h" #include "lnk_config.h" -#include "lnk_chunk.h" -#include "lnk_reloc.h" #include "lnk_symbol_table.h" #include "lnk_section_table.h" #include "lnk_debug_helper.h" #include "lnk_obj.h" -#include "lnk_import_table.h" -#include "lnk_export_table.h" #include "lnk_lib.h" #include "lnk_debug_info.h" #include "lnk.h" @@ -134,86 +133,21 @@ #include "lnk_timer.c" #include "lnk_io.c" #include "lnk_cmd_line.c" +#include "lnk_input.c" +#include "lnk_import_table.c" +#include "lnk_export_table.c" #include "lnk_config.c" -#include "lnk_chunk.c" -#include "lnk_reloc.c" #include "lnk_symbol_table.c" #include "lnk_section_table.c" #include "lnk_obj.c" #include "lnk_debug_helper.c" -#include "lnk_import_table.c" -#include "lnk_export_table.c" #include "lnk_lib.c" #include "lnk_debug_info.c" //////////////////////////////// -internal LNK_InputImport * -lnk_input_import_list_push(Arena *arena, LNK_InputImportList *list) -{ - LNK_InputImport *node = push_array(arena, LNK_InputImport, 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_InputImport ** -lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list) -{ - LNK_InputImport **result = push_array_no_zero(arena, LNK_InputImport *, list.count); - U64 idx = 0; - for (LNK_InputImport *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_InputImport **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 = raw_a; - LNK_InputImport **b = raw_b; - int cmp = str8_compar_ignore_case(&(*a)->import_header.dll_name, &(*b)->import_header.dll_name); - if (cmp == 0) { - cmp = str8_compar_case_sensitive(&(*a)->import_header.func_name, &(*b)->import_header.func_name); - } - return cmp < 0; -} - -int -lnk_input_import_compar(const void *raw_a, const void *raw_b) -{ - LNK_InputImport * const *a = raw_a; - LNK_InputImport * const *b = raw_b; - int cmp = str8_compar_ignore_case(&(*a)->import_header.dll_name, &(*b)->import_header.dll_name); - if (cmp == 0) { - cmp = str8_compar_case_sensitive(&(*a)->import_header.func_name, &(*b)->import_header.func_name); - } - return cmp; -} - -//////////////////////////////// - internal String8 -lnk_make_full_path(Arena *arena, String8 work_dir, PathStyle system_path_style, String8 path) +lnk_make_full_path(Arena *arena, PathStyle system_path_style, String8 work_dir, String8 path) { ProfBeginFunction(); String8 result = str8(0,0); @@ -232,7 +166,49 @@ lnk_make_full_path(Arena *arena, String8 work_dir, PathStyle system_path_style, return result; } -//////////////////////////////// +internal +THREAD_POOL_TASK_FUNC(lnk_blake3_hasher_task) +{ + ProfBeginFunction(); + + LNK_Blake3Hasher *task = raw_task; + Rng1U64 range = task->ranges[task_id]; + String8 sub_data = str8_substr(task->data, range); + + blake3_hasher hasher; blake3_hasher_init(&hasher); + blake3_hasher_update(&hasher, sub_data.str, sub_data.size); + blake3_hasher_finalize(&hasher, (U8 *)task->hashes[task_id].u64, sizeof(task->hashes[task_id].u64)); + + ProfEnd(); +} + +internal U128 +lnk_blake3_hash_parallel(TP_Context *tp, U64 chunk_count, String8 data) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + + ProfBegin("Hash Chunks"); + LNK_Blake3Hasher task = {0}; + task.data = data; + task.ranges = tp_divide_work(scratch.arena, data.size, chunk_count); + task.hashes = push_array(scratch.arena, U128, chunk_count); + tp_for_parallel(tp, 0, chunk_count, lnk_blake3_hasher_task, &task); + ProfEnd(); + + ProfBegin("Combine Hashes"); + blake3_hasher hasher; blake3_hasher_init(&hasher); + for (U64 i = 0; i < chunk_count; ++i) { + blake3_hasher_update(&hasher, (U8 *)task.hashes[i].u64, sizeof(task.hashes[i].u64)); + } + U128 result; + blake3_hasher_finalize(&hasher, (U8 *)result.u64, sizeof(result.u64)); + ProfEnd(); + + scratch_end(scratch); + ProfEnd(); + return result; +} internal String8 lnk_make_linker_manifest(Arena *arena, @@ -393,8 +369,6 @@ lnk_manifest_from_inputs(Arena *arena, return manifest_data; } -//////////////////////////////// - internal int lnk_res_string_id_is_before(void *raw_a, void *raw_b) { @@ -666,46 +640,13 @@ lnk_add_resource_debug_s(COFF_ObjWriter *obj_writer, internal String8 lnk_make_res_obj(Arena *arena, - PE_ResourceDir *root_dir, - COFF_TimeStamp time_stamp, + String8List res_data_list, + String8List res_path_list, COFF_MachineType machine, - String8 path, - String8 cwd_path, - String8 exe_path, - String8List res_file_list, - MD5Hash *res_hash_array) -{ - ProfBeginFunction(); - - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); - - // obj features - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("@feat.00"), COFF_SymStorageClass_Static, MSCRT_FeatFlag_HAS_SAFE_SEH|MSCRT_FeatFlag_UNKNOWN_4); - - // serialize resource tree - lnk_serialize_pe_resource_tree(obj_writer, root_dir); - - // push resource debug info - lnk_add_resource_debug_s(obj_writer, path, cwd_path, exe_path, cv_arch_from_coff_machine(machine), res_file_list, res_hash_array); - - // finalize obj - String8 res_obj = coff_obj_writer_serialize(arena, obj_writer); - - coff_obj_writer_release(&obj_writer); - - ProfEnd(); - return res_obj; -} - -internal String8 -lnk_obj_from_res_file_list(Arena *arena, - String8List res_data_list, - String8List res_path_list, - COFF_MachineType machine, - U32 time_stamp, - String8 work_dir, - PathStyle system_path_style, - String8 obj_name) + U32 time_stamp, + String8 work_dir, + PathStyle system_path_style, + String8 obj_name) { ProfBeginFunction(); Temp scratch = scratch_begin(&arena,1); @@ -724,7 +665,7 @@ lnk_obj_from_res_file_list(Arena *arena, // convert res paths to stable paths String8List stable_res_file_list = {0}; for (String8Node *node = res_path_list.first; node != 0; node = node->next) { - String8 stable_res_path = lnk_make_full_path(scratch.arena, work_dir, system_path_style, node->string); + String8 stable_res_path = lnk_make_full_path(scratch.arena, system_path_style, work_dir, node->string); str8_list_push(scratch.arena, &stable_res_file_list, stable_res_path); } @@ -733,23 +674,31 @@ lnk_obj_from_res_file_list(Arena *arena, String8List exe_path_strs = {0}; str8_list_push(scratch.arena, &exe_path_strs, process_info->binary_path); String8 exe_path = str8_list_first(&exe_path_strs); - String8 res_obj = lnk_make_res_obj(arena, - root_dir, - time_stamp, - machine, - obj_name, - work_dir, - exe_path, - stable_res_file_list, - res_hash_array); + + String8 res_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); + + // obj features + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("@feat.00"), COFF_SymStorageClass_Static, MSCRT_FeatFlag_HAS_SAFE_SEH|MSCRT_FeatFlag_UNKNOWN_4); + + // serialize resource tree + lnk_serialize_pe_resource_tree(obj_writer, root_dir); + + // push resource debug info + lnk_add_resource_debug_s(obj_writer, obj_name, work_dir, exe_path, cv_arch_from_coff_machine(machine), stable_res_file_list, res_hash_array); + + // finalize obj + res_obj = coff_obj_writer_serialize(arena, obj_writer); + + coff_obj_writer_release(&obj_writer); + } scratch_end(scratch); ProfEnd(); return res_obj; } -//////////////////////////////// - internal String8 lnk_make_linker_coff_obj(Arena *arena, COFF_TimeStamp time_stamp, @@ -806,7 +755,60 @@ lnk_make_linker_coff_obj(Arena *arena, return obj; } -//////////////////////////////// +internal void +lnk_push_pe_debug_data_directory(COFF_ObjWriter *obj_writer, + COFF_ObjSymbol *data_symbol, + U64 data_size, + String8 dir_name, + PE_DebugDirectoryType type, + COFF_TimeStamp time_stamp) +{ + // init directory + PE_DebugDirectory *dir = push_array(obj_writer->arena, PE_DebugDirectory, 1); + dir->time_stamp = time_stamp; + dir->type = type; + dir->size = data_size; + //dir->voff = 0; // relocated through 'data_symbol' + //dir->foff = 0; // relocated through 'data_symbol' + COFF_ObjSection *debug_dir_sect = coff_obj_writer_push_section(obj_writer, dir_name, LNK_DATA_SECTION_FLAGS, str8_struct(dir)); + coff_obj_writer_section_push_reloc(obj_writer, debug_dir_sect, OffsetOf(PE_DebugDirectory, voff), data_symbol, COFF_Reloc_X64_Addr32Nb); + coff_obj_writer_section_push_reloc(obj_writer, debug_dir_sect, OffsetOf(PE_DebugDirectory, foff), data_symbol, COFF_Reloc_X64_Abs); +} + +internal String8 +lnk_make_debug_directory_obj(Arena *arena, LNK_Config *config) +{ + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, config->machine); + COFF_ObjSection *sect_a = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$a"), LNK_DATA_SECTION_FLAGS, str8_zero()); + COFF_ObjSection *sect_z = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$z"), LNK_DATA_SECTION_FLAGS, str8_zero()); + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); + return obj; +} + +internal String8 +lnk_make_debug_directory_pdb_obj(Arena *arena, LNK_Config *config) +{ + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, config->machine); + String8 debug_pdb_data = pe_make_debug_header_pdb70(obj_writer->arena, config->guid, config->age, config->pdb_alt_path); + COFF_ObjSection *debug_pdb_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), LNK_DATA_SECTION_FLAGS, debug_pdb_data); + COFF_ObjSymbol *debug_pdb_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("PDB_DEBUG_HEADER_70"), 0, debug_pdb_sect); + lnk_push_pe_debug_data_directory(obj_writer, debug_pdb_symbol, debug_pdb_data.size, str8_lit(".RAD_LINKER_DEBUG_DIR$PDB"), PE_DebugDirectoryType_CODEVIEW, config->time_stamp); + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + return obj; +} + +internal String8 +lnk_make_debug_directory_rdi_obj(Arena *arena, LNK_Config *config) +{ + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, COFF_MachineType_Unknown); + String8 debug_rdi_data = pe_make_debug_header_rdi(obj_writer->arena, config->guid, config->rad_debug_alt_path); + COFF_ObjSection *debug_rdi_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), LNK_DATA_SECTION_FLAGS, debug_rdi_data); + COFF_ObjSymbol *debug_rdi_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("RDI_DEBUG_HEADER"), 0, debug_rdi_sect); + lnk_push_pe_debug_data_directory(obj_writer, debug_rdi_symbol, debug_rdi_data.size, str8_lit("RAD_LINKER_DEBUG_DIR$RDI"), PE_DebugDirectoryType_CODEVIEW, config->time_stamp); + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + return obj; +} internal THREAD_POOL_TASK_FUNC(lnk_load_thin_objs_task) @@ -865,55 +867,11 @@ lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path) } } -internal void -lnk_push_input_from_lazy(Arena *arena, PathStyle path_style, LNK_LazySymbol *lazy, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list) +internal LNK_InputObjList +lnk_push_linker_symbols(Arena *arena, LNK_Config *config) { - // parse member - COFF_ArchiveMember member_info = coff_archive_member_from_offset(lazy->lib->data, lazy->member_offset); - COFF_DataType member_type = coff_data_type_from_data(member_info.data); - - switch (member_type) { - case COFF_DataType_Null: break; - case COFF_DataType_Import: { - LNK_InputImport *input = lnk_input_import_list_push(arena, input_import_list); - input->import_header = coff_archive_import_from_data(member_info.data); - } break; - case COFF_DataType_BigObj: - case COFF_DataType_Obj: { - String8 obj_path = coff_parse_long_name(lazy->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); - } - - // obj path in thin archive is relative to directory with archive - B32 is_thin = lazy->lib->type == COFF_Archive_Thin; - if (is_thin) { - Temp scratch = scratch_begin(&arena, 1); - String8List obj_path_list; MemoryZeroStruct(&obj_path_list); - str8_list_push(scratch.arena, &obj_path_list, str8_chop_last_slash(lazy->lib->path)); - str8_list_push(scratch.arena, &obj_path_list, obj_path); - obj_path = str8_path_list_join_by_style(arena, &obj_path_list, path_style); - scratch_end(scratch); - } - - 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", lazy->lib->path, obj_path); - input->path = obj_path; - input->data = member_info.data; - input->lib_path = lazy->lib->path; - } break; - } -} + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, config->machine); -internal void -lnk_push_linker_symbols(LNK_SymbolTable *symtab, COFF_MachineType machine) -{ // Emit __ImageBase symbol. // // This symbol is used with REL32 to compute delta from current IP @@ -921,30 +879,298 @@ lnk_push_linker_symbols(LNK_SymbolTable *symtab, COFF_MachineType machine) // passing it around as a function argument. // // 100h: lea rax, [rip + ffffff00h] ; -100h - LNK_Symbol *image_base = lnk_symbol_table_push_defined_chunk(symtab, str8_lit("__ImageBase"), LNK_DefinedSymbolVisibility_Extern, 0, g_null_chunk_ptr, 0, COFF_ComdatSelect_Any, 0); + U64 base_addr = lnk_get_base_addr(config); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("__ImageBase"), base_addr, COFF_SymStorageClass_External); { // load config symbols - if (machine == COFF_MachineType_X86) { - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_SAFE_SE_HANDLER_TABLE_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Extern, 0, g_null_chunk_ptr, 0, COFF_ComdatSelect_NoDuplicates, 0); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_SAFE_SE_HANDLER_COUNT_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Extern, 0, g_null_chunk_ptr, 0, COFF_ComdatSelect_NoDuplicates, 0); + if (config->machine == COFF_MachineType_X86) { + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_SAFE_SE_HANDLER_TABLE_SYMBOL_NAME), 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_SAFE_SE_HANDLER_COUNT_SYMBOL_NAME), 0, COFF_SymStorageClass_External); } // TODO: investigate IMAGE_ENCLAVE_CONFIG 32/64 - lnk_symbol_table_push_defined_va(symtab, str8_lit(LNK_ENCLAVE_CONFIG_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Extern, 0, 0); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_ENCLAVE_CONFIG_SYMBOL_NAME), 0, COFF_SymStorageClass_External); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_FLAGS_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Extern, 0); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_FIDS_TABLE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Extern, 0); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_FIDS_COUNT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Extern, 0); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_IAT_TABLE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Extern, 0); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_IAT_COUNT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Extern, 0); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_LONGJMP_TABLE_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Extern, 0); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_LONGJMP_COUNT_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Extern, 0); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_EHCONT_TABLE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Extern, 0); - lnk_symbol_table_push_defined(symtab, str8_lit(LNK_GUARD_EHCONT_COUNT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Extern, 0); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_FLAGS_SYMBOL_NAME) , 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_FIDS_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_FIDS_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_IAT_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_IAT_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_LONGJMP_TABLE_SYMBOL_NAME), 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_LONGJMP_COUNT_SYMBOL_NAME), 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_EHCONT_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_GUARD_EHCONT_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External); + } + + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); + + LNK_InputObjList result = {0}; + LNK_InputObj *input = lnk_input_obj_list_push(arena, &result); + input->path = str8_lit("* Linker Symbols *"); + input->dedup_id = input->path; + input->data = obj; + + return result; +} + +internal void +lnk_queue_lib_member_input(Arena *arena, PathStyle path_style, LNK_SymbolLib *symbol, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list) +{ + // parse member + COFF_ArchiveMember member_info = coff_archive_member_from_offset(symbol->lib->data, symbol->member_offset); + COFF_DataType member_type = coff_data_type_from_data(member_info.data); + + switch (member_type) { + case COFF_DataType_Null: break; + case COFF_DataType_Import: { + LNK_InputImport *input = lnk_input_import_list_push(arena, input_import_list); + input->import_header = coff_archive_import_from_data(member_info.data); + } break; + case COFF_DataType_BigObj: + case COFF_DataType_Obj: { + String8 obj_path = coff_parse_long_name(symbol->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); + } + + // obj path in thin archive is relative to directory with archive + B32 is_thin = symbol->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(symbol->lib->path)); + str8_list_push(scratch.arena, &obj_path_list, obj_path); + obj_path = str8_path_list_join_by_style(arena, &obj_path_list, path_style); + scratch_end(scratch); + } + + 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", symbol->lib->path, obj_path); + input->path = obj_path; + input->data = member_info.data; + input->lib_path = symbol->lib->path; + } break; } } -//////////////////////////////// +internal int +lnk_section_contrib_is_before(void *raw_a, void *raw_b) +{ + // Grouped Sections (PE Format) + // "All contributions with the same object-section name are allocated contiguously in the image, + // and the blocks of contributions are sorted in lexical order by object-section name." + LNK_SectionContrib *a = raw_a; + LNK_SectionContrib *b = raw_b; + + int cmp; + + // place sections without sort postfix first + if (a->u.sort_idx_size == 0 && b->u.sort_idx_size > 0) { + cmp = -1; + } else if (a->u.sort_idx_size > 0 && b->u.sort_idx_size == 0) { + cmp = +1; + } else { + // sort on section postfix + String8 a_sort_idx = str8(a->u.sort_idx, a->u.sort_idx_size); + String8 b_sort_idx = str8(b->u.sort_idx, b->u.sort_idx_size); + cmp = str8_compar_case_sensitive(&a_sort_idx, &b_sort_idx); + + // sort on obj position on command line + if (cmp == 0) { + cmp = u64_compar(&a->u.obj_idx, &b->u.obj_idx); + } + } + + int is_before = cmp < 0; + return is_before; +} + +internal int +lnk_common_block_contrib_is_before(void *raw_a, void *raw_b) +{ + LNK_CommonBlockContrib *a = raw_a; + LNK_CommonBlockContrib *b = raw_b; + + int is_before; + if (a->u.size == b->u.size) { + LNK_Symbol *a_symbol = a->symbol; + LNK_Symbol *b_symbol = b->symbol; + if (a_symbol->u.defined.obj->input_idx == b_symbol->u.defined.obj->input_idx) { + is_before = a_symbol->u.defined.symbol_idx < b_symbol->u.defined.symbol_idx; + } else { + is_before = a_symbol->u.defined.obj->input_idx < b_symbol->u.defined.obj->input_idx; + } + } else { + is_before = a->u.size > b->u.size; + } + + return is_before; +} + +internal U64 +lnk_compute_win32_image_header_size(LNK_Config *config, U64 sect_count) +{ + U64 image_header_size = 0; + image_header_size += sizeof(PE_DosHeader) + pe_dos_program.size; + image_header_size += sizeof(U32); // PE_MAGIC + image_header_size += sizeof(COFF_FileHeader); + image_header_size += pe_has_plus_header(config->machine) ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32); + image_header_size += sizeof(PE_DataDirectory) * config->data_dir_count; + image_header_size += sizeof(COFF_SectionHeader) * sect_count; + return image_header_size; +} + +internal +THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) +{ + LNK_ObjRelocPatcher *task = raw_task; + LNK_Obj *obj = task->objs[task_id]; + + COFF_FileHeaderInfo obj_header = obj->header; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj_header.section_table_range).str; + String8 symbol_table = str8_substr(obj->data, obj_header.symbol_table_range); + String8 string_table = str8_substr(obj->data, obj_header.string_table_range); + + for (U64 sect_idx = 0; sect_idx < obj_header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *section_header = §ion_table[sect_idx]; + + if (section_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + + COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, section_header); + COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); + + for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { + COFF_Reloc *reloc = &relocs[reloc_idx]; + + // compute relocation file/virtual offsets + U64 reloc_file_offset = section_header->foff + reloc->apply_off; + U64 reloc_virtual_offset = section_header->voff + reloc->apply_off; + + // compute symbol location values + U32 symbol_section_number = 0; + U32 symbol_section_offset = 0; + U32 symbol_virtual_offset = 0; + U64 symbol_address = 0; + { + COFF_ParsedSymbol symbol; + if (obj_header.is_big_obj) { + symbol = coff_parse_symbol32(string_table, (COFF_Symbol32 *)symbol_table.str + reloc->isymbol); + } else { + symbol = coff_parse_symbol16(string_table, (COFF_Symbol16 *)symbol_table.str + reloc->isymbol); + } + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + symbol_section_number = symbol.section_number; + symbol_section_offset = symbol.value; + symbol_virtual_offset = task->image_section_table[symbol.section_number]->voff + symbol_section_offset; + symbol_address = task->base_addr + symbol_virtual_offset; + } else if (interp == COFF_SymbolValueInterp_Abs) { + symbol_section_number = max_U32; + symbol_section_offset = max_U32; + symbol_virtual_offset = symbol.value - task->base_addr; + symbol_address = symbol.value; + } else if (interp == COFF_SymbolValueInterp_Weak) { + // unresolved weak + } else if (interp == COFF_SymbolValueInterp_Undefined) { + // unresolved undefined + } else { + InvalidPath; + } + } + + // pick reloc value + COFF_RelocValue reloc_value = {0}; + switch (obj_header.machine) { + case COFF_MachineType_Unknown: {} break; + case COFF_MachineType_X64: { reloc_value = coff_pick_reloc_value_x64(reloc->type, reloc_virtual_offset, symbol_section_number, symbol_section_offset, symbol_virtual_offset, symbol_address); } break; + default: { NotImplemented; } break; + } + + // read addend + Assert(reloc_file_offset + reloc_value.size <= task->image_data.size); + U64 raw_addend = 0; + str8_deserial_read(task->image_data, reloc_file_offset, &raw_addend, reloc_value.size, reloc_value.size); + + // compute new reloc value + S64 addend = extend_sign64(raw_addend, reloc_value.size); + U64 reloc_result = reloc_value.value + addend; + + // commit new reloc value + MemoryCopy(task->image_data.str + reloc_file_offset, &reloc_result, reloc_value.size); + } + } +} + +internal void +lnk_patch_weak_external_symbol(B32 is_big_obj, void *symbol, COFF_ParsedSymbol parsed_symbol) +{ + COFF_SymbolValueInterpType parsed_symbol_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); + switch (parsed_symbol_interp) { + case COFF_SymbolValueInterp_Regular: { + if (is_big_obj) { + COFF_Symbol32 *symbol32 = symbol; + symbol32->section_number = parsed_symbol.section_number; + symbol32->value = parsed_symbol.value; + symbol32->type = parsed_symbol.type; + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol; + symbol16->section_number = safe_cast_u16(parsed_symbol.section_number); + symbol16->value = parsed_symbol.value; + symbol16->type = parsed_symbol.type; + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } break; + case COFF_SymbolValueInterp_Common: { + InvalidPath; + } break; + case COFF_SymbolValueInterp_Abs: { + if (is_big_obj) { + COFF_Symbol32 *symbol32 = symbol; + symbol32->section_number = COFF_Symbol_AbsSection32; + symbol32->value = parsed_symbol.value; + symbol32->type = parsed_symbol.type; + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol; + symbol16->section_number = COFF_Symbol_AbsSection16; + symbol16->value = parsed_symbol.value; + symbol16->type = parsed_symbol.type; + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } break; + case COFF_SymbolValueInterp_Weak: + case COFF_SymbolValueInterp_Debug: + case COFF_SymbolValueInterp_Undefined: { + InvalidPath; + } break; + default: { NotImplemented; } break; + } +} + +internal int +lnk_section_definition_is_before(void *raw_a, void *raw_b) +{ + LNK_SectionDefinition **a = raw_a; + LNK_SectionDefinition **b = raw_b; + int is_before; + if ((*a)->obj->input_idx == (*b)->obj->input_idx) { + is_before = (*a)->obj_sect_idx < (*b)->obj_sect_idx; + } else { + is_before = (*a)->obj->input_idx < (*b)->obj->input_idx; + } + return is_before; +} internal void lnk_push_coff_symbols_from_data(Arena *arena, LNK_SymbolList *symbol_list, String8 data, LNK_SymbolArray obj_symbols) @@ -988,81 +1214,22 @@ lnk_build_guard_data(Arena *arena, U64Array voff_arr, U64 stride) return guard_data; } -internal void -lnk_push_pe_debug_data_directory(COFF_ObjWriter *obj_writer, - COFF_ObjSymbol *data_symbol, - PE_DebugDirectoryType type, - COFF_TimeStamp time_stamp) -{ - // init directory - PE_DebugDirectory *dir = push_array(obj_writer->arena, PE_DebugDirectory, 1); - dir->time_stamp = time_stamp; - dir->type = type; - //dir->voff = 0; // relocated through 'data_symbol' - //dir->foff = 0; // relocated through 'data_symbol' - //dir->size = 0; // relocated through 'data_symbol' - COFF_ObjSection *debug_dir_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$d"), LNK_DATA_SECTION_FLAGS, str8_struct(dir)); - coff_obj_writer_section_push_reloc(obj_writer, debug_dir_sect, OffsetOf(PE_DebugDirectory, voff), data_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, debug_dir_sect, OffsetOf(PE_DebugDirectory, foff), data_symbol, LNK_COFF_RELOC_FILE_OFFSET32); - coff_obj_writer_section_push_reloc(obj_writer, debug_dir_sect, OffsetOf(PE_DebugDirectory, size), data_symbol, LNK_COFF_RELOC_SECT_SIZE32); -} - -internal LNK_InputObjList -lnk_build_debug_directory_objs(Arena *arena, LNK_Config *config) -{ - LNK_InputObjList result = {0}; - - B32 build_pdb_header = config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null; - B32 build_rdi_header = config->rad_debug == LNK_SwitchState_Yes; - - if (build_pdb_header || build_rdi_header) { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, config->machine); - - COFF_ObjSection *sect_a = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$a"), LNK_DATA_SECTION_FLAGS, str8_zero()); - COFF_ObjSection *sect_z = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$z"), LNK_DATA_SECTION_FLAGS, str8_zero()); - - // marker symbols for debug directory patcher - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("RAD_LINKER_DEBUG_SECTION_A"), 0, sect_a); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("RAD_LINKER_DEBUG_SECTION_Z"), 0, sect_z); - - // debug entry for PDB - if (build_pdb_header) { - String8 debug_pdb_data = pe_make_debug_header_pdb70(obj_writer->arena, config->guid, config->age, config->pdb_alt_path); - COFF_ObjSection *debug_pdb_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), LNK_DATA_SECTION_FLAGS, debug_pdb_data); - COFF_ObjSymbol *debug_pdb_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("PDB_DEBUG_HEADER_70"), 0, debug_pdb_sect); - lnk_push_pe_debug_data_directory(obj_writer, debug_pdb_symbol, PE_DebugDirectoryType_CODEVIEW, config->time_stamp); - } - - // debug entry for RDI - if (build_rdi_header) { - String8 debug_rdi_data = pe_make_debug_header_rdi(obj_writer->arena, config->guid, config->rad_debug_alt_path); - COFF_ObjSection *debug_rdi_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), LNK_DATA_SECTION_FLAGS, debug_rdi_data); - COFF_ObjSymbol *debug_rdi_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("RDI_DEBUG_HEADER"), 0, debug_rdi_sect); - lnk_push_pe_debug_data_directory(obj_writer, debug_rdi_symbol, PE_DebugDirectoryType_CODEVIEW, config->time_stamp); - } - - LNK_InputObj *input = lnk_input_obj_list_push(arena, &result); - input->path = str8_lit("* Debug Directory *"); - input->dedup_id = input->path; - input->data = coff_obj_writer_serialize(arena, obj_writer); - - coff_obj_writer_release(&obj_writer); - } - - return result; -} - -internal void +internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, - LNK_ObjList obj_list, + U64 objs_count, + LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag) { + NotImplemented; + String8List result = {0}; + return result; +#if 0 ProfBeginFunction(); Temp scratch = scratch_begin(0, 0); @@ -1079,32 +1246,31 @@ lnk_build_guard_tables(TP_Context *tp, if (has_guard_flags) { LNK_SymbolArray symbol_arr = lnk_symbol_array_from_list(scratch.arena, obj->symbol_list); if (guard_flags & LNK_Guard_Cf) { - LNK_ChunkList gfids_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gfids"), str8_zero(), 1); - for (LNK_ChunkNode *node = gfids_list.first; node != 0; node = node->next) { - Assert(node->data->type == LNK_Chunk_Leaf); - lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], node->data->u.leaf, symbol_arr); + String8List gfids_list = lnk_collect_obj_chunks(scratch.arena, obj, str8_lit(".gfids"), str8_zero(), 1); + for (String8Node *node = gfids_list.first; node != 0; node = node->next) { + lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], node->string, symbol_arr); } - LNK_ChunkList giats_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".giats"), str8_zero(), 1); - for (LNK_ChunkNode *node = giats_list.first; node != 0; node = node->next) { - Assert(node->data->type == LNK_Chunk_Leaf); - lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_IATS], node->data->u.leaf, symbol_arr); + String8List giats_list = lnk_collect_obj_chunks(scratch.arena, obj, str8_lit(".giats"), str8_zero(), 1); + for (String8Node *node = giats_list.first; node != 0; node = node->next) { + lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_IATS], node->string, symbol_arr); } } if (guard_flags & LNK_Guard_LongJmp) { - LNK_ChunkList gljmp_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gljmp"), str8_zero(), 1); - for (LNK_ChunkNode *node = gljmp_list.first; node != 0; node = node->next) { - Assert(node->data->type == LNK_Chunk_Leaf); - lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_LJMP], node->data->u.leaf, symbol_arr); + String8List gljmp_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gljmp"), str8_zero(), 1); + for (String8Node *node = gljmp_list.first; node != 0; node = node->next) { + lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_LJMP], node->string, symbol_arr); } } if (guard_flags & LNK_Guard_EhCont) { - LNK_ChunkList gehcont_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gehcont"), str8_zero(), 1); - for (LNK_ChunkNode *node = gehcont_list.first; node != 0; node = node->next) { - Assert(node->data->type == LNK_Chunk_Leaf); - lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_EHCONT], node->data->u.leaf, symbol_arr); + String8List gehcont_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gehcont"), str8_zero(), 1); + for (String8Node *node = gehcont_list.first; node != 0; node = node->next) { + lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_EHCONT], node->string, symbol_arr); } } } else { + // TODO: loop over COFF relocs + NotImplemented; +#if 0 // use relocation data in code sections to get function symbols for (U64 isect = 0; isect < obj->sect_count; ++isect) { LNK_Chunk *chunk = obj->chunk_arr[isect]; @@ -1140,11 +1306,12 @@ lnk_build_guard_tables(TP_Context *tp, lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], symbol); } } +#endif } } // entry point - LNK_Symbol *entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, entry_point_name); + LNK_Symbol *entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, entry_point_name); lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], entry_point_symbol); // push exports @@ -1163,9 +1330,9 @@ lnk_build_guard_tables(TP_Context *tp, NotImplemented; #if 0 // push thunks - LNK_SymbolScopeIndex scope_array[] = { LNK_SymbolScopeIndex_Defined, LNK_SymbolScopeIndex_Internal }; + LNK_SymbolScope scope_array[] = { LNK_SymbolScope_Defined, LNK_SymbolScope_Internal }; for (U64 iscope = 0; iscope < ArrayCount(scope_array); ++iscope) { - LNK_SymbolScopeIndex scope = scope_array[iscope]; + LNK_SymbolScope scope = scope_array[iscope]; for (U64 ibucket = 0; ibucket < symtab->bucket_count[scope]; ++ibucket) { for (LNK_SymbolNode *symbol_node = symtab->buckets[scope][ibucket].first; symbol_node != NULL; @@ -1225,7 +1392,6 @@ lnk_build_guard_tables(TP_Context *tp, }; for (U64 i = 0; i < ArrayCount(sect_layout); ++i) { LNK_Section *sect = lnk_section_table_push(sectab, str8_cstring(sect_layout[i].name), sect_layout[i].flags); - lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(sect_layout[i].symbol), LNK_DefinedSymbolVisibility_Internal, 0, sect->root, 0, 0, 0); } // TODO: emit table for SEH on X86 @@ -1234,10 +1400,10 @@ lnk_build_guard_tables(TP_Context *tp, lnk_not_implemented("__safe_se_handler_count"); } - LNK_Symbol *gfids_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_GFIDS_SYMBOL_NAME)); - LNK_Symbol *giats_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_GIATS_SYMBOL_NAME)); - LNK_Symbol *gljmp_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_GLJMP_SYMBOL_NAME)); - LNK_Symbol *gehcont_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_GEHCONT_SYMBOL_NAME)); + LNK_Symbol *gfids_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GFIDS_SYMBOL_NAME)); + LNK_Symbol *giats_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GIATS_SYMBOL_NAME)); + LNK_Symbol *gljmp_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GLJMP_SYMBOL_NAME)); + LNK_Symbol *gehcont_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GEHCONT_SYMBOL_NAME)); LNK_Section *gfids_sect = lnk_section_table_search_id(sectab, gfids_symbol->u.defined.u.chunk->ref.sect_id); LNK_Section *giats_sect = lnk_section_table_search_id(sectab, giats_symbol->u.defined.u.chunk->ref.sect_id); @@ -1270,15 +1436,15 @@ lnk_build_guard_tables(TP_Context *tp, lnk_section_push_chunk_data(gljmp_sect, gljmp_array_chunk, gljmp_data, str8_zero()); lnk_section_push_chunk_data(gehcont_sect, gehcont_array_chunk, gehcont_data, str8_zero()); - LNK_Symbol *gflags_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_FLAGS_SYMBOL_NAME)); - LNK_Symbol *gfids_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_FIDS_TABLE_SYMBOL_NAME)); - LNK_Symbol *gfids_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_FIDS_COUNT_SYMBOL_NAME)); - LNK_Symbol *giats_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_IAT_TABLE_SYMBOL_NAME)); - LNK_Symbol *giats_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_IAT_COUNT_SYMBOL_NAME)); - LNK_Symbol *gljmp_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_LONGJMP_TABLE_SYMBOL_NAME)); - LNK_Symbol *gljmp_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_LONGJMP_COUNT_SYMBOL_NAME)); - LNK_Symbol *gehcont_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_EHCONT_TABLE_SYMBOL_NAME)); - LNK_Symbol *gehcont_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, str8_lit(LNK_GUARD_EHCONT_COUNT_SYMBOL_NAME)); + LNK_Symbol *gflags_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_FLAGS_SYMBOL_NAME)); + LNK_Symbol *gfids_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_FIDS_TABLE_SYMBOL_NAME)); + LNK_Symbol *gfids_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_FIDS_COUNT_SYMBOL_NAME)); + LNK_Symbol *giats_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_IAT_TABLE_SYMBOL_NAME)); + LNK_Symbol *giats_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_IAT_COUNT_SYMBOL_NAME)); + LNK_Symbol *gljmp_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_LONGJMP_TABLE_SYMBOL_NAME)); + LNK_Symbol *gljmp_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_LONGJMP_COUNT_SYMBOL_NAME)); + LNK_Symbol *gehcont_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_EHCONT_TABLE_SYMBOL_NAME)); + LNK_Symbol *gehcont_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(LNK_GUARD_EHCONT_COUNT_SYMBOL_NAME)); LNK_DefinedSymbol *gflags_def = &gflags_symbol->u.defined; LNK_DefinedSymbol *gfids_table_def = &gfids_table_symbol->u.defined; @@ -1350,108 +1516,93 @@ lnk_build_guard_tables(TP_Context *tp, scratch_end(scratch); ProfEnd(); -} - -internal void -lnk_emit_base_reloc_info(Arena *arena, - LNK_Section **sect_id_map, - B32 is_large_addr_aware, - U64 page_size, - HashTable *page_ht, - LNK_BaseRelocPageList *page_list, - LNK_Reloc *reloc) -{ - B32 is_addr = (reloc->type == LNK_Reloc_ADDR_64 || reloc->type == LNK_Reloc_ADDR_32); - if (is_addr) { - U64 reloc_voff = lnk_virt_off_from_reloc(sect_id_map, reloc); - U64 page_voff = AlignDownPow2(reloc_voff, page_size); - - LNK_BaseRelocPageNode *page; - { - KeyValuePair *is_page_present = hash_table_search_u64(page_ht, page_voff); - if (is_page_present) { - page = is_page_present->value_raw; - } else { - // fill out page - page = push_array(arena, LNK_BaseRelocPageNode, 1); - page->v.voff = page_voff; - - // push page - SLLQueuePush(page_list->first, page_list->last, page); - page_list->count += 1; - - // register page voff - hash_table_push_u64_raw(arena, page_ht, page_voff, page); - } - } - - if (reloc->type == LNK_Reloc_ADDR_32) { - if (is_large_addr_aware) { - lnk_error(LNK_Error_LargeAddrAwareRequired, "found out of range ADDR32 relocation for '%S', link with /LARGEADDRESSAWARE:NO", reloc->symbol->name); - } else { - u64_list_push(arena, &page->v.entries_addr32, reloc_voff); - } - } else if (reloc->type == LNK_Reloc_ADDR_64) { - u64_list_push(arena, &page->v.entries_addr64, reloc_voff); - } - } -} - -internal -THREAD_POOL_TASK_FUNC(lnk_emit_base_relocs_from_reloc_array_task) -{ - ProfBeginFunction(); - LNK_BaseRelocTask task = *(LNK_BaseRelocTask*)raw_task; - Rng1U64 range = task.range_arr[task_id]; - for (U64 reloc_idx = range.min; reloc_idx < range.max; reloc_idx += 1) { - LNK_Reloc *reloc = task.reloc_arr[reloc_idx]; - lnk_emit_base_reloc_info(arena, task.sect_id_map, task.is_large_addr_aware, task.page_size, task.page_ht_arr[task_id], &task.list_arr[task_id], reloc); - } - ProfEnd(); +#endif } internal THREAD_POOL_TASK_FUNC(lnk_emit_base_relocs_from_objs_task) { ProfBeginFunction(); - LNK_ObjBaseRelocTask task = *(LNK_ObjBaseRelocTask *)raw_task; - Rng1U64 range = task.ranges[task_id]; + + LNK_ObjBaseRelocTask *task = raw_task; + Rng1U64 range = task->ranges[task_id]; + + HashTable *page_ht = task->page_ht_arr[task_id]; + LNK_BaseRelocPageList *page_list = &task->list_arr[task_id]; + for (U64 obj_idx = range.min; obj_idx < range.max; ++obj_idx) { - LNK_Obj *obj = task.obj_arr[obj_idx]; - for (U64 sect_idx = 0; sect_idx < obj->sect_count; sect_idx += 1) { - B32 is_live = !lnk_chunk_is_discarded(obj->chunk_arr[sect_idx]); - if (is_live) { - LNK_RelocList reloc_list = obj->sect_reloc_list_arr[sect_idx]; - for (LNK_Reloc *reloc = reloc_list.first; reloc != 0; reloc = reloc->next) { - lnk_emit_base_reloc_info(arena, task.sect_id_map, task.is_large_addr_aware, task.page_size, task.page_ht_arr[task_id], &task.list_arr[task_id], reloc); + LNK_Obj *obj = task->obj_arr[obj_idx]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + + if (sect_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + + COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, sect_header); + COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); + + for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { + COFF_Reloc *r = &relocs[reloc_idx]; + + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + B32 is_symbol_address = symbol_interp == COFF_SymbolValueInterp_Regular; + + if (is_symbol_address) { + B32 is_addr32 = 0, is_addr64 = 0; + switch (obj->header.machine) { + case COFF_MachineType_Unknown: {} break; + case COFF_MachineType_X64: { + is_addr32 = r->type == COFF_Reloc_X64_Addr32; + is_addr64 = r->type == COFF_Reloc_X64_Addr64; + } break; + default: { NotImplemented; } break; + } + + if (is_addr32 || is_addr64) { + U64 reloc_voff = sect_header->voff + r->apply_off; + U64 page_voff = AlignDownPow2(reloc_voff, task->page_size); + + LNK_BaseRelocPageNode *page; + { + KeyValuePair *is_page_present = hash_table_search_u64(page_ht, page_voff); + if (is_page_present) { + page = is_page_present->value_raw; + } else { + // fill out page + page = push_array(arena, LNK_BaseRelocPageNode, 1); + page->v.voff = page_voff; + + // push page + SLLQueuePush(page_list->first, page_list->last, page); + page_list->count += 1; + + // register page voff + hash_table_push_u64_raw(arena, page_ht, page_voff, page); + } + } + + if (is_addr32) { + if (task->is_large_addr_aware) { + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); + lnk_error_obj(LNK_Error_LargeAddrAwareRequired, obj, "found out of range ADDR32 relocation for '%S', link with /LARGEADDRESSAWARE:NO", symbol.name); + } else { + u64_list_push(arena, &page->v.entries_addr32, reloc_voff); + } + } else { + u64_list_push(arena, &page->v.entries_addr64, reloc_voff); + } + } } } } } + ProfEnd(); } -internal LNK_BaseRelocPageArray -lnk_base_reloc_page_array_from_list(Arena* arena, LNK_BaseRelocPageList list) -{ - LNK_BaseRelocPageArray result = {0}; - result.count = 0; - result.v = push_array_no_zero(arena, LNK_BaseRelocPage, list.count); - for (LNK_BaseRelocPageNode* n = list.first; n != 0; n = n->next) { - result.v[result.count++] = n->v; - } - Assert(result.count == list.count); - return result; -} - -int -lnk_base_reloc_page_is_before(void *raw_a, void *raw_b) -{ - LNK_BaseRelocPage* a = raw_a; - LNK_BaseRelocPage* b = raw_b; - return a->voff < b->voff; -} - int lnk_base_reloc_page_compar(const void *raw_a, const void *raw_b) { @@ -1460,118 +1611,93 @@ lnk_base_reloc_page_compar(const void *raw_a, const void *raw_b) return u64_compar(&a->voff, &b->voff); } -internal void -lnk_base_reloc_page_array_sort(LNK_BaseRelocPageArray arr) +int +lnk_base_reloc_page_is_before(void *raw_a, void *raw_b) { - ProfBeginFunction(); - //radsort(arr.v, arr.count, lnk_base_reloc_page_is_before); - qsort(arr.v, arr.count, sizeof(arr.v[0]), lnk_base_reloc_page_compar); - ProfEnd(); + LNK_BaseRelocPage* a = raw_a; + LNK_BaseRelocPage* b = raw_b; + return a->voff < b->voff; } -internal void -lnk_build_base_relocs(TP_Context *tp, - TP_Arena *tp_arena, - LNK_SectionTable *sectab, - LNK_SymbolTable *symtab, - COFF_MachineType machine, - U64 page_size, - PE_ImageFileCharacteristics file_chars, - LNK_ObjList obj_list) +internal String8List +lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, U64 objs_count, LNK_Obj **objs) { ProfBeginFunction(); + + Arena *arena = tp_arena->v[0]; + Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); + tp_arena->v[0] = scratch.arena; + TP_Temp tp_temp = tp_temp_begin(tp_arena); - TP_Temp temp = tp_temp_begin(tp_arena); - - lnk_section_table_build_data(tp, sectab, machine); - lnk_section_table_assign_virtual_offsets(sectab); - - LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(tp_arena->v[0], sectab); - - LNK_BaseRelocPageList *page_list_arr = push_array(tp_arena->v[0], LNK_BaseRelocPageList, tp->worker_count); - HashTable **page_ht_arr = push_array_no_zero(tp_arena->v[0], HashTable *, tp->worker_count); - for (U64 i = 0; i < tp->worker_count; ++i) { - page_ht_arr[i] = hash_table_init(tp_arena->v[0], 1024); - } - - // emit pages from relocs defined in section table - ProfBegin("Emit Relocs From Section Table"); - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != 0; sect_node = sect_node->next) { - LNK_BaseRelocTask task = {0}; - task.page_size = page_size; - task.sect_id_map = sect_id_map; - task.list_arr = page_list_arr; - task.page_ht_arr = page_ht_arr; - task.reloc_arr = lnk_reloc_array_from_list(tp_arena->v[0], sect_node->data.reloc_list); - task.range_arr = tp_divide_work(tp_arena->v[0], sect_node->data.reloc_list.count, tp->worker_count); - task.is_large_addr_aware = !!(file_chars & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE); - tp_for_parallel(tp, tp_arena, tp->worker_count, lnk_emit_base_relocs_from_reloc_array_task, &task); - } - ProfEnd(); - - // emit pages from relocs defined in objs - ProfBegin("Emit Relocs From Objs"); + LNK_BaseRelocPageArray page_arr; { - LNK_ObjBaseRelocTask task = {0}; - task.ranges = tp_divide_work(tp_arena->v[0], obj_list.count, tp->worker_count); - task.page_size = page_size; - task.sect_id_map = sect_id_map; - task.page_ht_arr = page_ht_arr; - task.list_arr = page_list_arr; - task.obj_arr = lnk_obj_arr_from_list(tp_arena->v[0], obj_list); - task.is_large_addr_aware = !!(file_chars & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE); - tp_for_parallel(tp, tp_arena, tp->worker_count, lnk_emit_base_relocs_from_objs_task, &task); - } - ProfEnd(); - - // merge page lists - - ProfBegin("Merge Worker Page Lists"); - - HashTable *main_ht = page_ht_arr[0]; - LNK_BaseRelocPageList *main_page_list = &page_list_arr[0]; - - for (U64 list_idx = 1; list_idx < tp->worker_count; ++list_idx) { - LNK_BaseRelocPageList src = page_list_arr[list_idx]; - - for (LNK_BaseRelocPageNode *src_page = src.first, *src_next; src_page != 0; src_page = src_next) { - src_next = src_page->next; - - KeyValuePair *is_page_present = hash_table_search_u64(main_ht, src_page->v.voff); - if (is_page_present) { - // page exists concat voffs - LNK_BaseRelocPageNode *page = is_page_present->value_raw; - Assert(page != src_page); - u64_list_concat_in_place(&page->v.entries_addr32, &src_page->v.entries_addr32); - u64_list_concat_in_place(&page->v.entries_addr64, &src_page->v.entries_addr64); - } else { - // push page to main list - SLLQueuePush(main_page_list->first, main_page_list->last, src_page); - main_page_list->count += 1; - - // store lookup voff - hash_table_push_u64_raw(tp_arena->v[0], main_ht, src_page->v.voff, src_page); - } + LNK_BaseRelocPageList *page_list_arr = push_array(scratch.arena, LNK_BaseRelocPageList, tp->worker_count); + HashTable **page_ht_arr = push_array_no_zero(scratch.arena, HashTable *, tp->worker_count); + for (U64 i = 0; i < tp->worker_count; ++i) { + page_ht_arr[i] = hash_table_init(scratch.arena, 1024); } - } - - ProfEnd(); - - if (main_page_list->count > 0) { - LNK_Section *base_reloc_sect = lnk_section_table_push(sectab, str8_lit(".reloc"), LNK_RELOC_SECTION_FLAGS); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_BASE_RELOC_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, base_reloc_sect->root, 0, 0, 0); - + + { + ProfBegin("Emit Relocs From Objs"); + LNK_ObjBaseRelocTask task = {0}; + task.ranges = tp_divide_work(scratch.arena, objs_count, tp->worker_count); + task.page_size = config->page_size; + task.page_ht_arr = page_ht_arr; + task.list_arr = page_list_arr; + task.obj_arr = objs; + task.is_large_addr_aware = !!(config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE); + tp_for_parallel(tp, tp_arena, tp->worker_count, lnk_emit_base_relocs_from_objs_task, &task); + ProfEnd(); + } + + LNK_BaseRelocPageList *main_page_list = &page_list_arr[0]; + { + ProfBegin("Merge Worker Page Lists"); + HashTable *main_ht = page_ht_arr[0]; + for (U64 list_idx = 1; list_idx < tp->worker_count; ++list_idx) { + LNK_BaseRelocPageList src = page_list_arr[list_idx]; + + for (LNK_BaseRelocPageNode *src_page = src.first, *src_next; src_page != 0; src_page = src_next) { + src_next = src_page->next; + + KeyValuePair *is_page_present = hash_table_search_u64(main_ht, src_page->v.voff); + if (is_page_present) { + // page exists concat voffs + LNK_BaseRelocPageNode *page = is_page_present->value_raw; + Assert(page != src_page); + u64_list_concat_in_place(&page->v.entries_addr32, &src_page->v.entries_addr32); + u64_list_concat_in_place(&page->v.entries_addr64, &src_page->v.entries_addr64); + } else { + // push page to main list + SLLQueuePush(main_page_list->first, main_page_list->last, src_page); + main_page_list->count += 1; + + // store lookup voff + hash_table_push_u64_raw(scratch.arena, main_ht, src_page->v.voff, src_page); + } + } + } + ProfEnd(); + } + ProfBegin("Page List -> Array"); - LNK_BaseRelocPageArray page_arr = lnk_base_reloc_page_array_from_list(base_reloc_sect->arena, *main_page_list); + page_arr.count = 0; + page_arr.v = push_array_no_zero(scratch.arena, LNK_BaseRelocPage, main_page_list->count); + for (LNK_BaseRelocPageNode* n = main_page_list->first; n != 0; n = n->next) { + page_arr.v[page_arr.count++] = n->v; + } ProfEnd(); ProfBegin("Sort Pages on VOFF"); - lnk_base_reloc_page_array_sort(page_arr); + //radsort(page_arr.v, page_arr.count, lnk_base_reloc_page_is_before); + qsort(page_arr.v, page_arr.count, sizeof(page_arr.v[0]), lnk_base_reloc_page_compar); ProfEnd(); - - HashTable *voff_ht = hash_table_init(tp_arena->v[0], page_size); - + } + + String8List result = {0}; + if (page_arr.count) { ProfBegin("Serialize Pages"); + HashTable *voff_ht = hash_table_init(scratch.arena, config->page_size); for (U64 page_idx = 0; page_idx < page_arr.count; ++page_idx) { LNK_BaseRelocPage *page = &page_arr.v[page_idx]; @@ -1582,7 +1708,7 @@ lnk_build_base_relocs(TP_Context *tp, // push buffer U64 buf_align = sizeof(U32); U64 buf_size = AlignPow2(sizeof(U32)*2 + sizeof(U16)*total_entry_count, buf_align); - U8 *buf = push_array_no_zero(base_reloc_sect->arena, U8, buf_size); + U8 *buf = push_array_no_zero(arena, U8, buf_size); // setup pointers into buffer U32 *page_voff_ptr = (U32*)buf; @@ -1596,11 +1722,11 @@ lnk_build_base_relocs(TP_Context *tp, if (hash_table_search_u64(voff_ht, i->data)) { continue; } - hash_table_push_u64_u64(tp_arena->v[0], voff_ht, i->data, 0); + hash_table_push_u64_u64(scratch.arena, voff_ht, i->data, 0); // write entry U64 rel_off = i->data - page->voff; - Assert(rel_off <= page_size); + Assert(rel_off <= config->page_size); *reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_HIGHLOW, rel_off); } @@ -1610,11 +1736,11 @@ lnk_build_base_relocs(TP_Context *tp, if (hash_table_search_u64(voff_ht, i->data)) { continue; } - hash_table_push_u64_u64(tp_arena->v[0], voff_ht, i->data, 0); + hash_table_push_u64_u64(scratch.arena, voff_ht, i->data, 0); // write entry U64 rel_off = i->data - page->voff; - Assert(rel_off <= page_size); + Assert(rel_off <= config->page_size); *reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_DIR64, rel_off); } @@ -1631,404 +1757,1324 @@ lnk_build_base_relocs(TP_Context *tp, *page_voff_ptr = safe_cast_u32(page->voff); *block_size_ptr = safe_cast_u32(block_size); Assert(*block_size_ptr <= buf_size); + + // push page + str8_list_push(arena, &result, str8(buf, block_size)); - // push page chunk - LNK_Chunk *page_chunk = lnk_section_push_chunk_raw(base_reloc_sect, base_reloc_sect->root, buf, block_size, str8_zero()); - lnk_chunk_set_debugf(base_reloc_sect->arena, page_chunk, "Base Reloc Page (VirtOff: %#x Size: %#x, Pads: %#x)", page->voff, block_size, pad_reloc_count); - - // purge voffs for next run + // purge voffs for next page hash_table_purge(voff_ht); } ProfEnd(); } - tp_temp_end(temp); + tp_temp_end(tp_temp); // scratch is cleared here + tp_arena->v[0] = arena; + ProfEnd(); + return result; } -internal LNK_Chunk * -lnk_build_dos_header(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent_chunk) -{ - U32 dos_stub_size = sizeof(PE_DosHeader) + pe_dos_program.size; - - PE_DosHeader *dos_header = push_array(header_sect->arena, PE_DosHeader, 1); - dos_header->magic = PE_DOS_MAGIC; - dos_header->last_page_size = dos_stub_size % 512; - dos_header->page_count = CeilIntegerDiv(dos_stub_size, 512); - dos_header->paragraph_header_size = sizeof(PE_DosHeader) / 16; - dos_header->min_paragraph = 0; - dos_header->max_paragraph = 0; - dos_header->init_ss = 0; - dos_header->init_sp = 0; - dos_header->checksum = 0; - dos_header->init_ip = 0xFFFF; - dos_header->init_cs = 0; - dos_header->reloc_table_file_off = sizeof(PE_DosHeader); - dos_header->overlay_number = 0; - MemoryZeroStruct(dos_header->reserved); - dos_header->oem_id = 0; - dos_header->oem_info = 0; - MemoryZeroArray(dos_header->reserved2); - dos_header->coff_file_offset = 0; // :coff_file_offset - - LNK_Chunk *dos_chunk = lnk_section_push_chunk_list(header_sect, parent_chunk, str8_zero()); - LNK_Chunk *dos_header_chunk = lnk_section_push_chunk_raw(header_sect, dos_chunk, dos_header, sizeof(*dos_header), str8_zero()); - LNK_Chunk *dos_program_chunk = lnk_section_push_chunk_data(header_sect, dos_chunk, pe_dos_program, str8_zero()); - lnk_chunk_set_debugf(header_sect->arena, dos_chunk, "DOS Header & Stub"); - lnk_chunk_set_debugf(header_sect->arena, dos_header_chunk, LNK_DOS_HEADER_SYMBOL_NAME); - lnk_chunk_set_debugf(header_sect->arena, dos_program_chunk, LNK_DOS_PROGRAM_SYMBOL_NAME); - - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_DOS_HEADER_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, dos_header_chunk, 0, 0, 0); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_DOS_PROGRAM_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, dos_program_chunk, 0, 0, 0); - - // :coff_file_offset - lnk_section_push_reloc_undefined(header_sect, dos_header_chunk, LNK_Reloc_FILE_OFF_32, OffsetOf(PE_DosHeader, coff_file_offset), str8_lit(LNK_NT_HEADERS_SYMBOL_NAME), LNK_SymbolScopeFlag_Internal); - - return dos_chunk; -} - -internal LNK_Chunk * -lnk_build_pe_magic(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent) -{ - U32 *pe_magic = push_array_no_zero(header_sect->arena, U32, 1); - *pe_magic = PE_MAGIC; - - LNK_Chunk *pe_magic_chunk = lnk_section_push_chunk_raw(header_sect, parent, pe_magic, sizeof(*pe_magic), str8_zero()); - lnk_chunk_set_debugf(header_sect->arena, pe_magic_chunk, LNK_PE_MAGIC_SYMBOL_NAME); - - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_PE_MAGIC_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, pe_magic_chunk, 0, 0, 0); - - return pe_magic_chunk; -} - -internal LNK_Chunk * -lnk_build_coff_file_header(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent, - COFF_MachineType machine, COFF_TimeStamp time_stamp, PE_ImageFileCharacteristics file_characteristics) -{ - COFF_FileHeader *file_header = push_array_no_zero(header_sect->arena, COFF_FileHeader, 1); - file_header->machine = machine; - file_header->time_stamp = time_stamp; - file_header->symbol_table_foff = 0; - file_header->symbol_count = 0; - file_header->section_count = 0; // :section_count - file_header->optional_header_size = 0; // :optional_header_size - file_header->flags = file_characteristics; - - LNK_Chunk *file_header_chunk = lnk_section_push_chunk_raw(header_sect, parent, file_header, sizeof(*file_header), str8_zero()); - lnk_chunk_set_debugf(header_sect->arena, file_header_chunk, LNK_COFF_FILE_HEADER_SYMBOL_NAME); - - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_COFF_FILE_HEADER_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, file_header_chunk, 0, 0, 0); - - // :section_count - lnk_section_push_reloc_undefined(header_sect, file_header_chunk, LNK_Reloc_ADDR_16, OffsetOf(COFF_FileHeader, section_count), str8_lit(LNK_COFF_SECT_HEADER_COUNT_SYMBOL_NAME), LNK_SymbolScopeFlag_Internal); - - // :optional_header_size - lnk_section_push_reloc_undefined(header_sect, file_header_chunk, LNK_Reloc_CHUNK_SIZE_FILE_16, OffsetOf(COFF_FileHeader, optional_header_size), str8_lit(LNK_PE_OPT_HEADER_SYMBOL_NAME), LNK_SymbolScopeFlag_Internal); - lnk_section_push_reloc_undefined(header_sect, file_header_chunk, LNK_Reloc_CHUNK_SIZE_FILE_16, OffsetOf(COFF_FileHeader, optional_header_size), str8_lit(LNK_PE_DIRECTORY_ARRAY_SYMBOL_NAME), LNK_SymbolScopeFlag_Internal); - - return file_header_chunk; -} - -internal LNK_Chunk * -lnk_build_pe_optional_header_x64(LNK_SymbolTable *symtab, - LNK_Section *header_sect, - LNK_Chunk *parent, - COFF_MachineType machine, - U64 base_addr, - U64 sect_align, - U64 file_align, - Version linker_ver, - Version os_ver, - Version image_ver, - Version subsystem_ver, - PE_WindowsSubsystem subsystem, - PE_DllCharacteristics dll_characteristics, - U64 stack_reserve, - U64 stack_commit, - U64 heap_reserve, - U64 heap_commit, - String8 entry_point_name, - LNK_SectionArray sect_arr) -{ - PE_OptionalHeader32Plus *opt_header = push_array_no_zero(header_sect->arena, PE_OptionalHeader32Plus, 1); - opt_header->magic = PE_PE32PLUS_MAGIC; - opt_header->major_linker_version = linker_ver.major; - opt_header->minor_linker_version = linker_ver.minor; - opt_header->sizeof_code = 0; // :sizeof_code - opt_header->sizeof_inited_data = 0; // :sizeof_inited_data - opt_header->sizeof_uninited_data = 0; // :sizeof_uninited_data - opt_header->entry_point_va = 0; // :entry_point_va - opt_header->code_base = 0; // :code_base - opt_header->image_base = base_addr; - opt_header->section_alignment = sect_align; - opt_header->file_alignment = file_align; - opt_header->major_os_ver = os_ver.major; - opt_header->minor_os_ver = os_ver.minor; - opt_header->major_img_ver = image_ver.major; - opt_header->minor_img_ver = image_ver.minor; - opt_header->major_subsystem_ver = subsystem_ver.major; - opt_header->minor_subsystem_ver = subsystem_ver.minor; - opt_header->win32_version_value = 0; // MSVC writes zero - opt_header->sizeof_image = 0; // :sizeof_image - opt_header->sizeof_headers = 0; // :sizeof_headers - opt_header->check_sum = 0; // :check_sum - opt_header->subsystem = subsystem; - opt_header->dll_characteristics = dll_characteristics; - opt_header->sizeof_stack_reserve = stack_reserve; - opt_header->sizeof_stack_commit = stack_commit; - opt_header->sizeof_heap_reserve = heap_reserve; - opt_header->sizeof_heap_commit = heap_commit; - opt_header->loader_flags = 0; // for dynamic linker, always zero - opt_header->data_dir_count = 0; // :data_dir_count - - // push chunk - LNK_Chunk *opt_header_chunk = lnk_section_push_chunk_raw(header_sect, parent, opt_header, sizeof(*opt_header), str8_zero()); - lnk_chunk_set_debugf(header_sect->arena, opt_header_chunk, LNK_PE_OPT_HEADER_SYMBOL_NAME); - - // define optional header symbol - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_PE_OPT_HEADER_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, opt_header_chunk, 0, 0, 0); - - // :entry_point_va - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_OptionalHeader32Plus, entry_point_va), entry_point_name, LNK_SymbolScopeFlag_Main); - - // :code_base - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_OptionalHeader32Plus, code_base), str8_lit(LNK_TEXT_SYMBOL_NAME), LNK_SymbolScopeFlag_Internal); - - LNK_Section *last_sect = 0; - for (LNK_Section *sect = §_arr.v[0], *sect_opl = sect + sect_arr.count; sect < sect_opl; sect += 1) { - if (!sect->has_layout) { - continue; - } - // :sizeof_uninited_data - if (sect->flags & COFF_SectionFlag_CntUninitializedData) { - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_CHUNK_SIZE_VIRT_32, OffsetOf(PE_OptionalHeader32Plus, sizeof_uninited_data), sect->name, LNK_SymbolScopeFlag_Internal); - } - - // :sizeof_inited_data - if (sect->flags & COFF_SectionFlag_CntInitializedData) { - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_CHUNK_SIZE_FILE_32, OffsetOf(PE_OptionalHeader32Plus, sizeof_inited_data), sect->name, LNK_SymbolScopeFlag_Internal); - } - - // :sizeof_code - if (sect->flags & COFF_SectionFlag_CntCode) { - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_CHUNK_SIZE_FILE_32, OffsetOf(PE_OptionalHeader32Plus, sizeof_code), sect->name, LNK_SymbolScopeFlag_Internal); - } - - last_sect = sect; - } - - // :sizeof_image - if (last_sect) { - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_OptionalHeader32Plus, sizeof_image), last_sect->name, LNK_SymbolScopeFlag_Internal); - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_CHUNK_SIZE_VIRT_32, OffsetOf(PE_OptionalHeader32Plus, sizeof_image), last_sect->name, LNK_SymbolScopeFlag_Internal); - lnk_section_push_reloc(header_sect, opt_header_chunk, LNK_Reloc_VIRT_ALIGN_32, OffsetOf(PE_OptionalHeader32Plus, sizeof_image), &g_null_symbol); - } - - // :sizeof_headers - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_CHUNK_SIZE_FILE_32, OffsetOf(PE_OptionalHeader32Plus, sizeof_headers), str8_lit(LNK_WIN32_HEADER_SYMBOL_NAME), LNK_SymbolScopeFlag_Internal); - lnk_section_push_reloc(header_sect, opt_header_chunk, LNK_Reloc_FILE_ALIGN_32, OffsetOf(PE_OptionalHeader32Plus, sizeof_headers), &g_null_symbol); - - // :check_sum - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_PE_CHECKSUM_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, opt_header_chunk, OffsetOf(PE_OptionalHeader32Plus, check_sum), COFF_ComdatSelect_NoDuplicates, 0); - - // :data_dir_count - lnk_section_push_reloc_undefined(header_sect, opt_header_chunk, LNK_Reloc_ADDR_32, OffsetOf(PE_OptionalHeader32Plus, data_dir_count), str8_lit(LNK_PE_DIRECTORY_COUNT_SYMBOL_NAME), LNK_SymbolScopeFlag_Internal); - - return opt_header_chunk; -} - -internal LNK_Chunk * -lnk_build_pe_directories(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent) -{ - static struct { - char *name; - PE_DataDirectoryIndex index; - LNK_SymbolScopeFlags scope; - } directory_map[] = { - { LNK_LOAD_CONFIG_SYMBOL_NAME , PE_DataDirectoryIndex_LOAD_CONFIG , LNK_SymbolScopeFlag_Main }, - { LNK_PDATA_SYMBOL_NAME , PE_DataDirectoryIndex_EXCEPTIONS , LNK_SymbolScopeFlag_Internal }, - { LNK_EDATA_SYMBOL_NAME , PE_DataDirectoryIndex_EXPORT , LNK_SymbolScopeFlag_Internal }, - { LNK_BASE_RELOC_SYMBOL_NAME , PE_DataDirectoryIndex_BASE_RELOC , LNK_SymbolScopeFlag_Internal }, - { LNK_IMPORT_DLL_TABLE_SYMBOL_NAME , PE_DataDirectoryIndex_IMPORT , LNK_SymbolScopeFlag_Internal }, - { LNK_IMPORT_IAT_SYMBOL_NAME , PE_DataDirectoryIndex_IMPORT_ADDR , LNK_SymbolScopeFlag_Internal }, - { LNK_DELAYED_IMPORT_DLL_TABLE_SYMBOL_NAME, PE_DataDirectoryIndex_DELAY_IMPORT, LNK_SymbolScopeFlag_Internal }, - { LNK_TLS_SYMBOL_NAME , PE_DataDirectoryIndex_TLS , LNK_SymbolScopeFlag_Main }, - { LNK_DEBUG_DIR_SYMBOL_NAME , PE_DataDirectoryIndex_DEBUG , LNK_SymbolScopeFlag_Internal }, - { LNK_RSRC_SYMBOL_NAME , PE_DataDirectoryIndex_RESOURCES , LNK_SymbolScopeFlag_Internal }, - }; - - // init directory virtual coords from symbol names - U64 directory_count = PE_DataDirectoryIndex_COUNT; - PE_DataDirectory *directory_array = push_array(header_sect->arena, PE_DataDirectory, directory_count); - - LNK_Chunk *directory_array_chunk = lnk_section_push_chunk_raw(header_sect, parent, directory_array, sizeof(directory_array[0])*directory_count, str8_zero()); - lnk_chunk_set_debugf(header_sect->arena, directory_array_chunk, LNK_PE_DIRECTORY_ARRAY_SYMBOL_NAME); - - // define PE directory symbols - LNK_Symbol *directory_array_symbol = lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_PE_DIRECTORY_ARRAY_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, directory_array_chunk, 0, 0, 0); - LNK_Symbol *directory_count_symbol = lnk_symbol_table_push_defined_va(symtab, str8_lit(LNK_PE_DIRECTORY_COUNT_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, directory_count); - - for (U64 dir_idx = 0; dir_idx < ArrayCount(directory_map); dir_idx += 1) { - String8 symbol_name = str8_cstring(directory_map[dir_idx].name); - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, directory_map[dir_idx].scope, symbol_name); - if (symbol) { - U64 virt_off_field_off = sizeof(PE_DataDirectory) * directory_map[dir_idx].index + OffsetOf(PE_DataDirectory, virt_off); - U64 virt_size_field_off = sizeof(PE_DataDirectory) * directory_map[dir_idx].index + OffsetOf(PE_DataDirectory, virt_size); - lnk_section_push_reloc(header_sect, directory_array_chunk, LNK_Reloc_VIRT_OFF_32, virt_off_field_off, symbol); - lnk_section_push_reloc(header_sect, directory_array_chunk, LNK_Reloc_CHUNK_SIZE_VIRT_32, virt_size_field_off, symbol); - } - } - - return directory_array_chunk; -} - -internal LNK_Chunk * -lnk_build_coff_section_table(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent_chunk, LNK_SectionArray sect_arr) -{ - // register section symbols - for (LNK_Section *sect = §_arr.v[0], *sect_opl = sect + sect_arr.count; - sect < sect_opl; - sect += 1) { - // was section symbol defined elsewhere? - LNK_Symbol *test_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, sect->name); - Assert(!test_symbol); (void)test_symbol; - - // define symbol - String8 sect_symbol_name = push_str8_copy(symtab->arena->v[0], sect->name); - lnk_symbol_table_push_defined_chunk(symtab, sect_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, sect->root, 0, 0, 0); - } - - // push COFF header array chunk - LNK_Chunk *coff_section_array_chunk = lnk_section_push_chunk_list(header_sect, parent_chunk, str8_zero()); - lnk_chunk_set_debugf(header_sect->arena, coff_section_array_chunk, LNK_COFF_SECT_HEADER_ARRAY_SYMBOL_NAME); - - // define symbol for COFF header array - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_COFF_SECT_HEADER_ARRAY_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, coff_section_array_chunk, 0, 0, 0); - - // push headers - for (LNK_Section *sect = §_arr.v[0], *sect_opl = sect + sect_arr.count; sect < sect_opl; sect += 1) { - if (!sect->emit_header) { - continue; - } - if (!sect->has_layout) { - continue; - } - COFF_SectionHeader *coff_section = push_array_no_zero(header_sect->arena, COFF_SectionHeader, 1); - - // TODO: for objs we can store long name in string table and write here /offset - if (sect->name.size > sizeof(coff_section->name)) { - lnk_error(LNK_Warning_LongSectionName, "not enough space in COFF section header to store entire name \"%S\"", sect->name); - } - - MemorySet(&coff_section->name[0], 0, sizeof(coff_section->name)); - MemoryCopy(&coff_section->name[0], sect->name.str, Min(sect->name.size, sizeof(coff_section->name))); - coff_section->vsize = 0; // :vsize - coff_section->voff = 0; // :voff - coff_section->fsize = 0; // :fsize - coff_section->foff = 0; // :foff - coff_section->relocs_foff = 0; // :relocs_foff - coff_section->lines_foff = 0; // obsolete - coff_section->reloc_count = 0; // :reloc_count - coff_section->line_count = 0; // obsolete - coff_section->flags = sect->flags; - - // push chunk - LNK_Chunk *coff_section_chunk = lnk_section_push_chunk_raw(header_sect, coff_section_array_chunk, coff_section, sizeof(*coff_section), str8_zero()); - lnk_chunk_set_debugf(header_sect->arena, coff_section_chunk, "COFF_SECTION_HEADER %S", sect->name); - - // :vsize - lnk_section_push_reloc_undefined(header_sect, coff_section_chunk, LNK_Reloc_CHUNK_SIZE_VIRT_32, OffsetOf(COFF_SectionHeader, vsize), sect->name, LNK_SymbolScopeFlag_Internal); - // :voff - lnk_section_push_reloc_undefined(header_sect, coff_section_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(COFF_SectionHeader, voff), sect->name, LNK_SymbolScopeFlag_Internal); - - if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { - // :fsize - lnk_section_push_reloc_undefined(header_sect, coff_section_chunk, LNK_Reloc_CHUNK_SIZE_FILE_32, OffsetOf(COFF_SectionHeader, fsize), sect->name, LNK_SymbolScopeFlag_Internal); - // :foff - lnk_section_push_reloc_undefined(header_sect, coff_section_chunk, LNK_Reloc_FILE_OFF_32, OffsetOf(COFF_SectionHeader, foff), sect->name, LNK_SymbolScopeFlag_Internal); - } - - // TODO: :reloc_off - // TODO: :reloc_count - } - - // push symbol for section header count - U64 header_count = coff_section_array_chunk->u.list->count; - lnk_symbol_table_push_defined_va(symtab, str8_lit(LNK_COFF_SECT_HEADER_COUNT_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, header_count); - - return coff_section_array_chunk; -} - -internal LNK_Chunk * -lnk_build_win32_image_header(LNK_SymbolTable *symtab, - LNK_Section *header_sect, - LNK_Chunk *parent_chunk, - LNK_Config *config, - LNK_SectionArray sect_arr) +internal String8List +lnk_build_win32_image_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sects, U64 expected_image_header_size) { ProfBeginFunction(); - - // header sections must be written first - Assert(header_sect->id == 0); - - LNK_Chunk *win32_header_chunk = lnk_section_push_chunk_list(header_sect, parent_chunk , str8_zero() ); - LNK_Chunk *dos_chunk = lnk_section_push_chunk_list(header_sect, win32_header_chunk, str8_lit("a")); - LNK_Chunk *nt_chunk = lnk_section_push_chunk_list(header_sect, win32_header_chunk, str8_lit("b")); - LNK_Chunk *pe_magic_chunk = lnk_section_push_chunk_list(header_sect, nt_chunk , str8_lit("a")); - LNK_Chunk *coff_file_header_chunk = lnk_section_push_chunk_list(header_sect, nt_chunk , str8_lit("b")); - LNK_Chunk *pe_optional_chunk = lnk_section_push_chunk_list(header_sect, nt_chunk , str8_lit("c")); - LNK_Chunk *coff_sect_header_chunk = lnk_section_push_chunk_list(header_sect, nt_chunk , str8_lit("d")); - - lnk_chunk_set_debugf(header_sect->arena, win32_header_chunk , "Win32 Headers" ); - lnk_chunk_set_debugf(header_sect->arena, dos_chunk , "DOS Chunk" ); - lnk_chunk_set_debugf(header_sect->arena, nt_chunk , "NT Chunk" ); - lnk_chunk_set_debugf(header_sect->arena, pe_magic_chunk , "PE Magic Container" ); - lnk_chunk_set_debugf(header_sect->arena, coff_file_header_chunk, "COFF File Header Container" ); - lnk_chunk_set_debugf(header_sect->arena, pe_optional_chunk , "PE Optional Header Container" ); - lnk_chunk_set_debugf(header_sect->arena, coff_sect_header_chunk, "COFF Section Headers Container"); - - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_WIN32_HEADER_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, win32_header_chunk , 0, 0, 0); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_DOS_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, dos_chunk , 0, 0, 0); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_NT_HEADERS_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, nt_chunk , 0, 0, 0); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_PE_MAGIC_CONTAINER_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, pe_magic_chunk , 0, 0, 0); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_COFF_FILE_HEADER_CONTAINER_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, coff_file_header_chunk, 0, 0, 0); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_PE_OPT_HEADER_CONTAINER_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, pe_optional_chunk , 0, 0, 0); - lnk_symbol_table_push_defined_chunk(symtab, str8_lit(LNK_COFF_SECTION_HEADER_CONTAINER_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, coff_sect_header_chunk, 0, 0, 0); - - lnk_build_dos_header(symtab, header_sect, dos_chunk); - lnk_build_pe_magic(symtab, header_sect, pe_magic_chunk); - lnk_build_coff_file_header(symtab, header_sect, coff_file_header_chunk, config->machine, config->time_stamp, config->file_characteristics); - switch (config->machine) { - case COFF_MachineType_X64: { - lnk_build_pe_optional_header_x64(symtab, - header_sect, - pe_optional_chunk, - config->machine, - lnk_get_base_addr(config), - config->sect_align, - config->file_align, - config->link_ver, - config->os_ver, - config->image_ver, - config->subsystem_ver, - config->subsystem, - config->dll_characteristics, - config->stack_reserve, - config->stack_commit, - config->heap_reserve, - config->heap_commit, - config->entry_point_name, - sect_arr); - } break; - default: { - lnk_not_implemented("TODO: PE Optional Header for %S", coff_string_from_machine_type(config->machine)); - } break; + + String8List result = {0}; + + // + // DOS header + // + U32 dos_stub_size = sizeof(PE_DosHeader) + pe_dos_program.size; + { + PE_DosHeader *dos_header = push_array(arena, PE_DosHeader, 1); + dos_header->magic = PE_DOS_MAGIC; + dos_header->last_page_size = dos_stub_size % 512; + dos_header->page_count = CeilIntegerDiv(dos_stub_size, 512); + dos_header->paragraph_header_size = sizeof(PE_DosHeader) / 16; + dos_header->min_paragraph = 0; + dos_header->max_paragraph = 0; + dos_header->init_ss = 0; + dos_header->init_sp = 0; + dos_header->checksum = 0; + dos_header->init_ip = 0xFFFF; + dos_header->init_cs = 0; + dos_header->reloc_table_file_off = sizeof(PE_DosHeader); + dos_header->overlay_number = 0; + MemoryZeroStruct(dos_header->reserved); + dos_header->oem_id = 0; + dos_header->oem_info = 0; + MemoryZeroArray(dos_header->reserved2); + dos_header->coff_file_offset = dos_stub_size; + + str8_list_push(arena, &result, str8_struct(dos_header)); + str8_list_push(arena, &result, pe_dos_program); } - lnk_build_pe_directories(symtab, header_sect, pe_optional_chunk); - lnk_build_coff_section_table(symtab, header_sect, coff_sect_header_chunk, sect_arr); - + + // + // PE magic + // + U32 *pe_magic = push_array(arena, U32, 1); + *pe_magic = PE_MAGIC; + str8_list_push(arena, &result, str8_struct(pe_magic)); + + // + // determine PE optional header type + // + B32 has_pe_plus_header = pe_has_plus_header(config->machine); + + // + // COFF file header + // + { + COFF_FileHeader *file_header = push_array_no_zero(arena, COFF_FileHeader, 1); + file_header->machine = config->machine; + file_header->time_stamp = config->time_stamp; + file_header->symbol_table_foff = 0; + file_header->symbol_count = 0; + file_header->section_count = sects.count; + file_header->optional_header_size = (has_pe_plus_header ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32)) + (sizeof(PE_DataDirectory) * config->data_dir_count); + file_header->flags = config->file_characteristics; + str8_list_push(arena, &result, str8_struct(file_header)); + } + + // + // compute code/inited/uninited sizes + // + U64 code_base = 0; + U64 sizeof_code = 0; + U64 sizeof_inited_data = 0; + U64 sizeof_uninited_data = 0; + U64 sizeof_image = 0; + for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + LNK_Section *sect = sects.v[sect_idx]; + if ( ! sect->has_layout) { + continue; + } + if (code_base == 0 && sect->flags & COFF_SectionFlag_CntCode) { + code_base = sect->voff; + } + if (sect->flags & COFF_SectionFlag_CntUninitializedData) { + sizeof_uninited_data += sect->vsize; + } + if (sect->flags & COFF_SectionFlag_CntInitializedData) { + sizeof_inited_data += sect->fsize; + } + if (sect->flags & COFF_SectionFlag_CntCode) { + sizeof_code += sect->fsize; + } + sizeof_image = AlignPow2(Max(sizeof_image, sects.v[sect_idx]->voff + sects.v[sect_idx]->vsize), 4096); + } + + // + // compute image headers size + // + U64 sizeof_image_headers = 0; + sizeof_image_headers += dos_stub_size; + sizeof_image_headers += sizeof(COFF_FileHeader); + sizeof_image_headers += has_pe_plus_header ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32); + sizeof_image_headers += sizeof(PE_DataDirectory) * config->data_dir_count; + sizeof_image_headers = AlignPow2(sizeof_image_headers, config->file_align); + + // + // fill out PE optional header + // + U32 *entry_point_va; + U32 *check_sum; + if (has_pe_plus_header) { + PE_OptionalHeader32Plus *opt_header = push_array_no_zero(arena, PE_OptionalHeader32Plus, 1); + opt_header->magic = PE_PE32PLUS_MAGIC; + opt_header->major_linker_version = config->link_ver.major; + opt_header->minor_linker_version = config->link_ver.minor; + opt_header->sizeof_code = safe_cast_u32(sizeof_code); + opt_header->sizeof_inited_data = safe_cast_u32(sizeof_inited_data); + opt_header->sizeof_uninited_data = safe_cast_u32(sizeof_uninited_data); + opt_header->entry_point_va = 0; + opt_header->code_base = code_base; + opt_header->image_base = lnk_get_base_addr(config); + opt_header->section_alignment = config->sect_align; + opt_header->file_alignment = config->file_align; + opt_header->major_os_ver = config->os_ver.major; + opt_header->minor_os_ver = config->os_ver.minor; + opt_header->major_img_ver = config->image_ver.major; + opt_header->minor_img_ver = config->image_ver.minor; + opt_header->major_subsystem_ver = config->subsystem_ver.major; + opt_header->minor_subsystem_ver = config->subsystem_ver.minor; + opt_header->win32_version_value = 0; // MSVC writes zero + opt_header->sizeof_image = sizeof_image; + opt_header->sizeof_headers = safe_cast_u32(sizeof_image_headers); + opt_header->check_sum = 0; // :check_sum + opt_header->subsystem = config->subsystem; + opt_header->dll_characteristics = config->dll_characteristics; + opt_header->sizeof_stack_reserve = config->stack_reserve; + opt_header->sizeof_stack_commit = config->stack_commit; + opt_header->sizeof_heap_reserve = config->heap_reserve; + opt_header->sizeof_heap_commit = config->heap_commit; + opt_header->loader_flags = 0; // for dynamic linker, always zero + opt_header->data_dir_count = safe_cast_u32(config->data_dir_count); + + entry_point_va = &opt_header->entry_point_va; + check_sum = &opt_header->check_sum; + + str8_list_push(arena, &result, str8_struct(opt_header)); + } else { + NotImplemented; + } + + // + // PE directories + // + PE_DataDirectory *directory_array; + { + directory_array = push_array(arena, PE_DataDirectory, config->data_dir_count); + str8_list_push(arena, &result, str8_array(directory_array, config->data_dir_count)); + } + + // + // COFF section table + // + COFF_SectionHeader *coff_section_table = push_array(arena, COFF_SectionHeader, sects.count); + U64 coff_section_table_count = 0; + { + for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + LNK_Section *sect = sects.v[sect_idx]; + if (!sect->has_layout) { + continue; + } + + COFF_SectionHeader *coff_section = &coff_section_table[sect_idx]; + + // TODO: for objs we can store long name in string table and write here /offset + if (sect->name.size > sizeof(coff_section->name)) { + lnk_error(LNK_Warning_LongSectionName, "not enough space in COFF section header to store entire name \"%S\"", sect->name); + } + + MemorySet(&coff_section->name[0], 0, sizeof(coff_section->name)); + MemoryCopy(&coff_section->name[0], sect->name.str, Min(sect->name.size, sizeof(coff_section->name))); + coff_section->vsize = sect->vsize; + coff_section->voff = sect->voff; + coff_section->fsize = sect->fsize; + coff_section->foff = sect->foff; + coff_section->relocs_foff = 0; // not present in image + coff_section->lines_foff = 0; // obsolete + coff_section->reloc_count = 0; // not present in image + coff_section->line_count = 0; // obsolete + coff_section->flags = sect->flags; + + coff_section_table_count += 1; + } + + str8_list_push(arena, &result, str8_array(coff_section_table, coff_section_table_count)); + } + + // align image headers + { + U64 image_headers_align_size = AlignPadPow2(result.total_size, config->file_align); + U8 *image_headers_align = push_array(arena, U8, image_headers_align_size); + str8_list_push(arena, &result, str8(image_headers_align, image_headers_align_size)); + } + + // + // entry point + // + { + Temp scratch = scratch_begin(&arena, 1); + + COFF_SectionHeader **section_table = push_array(arena, COFF_SectionHeader *, coff_section_table_count + 1); + for (U64 i = 1; i <= coff_section_table_count; i += 1) { section_table[i] = &coff_section_table[i-1]; } + + LNK_Symbol *entry_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, config->entry_point_name); + if (entry_symbol) { + *entry_point_va = safe_cast_u32(lnk_virt_off_from_symbol(section_table, entry_symbol)); + } + + scratch_end(scratch); + } + + Assert(result.total_size == expected_image_header_size); ProfEnd(); - return win32_header_chunk; + return result; +} + +internal int +lnk_pdata_is_before_x8664(void *raw_a, void *raw_b) +{ + PE_IntelPdata *a = raw_a; + PE_IntelPdata *b = raw_b; + int is_before = a->voff_first < b->voff_first; + return is_before; +} + +internal String8 +lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, LNK_ObjList obj_list) +{ + Temp scratch = scratch_begin(arena->v, arena->count); + + // + // init section table + // + LNK_SectionTable *sectab = lnk_section_table_alloc(); + lnk_section_table_push(sectab, str8_lit(".text"), LNK_TEXT_SECTION_FLAGS); + lnk_section_table_push(sectab, str8_lit(".data"), LNK_DATA_SECTION_FLAGS); + lnk_section_table_push(sectab, str8_lit(".rdata"), LNK_RDATA_SECTION_FLAGS); + + // + // obj list -> array + // + U64 objs_count = obj_list.count; + LNK_Obj **objs = lnk_obj_arr_from_list(scratch.arena, obj_list); + + { + ProfBegin("Define And Count Sections"); + Temp temp = temp_begin(scratch.arena); + + HashTable **sect_defn_ht_arr = push_array(temp.arena, HashTable *, tp->worker_count); + for (U64 i = 0; i < tp->worker_count; i += 1) sect_defn_ht_arr[i] = hash_table_init(temp.arena, 128); + HashTable *sect_defn_ht = sect_defn_ht_arr[0]; + + ProfBegin("Gather Section Definitions"); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = objs[obj_idx]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + + U32 obj_features = lnk_obj_get_features (obj); + B32 discards_guards = (obj_features & MSCRT_FeatFlag_GUARD_CF) || + (obj_features & MSCRT_FeatFlag_GUARD_EH_CONT); + + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + + // discard section + if (sect_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + + // parse section name + String8 full_sect_name = coff_name_from_section_header(string_table, sect_header); + String8 sect_name, sect_sort_idx; + coff_parse_section_name(full_sect_name, §_name, §_sort_idx); + + // remove debug sections + if (str8_match(sect_name, str8_lit(".debug"), 0)) { + sect_header->flags |= COFF_SectionFlag_LnkRemove; + continue; + } + + // strip linker flags + COFF_SectionFlags sect_flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; + + // was section defined? + String8 sect_name_with_flags = lnk_make_name_with_flags(temp.arena, sect_name, sect_flags); + LNK_SectionDefinition *sect_defn = 0; + hash_table_search_string_raw(sect_defn_ht, sect_name_with_flags, §_defn); + if (sect_defn == 0) { + sect_defn = push_array(temp.arena, LNK_SectionDefinition, 1); + sect_defn->name = sect_name; + sect_defn->flags = sect_flags; + sect_defn->obj = obj; + sect_defn->obj_sect_idx = sect_idx; + hash_table_push_string_raw(temp.arena, sect_defn_ht, sect_name_with_flags, sect_defn); + } + + // acc contrib count + sect_defn->contribs_count += 1; + } + } + ProfEnd(); + + ProfBegin("Merge Section Definitions"); + HashTable *main_sect_defns_ht = sect_defn_ht_arr[0]; + for (U64 worker_idx = 1; worker_idx < tp->worker_count; worker_idx += 1) { + U64 sect_defns_count = sect_defn_ht_arr[worker_idx]->count; + LNK_SectionDefinition **sect_defns = values_from_hash_table_raw(temp.arena, sect_defn_ht_arr[worker_idx]); + radsort(sect_defns, sect_defns_count, lnk_section_definition_is_before); + + for (U64 i = 0; i < sect_defns_count; i += 1) { + String8 name_with_flags = lnk_make_name_with_flags(temp.arena, sect_defns[i]->name, sect_defns[i]->flags); + LNK_SectionDefinition *main_defn = 0; + hash_table_search_string_raw(main_sect_defns_ht, name_with_flags, &main_defn); + if (main_defn == 0) { + main_defn = sect_defns[i]; + hash_table_push_string_raw(temp.arena, main_sect_defns_ht, name_with_flags, main_defn); + } else { + main_defn->contribs_count += sect_defns[i]->contribs_count; + } + } + } + ProfEnd(); + + ProfBegin("Push Sections And Reserve Section Contrib Memory"); + U64 sect_defns_count = main_sect_defns_ht->count; + LNK_SectionDefinition **sect_defns = values_from_hash_table_raw(temp.arena, main_sect_defns_ht); + radsort(sect_defns, sect_defns_count, lnk_section_definition_is_before); + + for (U64 defn_idx = 0; defn_idx < sect_defns_count; defn_idx += 1) { + LNK_SectionDefinition *sect_defn = sect_defns[defn_idx]; + + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + LNK_Section *sect = §_n->data; + if (str8_match(sect->name, sect_defn->name, 0)) { + if (sect->flags != sect_defn->flags) { + LNK_Obj *obj = sect_defn->obj; + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + COFF_SectionHeader *sect_header = §ion_table[sect_defn->obj_sect_idx]; + String8 full_sect_name = coff_name_from_section_header(string_table, sect_header); + U32 sect_number = sect_defn->obj_sect_idx + 1; + String8 expected_flags_str = coff_string_from_section_flags(temp.arena, sect->flags); + String8 current_flags_str = coff_string_from_section_flags(temp.arena, sect_defn->flags); + lnk_error_obj(LNK_Warning_SectionFlagsConflict, sect_defn->obj, "detected section flags conflict in %S(No. %X); expected {%S} but got {%S}", full_sect_name, sect_number, expected_flags_str, current_flags_str); + } + } + } + + LNK_Section *sect = lnk_section_table_search(sectab, sect_defn->name, sect_defn->flags); + if (sect == 0) { + sect = lnk_section_table_push(sectab, sect_defn->name, sect_defn->flags); + } + AssertAlways(sect->contribs.chunk_count == 0); + lnk_section_contrib_chunk_list_push_chunk(sectab->arena, §->contribs, sect_defn->contribs_count); + } + ProfEnd(); + + temp_end(temp); + ProfEnd(); + } + + + U64 expected_image_header_size; + { + // gather section contribs + LNK_SectionContrib ***sect_map = push_array(scratch.arena, LNK_SectionContrib **, objs_count); + { + U16 default_align = lnk_default_align_from_machine(config->machine); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = objs[obj_idx]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + + sect_map[obj_idx] = push_array(scratch.arena, LNK_SectionContrib *, obj->header.section_count_no_null); + + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + + if (sect_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + + // parse section name + String8 full_sect_name = coff_name_from_section_header(string_table, sect_header); + String8 sect_name, sect_sort_idx; + coff_parse_section_name(full_sect_name, §_name, §_sort_idx); + + // extract section bytes + String8 sect_data = str8_substr(obj->data, rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize)); + + // extract align + U16 sc_align = coff_align_size_from_section_flags(COFF_SectionFlags_ExtractAlign(sect_header->flags)); + if (sc_align == 0) { + sc_align = default_align; + } + + // search for section to contribute + COFF_SectionFlags flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; + LNK_Section *sect = lnk_section_table_search(sectab, sect_name, flags); + + // fill out contrib + LNK_SectionContribChunk *sc_chunk = sect->contribs.first; + LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(sc_chunk, 1); + sc->align = sc_align; + sc->u.obj_idx = obj_idx; + sc->u.sort_idx_size = (U16)sect_sort_idx.size; + sc->u.sort_idx = sect_sort_idx.str; + + String8Node *data_n = push_array(sect->arena, String8Node, 1); + data_n->string = sect_data; + SLLStackPush(sc->data_list, data_n); + + sect_map[obj_idx][sect_idx] = sc; + } + } + } + + // build obj section map + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = objs[obj_idx]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + + String8 *symlinks = push_array(scratch.arena, String8, obj->header.section_count_no_null); + { + 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) { + if (symbol.storage_class == COFF_SymStorageClass_External) { + U64 sect_idx = symbol.section_number-1; + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { + // TODO: check that we don't override COMDAT symbol link + symlinks[sect_idx] = symbol.name; + } + } + } + } + } + + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symlinks[sect_idx]); + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + sect_map[obj_idx][sect_idx] = sect_map[defn->u.defined.obj->input_idx][symbol.section_number-1]; + } + } + } + + // finalize sections layouts + { + ProfBegin("Finalize Sections Layout"); + + // sort contribs + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + for (LNK_SectionContribChunk *sc_chunk = sect_n->data.contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + Assert(sc_chunk->count == sc_chunk->cap); + radsort(sc_chunk->v, sc_chunk->count, lnk_section_contrib_is_before); + } + } + + // merge sections + if (config->flags & LNK_ConfigFlag_Merge) { + lnk_section_table_merge(sectab, config->merge_list); + } + + // assign contribs offsets, sizes, and section indices + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + lnk_finalize_section_layout(sectab, §_n->data, config->file_align); + } + + // remove empty sections + String8List empty_sect_list = {0}; + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + LNK_Section *sect = §_n->data; + if (sect->vsize == 0) { + str8_list_push(scratch.arena, &empty_sect_list, sect->name); + } + } + for (String8Node *name_n = empty_sect_list.first; name_n != 0; name_n = name_n->next) { + lnk_section_table_remove(sectab, name_n->string); + } + + // assign section indices to sections + U64 sect_idx = 0; + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + sect_n->data.sect_idx = sect_idx++; + } + + // assign section indices to contribs + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + LNK_Section *sect = §_n->data; + for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + sc_chunk->v[sc_idx].u.sect_idx = sect->sect_idx; + } + } + } + + ProfEnd(); + } + + // build common block + // + // TODO: build common block in .bss and merge with .data + U64 common_block_contribs_count; + LNK_CommonBlockContrib *common_block_contribs; + LNK_Section *common_block_sect; + { + ProfBegin("Build Common Block"); + + ProfBegin("Count Contribs"); + common_block_contribs_count = 0; + for (U64 worker_id = 0; worker_id < tp->worker_count; worker_id += 1) { + for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; + chunk != 0; + chunk = chunk->next) { + for (U64 i = 0; i < chunk->count; i += 1) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); + if (parsed_interp == COFF_SymbolValueInterp_Common) { + common_block_contribs_count += 1; + } + } + } + } + ProfEnd(); + + ProfBegin("Gather Contribs"); + common_block_contribs = push_array(scratch.arena, LNK_CommonBlockContrib, common_block_contribs_count); + { + U64 cursor = 0; + for (U64 worker_id = 0; worker_id < tp->worker_count; worker_id += 1) { + for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; + chunk != 0; + chunk = chunk->next) { + for (U64 i = 0; i < chunk->count; i += 1) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); + if (parsed_interp == COFF_SymbolValueInterp_Common) { + LNK_CommonBlockContrib *contrib = &common_block_contribs[cursor++]; + contrib->symbol = chunk->v[i].symbol; + contrib->u.size = parsed_symbol.value; + } + } + } + } + } + ProfEnd(); + + if (common_block_contribs_count) { + ProfBeginV("Assign Common Block Offsets [count %llu]", common_block_contribs_count); + + // search/push .data + common_block_sect = lnk_section_table_search(sectab, str8_lit(".data"), LNK_DATA_SECTION_FLAGS); + if (common_block_sect == 0) { + common_block_sect = lnk_section_table_push(sectab, str8_lit(".data"), LNK_DATA_SECTION_FLAGS); + } + + // sort common blocks from largest to smallest for tighter packing + radsort(common_block_contribs, common_block_contribs_count, lnk_common_block_contrib_is_before); + + // compute common block offsets + for (U64 contrib_idx = 0; contrib_idx < common_block_contribs_count; contrib_idx += 1) { + LNK_CommonBlockContrib *contrib = &common_block_contribs[contrib_idx]; + U32 size = contrib->u.size; + U32 align = Min(32, u64_up_to_pow2(size)); // link.exe caps align at 32 bytes + common_block_sect->vsize = AlignPow2(common_block_sect->vsize, align); + contrib->u.offset = common_block_sect->vsize; + common_block_sect->vsize += size; + } + ProfEnd(); + } + + ProfEnd(); + } + + // + // section list -> array + // + LNK_SectionArray sects = lnk_section_array_from_list(scratch.arena, sectab->list); + + // patch symbol tables + { + ProfBegin("Patch Symbol Tables"); + + ProfBegin("Patch Common Block Leaders"); + B8 **was_patched = push_array(scratch.arena, B8 *, objs_count); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + was_patched[obj_idx] = push_array(scratch.arena, B8, objs[obj_idx]->header.symbol_count); + } + + for (U64 contrib_idx = 0; contrib_idx < common_block_contribs_count; contrib_idx += 1) { + LNK_CommonBlockContrib *contrib = &common_block_contribs[contrib_idx]; + LNK_Symbol *symbol = contrib->symbol; + LNK_Obj *obj = symbol->u.defined.obj; + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol->u.defined.symbol_idx); + + was_patched[obj->input_idx][symbol->u.defined.symbol_idx] = 1; + + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = parsed_symbol.raw_symbol; + symbol32->value = contrib->u.offset; + symbol32->section_number = safe_cast_u32(common_block_sect->sect_idx + 1); + } else { + COFF_Symbol16 *symbol16 = parsed_symbol.raw_symbol; + symbol16->value = contrib->u.offset; + symbol16->section_number = safe_cast_u16(common_block_sect->sect_idx + 1); + } + } + ProfEnd(); + + ProfBegin("Patch Regular Symbols"); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = 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); + + if (was_patched[obj_idx][symbol_idx]) { + continue; + } + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + LNK_SectionContrib *sc = sect_map[obj_idx][symbol.section_number-1]; + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = safe_cast_u32(sc->u.sect_idx + 1); + symbol32->value = safe_cast_u32(sc->u.off); + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = safe_cast_u16(sc->u.sect_idx + 1); + symbol16->value = safe_cast_u32(sc->u.off); + } + } + } + } + ProfEnd(); + + ProfBegin("Patch common blocks"); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = 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_Common) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); + if (defn) { + LNK_SectionContrib *sc = sect_map[obj_idx][symbol.section_number-1]; + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = safe_cast_u32(sc->u.sect_idx + 1); + symbol32->value = safe_cast_u32(sc->u.off); + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = safe_cast_u16(sc->u.sect_idx + 1); + symbol16->value = safe_cast_u32(sc->u.off); + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } + } + } + } + ProfEnd(); + + ProfBegin("Patch Abs"); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = 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_Abs && symbol.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); + + if (defn == 0) { + continue; + } + if (defn->u.defined.obj == obj && defn->u.defined.symbol_idx == symbol_idx) { + continue; + } + + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); + if (defn_interp == COFF_SymbolValueInterp_Regular) { + if (defn->u.defined.obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = defn_symbol.section_number; + symbol32->value = defn_symbol.value; + symbol32->type = defn_symbol.type; + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = defn_symbol.section_number; + symbol16->value = defn_symbol.value; + symbol16->type = defn_symbol.type; + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } else { + InvalidPath; + } + } + } + } + ProfEnd(); + + // patch undefined symbols + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = 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_Undefined) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); + if (defn) { + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + + if (defn_symbol.storage_class == COFF_SymStorageClass_WeakExternal) { + continue; + } + + lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); + } else { + // TODO: collect unresolved undefined + } + } + } + } + + // patch weak symbols with strong definition + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = 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_Weak) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); + if (defn) { + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); + if (defn_interp != COFF_SymbolValueInterp_Weak) { + lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); + } + } + } + } + } + + // patch weak with fallback definition + { + HashTable *visited_symbols_ht = hash_table_init(scratch.arena, 32); + struct LookupLocation { + struct LookupLocation *next; + LNK_Obj *obj; + U64 symbol_idx; + }; + struct LookupLocation *lookup_first = 0; + struct LookupLocation *lookup_last = 0; + struct LookupLocation *lookup_free_list = 0; + + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = 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_Undefined) { + String8 lookup_name = symbol.name; + LNK_Obj *lookup_obj = obj; + U64 lookup_symbol_idx = symbol_idx; + for (;;) { + // lookup definition + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, lookup_name); + if (defn == 0) { + break; + } + + // not external symbol? patch and move to next symbol + COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + if (defn_parsed.storage_class != COFF_SymStorageClass_WeakExternal) { + lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_parsed); + break; + } + + // check against cyclic refs + struct LookupLocation *was_visited = 0; + hash_table_search_string_raw(visited_symbols_ht, lookup_name, &was_visited); + if (was_visited != 0) { + Temp temp = temp_begin(scratch.arena); + + String8List list = {0}; + for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { + COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->obj, l->symbol_idx); + str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx) =>", l->obj->path, loc_symbol.name, l->symbol_idx); + } + { + COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(was_visited->obj, was_visited->symbol_idx); + str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx)", was_visited->obj->path, loc_symbol.name, was_visited->symbol_idx); + } + + String8 loc_string = str8_list_join(temp.arena, &list, &(StringJoin){.sep = str8_lit("\n") }); + lnk_error_obj(LNK_Error_WeakCycle, obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", symbol.name, loc_string); + + temp_end(temp); + break; + } + struct LookupLocation *loc = lookup_free_list; + if (lookup_free_list) { + SLLStackPop(lookup_free_list); + } else { + loc = push_array(scratch.arena, struct LookupLocation, 1); + } + loc->obj = lookup_obj; + loc->symbol_idx = symbol_idx; + SLLQueuePush(lookup_first, lookup_last, loc); + hash_table_push_string_raw(scratch.arena, visited_symbols_ht, lookup_name, loc); + + // fallback to weak tag for definition + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(defn_parsed, defn->u.defined.obj->header.is_big_obj); + COFF_ParsedSymbol parsed_tag = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, weak_ext->tag_index); + lookup_name = parsed_tag.name; + lookup_obj = defn->u.defined.obj; + lookup_symbol_idx = weak_ext->tag_index; + } + + hash_table_purge(visited_symbols_ht); + lookup_free_list = lookup_first; + lookup_first = 0; + lookup_last = 0; + } + } + } + } + + // patch weak symbols which have undefined tag + { + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = 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_Weak) { + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol, obj->header.is_big_obj); + AssertAlways(weak_ext->tag_index < symbol_idx); + + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, weak_ext->tag_index); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); + if (defn_interp != COFF_SymbolValueInterp_Undefined) { + lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); + } + } + } + } + } + + // report unresolved symbols + { + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = 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_Undefined || interp == COFF_SymbolValueInterp_Weak) { + lnk_error_obj(LNK_Error_UnresolvedSymbol, obj, "unresolved symbol %S", symbol.name); + } + } + } + } + + ProfEnd(); + } + + expected_image_header_size = lnk_compute_win32_image_header_size(config, sects.count); + + // assign virtual space + U64 voff_cursor = AlignPow2(expected_image_header_size + sizeof(COFF_SectionHeader), config->sect_align); + for (U64 i = 0; i < sects.count; i += 1) { + lnk_assign_section_virtual_space(sects.v[i], config->sect_align, &voff_cursor); + } + + // patch virtual offset and size in section headers + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = objs[obj_idx]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + if (~sect_header->flags & COFF_SectionFlag_LnkRemove) { + LNK_SectionContrib *sc = sect_map[obj_idx][sect_idx]; + LNK_Section *sect = sects.v[sc->u.sect_idx]; + sect_header->vsize = sc->u.size; + sect_header->voff = sect->voff + sc->u.off; + } + } + } + + // build base relocs + if (~config->flags & LNK_ConfigFlag_Fixed) { + String8List base_relocs_data = lnk_build_base_relocs(tp, arena, config, objs_count, objs); + if (base_relocs_data.total_size) { + LNK_Section *reloc = lnk_section_table_push(sectab, str8_lit(".reloc"), LNK_RELOC_SECTION_FLAGS); + LNK_SectionContribChunk *sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &reloc->contribs, 1); + LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(sc_chunk, 1); + sc->data_list = base_relocs_data.first; + sc->align = 1; + sc->u.sort_idx_size = 0; + sc->u.obj_idx = max_U32; + + lnk_finalize_section_layout(sectab, reloc, config->file_align); + lnk_assign_section_virtual_space(reloc, config->sect_align, &voff_cursor); + + sects = sects = lnk_section_array_from_list(scratch.arena, sectab->list); + expected_image_header_size = lnk_compute_win32_image_header_size(config, sects.count); + } + } + + // assign file space + U64 foff_cursor = AlignPow2(expected_image_header_size, config->file_align); + for (U64 i = 0; i < sects.count; i += 1) { + lnk_assign_section_file_space(sects.v[i], &foff_cursor); + } + + // patch file offset and size in section headers + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = objs[obj_idx]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + if (~sect_header->flags & COFF_SectionFlag_LnkRemove) { + LNK_SectionContrib *sc = sect_map[obj_idx][sect_idx]; + LNK_Section *sect = sects.v[sc->u.sect_idx]; + if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { + sect_header->fsize = sc->u.size; + sect_header->foff = sect->foff + sc->u.off; + } + } + } + } + } + + // build win32 image header + LNK_SectionArray sects; + { + sects = lnk_section_array_from_list(scratch.arena, sectab->list); + String8List image_header_data = lnk_build_win32_image_header(scratch.arena, symtab, config, sects, AlignPow2(expected_image_header_size, config->file_align)); + + LNK_Section *image_header_sect = lnk_section_table_push(sectab, str8_lit(".rad_linker_image_header_section"), 0); + LNK_SectionContribChunk *image_header_sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &image_header_sect->contribs, 1); + LNK_SectionContrib *image_header_sc = lnk_section_contrib_chunk_push(image_header_sc_chunk, 1); + + image_header_sc->align = config->file_align; + image_header_sc->data_list = image_header_data.first; + image_header_sc->u.size = safe_cast_u32(image_header_data.total_size); + + lnk_finalize_section_layout(sectab, image_header_sect, config->file_align); + + sects = lnk_section_array_from_list(scratch.arena, sectab->list); + } + + String8 image_data = {0}; + { + ProfBegin("Image Fill"); + + U64 image_size = 0; + for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + image_size += sects.v[sect_idx]->fsize; + } + + image_data.size = image_size; + image_data.str = push_array_no_zero(arena->v[0], U8, image_size); + + for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + LNK_Section *sect = sects.v[sect_idx]; + + if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { + // pick fill pick + U8 fill_byte = 0; + if (sect->flags & COFF_SectionFlag_CntCode) { + fill_byte = lnk_code_align_byte_from_machine(config->machine); + } + + // copy section contribution + U64 prev_sc_opl = 0; + for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + LNK_SectionContrib *sc = &sc_chunk->v[sc_idx]; + + // fill align bytes + Assert(sc->u.off >= prev_sc_opl); + U64 fill_size = sc->u.off - prev_sc_opl; + MemorySet(image_data.str + sect->foff + prev_sc_opl, fill_byte, fill_size); + prev_sc_opl = sc->u.off + sc->u.size; + + // copy contrib contents + { + U64 cursor = 0; + for (String8Node *data_n = sc->data_list; data_n != 0; data_n = data_n->next) { + Assert(sc->u.off + data_n->string.size <= sect->vsize); + MemoryCopy(image_data.str + sect->foff + sc->u.off + cursor, data_n->string.str, data_n->string.size); + cursor += data_n->string.size; + } + } + } + } + + // fill section align bytes + { + U64 fill_size = sect->fsize - prev_sc_opl; + MemorySet(image_data.str + sect->foff + prev_sc_opl, fill_byte, fill_size); + } + } + } + + ProfEnd(); + } + + { + ProfBegin("Image Patch"); + + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data); + COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range); + + // patch relocs + { + ProfBegin("Patch Relocs"); + LNK_ObjRelocPatcher task = {0}; + task.image_data = image_data; + task.objs = objs; + task.base_addr = pe.image_base; + task.image_section_table = image_section_table; + tp_for_parallel(tp, 0, objs_count, lnk_obj_reloc_patcher, &task); + ProfEnd(); + } + + LNK_Obj *debug_dir_obj = 0; + //hash_table_search_string_raw(loaded_obj_ht, str8_lit("* Debug Directory *"), &debug_dir_obj); + + // patch load config + { + LNK_Symbol *load_config_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit(LNK_LOAD_CONFIG_SYMBOL_NAME)); + if (load_config_symbol) { + U64 load_config_foff = lnk_file_off_from_symbol(image_section_table, load_config_symbol); + String8 load_config_data = str8_skip(image_data, load_config_foff); + + U32 load_config_size = 0; + if (sizeof(load_config_size) <= load_config_data.size) { + PE_DataDirectory *load_config_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_LOAD_CONFIG, sizeof(PE_DataDirectory)); + load_config_dir->virt_off = lnk_virt_off_from_symbol(image_section_table, load_config_symbol); + load_config_dir->virt_size = load_config_size; + } else { + // TODO: report corrupted load config + } + } + } + + // patch .pdata + { + LNK_Section *pdata_sect = lnk_section_table_search(sectab, str8_lit(".pdata"), LNK_PDATA_SECTION_FLAGS); + if (pdata_sect) { + String8 pdata = str8_substr(image_data, rng_1u64(pdata_sect->foff, pdata_sect->foff + pdata_sect->vsize)); + + ProfBegin("Sort Exception Info"); + if (pdata_sect) { + switch (config->machine) { + case COFF_MachineType_Unknown: break; + case COFF_MachineType_X86: + case COFF_MachineType_X64: { + U64 count = pdata.size / sizeof(PE_IntelPdata); + radsort((PE_IntelPdata *)pdata.str, count, lnk_pdata_is_before_x8664); + } break; + default: { NotImplemented; } break; + } + } + ProfEnd(); + + PE_DataDirectory *pdata_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_EXCEPTIONS, sizeof(PE_DataDirectory)); + pdata_dir->virt_off = pdata_sect->voff; + pdata_dir->virt_size = pdata_sect->vsize; + } + } + + // patch export + { + LNK_Section *edata_sect = lnk_section_table_search(sectab, str8_lit(".edata"), LNK_EDATA_SECTION_FLAGS); + if (edata_sect) { + PE_DataDirectory *export_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_EXPORT, sizeof(PE_DataDirectory)); + export_dir->virt_off = edata_sect->voff; + export_dir->virt_size = edata_sect->vsize; + } + } + + // patch base relocs + { + LNK_Section *reloc_sect = lnk_section_table_search(sectab, str8_lit(".reloc"), LNK_RELOC_SECTION_FLAGS); + if (reloc_sect) { + PE_DataDirectory *reloc_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_BASE_RELOC, sizeof(PE_DataDirectory)); + reloc_dir->virt_off = reloc_sect->voff; + reloc_dir->virt_size = reloc_sect->vsize; + } + } + + // patch import and import addr + { + LNK_Symbol *dll_table_begin_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINKER_IMPORT_TABLE_DLL_BEGIN")); + LNK_Symbol *dll_table_end_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINKER_IMPORT_TABLE_DLL_END")); + if (dll_table_begin_symbol && dll_table_end_symbol) { + COFF_ParsedSymbol dll_table_begin_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dll_table_begin_symbol->u.defined.obj, dll_table_begin_symbol->u.defined.symbol_idx); + COFF_ParsedSymbol dll_table_end_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dll_table_end_symbol->u.defined.obj, dll_table_end_symbol->u.defined.symbol_idx); + U64 dll_table_voff = image_section_table[dll_table_begin_parsed.section_number]->voff + dll_table_begin_parsed.value; + U64 dll_table_vsize = dll_table_end_parsed.value - dll_table_begin_parsed.value; + + PE_DataDirectory *import_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_IMPORT, sizeof(PE_DataDirectory)); + import_dir->virt_off = dll_table_voff; + import_dir->virt_size = dll_table_vsize; + } + + LNK_Symbol *iat_begin_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINKER IMPORT_ADDRESS_TABLE_BEGIN")); + LNK_Symbol *iat_end_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINKER_IMPORT_ADDRESS_TABLE_END")); + if (iat_begin_symbol && iat_end_symbol) { + COFF_ParsedSymbol iat_begin_parsed = lnk_parsed_symbol_from_coff_symbol_idx(iat_begin_symbol->u.defined.obj, iat_begin_symbol->u.defined.symbol_idx); + COFF_ParsedSymbol iat_end_parsed = lnk_parsed_symbol_from_coff_symbol_idx(iat_end_symbol->u.defined.obj, iat_end_symbol->u.defined.symbol_idx); + U64 iat_voff = image_section_table[iat_begin_parsed.section_number]->voff + iat_begin_parsed.value; + U64 iat_vsize = iat_end_parsed.value - iat_begin_parsed.value; + + PE_DataDirectory *import_addr_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_IMPORT_ADDR, sizeof(PE_DataDirectory)); + import_addr_dir->virt_off = iat_voff; + import_addr_dir->virt_size = iat_vsize; + } + } + + // patch delay import + { + LNK_Symbol *dll_table_begin_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINKER_IMPORT_TABLE_DLL_DELAY_BEGIN")); + LNK_Symbol *dll_table_end_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINKER_IMPORT_TABLE_DLL_DELAY_END")); + if (dll_table_begin_symbol && dll_table_end_symbol) { + COFF_ParsedSymbol dll_table_begin_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dll_table_begin_symbol->u.defined.obj, dll_table_begin_symbol->u.defined.symbol_idx); + COFF_ParsedSymbol dll_table_end_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dll_table_end_symbol->u.defined.obj, dll_table_end_symbol->u.defined.symbol_idx); + U64 dll_table_voff = image_section_table[dll_table_begin_parsed.section_number]->voff + dll_table_begin_parsed.value; + U64 dll_table_vsize = dll_table_end_parsed.value - dll_table_begin_parsed.value; + + PE_DataDirectory *delay_import_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_DELAY_IMPORT, sizeof(PE_DataDirectory)); + delay_import_dir->virt_off = dll_table_voff; + delay_import_dir->virt_size = dll_table_vsize; + } + } + + // patch TLS + LNK_Symbol *tls_used_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, LNK_TLS_SYMBOL_NAME); + if (tls_used_symbol) { + ProfBegin("Patch TLS Align"); + + // loop over .tls sections and extract max alignment + LNK_Section *tls_sect = lnk_section_table_search(sectab, str8_lit(".tls"), LNK_RDATA_SECTION_FLAGS); + U64 tls_align = 0; + + for (LNK_SectionContribChunk *sc_chunk = tls_sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + LNK_SectionContrib *sc = &sc_chunk->v[sc_idx]; + tls_align = Max(tls_align, sc->align); + } + } + + if (IsPow2(tls_align)) { + // compute TLS header offset + U64 tls_header_foff = lnk_file_off_from_symbol(image_section_table, tls_used_symbol); + + // patch TLS header + if (coff_word_size_from_machine(config->machine) == 8) { + String8 raw_tls_used = str8_substr(image_data, rng_1u64(tls_header_foff, tls_header_foff + sizeof(PE_TLSHeader64))); + PE_TLSHeader64 *tls_header = (PE_TLSHeader64 *) raw_tls_used.str; + tls_header->characteristics |= coff_section_flag_from_align_size(tls_align); + } else { + String8 raw_tls_used = str8_substr(image_data, rng_1u64(tls_header_foff, tls_header_foff + sizeof(PE_TLSHeader32))); + PE_TLSHeader32 *tls_header = (PE_TLSHeader32 *) raw_tls_used.str; + tls_header->characteristics |= coff_section_flag_from_align_size(tls_align); + } + } else { + lnk_error(LNK_Warning_TLSAlign, "unable to patch TLS Header characteristics, alignment must be power of two, align inferred from section flags: %llu", tls_align); + } + + PE_DataDirectory *tls_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_TLS, sizeof(PE_DataDirectory)); + tls_dir->virt_off = tls_sect->voff; + tls_dir->virt_size = tls_sect->vsize; + + ProfEnd(); + } + + // patch Debug + { + LNK_Section *debug_dir_sect = lnk_section_table_search(sectab, str8_lit(".RAD_LINKER_DEBUG_DIR"), LNK_RDATA_SECTION_FLAGS); + if (debug_dir_sect) { + PE_DataDirectory *debug_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_DEBUG, sizeof(PE_DataDirectory)); + debug_dir->virt_off = debug_dir_sect->voff; + debug_dir->virt_size = debug_dir_sect->vsize; + + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(debug_dir_obj->data, debug_dir_obj->header.section_table_range).str; + String8 string_table = str8_substr(debug_dir_obj->data, debug_dir_obj->header.string_table_range); + for (U64 sect_idx = 0; sect_idx < debug_dir_obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(debug_dir_obj->data, sect_header); + COFF_Reloc *relocs = (COFF_Reloc *)(debug_dir_obj->data.str + reloc_info.array_off); + + for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { + COFF_Reloc *r = &relocs[reloc_idx]; + if (r->type == 0) { + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(debug_dir_obj, r->isymbol); + + // patch PE_DebugDirectory.foff + U32 *debug_directory_foff = (U32 *)(image_data.str + sect_header->foff + r->apply_off); + *debug_directory_foff = image_section_table[symbol.section_number]->foff + symbol.value; + break; + } + } + } + } + } + + // patch resources + { + LNK_Section *rsrc_sect = lnk_section_table_search(sectab, str8_lit(".rsrc"), LNK_RSRC_SECTION_FLAGS); + if (rsrc_sect) { + PE_DataDirectory *rsrc_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_RESOURCES, sizeof(PE_DataDirectory)); + rsrc_dir->virt_off = rsrc_sect->voff; + rsrc_dir->virt_size = rsrc_sect->vsize; + } + } + + // image checksum + if (config->flags & LNK_ConfigFlag_WriteImageChecksum) { + ProfBegin("Image Checksum"); + *pe.check_sum = pe_compute_checksum(image_data.str, image_data.size); + ProfEnd(); + } + + // compute image guid, and patch PDB and RDI guids + { + LNK_Symbol *guid_pdb_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINKER_PDB_GUID")); + LNK_Symbol *guid_rdi_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINKER_RDI_GUID")); + + if (guid_pdb_symbol || guid_rdi_symbol) { + switch (config->guid_type) { + case LNK_DebugInfoGuid_Null: break; + case Lnk_DebugInfoGuid_ImageBlake3: { + ProfBegin("Hash Image With Blake3"); + U128 hash = lnk_blake3_hash_parallel(tp, 128, image_data); + MemoryCopy(&config->guid, hash.u8, sizeof(hash.u8)); + ProfEnd(); + } break; + } + } + + if (guid_pdb_symbol) { + LNK_Obj *obj = guid_pdb_symbol->u.defined.obj; + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, guid_pdb_symbol->u.defined.symbol_idx); + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + Guid *cv_guid_pdb = str8_deserial_get_raw_ptr(image_data, section_header->foff, sizeof(*cv_guid_pdb)); + *cv_guid_pdb = config->guid; + } + + if (guid_rdi_symbol) { + LNK_Obj *obj = guid_rdi_symbol->u.defined.obj; + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, guid_rdi_symbol->u.defined.symbol_idx); + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + Guid *cv_guid_rdi = str8_deserial_get_raw_ptr(image_data, section_header->foff, sizeof(*cv_guid_rdi)); + *cv_guid_rdi = config->guid; + } + } + + ProfEnd(); + } + + scratch_end(scratch); + return image_data; } //////////////////////////////// @@ -2041,22 +3087,19 @@ THREAD_POOL_TASK_FUNC(lnk_undef_symbol_finder) Rng1U64 range = task->range_arr[task_id]; for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) { - LNK_SymbolNode *symbol_node = task->lookup_node_arr.v[symbol_idx]; - LNK_Symbol *symbol = symbol_node->data; - Assert(symbol->type == LNK_Symbol_Undefined); - LNK_UndefinedSymbol *undef = &symbol->u.undefined; + LNK_SymbolNode *symbol_n = task->lookup_node_arr.v[symbol_idx]; + LNK_Symbol *symbol = symbol_n->data; - LNK_Symbol *has_defn = lnk_symbol_table_search(task->symtab, undef->scope_flags, symbol->name); + LNK_Symbol *has_defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol->name); if (has_defn) { - Assert(LNK_Symbol_IsDefined(has_defn->type) || has_defn->type == LNK_Symbol_Weak || has_defn->type == LNK_Symbol_Import); continue; } - LNK_Symbol *lazy = lnk_symbol_table_search(task->symtab, LNK_SymbolScopeFlag_Lib, symbol->name); - if (lazy) { - lnk_push_input_from_lazy(arena, task->path_style, &lazy->u.lazy, &result->input_import_list, &result->input_obj_list); + LNK_Symbol *member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); + if (member_symbol) { + lnk_queue_lib_member_input(arena, task->path_style, &member_symbol->u.lib, &result->input_import_list, &result->input_obj_list); } else { - lnk_symbol_list_push_node(&result->unresolved_symbol_list, symbol_node); + lnk_symbol_list_push_node(&result->unresolved_symbol_list, symbol_n); } } } @@ -2069,29 +3112,28 @@ THREAD_POOL_TASK_FUNC(lnk_weak_symbol_finder) Rng1U64 range = task->range_arr[task_id]; for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) { - LNK_SymbolNode *symbol_node = task->lookup_node_arr.v[symbol_idx]; - LNK_Symbol *symbol = symbol_node->data; - Assert(symbol->type == LNK_Symbol_Weak); - LNK_WeakSymbol *weak = &symbol->u.weak; + LNK_SymbolNode *symbol_n = task->lookup_node_arr.v[symbol_idx]; + LNK_Symbol *symbol = symbol_n->data; - Assert((weak->scope_flags & ~(LNK_SymbolScopeFlag_Defined | LNK_SymbolScopeFlag_Internal)) == 0); - LNK_Symbol *has_strong_defn = lnk_symbol_table_search(task->symtab, weak->scope_flags, symbol->name); + LNK_Symbol *has_strong_defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol->name); if (has_strong_defn) { - Assert(LNK_Symbol_IsDefined(has_strong_defn->type)); continue; } - - LNK_Symbol *lazy = 0; - switch (weak->lookup_type) { + + LNK_Symbol *member_symbol = 0; + { + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(parsed_symbol, symbol->u.defined.obj->header.is_big_obj); + switch (weak_ext->characteristics) { case COFF_WeakExt_NoLibrary: { // NOLIBRARY means weak symbol should be resolved in case where strong definition pulls in lib member. } break; case COFF_WeakExt_SearchLibrary: { - lazy = lnk_symbol_table_search(task->symtab, LNK_SymbolScopeFlag_Lib, symbol->name); + member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); } break; case COFF_WeakExt_SearchAlias: { - lazy = lnk_symbol_table_search(task->symtab, LNK_SymbolScopeFlag_Lib, symbol->name); - if (!lazy) { + member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); + if (member_symbol == 0) { if (str8_match_lit(".weak.", symbol->name, StringMatchFlag_RightSideSloppy)) { // TODO: Clang and MingGW encode extra info in alias // @@ -2109,16 +3151,18 @@ THREAD_POOL_TASK_FUNC(lnk_weak_symbol_finder) // In this case linker needs to parse .weak.bar.default.foo and search for bar and foo as well. Assert("TODO: MinGW weak symbol"); } else { - lazy = lnk_symbol_table_search(task->symtab, LNK_SymbolScopeFlag_Lib, weak->fallback_symbol->name); + COFF_ParsedSymbol tag = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, weak_ext->tag_index); + member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, tag.name); } } } break; + } } - if (lazy) { - lnk_push_input_from_lazy(arena, task->path_style, &lazy->u.lazy, &result->input_import_list, &result->input_obj_list); + if (member_symbol) { + lnk_queue_lib_member_input(arena, task->path_style, &member_symbol->u.lib, &result->input_import_list, &result->input_obj_list); } else { - lnk_symbol_list_push_node(&result->unresolved_symbol_list, symbol_node); + lnk_symbol_list_push_node(&result->unresolved_symbol_list, symbol_n); } } } @@ -2177,466 +3221,140 @@ lnk_run_symbol_finder(TP_Context *tp, return result; } -internal -THREAD_POOL_TASK_FUNC(lnk_defined_symbol_pusher_task) -{ - LNK_SymbolPusher *task = raw_task; - LNK_SymbolTable *symtab = task->symtab; - LNK_Obj *obj = &task->u.objs.v[task_id].data; - - LNK_SymbolHashTrieChunkList **chunk_lists = symtab->chunk_lists; - - for (LNK_SymbolNode *symnode = obj->symbol_list.first; symnode != 0; symnode = symnode->next) { - if (symnode->data->type == LNK_Symbol_DefinedExtern) { - U64 hash = lnk_symbol_hash(symnode->data->name); - lnk_symbol_table_push_(symtab, arena, &chunk_lists[LNK_SymbolScopeIndex_Defined][worker_id], LNK_SymbolScopeIndex_Defined, hash, symnode->data); - } else if (symnode->data->type == LNK_Symbol_Weak) { - U64 hash = lnk_symbol_hash(symnode->data->name); - lnk_symbol_table_push_(symtab, arena, &chunk_lists[LNK_SymbolScopeIndex_Weak][worker_id], LNK_SymbolScopeIndex_Weak, hash, symnode->data); - } - } -} - -internal void -lnk_push_defined_symbols(TP_Context *tp, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs) -{ - ProfBeginFunction(); - LNK_SymbolPusher task = {0}; - task.symtab = symtab; - task.u.objs = objs; - tp_for_parallel(tp, symtab->arena, objs.count, lnk_defined_symbol_pusher_task, &task); - ProfEnd(); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_lazy_symbol_pusher_task) -{ - LNK_SymbolPusher *task = raw_task; - LNK_SymbolTable *symtab = task->symtab; - LNK_Lib *lib = &task->u.libs.v[task_id].data; - String8Node *name_node = lib->symbol_name_list.first; - - LNK_Symbol *lazy_symbols = push_array_no_zero(arena, LNK_Symbol, lib->symbol_count); - - for (U64 symbol_idx = 0; symbol_idx < lib->symbol_count; ++symbol_idx, name_node = name_node->next) { - LNK_Symbol *symbol = &lazy_symbols[symbol_idx]; - lnk_init_lazy_symbol(symbol, name_node->string, lib, lib->member_off_arr[symbol_idx]); - - U64 hash = lnk_symbol_hash(symbol->name); - lnk_symbol_table_push_(symtab, arena, &symtab->chunk_lists[LNK_SymbolScopeIndex_Lib][worker_id], LNK_SymbolScopeIndex_Lib, hash, symbol); - } -} - -internal void -lnk_push_lazy_symbols(TP_Context *tp, LNK_SymbolTable *symtab, LNK_LibNodeArray libs) -{ - ProfBeginFunction(); - LNK_SymbolPusher task = {0}; - task.symtab = symtab; - task.u.libs = libs; - tp_for_parallel(tp, symtab->arena, libs.count, lnk_lazy_symbol_pusher_task, &task); - ProfEnd(); -} - //////////////////////////////// -internal void -lnk_apply_reloc(U64 base_addr, - U64 virt_align, - U64 file_align, - LNK_Section **sect_id_map, - LNK_SymbolTable *symtab, - String8 chunk_data, - LNK_Reloc *reloc) -{ - LNK_Symbol *symbol = lnk_resolve_symbol(symtab, reloc->symbol); - - // TODO: check if user forced to link with unresolved symbols and accordingly report the error - if (!LNK_Symbol_IsDefined(symbol->type)) { - lnk_error(LNK_Error_UndefinedSymbol, "%S", symbol->name); - return; - } - - U64 symbol_vsize = 0; - U64 symbol_fsize = 0; - U64 symbol_isect = 0; - U64 symbol_off = 0; - U64 symbol_voff = 0; - U64 symbol_foff = 0; - - LNK_DefinedSymbol *defined_symbol = &symbol->u.defined; - switch (defined_symbol->value_type) { - case LNK_DefinedSymbolValue_Null: break; - case LNK_DefinedSymbolValue_Chunk: { - symbol_isect = lnk_isect_from_symbol(sect_id_map, symbol); - symbol_vsize = lnk_virt_size_from_symbol(sect_id_map, symbol); - symbol_fsize = lnk_file_size_from_symbol(sect_id_map, symbol); - symbol_off = lnk_sect_off_from_symbol(sect_id_map, symbol); - symbol_voff = lnk_virt_off_from_symbol(sect_id_map, symbol); - symbol_foff = lnk_file_off_from_symbol(sect_id_map, symbol); - } break; - case LNK_DefinedSymbolValue_VA: { - symbol_voff = defined_symbol->u.va - base_addr; - } break; - } - - U64 reloc_align = 1; - U64 reloc_size = 0; - S64 reloc_value = 0; - - switch (reloc->type) { - case LNK_Reloc_NULL: /* ignore */ break; - case LNK_Reloc_ADDR_16: { - reloc_value = safe_cast_u16(base_addr + symbol_voff); - reloc_size = 2; - } break; - case LNK_Reloc_ADDR_32: { - reloc_value = (U32)(base_addr + symbol_voff); - reloc_size = 4; - } break; - case LNK_Reloc_ADDR_64: { - reloc_value = base_addr + symbol_voff; - reloc_size = 8; - } break; - case LNK_Reloc_CHUNK_SIZE_FILE_16: { - reloc_value = safe_cast_u16(symbol_fsize); - reloc_size = 2; - } break; - case LNK_Reloc_CHUNK_SIZE_FILE_32: { - reloc_value = symbol_fsize; - reloc_size = 4; - } break; - case LNK_Reloc_CHUNK_SIZE_VIRT_32: { - reloc_value = symbol_vsize; - reloc_size = 4; - } break; - case LNK_Reloc_FILE_ALIGN_32: { - reloc_value = 0; - reloc_size = 4; - reloc_align = file_align; - } break; - case LNK_Reloc_FILE_OFF_32: { - reloc_value = safe_cast_u32(symbol_foff); - reloc_size = 4; - } break; - case LNK_Reloc_FILE_OFF_64: { - reloc_value = symbol_foff; - reloc_size = 8; - } break; - case LNK_Reloc_REL32: { - U64 reloc_voff = lnk_virt_off_from_reloc(sect_id_map, reloc); - reloc_value = safe_cast_s32((S64)(symbol_voff - reloc_voff) - (4 + 0)); - reloc_size = 4; - } break; - case LNK_Reloc_REL32_1: { - U64 reloc_voff = lnk_virt_off_from_reloc(sect_id_map, reloc); - reloc_value = safe_cast_s32((S64)(symbol_voff - reloc_voff) - (4 + 1)); - reloc_size = 4; - } break; - case LNK_Reloc_REL32_2: { - U64 reloc_voff = lnk_virt_off_from_reloc(sect_id_map, reloc); - reloc_value = safe_cast_s32((S64)(symbol_voff - reloc_voff) - (4 + 2)); - reloc_size = 4; - } break; - case LNK_Reloc_REL32_3: { - U64 reloc_voff = lnk_virt_off_from_reloc(sect_id_map, reloc); - reloc_value = safe_cast_s32((S64)(symbol_voff - reloc_voff) - (4 + 3)); - reloc_size = 4; - } break; - case LNK_Reloc_REL32_4: { - U64 reloc_voff = lnk_virt_off_from_reloc(sect_id_map, reloc); - reloc_value = safe_cast_s32((S64)(symbol_voff - reloc_voff) - (4 + 4)); - reloc_size = 4; - } break; - case LNK_Reloc_REL32_5: { - U64 reloc_voff = lnk_virt_off_from_reloc(sect_id_map, reloc); - reloc_value = safe_cast_s32((S64)(symbol_voff - reloc_voff) - (4 + 5)); - reloc_size = 4; - } break; - case LNK_Reloc_SECT_REL: { - reloc_value = safe_cast_u32(symbol_off); - reloc_size = 4; - } break; - case LNK_Reloc_SECT_IDX: { - reloc_value = safe_cast_u32(symbol_isect); - reloc_size = 4; - } break; - case LNK_Reloc_VIRT_ALIGN_32: { - reloc_value = 0; - reloc_size = 4; - reloc_align = virt_align; - } break; - case LNK_Reloc_VIRT_OFF_32: { - reloc_value = safe_cast_u32(symbol_voff); - reloc_size = 4; - } break; - default: NotImplemented; - } - - // read addend - Assert(reloc->apply_off + reloc_size <= chunk_data.size); - U64 raw_addend = 0; - MemoryCopy(&raw_addend, chunk_data.str + reloc->apply_off, reloc_size); - S64 addend = extend_sign64(raw_addend, reloc_size); - - // commit reloc value - reloc_value += addend; - reloc_value = AlignPow2(reloc_value, reloc_align); - MemoryCopy(chunk_data.str + reloc->apply_off, &reloc_value, reloc_size); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_section_reloc_patcher) -{ - LNK_SectionRelocPatcher *task = raw_task; - - String8 image_data = task->image_data; - LNK_SymbolTable *symtab = task->symtab; - LNK_SectionTable *sectab = task->sectab; - LNK_Section **sect_id_map = task->sect_id_map; - U64 base_addr = task->base_addr; - Rng1U64 range = task->range_arr[task_id]; - - for (U64 sect_idx = range.min; sect_idx < range.max; sect_idx += 1) { - LNK_Section *sect = task->sect_arr[sect_idx]; - - for (LNK_Reloc *reloc = sect->reloc_list.first; reloc != 0; reloc = reloc->next) { - LNK_Chunk *chunk = reloc->chunk; - if (lnk_chunk_is_discarded(chunk)) { - continue; - } - String8 chunk_data = lnk_data_from_chunk_ref(sect_id_map, image_data, chunk->ref); - lnk_apply_reloc(base_addr, sectab->sect_align, sectab->file_align, sect_id_map, symtab, chunk_data, reloc); - int bad_vs = 0; (void)bad_vs; - } - } -} - -internal void -lnk_patch_relocs_linker(TP_Context *tp, LNK_SymbolTable *symtab, LNK_SectionTable *sectab, LNK_Section **sect_id_map, String8 image_data, U64 base_addr) +internal String8List +lnk_build_rad_chunk_map(Arena *arena, String8 image_data, U64 thread_count, LNK_ObjList objs, LNK_LibList lib_index[LNK_InputSource_Count], LNK_SectionTable *sectab) { ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); - - LNK_SectionPtrArray sect_arr = lnk_section_ptr_array_from_list(scratch.arena, sectab->list); - - LNK_SectionRelocPatcher task = {0}; - task.image_data = image_data; - task.symtab = symtab; - task.sectab = sectab; - task.sect_id_map = sect_id_map; - task.sect_arr = sect_arr.v; - task.base_addr = base_addr; - task.range_arr = tp_divide_work(scratch.arena, sect_arr.count, tp->worker_count); - tp_for_parallel(tp, 0, tp->worker_count, lnk_section_reloc_patcher, &task); - - scratch_end(scratch); - ProfEnd(); -} + Temp scratch = scratch_begin(&arena, 1); -internal -THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) -{ - LNK_ObjRelocPatcher *task = raw_task; - String8 image_data = task->image_data; - LNK_Obj *obj = task->obj_arr[task_id]; - LNK_Section **sect_id_map = task->sect_id_map; - - for (U64 sect_idx = 0; sect_idx < obj->sect_count; sect_idx += 1) { - LNK_RelocList reloc_list = obj->sect_reloc_list_arr[sect_idx]; - for (LNK_Reloc *reloc = reloc_list.first; reloc != 0; reloc = reloc->next) { - if (lnk_chunk_is_discarded(reloc->chunk)) { - continue; - } - String8 chunk_data = lnk_data_from_chunk_ref(sect_id_map, image_data, reloc->chunk->ref); - lnk_apply_reloc(task->base_addr, task->sectab->sect_align, task->sectab->file_align, task->sect_id_map, task->symtab, chunk_data, reloc); - int bad_vs = 0; (void)bad_vs; - } - } -} + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data); + COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range); -internal void -lnk_patch_relocs_obj(TP_Context *tp, LNK_ObjList obj_list, LNK_SymbolTable *symtab, LNK_SectionTable *sectab, LNK_Section **sect_id_map, String8 image_data, U64 base_addr) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); - - LNK_ObjRelocPatcher task; - task.image_data = image_data; - task.symtab = symtab; - task.sectab = sectab; - task.sect_id_map = sect_id_map; - task.base_addr = base_addr; - task.obj_arr = lnk_obj_arr_from_list(scratch.arena, obj_list); - tp_for_parallel(tp, 0, obj_list.count, lnk_obj_reloc_patcher, &task); - - scratch_end(scratch); - ProfEnd(); -} + String8List map = {0}; -//////////////////////////////// - -internal LNK_SectionTable * -lnk_init_section_table(LNK_SymbolTable *symtab, U64 section_virt_off, U64 sect_align, U64 file_align) -{ - ProfBeginFunction(); - - static struct { - char *name; - char *symbol; - int flags; - } sect_layout[] = { - { ".null", LNK_NULL_SYMBOL_NAME, 0 }, - { ".text", LNK_TEXT_SYMBOL_NAME, LNK_TEXT_SECTION_FLAGS }, - { ".data", LNK_DATA_SYMBOL_NAME, LNK_DATA_SECTION_FLAGS }, - { ".rdata", LNK_RDATA_SYMBOL_NAME, LNK_RDATA_SECTION_FLAGS }, - { ".bss", LNK_BSS_SYMBOL_NAME, LNK_BSS_SECTION_FLAGS }, - { ".xdata", LNK_XDATA_SYMBOL_NAME, LNK_XDATA_SECTION_FLAGS }, - { ".pdata", LNK_PDATA_SYMBOL_NAME, LNK_PDATA_SECTION_FLAGS }, - { ".edata", LNK_EDATA_SYMBOL_NAME, LNK_EDATA_SECTION_FLAGS }, - { ".rsrc", LNK_RSRC_SYMBOL_NAME, LNK_RSRC_SECTION_FLAGS }, - { ".debug", LNK_DEBUG_SYMBOL_NAME, LNK_DEBUG_SECTION_FLAGS }, - { ".tls", LNK_TLS_SYMBOL_NAME, LNK_DATA_SECTION_FLAGS }, - }; - - LNK_SectionTable *sectab = lnk_section_table_alloc(section_virt_off, sect_align, file_align); - for (U64 i = 0; i < ArrayCount(sect_layout); ++i) { - LNK_Section *sect = lnk_section_table_push(sectab, str8_cstring(sect_layout[i].name), sect_layout[i].flags); - sect->symbol_name = str8_cstring(sect_layout[i].symbol); - sect->symbol_name = push_str8_copy(sect->arena, sect->symbol_name); - - lnk_symbol_table_push_defined_chunk(symtab, sect->symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, sect->root, 0, 0, 0); - } - - sectab->null_sect = lnk_section_list_remove(§ab->list, str8_lit(".null")); - - // dont build layout because we discard debug from image and move it to pdb - LNK_Section *debug_sect = lnk_section_table_search(sectab, str8_lit(".debug")); - debug_sect->emit_header = 0; - debug_sect->has_layout = 0; - - ProfEnd(); - return sectab; -} - -internal void -lnk_discard_meta_data_sections(LNK_SectionTable *sectab) -{ - static char * meta_data_sect_arr[] = { - ".gfids", - ".giats", - ".gljmp", - ".gehcont", - }; - for (U64 meta_idx = 0; meta_idx < ArrayCount(meta_data_sect_arr); meta_idx += 1) { - String8 name = str8_cstring(meta_data_sect_arr[meta_idx]); - LNK_Section *sect = lnk_section_table_search(sectab, name); - if (sect) { - lnk_visit_chunks(sect->id, sect->root, lnk_chunk_mark_discarded, NULL); - sect->root->is_discarded = 0; - } - } -} - -//////////////////////////////// - -internal int -lnk_pdata_is_before_x8664(void *raw_a, void *raw_b) -{ - PE_IntelPdata *a = raw_a; - PE_IntelPdata *b = raw_b; - int is_before = a->voff_first < b->voff_first; - return is_before; -} - -//////////////////////////////// - -internal void -lnk_log_size_breakdown(LNK_SectionTable *sectab, LNK_SymbolTable *symtab) -{ - Temp scratch = scratch_begin(0, 0); - - LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, sectab); - - U64 code_size = 0; - U64 data_size = 0; - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != NULL; sect_node = sect_node->next) { - LNK_Section *sect = §_node->data; + ProfBegin("SECTIONS"); + str8_list_pushf(arena, &map, "# SECTIONS\n"); + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + LNK_Section *sect = §_n->data; if (sect->has_layout) { - U64 sect_size = lnk_file_size_from_chunk_ref(sect_id_map, sect->root->ref); - if (sect->flags & COFF_SectionFlag_CntCode) { - code_size += sect_size; - } else if (sect->flags & COFF_SectionFlag_CntInitializedData) { - data_size += sect_size; + str8_list_pushf(arena, &map, "%S\n", sect->name); + str8_list_pushf(arena, &map, "%-8s %-8s %-8s %-8s %-16s %-8s %s\n", "FileOff", "VirtOff", "VirtSize", "FileSize", "Blake3", "SC", "Source"); + + for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + Temp temp = temp_begin(scratch.arena); + LNK_SectionContrib *sc = &sc_chunk->v[sc_idx]; + + U64 file_off = image_section_table[sc->u.sect_idx]->foff + sc->u.off; + U64 virt_off = image_section_table[sc->u.sect_idx]->voff + sc->u.off; + U64 virt_size = sc->u.size; + U64 file_size = sc->u.size; + String8 sc_data = str8_substr(image_data, rng_1u64(file_off, file_off + virt_size)); + + U128 sc_hash = {0}; + if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { + blake3_hasher hasher; blake3_hasher_init(&hasher); + blake3_hasher_update(&hasher, sc_data.str, sc_data.size); + blake3_hasher_finalize(&hasher, (U8 *)&sc_hash, sizeof(sc_hash)); + } + + String8 file_off_str = push_str8f(temp.arena, "%08x", file_off); + String8 virt_off_str = push_str8f(temp.arena, "%08x", virt_off); + String8 virt_size_str = push_str8f(temp.arena, "%08x", virt_size); + String8 file_size_str = push_str8f(temp.arena, "%08x", file_size); + String8 sc_hash_str = push_str8f(temp.arena, "%08x%08x", sc_hash.u64[0], sc_hash.u64[1]); + String8 sc_idx_str = push_str8f(temp.arena, "%llx", sc_idx); + String8 source_str; + { + String8List source_list = {0}; + +#if 0 + // location + if (chunk->obj) { + if (chunk->obj->lib_path.size) { + String8 lib_name = chunk->obj->lib_path; + lib_name = str8_skip_last_slash(lib_name); + lib_name = str8_chop_last_dot(lib_name); + + String8 obj_name = chunk->obj->path; + obj_name = str8_skip_last_slash(obj_name); + + str8_list_pushf(temp.arena, &source_list, "%S:%S", lib_name, obj_name); + } else { + str8_list_push(temp.arena, &source_list, chunk->obj->path); + } + } +#else + str8_list_pushf(temp.arena, &source_list, ""); +#endif + + // string join + source_str = str8_list_join(temp.arena, &source_list, &(StringJoin){.sep=str8_lit(" ")}); + } + + str8_list_pushf(arena, &map, "%-8S %-8S %-8S %-8S %-16S %-8S %S\n", file_off_str, virt_off_str, virt_size_str, file_size_str, sc_hash, sc_idx_str, source_str); + + temp_end(temp); + } + } + str8_list_pushf(arena, &map, "\n"); + } + } + ProfEnd(); + + + ProfBegin("SYMBOLS"); + str8_list_pushf(arena, &map, "# SYMBOLS\n"); + str8_list_pushf(arena, &map, "%-8s %s\n", "Sect:Idx", "Symbol"); + for (LNK_ObjNode *obj_n = objs.first; obj_n != 0; obj_n = obj_n->next) { + LNK_Obj *obj = &obj_n->data; + + 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) { + String8 sc = push_str8f(scratch.arena, "%x:%x", symbol.section_number, symbol.value); + + String8 lib_name = obj->lib_path; + lib_name = str8_skip_last_slash(lib_name); + lib_name = str8_chop_last_dot(lib_name); + + String8 obj_name = obj->path; + obj_name = str8_skip_last_slash(obj_name); + + str8_list_pushf(arena, &map, "%-8S (%S%s%S) %S\n", + sc, + lib_name, lib_name.size ? ":" : "", obj_name, + symbol.name); } } } - - LNK_Symbol *dos_header_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_DOS_HEADER_SYMBOL_NAME)); - LNK_Symbol *dos_program_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_DOS_PROGRAM_SYMBOL_NAME)); - LNK_Symbol *coff_file_header_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_COFF_FILE_HEADER_SYMBOL_NAME)); - LNK_Symbol *coff_section_header_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_COFF_SECT_HEADER_ARRAY_SYMBOL_NAME)); - LNK_Symbol *pe_opt_header_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_PE_OPT_HEADER_SYMBOL_NAME)); - LNK_Symbol *pe_directories_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Internal, str8_lit(LNK_PE_DIRECTORY_ARRAY_SYMBOL_NAME)); - - LNK_Chunk *dos_header_chunk = dos_header_symbol->u.defined.u.chunk; - LNK_Chunk *dos_program_chunk = dos_program_symbol->u.defined.u.chunk; - LNK_Chunk *coff_file_header_chunk = coff_file_header_symbol->u.defined.u.chunk; - LNK_Chunk *coff_section_header_chunk = coff_section_header_symbol->u.defined.u.chunk; - LNK_Chunk *pe_opt_header_chunk = pe_opt_header_symbol->u.defined.u.chunk; - LNK_Chunk *pe_directories_chunk = pe_directories_symbol->u.defined.u.chunk; - - U64 dos_header_size = lnk_file_size_from_chunk_ref(sect_id_map, dos_header_chunk->ref); - U64 dos_program_size = lnk_file_size_from_chunk_ref(sect_id_map, dos_program_chunk->ref); - U64 coff_file_header_size = lnk_file_size_from_chunk_ref(sect_id_map, coff_file_header_chunk->ref); - U64 coff_section_header_size = lnk_file_size_from_chunk_ref(sect_id_map, coff_section_header_chunk->ref); - U64 pe_opt_header_size = lnk_file_size_from_chunk_ref(sect_id_map, pe_opt_header_chunk->ref); - U64 pe_directories_size = lnk_file_size_from_chunk_ref(sect_id_map, pe_directories_chunk->ref); - - String8List output_list; MemoryZeroStruct(&output_list); - str8_list_pushf(scratch.arena, &output_list, "--- Image Size Breakdown -------------------------------------------------------"); - str8_list_pushf(scratch.arena, &output_list, " DOS Header: %M", dos_header_size); - str8_list_pushf(scratch.arena, &output_list, " DOS Program Stub: %M", dos_program_size); - str8_list_pushf(scratch.arena, &output_list, " COFF Header: %M", coff_file_header_size); - str8_list_pushf(scratch.arena, &output_list, " COFF Section Headers: %M", coff_section_header_size); - str8_list_pushf(scratch.arena, &output_list, " PE Header: %M", pe_opt_header_size); - str8_list_pushf(scratch.arena, &output_list, " Directories: %M", pe_directories_size); - str8_list_pushf(scratch.arena, &output_list, " Code Size: %M", code_size); - str8_list_pushf(scratch.arena, &output_list, " Data Size: %M", data_size); - - StringJoin new_line_join = { str8_lit_comp(""), str8_lit_comp("\n"), str8_lit_comp("") }; - String8 output = str8_list_join(scratch.arena, &output_list, &new_line_join); - lnk_log(LNK_Log_SizeBreakdown, "%S\n", output); - + str8_list_pushf(arena, &map, "\n"); + ProfEnd(); + + + 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); + } + } + } + ProfEnd(); + + scratch_end(scratch); + ProfEnd(); + return map; } -internal void -lnk_log_link_stats(LNK_ObjList obj_list, LNK_LibList *lib_index, LNK_SectionTable *sectab) -{ - Temp scratch = scratch_begin(0, 0); - - U32 lib_count = 0; - for (U32 i = 0; i < LNK_InputSource_Count; i += 1) { - lib_count += lib_index[i].count; - } - U32 reloc_count = 0; - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != NULL; sect_node = sect_node->next) { - reloc_count += sect_node->data.reloc_list.count; - } - - String8List output_list = {0}; - str8_list_pushf(scratch.arena, &output_list, "------ Link Stats --------------------------------------------------------------"); - str8_list_pushf(scratch.arena, &output_list, " Linked Objs: %u", obj_list.count); - str8_list_pushf(scratch.arena, &output_list, " Linked Libs: %u", lib_count); - str8_list_pushf(scratch.arena, &output_list, " Relocs Patched: %u", reloc_count); - - StringJoin new_line_join = { str8_lit_comp(""), str8_lit_comp("\n"), str8_lit_comp("") }; - String8 output = str8_list_join(scratch.arena, &output_list, &new_line_join); - lnk_log(LNK_Log_LinkStats, "%S\n", output); - - scratch_end(scratch); -} +//////////////////////////////// internal void lnk_log_timers(void) @@ -2680,292 +3398,29 @@ lnk_write_thread(void *raw_ctx) ProfEnd(); } -internal -THREAD_POOL_TASK_FUNC(lnk_blake3_hasher_task) -{ - ProfBeginFunction(); - - LNK_Blake3Hasher *task = raw_task; - Rng1U64 range = task->ranges[task_id]; - String8 sub_data = str8_substr(task->data, range); - - blake3_hasher hasher; blake3_hasher_init(&hasher); - blake3_hasher_update(&hasher, sub_data.str, sub_data.size); - blake3_hasher_finalize(&hasher, (U8 *)task->hashes[task_id].u64, sizeof(task->hashes[task_id].u64)); - - ProfEnd(); -} - -internal U128 -lnk_blake3_hash_parallel(TP_Context *tp, U64 chunk_count, String8 data) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - ProfBegin("Hash Chunks"); - LNK_Blake3Hasher task = {0}; - task.data = data; - task.ranges = tp_divide_work(scratch.arena, data.size, chunk_count); - task.hashes = push_array(scratch.arena, U128, chunk_count); - tp_for_parallel(tp, 0, chunk_count, lnk_blake3_hasher_task, &task); - ProfEnd(); - - ProfBegin("Combine Hashes"); - blake3_hasher hasher; blake3_hasher_init(&hasher); - for (U64 i = 0; i < chunk_count; ++i) { - blake3_hasher_update(&hasher, (U8 *)task.hashes[i].u64, sizeof(task.hashes[i].u64)); - } - U128 result; - blake3_hasher_finalize(&hasher, (U8 *)result.u64, sizeof(result.u64)); - ProfEnd(); - - scratch_end(scratch); - ProfEnd(); - return result; -} - -LNK_CHUNK_VISITOR_SIG(lnk_max_tls_align) -{ - if (chunk->type == LNK_Chunk_Leaf) { - U64 *max_align = ud; - *max_align = Max(*max_align, chunk->align); - } - return 0; -} - -global LNK_Section **g_rad_sort_sect_id_map = 0; - -typedef struct -{ - U64 off; - LNK_Chunk *chunk; -} LNK_ChunkOffPair; - -internal int -lnk_map_sort_on_chunk_file_off(void *raw_a, void *raw_b) -{ - LNK_Chunk **a = raw_a; - LNK_Chunk **b = raw_b; - - U64 a_file_off = lnk_virt_off_from_chunk_ref(g_rad_sort_sect_id_map, (*a)->ref); - U64 b_file_off = lnk_virt_off_from_chunk_ref(g_rad_sort_sect_id_map, (*b)->ref); - - int is_before = a_file_off < b_file_off; - return is_before; -} - -internal int -lnk_map_sort_on_chunk_off(void *raw_a, void *raw_b) -{ - LNK_ChunkOffPair *a = raw_a; - LNK_ChunkOffPair *b = raw_b; - - int is_before = a->off < b->off; - return is_before; -} - -internal U64 -lnk_chunk_off_pair_array_bsearch(LNK_ChunkOffPair *arr, U64 count, U64 value) -{ - if(count > 1 && arr[0].off <= value && value < arr[count-1].off) - { - U64 l = 0; - U64 r = count - 1; - for(; l <= r; ) - { - U64 m = l + (r - l) / 2; - if(arr[m].off == value) - { - return m; - } - else if(arr[m].off < value) - { - l = m + 1; - } - else - { - r = m - 1; - } - } - } - else if (count == 1 && arr[0].off == value) - { - return 0; - } - return max_U64; -} - -internal String8List -lnk_build_rad_chunk_map(Arena *arena, String8 image_data, U64 thread_count, LNK_ObjList objs, LNK_LibList lib_index[LNK_InputSource_Count], LNK_SectionTable *sectab, LNK_SymbolTable *symtab) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - String8List map = {0}; - - LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, sectab); - - ProfBegin("SECTIONS"); - str8_list_pushf(arena, &map, "# SECTIONS\n"); - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - LNK_Section *sect = §_n->data; - if (sect->has_layout) { - LNK_Chunk **chunks = push_array_no_zero(scratch.arena, LNK_Chunk *, sect->layout.total_count); - MemoryCopyTyped(chunks, sect->layout.chunk_ptr_array, sect->layout.total_count); - - g_rad_sort_sect_id_map = sect_id_map; - radsort(chunks, sect->layout.total_count, lnk_map_sort_on_chunk_file_off); - - str8_list_pushf(arena, &map, "%S\n", sect->name); - str8_list_pushf(arena, &map, "%-8s %-8s %-8s %-8s %-16s %-8s %s\n", "FileOff", "VirtOff", "VirtSize", "FileSize", "Blake3", "ChunkRef", "Source"); - for (U64 chunk_idx = 0; chunk_idx < sect->layout.total_count; ++chunk_idx) { - LNK_Chunk *chunk = chunks[chunk_idx]; - if (chunk != g_null_chunk_ptr) { - Temp temp = temp_begin(scratch.arena); - - U64 file_off = lnk_file_off_from_chunk_ref(sect_id_map, chunk->ref); - U64 virt_off = lnk_virt_off_from_chunk_ref(sect_id_map, chunk->ref); - U64 virt_size = lnk_virt_size_from_chunk_ref(sect_id_map, chunk->ref); - U64 file_size = lnk_file_size_from_chunk_ref(sect_id_map, chunk->ref); - String8 chunk_data = lnk_data_from_chunk_ref(sect_id_map, image_data, chunk->ref); - - U128 chunk_hash = {0}; - if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { - blake3_hasher hasher; blake3_hasher_init(&hasher); - blake3_hasher_update(&hasher, chunk_data.str, chunk_data.size); - blake3_hasher_finalize(&hasher, (U8 *)&chunk_hash, sizeof(chunk_hash)); - } - - String8 file_off_str = push_str8f(temp.arena, "%08x", file_off); - String8 virt_off_str = push_str8f(temp.arena, "%08x", virt_off); - String8 virt_size_str = push_str8f(temp.arena, "%08x", virt_size); - String8 file_size_str = push_str8f(temp.arena, "%08x", file_size); - String8 chunk_hash_str = push_str8f(temp.arena, "%08x%08x", chunk_hash.u64[0], chunk_hash.u64[1]); - String8 chunk_ref_str = push_str8f(temp.arena, "{%llx,%llx}", chunk->ref.sect_id, chunk->ref.chunk_id); - String8 source_str; - { - String8List source_list = {0}; - - // chunk type - str8_list_pushf(temp.arena, &source_list, "[%S]", lnk_string_from_chunk_type(chunk->type)); - - // location - if (chunk->obj) { - if (chunk->obj->lib_path.size) { - String8 lib_name = chunk->obj->lib_path; - lib_name = str8_skip_last_slash(lib_name); - lib_name = str8_chop_last_dot(lib_name); - - String8 obj_name = chunk->obj->path; - obj_name = str8_skip_last_slash(obj_name); - - str8_list_pushf(temp.arena, &source_list, "%S:%S", lib_name, obj_name); - } else { - str8_list_push(temp.arena, &source_list, chunk->obj->path); - } - } - - // debug comment -#if LNK_DEBUG_CHUNKS - if (chunk->debug.size) { - if (source_str.size) { - str8_list_pushf(temp.arena, &source_list, "(%S)", chunk->debug); - } else if (chunk->debug.size) { - str8_list_push(temp.arena, &source_list, chunk->debug); - } - } -#endif - - // string join - source_str = str8_list_join(temp.arena, &source_list, &(StringJoin){.sep=str8_lit(" ")}); - } - - str8_list_pushf(arena, &map, "%-8S %-8S %-8S %-8S %-16S %-8S %S\n", file_off_str, virt_off_str, virt_size_str, file_size_str, chunk_hash_str, chunk_ref_str, source_str); - - temp_end(temp); - } - } - str8_list_pushf(arena, &map, "\n"); - } - } - ProfEnd(); - - - ProfBegin("SYMBOLS"); - str8_list_pushf(arena, &map, "# SYMBOLS\n"); - str8_list_pushf(arena, &map, "%-8s %s\n", "ChunkRef", "Symbol"); - for (LNK_ObjNode *obj_n = objs.first; obj_n != 0; obj_n = obj_n->next) { - LNK_Obj *obj = &obj_n->data; - for (LNK_SymbolNode *symbol_n = obj->symbol_list.first; symbol_n != 0; symbol_n = symbol_n->next) { - LNK_Symbol *symbol = symbol_n->data; - if (LNK_Symbol_IsDefined(symbol->type)) { - if (symbol->u.defined.value_type == LNK_DefinedSymbolValue_Chunk) { - LNK_Chunk *chunk = symbol->u.defined.u.chunk; - String8 chunk_ref_str = push_str8f(scratch.arena, "{%llx,%llx}", chunk->ref.sect_id, chunk->ref.chunk_id); - - String8 lib_name = obj->lib_path; - lib_name = str8_skip_last_slash(lib_name); - lib_name = str8_chop_last_dot(lib_name); - - String8 obj_name = obj->path; - obj_name = str8_skip_last_slash(obj_name); - - str8_list_pushf(arena, &map, "%-8S (%S%s%S) %S\n", - chunk_ref_str, - lib_name, lib_name.size ? ":" : "", obj_name, - symbol->name); - } - } - } - } - str8_list_pushf(arena, &map, "\n"); - ProfEnd(); - - - 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); - } - } - } - ProfEnd(); - - - scratch_end(scratch); - ProfEnd(); - return map; -} - internal void lnk_run(int argc, char **argv) { enum State { State_Null, - State_InputSymbols, - State_InputImports, State_InputDisallowLibs, - State_InputLibs, + State_InputImports, + State_InputSymbols, State_InputObjs, - State_LookupUndef, - State_LookupWeak, - State_BuildImportObjs, - State_BuildExportObjs, - State_BuildDebugDirectoryObjs, - State_BuildAndInputLinkerObj, - State_BuildAndInputResObj, + State_InputLibs, State_PushDllHelperUndefSymbol, State_PushLinkerSymbols, State_PushLoadConfigUndefSymbol, - State_SearchEntryPoint, + State_LookupUndef, + State_LookupWeak, + State_LookupEntryPoint, State_ReportUnresolvedSymbols, - State_DiscardMetaDataSections, - State_MergeSections, - State_BuildCFGuards, - State_BuildBaseRelocs, - State_FinalizeImage, + State_InputImportObjs, + State_InputExportObjs, + State_InputResObj, + State_InputLinkerObj, + State_InputDebugDirObj, + State_BuildImage, State_BuildImpLib, State_BuildRadChunkMap, State_BuildDebugInfo, @@ -2994,22 +3449,25 @@ lnk_run(int argc, char **argv) lnk_init_error_handler(); - LNK_Config *config = lnk_build_config(scratch.arena, argc, argv); + LNK_Config *config = lnk_build_config(scratch.arena, argc, argv); + TP_Context *tp = tp_alloc(scratch.arena, config->worker_count, config->max_worker_count, config->shared_thread_pool_name); TP_Arena *tp_arena = tp_arena_alloc(tp); // inputs String8List include_symbol_list = config->include_symbol_list; String8List input_disallow_lib_list = config->disallow_lib_list; - String8List input_manifest_path_list = str8_list_copy(tp_arena->v[0], &config->input_list[LNK_Input_Manifest]); + String8List input_manifest_path_list = str8_list_copy(scratch.arena, &config->input_list[LNK_Input_Manifest]); String8List manifest_dep_list = str8_list_copy(scratch.arena, &config->manifest_dependency_list); LNK_ExportParseList export_symbol_list = config->export_symbol_list; - LNK_MergeDirectiveList merge_list = config->merge_list; LNK_AltNameList alt_name_list = config->alt_name_list; - LNK_InputLibList input_libs[LNK_InputSource_Count] = {0}; LNK_InputObjList input_obj_list = {0}; LNK_InputImportList input_import_list = {0}; LNK_SymbolList input_weak_list = {0}; + LNK_InputLibList input_libs[LNK_InputSource_Count] = { + config->input_list[LNK_Input_Lib], // command line libs + config->input_default_lib_list // default libs + }; // :null_obj lnk_input_obj_list_push(scratch.arena, &input_obj_list); @@ -3017,60 +3475,45 @@ lnk_run(int argc, char **argv) // 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); - - // input command line libs - input_libs[LNK_InputSource_CmdLine] = config->input_list[LNK_Input_Lib]; - input_libs[LNK_InputSource_Default] = config->input_default_lib_list; - LNK_ImportTableFlags imptab_flags = 0; - if (config->flags & LNK_ConfigFlag_DelayUnload) { - imptab_flags |= LNK_ImportTableFlag_EmitUiat; - } - if (config->flags & LNK_ConfigFlag_DelayBind) { - imptab_flags |= LNK_ImportTableFlag_EmitUiat; - } - // state - LNK_SymbolTable *symtab = lnk_symbol_table_init(tp_arena); - LNK_SectionTable *sectab = lnk_init_section_table(symtab, config->section_virt_off, config->sect_align, config->file_align); - LNK_ImportTable *imptab_static = lnk_import_table_alloc(0); - LNK_ImportTable *imptab_delayed = lnk_import_table_alloc(imptab_flags); - LNK_ExportTable *exptab = lnk_export_table_alloc(); - Arena *ht_arena = arena_alloc(); - HashTable *disallow_lib_ht = hash_table_init(scratch.arena, 0x100); - HashTable *delay_load_dll_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); - LNK_SymbolList lookup_undef_list = {0}; - LNK_SymbolList lookup_weak_list = {0}; - LNK_SymbolList unresolved_undef_list = {0}; - LNK_SymbolList unresolved_weak_list = {0}; - U64 entry_search_attempts = 0; - B32 build_debug_info = lnk_do_debug_info(config); - B32 build_linker_obj = build_debug_info; - B32 build_debug_directory_objs = build_debug_info; - B32 build_import_objs = 1; - B32 build_export_objs = 1; - B32 build_res_obj = 1; - B32 discard_meta_data_sections = 1; - B32 merge_sections = !!(config->flags & LNK_ConfigFlag_Merge); - B32 build_cf_guards = 0; // (config->flags != LNK_Guard_NONE); - B32 build_base_relocs = !(config->flags & LNK_ConfigFlag_Fixed); - B32 report_unresolved_symbols = 1; - B32 build_imp_lib = config->build_imp_lib; - B32 build_rad_chunk_map = (config->rad_chunk_map == LNK_SwitchState_Yes); - LNK_ObjList obj_list = {0}; - LNK_LibList lib_index[LNK_InputSource_Count] = {0}; - String8 image_data = str8_zero(); - OS_Handle image_write_thread = {0}; + LNK_SymbolTable *symtab = lnk_symbol_table_init(tp_arena); + LNK_SectionTable *sectab = 0; + LNK_ImportTable *imptab_static = lnk_import_table_alloc(0); + LNK_ImportTable *imptab_delayed = lnk_import_table_alloc(config->import_table_flags); + LNK_ExportTable *exptab = lnk_export_table_alloc(); + LNK_ObjList obj_list = {0}; + LNK_LibList lib_index[LNK_InputSource_Count] = {0}; + Arena *ht_arena = arena_alloc(); + HashTable *disallow_lib_ht = hash_table_init(scratch.arena, 0x100); + HashTable *delay_load_dll_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); + LNK_SymbolList lookup_undef_list = {0}; + LNK_SymbolList lookup_weak_list = {0}; + LNK_SymbolList unresolved_undef_list = {0}; + LNK_SymbolList unresolved_weak_list = {0}; + U64 entry_point_lookup_attempts = 0; + B32 report_unresolved_symbols = 1; + B32 input_import_objs = 1; + B32 input_export_objs = 1; + B32 input_res_obj = 1; + B32 input_linker_obj = lnk_do_debug_info(config); + B32 input_debug_dir_obj = lnk_do_debug_info(config); + B32 build_imp_lib = config->build_imp_lib; + B32 build_rad_chunk_map = (config->rad_chunk_map == LNK_SwitchState_Yes); + B32 build_debug_info = lnk_do_debug_info(config); + String8 image_data = str8_zero(); + OS_Handle image_write_thread = {0}; - // init state machine + // + // Init state machine + // struct StateList state_list = {0}; state_list_push(scratch.arena, state_list, State_InputDisallowLibs); state_list_push(scratch.arena, state_list, State_InputObjs); state_list_push(scratch.arena, state_list, State_InputLibs); - state_list_push(scratch.arena, state_list, State_PushLinkerSymbols); if (config->delay_load_dll_list.node_count) { for (String8Node *delay_load_dll_node = config->delay_load_dll_list.first; delay_load_dll_node != 0; @@ -3083,11 +3526,17 @@ lnk_run(int argc, char **argv) state_list_push(scratch.arena, state_list, State_PushLoadConfigUndefSymbol); } - // default section merges - lnk_merge_directive_list_push(scratch.arena, &merge_list, (LNK_MergeDirective){ str8_lit_comp(".xdata") , str8_lit_comp(".rdata") }); + // + // Default section merges + // + lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".xdata"), str8_lit_comp(".rdata") }); + lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".tls"), str8_lit_comp(".data") }); + lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".edata"), str8_lit_comp(".rdata") }); + lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".idata"), str8_lit_comp(".rdata") }); + lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".didat"), str8_lit_comp(".didat") }); + lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".RAD_LINKER_DEBUG_DIR"), str8_lit_comp(".rdata") }); ProfBegin("Image"); // :EndImage - ProfBegin("Build"); // :EndBuild lnk_timer_begin(LNK_Timer_Image); // run states @@ -3096,177 +3545,18 @@ lnk_run(int argc, char **argv) enum State state = state_list_pop(state_list); switch (state) { case State_Null: break; - case State_SearchEntryPoint: { - ProfBegin("Search Entry Point"); - LNK_Symbol *entry_point_symbol = 0; - - B32 is_entry_point_unspecified = config->entry_point_name.size == 0; - if (is_entry_point_unspecified) { - if (config->subsystem == PE_WindowsSubsystem_UNKNOWN) { - // we don't have a subsystem and entry point name, - // so we loop over every subsystem and search potential entry - // points in the symbol table - for (U64 subsys_idx = 0; subsys_idx < PE_WindowsSubsystem_COUNT; subsys_idx += 1) { - String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); - for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { - entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Defined, name_arr.v[entry_idx]); - if (entry_point_symbol) { - config->subsystem = (PE_WindowsSubsystem)subsys_idx; - goto dbl_break; - } - } - } - - // search for potential entry points in libs - if (!entry_point_symbol) { - for (U64 subsys_idx = 0; subsys_idx < PE_WindowsSubsystem_COUNT; subsys_idx += 1) { - String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); - for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { - entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Lib, name_arr.v[entry_idx]); - if (entry_point_symbol) { - config->subsystem = (PE_WindowsSubsystem)subsys_idx; - goto dbl_break; - } - } - } - } - - dbl_break:; - } else { - // we have subsystem but no entry point name, get potential entry point names - // and see which is in the symbol table - String8Array name_arr = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics); - for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Defined, name_arr.v[entry_idx]); - if (symbol) { - if (entry_point_symbol) { - lnk_error(LNK_Error_EntryPoint, - "multiple entry point symbols found: %S(%S) and %S(%S)", - entry_point_symbol->name, entry_point_symbol->obj->path, - symbol->name, symbol->obj->path); - } else { - entry_point_symbol = symbol; - } - } - } - - // search for entry point in libs - if (!entry_point_symbol) { - for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { - entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Lib, name_arr.v[entry_idx]); - if (entry_point_symbol) { - break; - } - } - } - } - - // redirect user entry to appropriate CRT entry - if (entry_point_symbol) { - config->entry_point_name = entry_point_symbol->name; - if (str8_match_lit("wmain", config->entry_point_name, 0)) { - config->entry_point_name = str8_lit("wmainCRTStartup"); - } else if (str8_match_lit("main", config->entry_point_name, 0)) { - config->entry_point_name = str8_lit("mainCRTStartup"); - } else if (str8_match_lit("WinMain", config->entry_point_name, 0)) { - config->entry_point_name = str8_lit("WinMainCRTStartup"); - } else if (str8_match_lit("wWinMain", config->entry_point_name, 0)) { - config->entry_point_name = str8_lit("wWinMainCRTStartup"); - } - } - } - - // generate undefined symbol so in case obj is in lib it will be linked - if (config->entry_point_name.size) { - str8_list_push(scratch.arena, &include_symbol_list, config->entry_point_name); - } - // no entry point, error and exit - else { - lnk_error(LNK_Error_EntryPoint, "unable to find entry point symbol"); - } - - // 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; - } - } - - // do we have a subsystem? - if (config->subsystem == PE_WindowsSubsystem_UNKNOWN) { - lnk_error(LNK_Error_NoSubsystem, "unknown subsystem, please use /SUBSYSTEM to set subsytem type you need"); - } - - if (config->subsystem_ver.major == 0 && config->subsystem_ver.minor == 0) { - // subsystem version not specified, set default values - 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); - int ver_cmp = version_compar(config->subsystem_ver, min_subsystem_ver); - if (ver_cmp < 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); - } - - ProfEnd(); - } break; - case State_PushDllHelperUndefSymbol: { - ProfBegin("Puhs Dll Helper Undef Symbol"); - - String8 delay_helper_name = str8_zero(); - switch (config->machine) { - case COFF_MachineType_X86: delay_helper_name = str8_cstring(LNK_DELAY_LOAD_HELPER2_X86_SYMBOL_NAME); break; - case COFF_MachineType_X64: delay_helper_name = str8_cstring(LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME); break; - default: NotImplemented; - } - - str8_list_push(scratch.arena, &include_symbol_list, delay_helper_name); - ProfEnd(); - } break; - case State_PushLoadConfigUndefSymbol: { - ProfBegin("Push Load Config Undef Symbol"); - String8 load_config_name = str8_lit(LNK_LOAD_CONFIG_SYMBOL_NAME); - str8_list_push(scratch.arena, &include_symbol_list, load_config_name); - ProfEnd(); - } break; - case State_PushLinkerSymbols: { - ProfBegin("Push Linker Symbols"); - lnk_push_linker_symbols(symtab, config->machine); - ProfEnd(); - } break; - case State_InputSymbols: { - ProfBegin("Input Symbols"); - - ProfBegin("Push /INCLUDE Symbols"); - for (String8Node *include_node = include_symbol_list.first; include_node != 0; include_node = include_node->next) { - String8 name = push_str8_copy(symtab->arena->v[0], include_node->string); - LNK_Symbol *symbol = lnk_make_undefined_symbol(symtab->arena->v[0], name, LNK_SymbolScopeFlag_Main); - lnk_symbol_list_push(scratch.arena, &lookup_undef_list, symbol); - } - ProfEnd(); - - ProfBegin("Push /ALTERNATIVENAME Symbols"); - Assert(alt_name_list.from_list.node_count == alt_name_list.to_list.node_count); - for (String8Node *from_node = alt_name_list.from_list.first, *to_node = alt_name_list.to_list.first; - from_node != 0; - from_node = from_node->next, to_node = to_node->next) { - LNK_Symbol *weak = lnk_symbol_table_push_weak(symtab, from_node->string, COFF_WeakExt_SearchAlias, to_node->string); - lnk_symbol_list_push(scratch.arena, &input_weak_list, weak); - } - ProfEnd(); - // we defined new symbols, give unresolved symbols another chance to be resolved - lnk_symbol_list_concat_in_place(&lookup_undef_list, &unresolved_undef_list); - lnk_symbol_list_concat_in_place(&lookup_weak_list, &input_weak_list); - lnk_symbol_list_concat_in_place(&lookup_weak_list, &unresolved_weak_list); + case State_InputDisallowLibs: { + ProfBegin("Input /disallowlib"); - // reset inputs - MemoryZeroStruct(&include_symbol_list); - MemoryZeroStruct(&alt_name_list); - MemoryZeroStruct(&input_weak_list); + for (String8Node *name_n = input_disallow_lib_list.first; name_n != 0; name_n = name_n->next) { + if ( ! lnk_is_lib_disallowed(disallow_lib_ht, name_n->string)) { + lnk_push_disallow_lib(scratch.arena, disallow_lib_ht, name_n->string); + } + } + + // reset input + MemoryZeroStruct(&input_disallow_lib_list); ProfEnd(); } break; @@ -3324,17 +3614,41 @@ lnk_run(int argc, char **argv) ProfEnd(); } break; - case State_InputDisallowLibs: { - ProfBegin("Input /disallowlib"); + case State_InputSymbols: { + ProfBegin("Input Symbols"); - for (String8Node *name_n = input_disallow_lib_list.first; name_n != 0; name_n = name_n->next) { - if ( ! lnk_is_lib_disallowed(disallow_lib_ht, name_n->string)) { - lnk_push_disallow_lib(scratch.arena, disallow_lib_ht, name_n->string); - } + ProfBegin("Push /INCLUDE Symbols"); + for (String8Node *include_node = include_symbol_list.first; include_node != 0; include_node = include_node->next) { + String8 name = push_str8_copy(symtab->arena->v[0], include_node->string); + LNK_Symbol *symbol = lnk_make_undefined_symbol(symtab->arena->v[0], name, 0); + lnk_symbol_list_push(scratch.arena, &lookup_undef_list, symbol); } + ProfEnd(); - // reset input - MemoryZeroStruct(&input_disallow_lib_list); +#if 0 + ProfBegin("Push /ALTERNATIVENAME Symbols"); + Assert(alt_name_list.from_list.node_count == alt_name_list.to_list.node_count); + for (String8Node *from_node = alt_name_list.from_list.first, *to_node = alt_name_list.to_list.first; + from_node != 0; + from_node = from_node->next, to_node = to_node->next) { + LNK_Symbol *fallback = lnk_make_undefined_symbol(scratch.arena, to_node->string); + LNK_Symbol *weak = lnk_make_weak_symbol(scratch.arena, from_node->string, COFF_WeakExt_SearchAlias, fallback); + lnk_symbol_list_push(scratch.arena, &lookup_undef_list, fallback); + lnk_symbol_list_push(scratch.arena, &input_weak_list, weak); + lnk_symbol_table_push(symtab, weak); + } + ProfEnd(); +#endif + + // we defined new symbols, give unresolved symbols another chance to be resolved + lnk_symbol_list_concat_in_place(&lookup_undef_list, &unresolved_undef_list); + lnk_symbol_list_concat_in_place(&lookup_weak_list, &input_weak_list); + lnk_symbol_list_concat_in_place(&lookup_weak_list, &unresolved_weak_list); + + // reset inputs + MemoryZeroStruct(&include_symbol_list); + MemoryZeroStruct(&alt_name_list); + MemoryZeroStruct(&input_weak_list); ProfEnd(); } break; @@ -3386,22 +3700,15 @@ lnk_run(int argc, char **argv) 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, - sectab, - config->function_pad_min, - config->machine, - unique_obj_input_list.count, - input_obj_arr); + 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 (U64 obj_idx = 0; obj_idx < obj_node_arr.count; obj_idx += 1) { - if (obj_node_arr.v[obj_idx].data.machine != COFF_MachineType_Unknown) { - config->machine = obj_node_arr.v[obj_idx].data.machine; + 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; } } @@ -3413,12 +3720,11 @@ lnk_run(int argc, char **argv) LNK_DirectiveInfo directive_info = {0}; { - COFF_FileHeaderInfo coff_info = coff_file_header_info_from_data(obj->data); - COFF_SectionHeader *coff_section_table = (COFF_SectionHeader *)str8_substr(obj->data, coff_info.section_table_range).str; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; String8List drectve_data = {0}; - for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *sect_header = &coff_section_table[sect_idx]; + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; if (sect_header->flags & COFF_SectionFlag_LnkInfo) { String8 sect_name = str8_cstring_capped(sect_header->name, sect_header->name + sizeof(sect_header->name)); if (str8_match(sect_name, str8_lit(".drectve"), 0)) { @@ -3467,7 +3773,7 @@ lnk_run(int argc, char **argv) for (String8Node *value_n = dir->value_list.first; value_n != 0; value_n = value_n->next) { LNK_MergeDirective merge_dir; if (lnk_parse_merge_directive(value_n->string, &merge_dir)) { - lnk_merge_directive_list_push(scratch.arena, &merge_list, merge_dir); + lnk_merge_directive_list_push(scratch.arena, &config->merge_list, merge_dir); } else { lnk_error_obj(LNK_Warning_IllData, obj, "can't parse merge directive \"%S\"", value_n->string); } @@ -3514,16 +3820,11 @@ lnk_run(int argc, char **argv) } ProfEnd(); - // update symbol table - lnk_push_defined_symbols(tp, symtab, obj_node_arr); - - // collect symbols for input - LNK_SymbolList new_weak_list = lnk_run_symbol_collector(tp, tp_arena, obj_node_arr, LNK_Symbol_Weak); - LNK_SymbolList new_undef_list = lnk_run_symbol_collector(tp, tp_arena, obj_node_arr, LNK_Symbol_Undefined); // TODO: allocate these on temp arena + LNK_SymbolInputResult input_result = lnk_input_obj_symbols(tp, tp_arena, symtab, obj_node_arr); // schedule symbol input - lnk_symbol_list_concat_in_place(&input_weak_list, &new_weak_list); - lnk_symbol_list_concat_in_place(&lookup_undef_list, &new_undef_list); + lnk_symbol_list_concat_in_place(&input_weak_list, &input_result.weak_symbols); + lnk_symbol_list_concat_in_place(&lookup_undef_list, &input_result.undef_symbols); // reset input objs MemoryZeroStruct(&input_obj_list); @@ -3601,22 +3902,20 @@ lnk_run(int argc, char **argv) ProfEnd(); ProfBegin("Disk Read Libs"); - String8Array path_arr = str8_array_from_list(temp.arena, &unique_input_lib_list); - String8Array data_arr = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], path_arr); + 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], paths); ProfEnd(); ProfBegin("Lib Init"); - LNK_LibNodeArray lib_arr = lnk_lib_list_push_parallel(tp, tp_arena, &lib_index[input_source], data_arr, path_arr); + LNK_LibNodeArray libs = lnk_lib_list_push_parallel(tp, tp_arena, &lib_index[input_source], datas, paths); ProfEnd(); - lnk_push_lazy_symbols(tp, symtab, lib_arr); + lnk_input_lib_symbols(tp, symtab, libs); if (lnk_get_log_status(LNK_Log_InputLib)) { - if (lib_arr.count > 0) { + if (libs.count > 0) { U64 input_size = 0; - for (U64 i = 0; i < lib_arr.count; ++i) { - input_size += lib_arr.v[i].data.data.size; - } + for (U64 i = 0; i < libs.count; ++i) { input_size += libs.v[i].data.data.size; } lnk_log(LNK_Log_InputObj, "[ Lib Input Size %M ]", input_size); } } @@ -3630,51 +3929,230 @@ lnk_run(int argc, char **argv) ProfEnd(); } break; - case State_BuildImportObjs: { - ProfBegin("Build Import Table"); - - if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { - if (imptab_delayed) { - for (String8Node *node = config->delay_load_dll_list.first; node != 0; node = node->next) { - LNK_ImportDLL *dll = lnk_import_table_search_dll(imptab_delayed, node->string); - if (dll == 0) { - lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", node->string); + case State_PushDllHelperUndefSymbol: { + ProfBegin("Puhs Dll Helper Undef Symbol"); + + String8 delay_helper_name = str8_zero(); + switch (config->machine) { + case COFF_MachineType_X86: delay_helper_name = str8_cstring(LNK_DELAY_LOAD_HELPER2_X86_SYMBOL_NAME); break; + case COFF_MachineType_X64: delay_helper_name = str8_cstring(LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME); break; + default: NotImplemented; + } + + str8_list_push(scratch.arena, &include_symbol_list, delay_helper_name); + ProfEnd(); + } break; + case State_PushLinkerSymbols: { + ProfBegin("Push Linker Symbols"); + LNK_InputObjList linker_objs = lnk_push_linker_symbols(scratch.arena, config); + lnk_input_obj_list_concat_in_place(&input_obj_list, &linker_objs); + ProfEnd(); + } break; + case State_PushLoadConfigUndefSymbol: { + ProfBegin("Push Load Config Undef Symbol"); + String8 load_config_name = str8_lit(LNK_LOAD_CONFIG_SYMBOL_NAME); + str8_list_push(scratch.arena, &include_symbol_list, load_config_name); + ProfEnd(); + } break; + case State_LookupUndef: { + ProfBegin("Lookup Undefined Symbols"); + // search archives + LNK_SymbolFinderResult result = lnk_run_symbol_finder(tp, tp_arena, config->path_style, symtab, lookup_undef_list, lnk_undef_symbol_finder); // TODO: put these on temp arena + + // new inputs found + input_obj_list = result.input_obj_list; + input_import_list = result.input_import_list; + + // undefined symbols that weren't resolved + lnk_symbol_list_concat_in_place(&unresolved_undef_list, &result.unresolved_symbol_list); + + // reset input + MemoryZeroStruct(&lookup_undef_list); + ProfEnd(); + } break; + case State_LookupWeak: { + ProfBegin("Lookup Weak Symbols"); + // search archives + LNK_SymbolFinderResult result = lnk_run_symbol_finder(tp, tp_arena, config->path_style, symtab, lookup_weak_list, lnk_weak_symbol_finder); // TODO: put these on temp arena + + // schedule new inputs + input_obj_list = result.input_obj_list; + input_import_list = result.input_import_list; + + // weak symbols that weren't resolved + lnk_symbol_list_concat_in_place(&unresolved_weak_list, &result.unresolved_symbol_list); + + // reset input + MemoryZeroStruct(&lookup_weak_list); + ProfEnd(); + } break; + case State_LookupEntryPoint: { + ProfBegin("Lookup Entry Point"); + LNK_Symbol *entry_point_symbol = 0; + + B32 is_entry_point_unspecified = config->entry_point_name.size == 0; + if (is_entry_point_unspecified) { + if (config->subsystem == PE_WindowsSubsystem_UNKNOWN) { + // we don't have a subsystem and entry point name, + // so we loop over every subsystem and search potential entry + // points in the symbol table + for (U64 subsys_idx = 0; subsys_idx < PE_WindowsSubsystem_COUNT; subsys_idx += 1) { + String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); + for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { + entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, name_arr.v[entry_idx]); + if (entry_point_symbol) { + config->subsystem = (PE_WindowsSubsystem)subsys_idx; + goto dbl_break; + } + } + } + + // search for potential entry points in libs + if (!entry_point_symbol) { + for (U64 subsys_idx = 0; subsys_idx < PE_WindowsSubsystem_COUNT; subsys_idx += 1) { + String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); + for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { + entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, name_arr.v[entry_idx]); + if (entry_point_symbol) { + config->subsystem = (PE_WindowsSubsystem)subsys_idx; + goto dbl_break; + } + } + } + } + + dbl_break:; + } else { + // we have subsystem but no entry point name, get potential entry point names + // and see which is in the symbol table + String8Array name_arr = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics); + for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, name_arr.v[entry_idx]); + if (symbol) { + if (entry_point_symbol) { + lnk_error(LNK_Error_EntryPoint, + "multiple entry point symbols found: %S(%S) and %S(%S)", + entry_point_symbol->name, entry_point_symbol->u.defined.obj->path, + symbol->name, symbol->u.defined.obj->path); + } else { + entry_point_symbol = symbol; + } + } + } + + // search for entry point in libs + if (!entry_point_symbol) { + for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { + entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, name_arr.v[entry_idx]); + if (entry_point_symbol) { + break; + } } } } + + // redirect user entry to appropriate CRT entry + if (entry_point_symbol) { + config->entry_point_name = entry_point_symbol->name; + if (str8_match_lit("wmain", config->entry_point_name, 0)) { + config->entry_point_name = str8_lit("wmainCRTStartup"); + } else if (str8_match_lit("main", config->entry_point_name, 0)) { + config->entry_point_name = str8_lit("mainCRTStartup"); + } else if (str8_match_lit("WinMain", config->entry_point_name, 0)) { + config->entry_point_name = str8_lit("WinMainCRTStartup"); + } else if (str8_match_lit("wWinMain", config->entry_point_name, 0)) { + config->entry_point_name = str8_lit("wWinMainCRTStartup"); + } + } } - - LNK_InputObjList static_imports = lnk_import_table_serialize(scratch.arena, imptab_static, str8_skip_last_slash(config->image_name), config->machine); - LNK_InputObjList delayed_imports = lnk_import_table_serialize(scratch.arena, imptab_delayed, str8_skip_last_slash(config->image_name), config->machine); - - lnk_input_obj_list_concat_in_place(&input_obj_list, &static_imports); - lnk_input_obj_list_concat_in_place(&input_obj_list, &delayed_imports); - - ProfEnd(); - } break; - case State_BuildExportObjs: { - ProfBegin("Build Export Table"); - - ProfBeginV("Push Exports [Count %u]", export_symbol_list.count); - for (LNK_ExportParse *exp_parse = export_symbol_list.first; exp_parse != 0; exp_parse = exp_parse->next) { - lnk_export_table_push_export(exptab, symtab, exp_parse); + + // generate undefined symbol so in case obj is in lib it will be linked + if (config->entry_point_name.size) { + str8_list_push(scratch.arena, &include_symbol_list, config->entry_point_name); } - ProfEnd(); - - LNK_InputObjList export_objs = lnk_export_table_serialize(scratch.arena, exptab, str8_skip_last_slash(config->image_name), config->machine); - lnk_input_obj_list_concat_in_place(&input_obj_list, &export_objs); - + // no entry point, error and exit + else { + lnk_error(LNK_Error_EntryPoint, "unable to find entry point symbol"); + } + + // 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; + } + } + + // do we have a subsystem? + if (config->subsystem == PE_WindowsSubsystem_UNKNOWN) { + lnk_error(LNK_Error_NoSubsystem, "unknown subsystem, please use /SUBSYSTEM to set subsytem type you need"); + } + + if (config->subsystem_ver.major == 0 && config->subsystem_ver.minor == 0) { + // subsystem version not specified, set default values + 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); + int ver_cmp = version_compar(config->subsystem_ver, min_subsystem_ver); + if (ver_cmp < 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); + } + ProfEnd(); } break; - case State_BuildDebugDirectoryObjs: { - ProfBegin("Build Debug Directory"); - - LNK_InputObjList debug_objs = lnk_build_debug_directory_objs(scratch.arena, config); - lnk_input_obj_list_concat_in_place(&input_obj_list, &debug_objs); - - ProfEnd(); + case State_ReportUnresolvedSymbols: { + // report unresolved symbols + for (LNK_SymbolNode *node = unresolved_undef_list.first; node != 0; node = node->next) { + lnk_error(LNK_Error_UnresolvedSymbol, "unresolved symbol %S", node->data->name); + } + if (unresolved_undef_list.count) { + goto exit; + } } break; - case State_BuildAndInputResObj: { + case State_InputImportObjs: { + if (input_import_list.count) { + ProfBegin("Build Import Table"); + + if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { + if (imptab_delayed) { + for (String8Node *node = config->delay_load_dll_list.first; node != 0; node = node->next) { + LNK_ImportDLL *dll = lnk_import_table_search_dll(imptab_delayed, node->string); + if (dll == 0) { + lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", node->string); + } + } + } + } + + LNK_InputObjList static_imports = lnk_import_table_serialize(scratch.arena, imptab_static, str8_skip_last_slash(config->image_name), config->machine); + LNK_InputObjList delayed_imports = lnk_import_table_serialize(scratch.arena, imptab_delayed, str8_skip_last_slash(config->image_name), config->machine); + + lnk_input_obj_list_concat_in_place(&input_obj_list, &static_imports); + lnk_input_obj_list_concat_in_place(&input_obj_list, &delayed_imports); + + ProfEnd(); + } + } break; + case State_InputExportObjs: { + if (export_symbol_list.count) { + ProfBegin("Build Export Table"); + + ProfBeginV("Push Exports [Count %u]", export_symbol_list.count); + for (LNK_ExportParse *exp_parse = export_symbol_list.first; exp_parse != 0; exp_parse = exp_parse->next) { + lnk_export_table_push_export(exptab, symtab, exp_parse); + } + ProfEnd(); + + LNK_InputObjList export_objs = lnk_export_table_serialize(scratch.arena, exptab, str8_skip_last_slash(config->image_name), config->machine); + lnk_input_obj_list_concat_in_place(&input_obj_list, &export_objs); + + ProfEnd(); + } + } break; + case State_InputResObj: { String8List res_data_list = {0}; String8List res_path_list = {0}; @@ -3718,7 +4196,7 @@ lnk_run(int argc, char **argv) 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->work_dir, config->path_style, node->string); + 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); @@ -3733,14 +4211,14 @@ lnk_run(int argc, char **argv) ProfBegin("Build * Resources *"); String8 obj_name = str8_lit("* Resources *"); - String8 obj_data = lnk_obj_from_res_file_list(scratch.arena, - res_data_list, - res_path_list, - config->machine, - config->time_stamp, - config->work_dir, - config->path_style, - obj_name); + String8 obj_data = lnk_make_res_obj(scratch.arena, + 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; @@ -3750,15 +4228,12 @@ lnk_run(int argc, char **argv) ProfEnd(); } } break; - case State_BuildAndInputLinkerObj: { + case State_InputLinkerObj: { ProfBegin("Build * Linker * Obj"); - String8 obj_name = str8_lit("* Linker *"); - - StringJoin join = { str8_lit_comp(""), str8_lit_comp(" "), str8_lit_comp("") }; - String8 raw_cmd_line = str8_list_join(scratch.arena, &config->raw_cmd_line, &join); - - String8 obj_data = lnk_make_linker_coff_obj(scratch.arena, config->time_stamp, config->machine, config->work_dir, config->image_name, config->pdb_name, raw_cmd_line, obj_name); + 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(scratch.arena, 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; @@ -3767,203 +4242,37 @@ lnk_run(int argc, char **argv) ProfEnd(); } break; - case State_LookupUndef: { - ProfBegin("Lookup Undefined Symbols"); - // search archives - LNK_SymbolFinderResult result = lnk_run_symbol_finder(tp, tp_arena, config->path_style, symtab, lookup_undef_list, lnk_undef_symbol_finder); // TODO: put these on temp arena - - // new inputs found - input_obj_list = result.input_obj_list; - input_import_list = result.input_import_list; - - // undefined symbols that weren't resolved - lnk_symbol_list_concat_in_place(&unresolved_undef_list, &result.unresolved_symbol_list); - - // reset input - MemoryZeroStruct(&lookup_undef_list); - ProfEnd(); - } break; - case State_LookupWeak: { - ProfBegin("Lookup Weak Symbols"); - // search archives - LNK_SymbolFinderResult result = lnk_run_symbol_finder(tp, tp_arena, config->path_style, symtab, lookup_weak_list, lnk_weak_symbol_finder); // TODO: put these on temp arena - - // schedule new inputs - input_obj_list = result.input_obj_list; - input_import_list = result.input_import_list; - - // weak symbols that weren't resolved - lnk_symbol_list_concat_in_place(&unresolved_weak_list, &result.unresolved_symbol_list); - - // reset input - MemoryZeroStruct(&lookup_weak_list); - ProfEnd(); - } break; - case State_ReportUnresolvedSymbols: { - // report unresolved symbols - for (LNK_SymbolNode *node = unresolved_undef_list.first; node != 0; node = node->next) { - lnk_error(LNK_Error_UnresolvedSymbol, "unresolved symbol %S", node->data->name); + case State_InputDebugDirObj: { + ProfBegin("Build Debug Directory"); + + { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->path = str8_lit("* Debug Directory *"); + input->dedup_id = input->path; + input->data = lnk_make_debug_directory_obj(scratch.arena, config); } - if (unresolved_undef_list.count) { - goto exit; + + 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->path = str8_lit("* Debug Directory PDB *"); + input->dedup_id = input->path; + input->data = lnk_make_debug_directory_pdb_obj(scratch.arena, config); } - } break; - case State_DiscardMetaDataSections: { - ProfBegin("Discard Meta Data Sections"); - lnk_discard_meta_data_sections(sectab); - ProfEnd(); - } break; - case State_MergeSections: { - ProfBegin("Merge Sections"); - lnk_section_table_merge(sectab, merge_list); - ProfEnd(); - } break; - case State_BuildCFGuards: { - ProfBegin("Build CF Guards"); - B32 emit_suppress_flag = 1; // MSVC emits this flag but every entry has zero set. - lnk_build_guard_tables(tp, sectab, symtab, exptab, obj_list, config->machine, config->entry_point_name, config->guard_flags, emit_suppress_flag); - ProfEnd(); - } break; - case State_BuildBaseRelocs: { - ProfBegin("Base Relocs"); - lnk_build_base_relocs(tp, tp_arena, sectab, symtab, config->machine, config->page_size, config->file_characteristics, obj_list); - ProfEnd(); - } break; - case State_FinalizeImage: { - ProfBegin("Build Win32 Header"); - - // remove empty section headers from output image - lnk_section_table_remove_empties(sectab, symtab); - - // collect output sections - LNK_SectionArray out_sect_arr = lnk_section_table_get_output_sections(scratch.arena, sectab); - - // push back null section where we store image header - LNK_Section *header_sect = lnk_section_table_push_null(sectab); - - // fill out header section with win32 image header data - lnk_build_win32_image_header(symtab, header_sect, header_sect->root, config, out_sect_arr); - - ProfEnd(); - - // finalize sections - lnk_section_table_build_data(tp, sectab, config->machine); - lnk_section_table_assign_indices(sectab); - lnk_section_table_assign_virtual_offsets(sectab); - lnk_section_table_assign_file_offsets(sectab); - - ProfBegin("Image Serialize"); - image_data = lnk_section_table_serialize(tp, scratch.arena, sectab, config->machine); - Assert(image_data.size > 0); - ProfEnd(); - - // image layout is finalized, section id map is stable after this point - LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, sectab); - - ProfBegin("Patch Relocs"); - - U64 base_addr = lnk_get_base_addr(config); - lnk_patch_relocs_obj(tp, obj_list, symtab, sectab, sect_id_map, image_data, base_addr); - lnk_patch_relocs_linker(tp, symtab, sectab, sect_id_map, image_data, base_addr); - - ProfEnd(); - - ProfBegin("Sort Exception Info"); - - LNK_Symbol *pdata_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScopeFlag_Internal, LNK_PDATA_SYMBOL_NAME); - if (pdata_symbol) { - String8 pdata = lnk_data_from_chunk_ref_no_pad(sect_id_map, image_data, pdata_symbol->u.defined.u.chunk->ref); - switch (config->machine) { - case COFF_MachineType_X86: - case COFF_MachineType_X64: { - U64 count = pdata.size / sizeof(PE_IntelPdata); - radsort((PE_IntelPdata *)pdata.str, count, lnk_pdata_is_before_x8664); - } break; - case COFF_MachineType_Arm64: - case COFF_MachineType_Arm: { - AssertAlways(!"TOOD: ARM"); - } break; - case COFF_MachineType_MipsFpu: - case COFF_MachineType_Mips16: - case COFF_MachineType_MipsFpu16: { - AssertAlways(!"TODO: MIPS"); - } break; - } + if (config->rad_debug == LNK_SwitchState_Yes) { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->path = str8_lit("* Debug Directory RDI *"); + input->dedup_id = input->path; + input->data = lnk_make_debug_directory_rdi_obj(scratch.arena, config); } ProfEnd(); + } break; + case State_BuildImage: { + // build image + image_data = lnk_build_win32_image(tp_arena, tp, config, symtab, exptab, obj_list); - ProfEnd(); // :EndBuild - - LNK_Symbol *tls_used_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScopeFlag_Main, LNK_TLS_SYMBOL_NAME); - if (tls_used_symbol) { - ProfBegin("Patch TLS Align"); - - // loop over .tls sections and extract max alignment - LNK_Symbol *tls_sect_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScopeFlag_Internal, LNK_TLS_SYMBOL_NAME); - LNK_Chunk *tls_root_chunk = tls_sect_symbol->u.defined.u.chunk; - U64 tls_align = 0; - lnk_visit_chunks(0, tls_root_chunk, lnk_max_tls_align, &tls_align); - - if (IsPow2(tls_align)) { - // compute TLS header offset - U64 tls_header_foff = lnk_file_off_from_symbol(sect_id_map, tls_used_symbol); - - // patch TLS header - if (coff_word_size_from_machine(config->machine) == 8) { - String8 raw_tls_used = str8_substr(image_data, rng_1u64(tls_header_foff, tls_header_foff + sizeof(PE_TLSHeader64))); - PE_TLSHeader64 *tls_header = (PE_TLSHeader64 *) raw_tls_used.str; - tls_header->characteristics |= coff_section_flag_from_align_size(tls_align); - } else { - String8 raw_tls_used = str8_substr(image_data, rng_1u64(tls_header_foff, tls_header_foff + sizeof(PE_TLSHeader32))); - PE_TLSHeader32 *tls_header = (PE_TLSHeader32 *) raw_tls_used.str; - tls_header->characteristics |= coff_section_flag_from_align_size(tls_align); - } - } else { - lnk_error(LNK_Warning_TLSAlign, "unable to patch TLS Header characteristics, alignment must be power of two, align inferred from section flags: %llu", tls_align); - } - - ProfEnd(); - } - - if (config->flags & LNK_ConfigFlag_WriteImageChecksum) { - ProfBegin("Image Checksum"); - - U32 image_checksum = pe_compute_checksum(image_data.str, image_data.size); - - LNK_Symbol *checksum_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScopeFlag_Internal, LNK_PE_CHECKSUM_SYMBOL_NAME); - U64 checksum_foff = lnk_file_off_from_symbol(sect_id_map, checksum_symbol); - - U32 *checksum_ptr = (U32 *)(image_data.str + checksum_foff); - *checksum_ptr = image_checksum; - - ProfEnd(); - } - - LNK_Symbol *guid_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScopeFlag_Internal, LNK_CV_HEADER_GUID_SYMBOL_NAME); - if (guid_symbol) { - // we can't have a debug directory to patch without debug info - Assert(build_debug_info); - - switch (config->guid_type) { - case LNK_DebugInfoGuid_Null: break; - case Lnk_DebugInfoGuid_ImageBlake3: { - ProfBegin("Hash Image With Blake3"); - - U128 hash = lnk_blake3_hash_parallel(tp, 128, image_data); - MemoryCopy(&config->guid, hash.u64, sizeof(hash.u64)); - - U64 guid_foff = lnk_file_off_from_symbol(sect_id_map, guid_symbol); - Guid *guid_ptr = (Guid *)(image_data.str + guid_foff); - MemoryCopy(guid_ptr, hash.u64, sizeof(hash.u64)); - - ProfEnd(); - } break; - } - } - - // write image file in background + // write image to disk in a background thread { LNK_WriteThreadContext *ctx = push_array(scratch.arena, LNK_WriteThreadContext, 1); ctx->path = config->image_name; @@ -3972,24 +4281,23 @@ lnk_run(int argc, char **argv) image_write_thread = os_thread_launch(lnk_write_thread, ctx, 0); } - 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; + // 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); } - 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 (U64 i = 0; i < ArrayCount(lib_index); ++i) { - 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; + if (lnk_get_log_status(LNK_Log_InputLib)) { + U64 total_input_size = 0; + for (U64 i = 0; i < ArrayCount(lib_index); ++i) { + 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); } - lnk_log(LNK_Log_InputLib, "[Total Lib Input Size %M]", total_input_size); } - + lnk_timer_end(LNK_Timer_Image); ProfEnd(); // :EndImage } break; @@ -4003,7 +4311,7 @@ lnk_run(int argc, char **argv) } break; case State_BuildRadChunkMap: { ProfBegin("RAD Chunk Map"); - String8List map = lnk_build_rad_chunk_map(scratch.arena, image_data, config->worker_count, obj_list, lib_index, sectab, symtab); + String8List map = lnk_build_rad_chunk_map(scratch.arena, image_data, config->worker_count, obj_list, lib_index, sectab); lnk_write_data_list_to_file_path(config->rad_chunk_map_name, config->temp_rad_chunk_map_name, map); ProfEnd(); } break; @@ -4011,9 +4319,8 @@ lnk_run(int argc, char **argv) ProfBegin("Debug Info"); lnk_timer_begin(LNK_Timer_Debug); - LNK_CodeViewInput input = lnk_make_code_view_input(tp, tp_arena, config->lib_dir_list, obj_list); - CV_DebugT *types = lnk_import_types(tp, tp_arena, &input); - LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, sectab); + LNK_CodeViewInput input = lnk_make_code_view_input(tp, tp_arena, config->lib_dir_list, obj_list); + CV_DebugT *types = lnk_import_types(tp, tp_arena, &input); if (config->rad_debug == LNK_SwitchState_Yes) { lnk_timer_begin(LNK_Timer_Rdi); @@ -4027,7 +4334,6 @@ lnk_run(int argc, char **argv) config->image_name, image_data, image_sects, - sect_id_map, input.count, input.obj_arr, input.debug_s_arr, @@ -4053,16 +4359,8 @@ lnk_run(int argc, char **argv) String8List pdb_data = lnk_build_pdb(tp, tp_arena, image_data, - config->guid, - config->machine, - config->time_stamp, - config->age, - config->pdb_page_size, - config->pdb_name, - config->lib_dir_list, - config->natvis_list, + config, symtab, - sect_id_map, input.count, input.obj_arr, input.debug_s_arr, @@ -4124,19 +4422,11 @@ lnk_run(int argc, char **argv) // we can't find strong definitions for unresolved weak symbols // so now we have to use fallback symbols MemoryZeroStruct(&unresolved_weak_list); - - // make sure fallback symbols are defined, if not try to find definitions - for (LNK_SymbolNode *symbol_n = unresolved_weak_list.first; symbol_n != 0; symbol_n = symbol_n->next) { - if (symbol_n->data->u.weak.fallback_symbol->type == LNK_Symbol_Undefined) { - lnk_symbol_list_push(scratch.arena, &lookup_undef_list, symbol_n->data->u.weak.fallback_symbol); - } - } - continue; } - if (entry_search_attempts == 0) { - state_list_push(scratch.arena, state_list, State_SearchEntryPoint); - entry_search_attempts += 1; + if (entry_point_lookup_attempts == 0) { + state_list_push(scratch.arena, state_list, State_LookupEntryPoint); + entry_point_lookup_attempts += 1; continue; } if (unresolved_undef_list.count) { @@ -4147,57 +4437,36 @@ lnk_run(int argc, char **argv) } } - if (build_import_objs) { - build_import_objs = 0; - state_list_push(scratch.arena, state_list, State_BuildImportObjs); - continue; - } - if (build_export_objs) { - build_export_objs = 0; - state_list_push(scratch.arena, state_list, State_BuildExportObjs); - continue; - } - if (build_debug_directory_objs) { - build_debug_directory_objs = 0; - state_list_push(scratch.arena, state_list, State_BuildDebugDirectoryObjs); - continue; - } - if (build_res_obj) { - build_res_obj = 0; - state_list_push(scratch.arena, state_list, State_BuildAndInputResObj); - continue; - } - if (build_linker_obj) { - build_linker_obj = 0; - state_list_push(scratch.arena, state_list, State_BuildAndInputLinkerObj); - continue; - } - - if (discard_meta_data_sections) { - discard_meta_data_sections = 0; - state_list_push(scratch.arena, state_list, State_DiscardMetaDataSections); + if (input_import_objs) { + input_import_objs = 0; + state_list_push(scratch.arena, state_list, State_InputImportObjs); continue; } - if (merge_sections) { - merge_sections = 0; - state_list_push(scratch.arena, state_list, State_MergeSections); + if (input_export_objs) { + input_export_objs = 0; + state_list_push(scratch.arena, state_list, State_InputExportObjs); continue; } - if (build_cf_guards) { - build_cf_guards = 0; - state_list_push(scratch.arena, state_list, State_BuildCFGuards); + if (input_res_obj) { + input_res_obj = 0; + state_list_push(scratch.arena, state_list, State_InputResObj); continue; } - if (build_base_relocs) { - build_base_relocs = 0; - state_list_push(scratch.arena, state_list, State_BuildBaseRelocs); + if (input_linker_obj) { + input_linker_obj = 0; + state_list_push(scratch.arena, state_list, State_InputLinkerObj); + continue; + } + if (input_debug_dir_obj) { + input_debug_dir_obj = 0; + state_list_push(scratch.arena, state_list, State_InputDebugDirObj); continue; } if (image_data.size == 0) { - state_list_push(scratch.arena, state_list, State_FinalizeImage); + state_list_push(scratch.arena, state_list, State_BuildImage); continue; } if (build_imp_lib) { @@ -4223,13 +4492,7 @@ lnk_run(int argc, char **argv) break; } - - if (lnk_get_log_status(LNK_Log_SizeBreakdown)) { - lnk_log_size_breakdown(sectab, symtab); - } - if (lnk_get_log_status(LNK_Log_LinkStats)) { - lnk_log_link_stats(obj_list, lib_index, sectab); - } + if (lnk_get_log_status(LNK_Log_Timers)) { lnk_log_timers(); } @@ -4257,18 +4520,3 @@ entry_point(CmdLine *cmdline) lnk_run(cmdline->argc, cmdline->argv); } -//////////////////////////////// - -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; -} - diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 275fd652..3977a20c 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -1,67 +1,11 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once -#define LNK_NULL_SYMBOL_NAME "NULL" -#define LNK_TEXT_SYMBOL_NAME "TEXT" -#define LNK_DATA_SYMBOL_NAME "DATA" -#define LNK_RDATA_SYMBOL_NAME "RDATA" -#define LNK_BSS_SYMBOL_NAME "BSS" -#define LNK_XDATA_SYMBOL_NAME "XDATA" -#define LNK_PDATA_SYMBOL_NAME "PDATA" -#define LNK_BASE_RELOC_SYMBOL_NAME "BASE_RELOC" -#define LNK_EDATA_SYMBOL_NAME "EDATA" -#define LNK_DEBUG_DIR_SYMBOL_NAME "DEDIR" -#define LNK_DEBUG_DATA_SYMBOL_NAME "DEDAT" -#define LNK_CV_DIR_SYMBOL_NAME "CV_DIR" -#define LNK_CV_HEADER_PDB70_SYMBOL_NAME "CV_HEADER_PDB70" -#define LNK_CV_HEADER_RDI_SYMBOL_NAME "CV_HEADER_RDI" -#define LNK_CV_HEADER_GUID_SYMBOL_NAME "CV_HEADER_GUID" -#define LNK_RSRC_SYMBOL_NAME "RSRC" -#define LNK_DEBUG_SYMBOL_NAME "DEBUG" -#define LNK_GFIDS_SYMBOL_NAME "GFIDS" -#define LNK_GIATS_SYMBOL_NAME "GIATS" -#define LNK_GLJMP_SYMBOL_NAME "GLJMP" -#define LNK_GEHCONT_SYMBOL_NAME "GEHCONT" -#define LNK_IMPORT_NAME_TABLE_SYMBOL_NAME "IMPORT_STR" -#define LNK_IMPORT_DLL_TABLE_SYMBOL_NAME "IDATA" -#define LNK_IMPORT_ILT_SYMBOL_NAME "ILT" -#define LNK_IMPORT_IAT_SYMBOL_NAME "IAT" -#define LNK_IMPORT_JMP_SYMBOL_NAME "IMPORT_THUNKS" -#define LNK_DELAYED_IMPORT_DLL_TABLE_SYMBOL_NAME "DELAYED_IMPORT_DLL_TABLE" -#define LNK_DELAYED_IMPORT_HANDLE_TABLE_SYMBOL_NAME "DELAYED_IMPORT_HANDLE_TABLE" -#define LNK_DELAYED_IMPORT_INT_SYMBOL_NAME "DELAYED_IMPORT_INT" -#define LNK_DELAYED_IMPORT_IAT_SYMBOL_NAME "DELAYED_IMPORT_IAT" -#define LNK_DELAYED_IMPORT_ILT_SYMBOL_NAME "DELAYED_IMPORT_ILT" -#define LNK_DELAYED_IMPORT_BIAT_SYMBOL_NAME "DELAYED_IMPORT_BIAT" -#define LNK_DELAYED_IMPORT_UIAT_SYMBOL_NAME "DELAYED_IMPORT_UIAT" -#define LNK_DELAYED_IMPORT_CODE_SYMBOL_NAME "DELAYED_IMPORT_CODE" +// --- CRT Symbols ------------------------------------------------------------- -#define LNK_WIN32_HEADER_SYMBOL_NAME "WIN32_HEADER" -#define LNK_DOS_SYMBOL_NAME "DOS" -#define LNK_NT_HEADERS_SYMBOL_NAME "NT_HEADERS" -#define LNK_PE_MAGIC_CONTAINER_SYMBOL_NAME "PE_MAGIC_CONTAINER" -#define LNK_COFF_FILE_HEADER_CONTAINER_SYMBOL_NAME "COFF_FILE_HEADER_CONTAINER" -#define LNK_PE_OPT_HEADER_CONTAINER_SYMBOL_NAME "PE_OPTIONAL_HEADER_CONTAINER" -#define LNK_COFF_SECTION_HEADER_CONTAINER_SYMBOL_NAME "COFF_SECTION_HEADER_CONTAINER" - -#define LNK_DOS_HEADER_SYMBOL_NAME "DOS_HEADER" -#define LNK_DOS_PROGRAM_SYMBOL_NAME "DOS_PROGRAM" -#define LNK_PE_MAGIC_SYMBOL_NAME "PE_MAGIC" -#define LNK_COFF_FILE_HEADER_SYMBOL_NAME "COFF_FILE_HEADER" -#define LNK_PE_DIRECTORY_ARRAY_SYMBOL_NAME "PE_DIRECTORY_ARRAY" -#define LNK_PE_DIRECTORY_COUNT_SYMBOL_NAME "PE_DIRECTORY_COUNT" -#define LNK_PE_OPT_HEADER_SYMBOL_NAME "PE_OPTIONAL_HEADER" -#define LNK_COFF_SECT_HEADER_ARRAY_SYMBOL_NAME "COFF_SECT_HEADER_ARRAY" -#define LNK_COFF_SECT_HEADER_COUNT_SYMBOL_NAME "COFF_SECT_HEADER_COUNT" -#define LNK_PE_CHECKSUM_SYMBOL_NAME "PE_CHECKSUM" - -// _tls_used is a special section in CRT which has format of -// PE_TLSHeader32 or PE_TLSHeader64, according to machine type. -#define LNK_TLS_SYMBOL_NAME "_tls_used" - -// _load_config_used points to SYMS_PeLoadConfig32/SYMS_PeLoadConfig64 +// _load_config_used points to PE_LoadConfig32/PE_LoadConfig64 // and symbols below are used to patch patricual fields of the struct. #define LNK_LOAD_CONFIG_SYMBOL_NAME "_load_config_used" #define LNK_ENCLAVE_CONFIG_SYMBOL_NAME "__enclave_config" @@ -74,6 +18,7 @@ #define LNK_GUARD_LONGJMP_COUNT_SYMBOL_NAME "__guard_longjmp_count" #define LNK_GUARD_EHCONT_TABLE_SYMBOL_NAME "__guard_eh_cont_table" #define LNK_GUARD_EHCONT_COUNT_SYMBOL_NAME "__guard_eh_cont_count" + // x86 load config fields #define LNK_SAFE_SE_HANDLER_TABLE_SYMBOL_NAME "__safe_se_handler_table" #define LNK_SAFE_SE_HANDLER_COUNT_SYMBOL_NAME "__safe_se_handler_count" @@ -82,6 +27,12 @@ #define LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME "__delayLoadHelper2" #define LNK_DELAY_LOAD_HELPER2_X86_SYMBOL_NAME "___delayLoadHelper2@8" +// _tls_used is a special section in CRT which has format of +// PE_TLSHeader32 or PE_TLSHeader64, according to machine type. +#define LNK_TLS_SYMBOL_NAME "_tls_used" + +// --- Default Section Flags -------------------------------------------------- + #define LNK_TEXT_SECTION_FLAGS (COFF_SectionFlag_CntCode|COFF_SectionFlag_MemExecute|COFF_SectionFlag_MemRead) #define LNK_DATA_SECTION_FLAGS (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite) #define LNK_RDATA_SECTION_FLAGS (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead) @@ -101,38 +52,7 @@ #define LNK_RELOC_SECTION_FLAGS (LNK_RDATA_SECTION_FLAGS | COFF_SectionFlag_MemDiscardable) #define LNK_DEBUG_SECTION_FLAGS (LNK_RDATA_SECTION_FLAGS | COFF_SectionFlag_MemDiscardable) -//////////////////////////////// - -#define LNK_COFF_RELOC_FILE_OFFSET32 0x1000 -#define LNK_COFF_RELOC_SECT_SIZE32 0x1001 - -//////////////////////////////// - -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 -{ - COFF_ParsedArchiveImportHeader import_header; - struct LNK_InputImport *next; -} LNK_InputImport; - -typedef struct LNK_InputImportList -{ - U64 count; - LNK_InputImport *first; - LNK_InputImport *last; -} LNK_InputImportList; - -//////////////////////////////// +// --- Base Reloc -------------------------------------------------------------- typedef struct LNK_BaseRelocPage { @@ -160,11 +80,11 @@ typedef struct LNK_BaseRelocPageArray LNK_BaseRelocPage *v; } LNK_BaseRelocPageArray; +// --- Workers Contexts -------------------------------------------------------- + typedef struct { U64 page_size; - LNK_Section **sect_id_map; - LNK_Reloc **reloc_arr; Rng1U64 *range_arr; LNK_BaseRelocPageList *list_arr; HashTable **page_ht_arr; @@ -175,7 +95,6 @@ typedef struct { Rng1U64 *ranges; U64 page_size; - LNK_Section **sect_id_map; LNK_BaseRelocPageList *list_arr; LNK_Obj **obj_arr; HashTable **page_ht_arr; @@ -200,32 +119,10 @@ typedef struct typedef struct { - LNK_SymbolTable *symtab; - union { - LNK_ObjNodeArray objs; - LNK_LibNodeArray libs; - } u; -} LNK_SymbolPusher; - -typedef struct -{ - String8 image_data; - LNK_SymbolTable *symtab; - LNK_SectionTable *sectab; - LNK_Section **sect_id_map; - U64 base_addr; - LNK_Section **sect_arr; - Rng1U64 *range_arr; -} LNK_SectionRelocPatcher; - -typedef struct -{ - String8 image_data; - LNK_SymbolTable *symtab; - LNK_SectionTable *sectab; - LNK_Section **sect_id_map; - U64 base_addr; - LNK_Obj **obj_arr; + String8 image_data; + LNK_Obj **objs; + U64 base_addr; + COFF_SectionHeader **image_section_table; } LNK_ObjRelocPatcher; typedef struct @@ -242,17 +139,42 @@ typedef struct U128 *hashes; } LNK_Blake3Hasher; -//////////////////////////////// +typedef struct +{ + LNK_SymbolTable *symtab; + union { + LNK_ObjNodeArray objs; + LNK_LibNodeArray libs; + } u; +} LNK_SymbolPusher; -internal LNK_InputImport * 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_InputImport ** lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list); -internal LNK_InputImportList lnk_list_from_input_import_arr(LNK_InputImport **arr, U64 count); +// --- Entry Point ------------------------------------------------------------- -//////////////////////////////// -// Helpers +internal void lnk_run(int argc, char **argv); -internal String8 lnk_make_full_path(Arena *arena, String8 work_dir, PathStyle system_path_style, String8 path); +// --- Path -------------------------------------------------------------------- + +internal String8 lnk_make_full_path(Arena *arena, PathStyle system_path_style, String8 work_dir, String8 path); + +// --- Hasher ------------------------------------------------------------------ + +internal U128 lnk_blake3_hash_parallel(TP_Context *tp, U64 chunk_count, String8 data); + +// --- Manifest ---------------------------------------------------------------- + +internal String8 lnk_make_linker_manifest(Arena *arena, B32 manifest_uac, String8 manifest_level, String8 manifest_ui_access, String8List manifest_dependency_list); +internal void lnk_merge_manifest_files(String8 mt_path, String8 out_name, String8List manifest_path_list); +internal String8 lnk_manifest_from_inputs(Arena *arena, String8 mt_path, String8 manifest_name, B32 manifest_uac, String8 manifest_level, String8 manifest_ui_access, String8List input_manifest_path_list, String8List deps_list); + +// --- Internal Objs ----------------------------------------------------------- + +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_coff_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 cwd_path, String8 exe_path, String8 pdb_path, String8 cmd_line, String8 obj_name); +internal String8 lnk_make_debug_directory_obj(Arena *arena, LNK_Config *config); +internal String8 lnk_make_debug_directory_pdb_obj(Arena *arena, LNK_Config *config); +internal String8 lnk_make_debug_directory_rdi_obj(Arena *arena, LNK_Config *config); + +// --- Symbol Resolver --------------------------------------------------------- internal String8 lnk_get_lib_name(String8 path); internal B32 lnk_is_lib_disallowed(HashTable *disallow_lib_ht, String8 path); @@ -260,56 +182,18 @@ 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); -//////////////////////////////// -// Manifest +internal LNK_InputObjList lnk_push_linker_symbols(Arena *arena, LNK_Config *config); +internal void lnk_queue_lib_member_input(Arena *arena, PathStyle path_style, LNK_SymbolLib *symbol, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list); -internal String8 lnk_make_linker_manifest(Arena *arena, B32 manifest_uac, String8 manifest_level, String8 manifest_ui_access, String8List manifest_dependency_list); -internal void lnk_merge_manifest_files(String8 mt_path, String8 out_name, String8List manifest_path_list); +// --- Win32 Image ------------------------------------------------------------- -//////////////////////////////// -// Resources +internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag); +internal String8List lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_temp, LNK_Config *config, U64 objs_count, LNK_Obj **objs); +internal String8List lnk_build_win32_image_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sect_arr, U64 expected_image_header_size); +internal String8 lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, LNK_ObjList obj_list); -internal void lnk_serialize_pe_resource_tree(COFF_ObjWriter *obj_writer, PE_ResourceDir *root_dir); -internal void lnk_add_resource_debug_s(COFF_ObjWriter *obj_writer, String8 obj_path, String8 cwd_path, String8 exe_path, CV_Arch arch, String8List res_file_list, MD5Hash *res_hash_array); -internal String8 lnk_make_res_obj(Arena *arena, PE_ResourceDir *root_dir, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 path, String8 cwd_path, String8 exe_path, String8List res_file_list, MD5Hash *res_hash_array); -internal String8 lnk_obj_from_res_file_list(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); +// --- Logger ------------------------------------------------------------------ -//////////////////////////////// -// Debug - -internal String8 lnk_make_linker_coff_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 cwd_path, String8 exe_path, String8 pdb_path, String8 cmd_line, String8 obj_name); - -//////////////////////////////// -// Win32 Image Helpers - -internal void lnk_build_debug_pdb(LNK_SectionTable *sectab, LNK_SymbolTable *symtab, LNK_Section *debug_sect, LNK_Chunk *debug_dir_array_chunk, COFF_TimeStamp time_stamp, Guid guid, U32 age, String8 pdb_path); -internal void lnk_build_debug_rdi(LNK_SectionTable *sectab, LNK_SymbolTable *symtab, LNK_Section *debug_sect, LNK_Chunk *debug_dir_array_chunk, COFF_TimeStamp time_stamp, Guid guid, String8 rdi_path); -internal void lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, LNK_ObjList obj_list, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag); -internal void lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_arena, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, COFF_MachineType machine, U64 page_size, PE_ImageFileCharacteristics file_chars, LNK_ObjList obj_list); -internal LNK_Chunk * lnk_build_dos_header(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent_chunk); -internal LNK_Chunk * lnk_build_pe_magic(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent); -internal LNK_Chunk * lnk_build_coff_file_header(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent, COFF_MachineType machine, COFF_TimeStamp time_stamp, PE_ImageFileCharacteristics file_characteristics); -internal LNK_Chunk * lnk_build_pe_optional_header_x64(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent, COFF_MachineType machine, U64 base_addr, U64 sect_align, U64 file_align, Version linker_ver, Version os_ver, Version image_ver, Version subsystem_ver, PE_WindowsSubsystem subsystem, PE_DllCharacteristics dll_characteristics, U64 stack_reserve, U64 stack_commit, U64 heap_reserve, U64 heap_commit, String8 entry_point_name, LNK_SectionArray sect_arr); -internal LNK_Chunk * lnk_build_pe_directories(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent); -internal LNK_Chunk * lnk_build_coff_section_table(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent_chunk, LNK_SectionArray sect_arr); -internal LNK_Chunk * lnk_build_win32_image_header(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent_chunk, LNK_Config *config, LNK_SectionArray sect_arr); - -//////////////////////////////// -// Relocs - -internal void lnk_patch_relocs_linker(TP_Context *tp, LNK_SymbolTable *symtab, LNK_SectionTable *sectab, LNK_Section **sect_id_map, String8 image_data, U64 base_addr); -internal void lnk_patch_relocs_obj(TP_Context *tp, LNK_ObjList obj_list, LNK_SymbolTable *symtab, LNK_SectionTable *sectab, LNK_Section **sect_id_map, String8 image_data, U64 base_addr); - -internal void lnk_apply_reloc(U64 base_addr, U64 virt_align, U64 file_align, LNK_Section **sect_id_map, LNK_SymbolTable *symtab, String8 chunk_data, LNK_Reloc *reloc); - -//////////////////////////////// - -internal void lnk_log_size_breakdown(LNK_SectionTable *sectab, LNK_SymbolTable *symtab); internal void lnk_log_link_stats(LNK_ObjList obj_list, LNK_LibList *lib_index, LNK_SectionTable *sectab); internal void lnk_log_timers(void); -//////////////////////////////// -// Enum <-> String - -internal String8 lnk_string_from_input_source(LNK_InputSourceType input_source); - diff --git a/src/linker/lnk_chunk.c b/src/linker/lnk_chunk.c deleted file mode 100644 index be7b7211..00000000 --- a/src/linker/lnk_chunk.c +++ /dev/null @@ -1,804 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal LNK_ChunkRef -lnk_chunk_ref(U64 sect_id, U64 chunk_id) -{ - LNK_ChunkRef ref = {0}; - ref.sect_id = sect_id; - ref.chunk_id = chunk_id; - return ref; -} - -internal B32 -lnk_chunk_ref_is_equal(LNK_ChunkRef a, LNK_ChunkRef b) -{ - B32 is_equal = a.sect_id == b.sect_id && a.chunk_id == b.chunk_id; - return is_equal; -} - -internal LNK_ChunkNode * -lnk_chunk_list_push(Arena *arena, LNK_ChunkList *list, LNK_Chunk *chunk) -{ - LNK_ChunkNode *node = push_array_no_zero(arena, LNK_ChunkNode, 1); - node->next = 0; - node->data = chunk; - - SLLQueuePush(list->first, list->last, node); - ++list->count; - - return node; -} - -internal void -lnk_chunk_list_concat_in_place(LNK_ChunkList *list, LNK_ChunkList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal void -lnk_chunk_list_concat_in_place_arr(LNK_ChunkList *list, LNK_ChunkList *arr, U64 count) -{ - SLLConcatInPlaceArray(list, arr, count); -} - -internal LNK_ChunkList ** -lnk_make_chunk_list_arr_arr(Arena *arena, U64 slot_count, U64 per_count) -{ - LNK_ChunkList **arr_arr = push_array_no_zero(arena, LNK_ChunkList *, slot_count); - for (U64 i = 0; i < slot_count; i += 1) { - arr_arr[i] = push_array(arena, LNK_ChunkList, per_count); - } - return arr_arr; -} - -internal int -lnk_chunk_sort_index_is_before(void *raw_a, void *raw_b) -{ - // Grouped Sections (PE Format) - // "All contributions with the same object-section name are allocated contiguously in the image, - // and the blocks of contributions are sorted in lexical order by object-section name." - LNK_ChunkPtr *a = raw_a; - LNK_ChunkPtr *b = raw_b; - - // sort on section postfix - int cmp = str8_compar_case_sensitive(&(*a)->sort_idx, &(*b)->sort_idx); - - // sort on obj position on command line - if (cmp == 0) { - cmp = u64_compar(&(*a)->input_idx, &(*b)->input_idx); - } - - int is_before = cmp < 0; - return is_before; -} - -internal void -lnk_chunk_array_sort(LNK_ChunkArray arr) -{ - radsort(arr.v, arr.count, lnk_chunk_sort_index_is_before); -} - -internal LNK_ChunkManager * -lnk_chunk_manager_alloc(Arena *arena, U64 id, U64 align) -{ - ProfBeginFunction(); - - LNK_ChunkList temp_list = {0}; - - LNK_Chunk temp_chunk = {0}; - temp_chunk.ref = lnk_chunk_ref(id, 0); - temp_chunk.align = align; - temp_chunk.type = LNK_Chunk_List; - temp_chunk.u.list = &temp_list; - - LNK_ChunkManager *cman = push_array_no_zero(arena, LNK_ChunkManager, 1); - cman->total_chunk_count = 1; // null chunk - cman->root = 0; - cman->root = lnk_chunk_push_list(arena, cman, &temp_chunk, str8(0,0)); - cman->root->align = align; - - ProfEnd(); - return cman; -} - -internal LNK_Chunk * -lnk_chunk_push_(Arena *arena, LNK_Chunk *parent, U64 chunk_id, String8 sort_index) -{ - ProfBeginFunction(); - - Assert(parent->type == LNK_Chunk_List); - LNK_ChunkList *list = parent->u.list; - - LNK_Chunk *chunk = push_array_no_zero(arena, LNK_Chunk, 1); - chunk->ref = lnk_chunk_ref(parent->ref.sect_id, chunk_id); - chunk->align = 1; - chunk->is_discarded = 0; - chunk->sort_chunk = 1; - chunk->type = LNK_Chunk_Null; - chunk->sort_idx = push_str8_copy(arena, sort_index); - chunk->input_idx = list->count; - chunk->flags = 0; - chunk->associate = 0; - - lnk_chunk_list_push(arena, list, chunk); - - ProfEnd(); - return chunk; -} - -internal LNK_Chunk * -lnk_chunk_push(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index) -{ - U64 chunk_id = cman->total_chunk_count; - ++cman->total_chunk_count; - LNK_Chunk *chunk = lnk_chunk_push_(arena, parent, chunk_id, sort_index); - return chunk; -} - -internal LNK_Chunk * -lnk_chunk_push_leaf(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index, void *raw_ptr, U64 raw_size) -{ - LNK_Chunk *chunk = lnk_chunk_push(arena, cman, parent, sort_index); - chunk->type = LNK_Chunk_Leaf; - chunk->u.leaf = str8((U8 *)raw_ptr, raw_size); - return chunk; -} - -internal LNK_Chunk * -lnk_chunk_push_list(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index) -{ - LNK_Chunk *chunk = lnk_chunk_push(arena, cman, parent, sort_index); - chunk->type = LNK_Chunk_List; - chunk->u.list = push_array(arena, LNK_ChunkList, 1); - return chunk; -} - -internal LNK_ChunkNode * -lnk_chunk_deep_copy(Arena *arena, LNK_Chunk *chunk) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - LNK_ChunkNode *dst_root_node = push_array_no_zero(arena, LNK_ChunkNode, 1); - LNK_ChunkNode *src_root_node = push_array_no_zero(scratch.arena, LNK_ChunkNode, 1); - src_root_node->next = 0; - src_root_node->data = chunk; - - struct Stack { - struct Stack *next; - LNK_ChunkNode *src_node; - LNK_ChunkNode *dst_node; - }; - struct Stack *stack = push_array_no_zero(scratch.arena, struct Stack, 1); - stack->next = 0; - stack->src_node = src_root_node; - stack->dst_node = dst_root_node; - - while (stack) { - while (stack->src_node) { - LNK_Chunk *src = stack->src_node->data; - LNK_Chunk *dst = stack->dst_node->data; - - stack->src_node = stack->src_node->next; - stack->dst_node = stack->dst_node->next; - - dst->ref = src->ref; - dst->type = src->type; - dst->align = src->align; - dst->is_discarded = src->is_discarded; - dst->sort_chunk = src->sort_chunk; - dst->sort_idx = push_str8_copy(arena, src->sort_idx); - dst->input_idx = src->input_idx; - dst->flags = src->flags; - //dst->associate = src->associate; - Assert(src->associate == 0); - lnk_chunk_set_debugf(arena, dst, "%S", src->debug); - - switch (src->type) { - case LNK_Chunk_Null: break; - case LNK_Chunk_Leaf: { - B32 is_bss = src->u.leaf.str == 0; - if (is_bss) { - dst->u.leaf = src->u.leaf; - } else { - dst->u.leaf = push_str8_copy(arena, src->u.leaf); - } - } break; - case LNK_Chunk_List: { - LNK_ChunkNode *chain = 0; - LNK_ChunkNode *curr = 0; - if (src->u.list->count > 0) { - chain = push_array(arena, LNK_ChunkNode, src->u.list->count); - curr = chain; - for (U64 i = 1; i < src->u.list->count; ++i) { - curr->next = &chain[i]; - curr = curr->next; - } - curr->next = 0; - } - - dst->u.list = push_array_no_zero(arena, LNK_ChunkList, 1); - dst->u.list->count = src->u.list->count; - dst->u.list->first = chain; - dst->u.list->last = curr; - - struct Stack *frame = push_array_no_zero(scratch.arena, struct Stack, 1); - frame->next = 0; - frame->src_node = src->u.list->first; - frame->dst_node = dst->u.list->first; - SLLStackPush(stack, frame); - } break; - default: InvalidPath; break; - } - } - - SLLStackPop(stack); - } - - scratch_end(scratch); - ProfEnd(); - return dst_root_node; -} - -internal LNK_ChunkNode * -lnk_merge_chunks(Arena *arena, LNK_ChunkManager *dst_cman, LNK_Chunk *dst, LNK_Chunk *src, U64 *id_map_out, U64 id_map_max) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 0); - - Assert(src->ref.sect_id != dst->ref.sect_id); - Assert(dst->type == LNK_Chunk_List); - Assert(src->type != LNK_Chunk_Null); - - LNK_ChunkNode *src_node = push_array(arena, LNK_ChunkNode, 1); - src_node->data = src; - - struct Stack { - struct Stack *next; - LNK_ChunkNode *node; - }; - struct Stack *stack = push_array_no_zero(scratch.arena, struct Stack, 1); - stack->next = 0; - stack->node = src_node; - - while (stack) { - while (stack->node) { - LNK_Chunk *chunk = stack->node->data; - - // advance node - stack->node = stack->node->next; - - // allocate id - U64 new_id = dst_cman->total_chunk_count++; - - // write id map - Assert(chunk->ref.chunk_id < id_map_max); - id_map_out[chunk->ref.chunk_id] = new_id; - - // update id - chunk->ref = lnk_chunk_ref(dst->ref.sect_id, new_id); - - // recurse down on lists - if (chunk->type == LNK_Chunk_List) { - struct Stack *frame = push_array_no_zero(scratch.arena, struct Stack, 1); - frame->next = 0; - frame->node = chunk->u.list->first; - SLLStackPush(stack, frame); - } - } - - // reached end of chunk list, pop frame - SLLStackPop(stack); - } - - // move source root copy to destination section - LNK_ChunkList *list = dst->u.list; - ++list->count; - SLLQueuePush(list->first, list->last, src_node); - - scratch_end(scratch); - ProfEnd(); - return src_node; -} - -internal -LNK_CHUNK_VISITOR_SIG(lnk_set_associate_on_chunks) -{ - chunk->associate = (LNK_Chunk *)ud; - return 0; -} - -internal void -lnk_chunk_associate(LNK_Chunk *head, LNK_Chunk *chunk) -{ - // for simplicity we don't support multiple associations, - // but it's possible to craft symbol table with multiple associations - AssertAlways(!chunk->associate); - lnk_visit_chunks(0, chunk, lnk_set_associate_on_chunks, head); -} - -internal B32 -lnk_chunk_is_discarded(LNK_Chunk *chunk) -{ - B32 is_discarded = chunk->is_discarded; - LNK_Chunk *curr = chunk->associate; - while (!is_discarded && curr) { - is_discarded = curr->is_discarded; - curr = curr->associate; - } - return is_discarded; -} - -internal U64 -lnk_chunk_get_size(LNK_Chunk *chunk) -{ - U64 result = 0; - switch (chunk->type) { - case LNK_Chunk_Null: break; - case LNK_Chunk_Leaf: { - result = chunk->u.leaf.size; - } break; - case LNK_Chunk_LeafArray: - case LNK_Chunk_List: { - Assert(!"TODO: list size"); - } break; - } - return result; -} - -internal U64 -lnk_chunk_list_get_node_count(LNK_Chunk *chunk) -{ - Assert(chunk->type == LNK_Chunk_List); - return chunk->u.list->count; -} - -internal void -lnk_chunk_pad_array_list_push(Arena *arena, Arena *scratch, LNK_ChunkPadArrayList *list, U64 cap, U64 align_off, U64 align_size) -{ - if (align_size > 0) { - if (list->last == 0 || list->last->data.count >= list->last->cap) { - LNK_ChunkPadArrayNode *node = push_array(scratch, LNK_ChunkPadArrayNode, 1); - node->cap = cap; - node->data.v = push_array_no_zero(arena, LNK_ChunkPad, cap); - - SLLQueuePush(list->first, list->last, node); - ++list->count; - } - - LNK_ChunkPadArray *last_array = &list->last->data; - LNK_ChunkPad *align = &last_array->v[last_array->count++]; - align->off = align_off; - align->size = align_size; - } -} - -internal -LNK_CHUNK_VISITOR_SIG(lnk_offset_chunks) -{ - LNK_OffsetChunks *offset_chunks = ud; - U64 offset = offset_chunks->offset; - LNK_ChunkLayout *layout = offset_chunks->layout; - - layout->chunk_off_array[chunk->ref.chunk_id] += offset; - - return 0; -} - -internal LNK_ChunkLayout -lnk_layout_from_chunk(Arena *arena, LNK_Chunk *root, U64 total_chunk_count) -{ - ProfBeginV("lnk_layout_from_chunk [total_chunk_count = %llu]", total_chunk_count); - Temp scratch = scratch_begin(&arena, 1); - - LNK_ChunkLayout layout = {0}; - layout.total_count = total_chunk_count; - layout.chunk_ptr_array = push_array_no_zero(arena, LNK_ChunkPtr, total_chunk_count); - layout.chunk_off_array = push_array_no_zero(arena, U64, total_chunk_count); - layout.chunk_file_size_array = push_array_no_zero(arena, U64, total_chunk_count); - layout.chunk_virt_size_array = push_array_no_zero(arena, U64, total_chunk_count); - - ProfBegin("Init Arrays"); - for (U64 i = 0; i < total_chunk_count; ++i) { - layout.chunk_ptr_array[i] = &g_null_chunk; - } -#if BUILD_DEBUG - MemorySet(layout.chunk_off_array, 0xff, total_chunk_count * sizeof(layout.chunk_off_array)); - MemorySet(layout.chunk_file_size_array, 0xff, total_chunk_count * sizeof(layout.chunk_file_size_array)); - MemorySet(layout.chunk_virt_size_array, 0xff, total_chunk_count * sizeof(layout.chunk_virt_size_array)); -#endif - ProfEnd(); - - // handle null chunk - layout.chunk_off_array[0] = 0; - layout.chunk_file_size_array[0] = 0; - layout.chunk_virt_size_array[0] = 0; - - // setup stack - struct Stack { - struct Stack *next; - LNK_ChunkArray chunk_array; - U64 ichunk; - }; - struct Stack *stack = push_array(scratch.arena, struct Stack, 1); - stack->chunk_array.count = 1; - stack->chunk_array.v = &root; - - U64 pad_cap = 4096; - LNK_ChunkPadArrayList pad_list = {0}; - - U64 cursor = 0; - - ProfBegin("Traverse chunks from root"); - for (; stack != 0; ) { - for (; stack->ichunk < stack->chunk_array.count; ) { - LNK_Chunk *chunk = stack->chunk_array.v[stack->ichunk++]; - - // skip discarded chunk - if (lnk_chunk_is_discarded(chunk)) { - continue; - } - - switch (chunk->type) { - case LNK_Chunk_Leaf: { - // push pad - if (chunk->u.leaf.size < chunk->min_size) { - U64 pad_size = chunk->min_size - chunk->u.leaf.size; - lnk_chunk_pad_array_list_push(arena, scratch.arena, &pad_list, pad_cap, cursor, pad_size); - cursor += pad_size; - } - - // push align - U64 align_size = AlignPadPow2(cursor, chunk->align); - lnk_chunk_pad_array_list_push(arena, scratch.arena, &pad_list, pad_cap, cursor, align_size); - cursor += align_size; - - // store id -> chunk - Assert(chunk->ref.chunk_id < total_chunk_count); - Assert(layout.chunk_ptr_array[chunk->ref.chunk_id] == &g_null_chunk); - layout.chunk_ptr_array[chunk->ref.chunk_id] = chunk; - - // store id -> offset - Assert(layout.chunk_off_array[chunk->ref.chunk_id] == max_U64); - layout.chunk_off_array[chunk->ref.chunk_id] = cursor; - - // store id -> file size - Assert(layout.chunk_file_size_array[chunk->ref.chunk_id] == max_U64); - layout.chunk_file_size_array[chunk->ref.chunk_id] = chunk->u.leaf.size; - - // store id -> virt size - Assert(layout.chunk_virt_size_array[chunk->ref.chunk_id] == max_U64); - layout.chunk_virt_size_array[chunk->ref.chunk_id] = chunk->u.leaf.size; - - // advance - cursor += chunk->u.leaf.size; - } break; - - case LNK_Chunk_LeafArray: { - // push align - U64 align_size = AlignPadPow2(cursor, chunk->align); - lnk_chunk_pad_array_list_push(arena, scratch.arena, &pad_list, pad_cap, cursor, align_size); - cursor += align_size; - - // store id -> chunk - Assert(chunk->ref.chunk_id < total_chunk_count); - Assert(layout.chunk_ptr_array[chunk->ref.chunk_id] == &g_null_chunk); - layout.chunk_ptr_array[chunk->ref.chunk_id] = chunk; - - // store id -> offset - Assert(layout.chunk_off_array[chunk->ref.chunk_id] == max_U64); - layout.chunk_off_array[chunk->ref.chunk_id] = cursor; - - // apply sort - if (chunk->sort_chunk) { - lnk_chunk_array_sort(*chunk->u.arr); - } - - // recurse into sub chunks - struct Stack *frame = push_array(scratch.arena, struct Stack, 1); - frame->chunk_array = *chunk->u.arr; - SLLStackPush(stack, frame); - } goto _continue; - - case LNK_Chunk_List: { - // push align - U64 align_size = AlignPadPow2(cursor, chunk->align); - lnk_chunk_pad_array_list_push(arena, scratch.arena, &pad_list, pad_cap, cursor, align_size); - cursor += align_size; - - // store id -> chunk - Assert(chunk->ref.chunk_id < total_chunk_count); - Assert(layout.chunk_ptr_array[chunk->ref.chunk_id] == &g_null_chunk); - layout.chunk_ptr_array[chunk->ref.chunk_id] = chunk; - - // store id -> offset - Assert(layout.chunk_off_array[chunk->ref.chunk_id] == max_U64); - layout.chunk_off_array[chunk->ref.chunk_id] = cursor; - - // list -> array - LNK_ChunkArray chunk_array = {0}; - chunk_array.v = push_array_no_zero(scratch.arena, LNK_ChunkPtr, chunk->u.list->count); - for (LNK_ChunkNode *cptr = chunk->u.list->first; cptr != 0; cptr = cptr->next) { - chunk_array.v[chunk_array.count++] = cptr->data; - } - - // apply sort - if (chunk->sort_chunk) { - lnk_chunk_array_sort(chunk_array); - } - - // recurse into sub chunks - struct Stack *frame = push_array(scratch.arena, struct Stack, 1); - frame->chunk_array = chunk_array; - SLLStackPush(stack, frame); - } goto _continue; - - case LNK_Chunk_Null: break; - } - } - - // terminate series - if (stack->next) { - // pop node chunk from stack - struct Stack *prev = stack->next; - - Assert(prev->ichunk > 0); - LNK_Chunk *chunk = prev->chunk_array.v[prev->ichunk-1]; - - U64 chunk_data_off = layout.chunk_off_array[chunk->ref.chunk_id]; - Assert(chunk_data_off != max_U64); - Assert(chunk_data_off <= cursor); - - U64 chunk_data_size = cursor - chunk_data_off; - - // store id -> virt size (no pad and align) - Assert(layout.chunk_virt_size_array[chunk->ref.chunk_id] == max_U64); - layout.chunk_virt_size_array[chunk->ref.chunk_id] = chunk_data_size; - - // push pad - if (chunk_data_size < chunk->min_size) { - U64 pad_size = chunk->min_size - chunk->u.leaf.size; - lnk_chunk_pad_array_list_push(arena, scratch.arena, &pad_list, pad_cap, chunk_data_off, pad_size); - - LNK_OffsetChunks ud = {0}; - ud.offset = pad_size; - ud.layout = &layout; - lnk_visit_chunks(0, chunk, lnk_offset_chunks, &ud); - } - - // align chunk end - U64 align_size = AlignPadPow2(cursor, chunk->align); - lnk_chunk_pad_array_list_push(arena, scratch.arena, &pad_list, pad_cap, cursor, align_size); - cursor += align_size; - - chunk_data_size = cursor - chunk_data_off; - - // store id -> file size (pad + align) - Assert(layout.chunk_file_size_array[chunk->ref.chunk_id] == max_U64); - layout.chunk_file_size_array[chunk->ref.chunk_id] = chunk_data_size; - } - - // move to next frame - SLLStackPop(stack); - - _continue:; - } - ProfEnd(); - - ProfBegin("Build Pad Array"); - layout.pad_array_count = 0; - layout.pad_array = push_array(arena, LNK_ChunkPadArray, pad_list.count); - for (LNK_ChunkPadArrayNode *node = pad_list.first; node != 0; node = node->next) { - layout.pad_array[layout.pad_array_count++] = node->data; - } - ProfEnd(); - - scratch_end(scratch); - ProfEnd(); - return layout; -} - -internal LNK_ChunkLayout -lnk_build_chunk_layout(Arena *arena, LNK_ChunkManager *cman) -{ - ProfBeginFunction(); - LNK_ChunkLayout layout = lnk_layout_from_chunk(arena, cman->root, cman->total_chunk_count); - ProfEnd(); - return layout; -} - -internal -THREAD_POOL_TASK_FUNC(lnk_fill_chunks_task) -{ - ProfBeginFunction(); - - LNK_ChunkLayoutSerializer *task = raw_task; - Rng1U64 range = task->ranges[task_id]; - LNK_ChunkLayout layout = task->layout; - String8 buffer = task->buffer; - - for (U64 chunk_idx = range.min; chunk_idx < range.max; ++chunk_idx) { - LNK_Chunk *chunk = layout.chunk_ptr_array[chunk_idx]; - - if (lnk_chunk_is_discarded(chunk)) { - continue; - } - - if (chunk->type == LNK_Chunk_Leaf) { - U64 off = layout.chunk_off_array[chunk->ref.chunk_id]; - Assert(off + chunk->u.leaf.size <= buffer.size); - U8 *buffer_ptr = buffer.str + off; - - if (chunk->u.leaf.str == 0) { - // zero out chunk bytes - MemorySet(buffer_ptr, 0, chunk->u.leaf.size); - } else { - // copy chunk bytes - MemoryCopy(buffer_ptr, chunk->u.leaf.str, chunk->u.leaf.size); - } - } - } - - ProfEnd(); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_fill_pads_task) -{ - ProfBeginFunction(); - - LNK_ChunkLayoutSerializer *task = raw_task; - Rng1U64 range = task->ranges[task_id]; - LNK_ChunkLayout layout = task->layout; - String8 buffer = task->buffer; - U8 fill_byte = task->fill_byte; - - for (U64 pad_array_idx = range.min; pad_array_idx < range.max; ++pad_array_idx) { - LNK_ChunkPadArray pad_array = layout.pad_array[pad_array_idx]; - for (U64 pad_idx = 0; pad_idx < pad_array.count; ++pad_idx) { - LNK_ChunkPad pad = pad_array.v[pad_idx]; - Assert(pad.off + pad.size <= buffer.size); - MemorySet(buffer.str + pad.off, fill_byte, pad.size); - } - } - - ProfEnd(); -} - -internal void -lnk_serialize_chunk_layout(TP_Context *tp, LNK_ChunkLayout layout, String8 buffer, U8 fill_byte) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); - - LNK_ChunkLayoutSerializer task; - task.layout = layout; - task.buffer = buffer; - task.fill_byte = fill_byte; - - ProfBeginV("Fill Chunks [Chunk Count %llu]", layout.total_count); - task.ranges = tp_divide_work(scratch.arena, layout.total_count, tp->worker_count); - tp_for_parallel(tp, 0, tp->worker_count, lnk_fill_chunks_task, &task); - ProfEnd(); - - ProfBeginV("Fill Pads [Array Count %llu]", layout.pad_array_count); - task.ranges = tp_divide_work(scratch.arena, layout.pad_array_count, tp->worker_count); - tp_for_parallel(tp, 0, tp->worker_count, lnk_fill_pads_task, &task); - ProfEnd(); - - scratch_end(scratch); - ProfEnd(); -} - -internal B32 -lnk_visit_chunks_(U64 sect_id, LNK_Chunk *chunk, LNK_ChunkVisitorSig *cb, void *ud) -{ - // visit chunk - B32 is_done = cb(sect_id, chunk, ud); - if (is_done) { - return is_done; - } - - switch (chunk->type) { - case LNK_Chunk_Null: - case LNK_Chunk_Leaf: { - // reached leaf - } break; - case LNK_Chunk_LeafArray: { - for (U64 idx = 0; idx < chunk->u.arr->count; idx += 1) { - is_done = lnk_visit_chunks_(sect_id, chunk->u.arr->v[idx], cb, ud); - if (is_done) { - break; - } - } - } break; - case LNK_Chunk_List: { - for (LNK_ChunkNode *i = chunk->u.list->first; i != 0; i = i->next) { - is_done = lnk_visit_chunks_(sect_id, i->data, cb, ud); - if (is_done) { - break; - } - } - } break; - } - - return is_done; -} - -internal void -lnk_visit_chunks(U64 sect_id, LNK_Chunk *chunk, LNK_ChunkVisitorSig *cb, void *ud) -{ - lnk_visit_chunks_(sect_id, chunk, cb, ud); -} - -LNK_CHUNK_VISITOR_SIG(lnk_save_chunk_ptr) -{ - LNK_Chunk **id_map = (LNK_Chunk **)ud; - if (!chunk->is_discarded) { - id_map[chunk->ref.chunk_id] = chunk; - } - return 0; -} - -internal LNK_ChunkPtr * -lnk_make_chunk_id_map(Arena *arena, LNK_ChunkManager *cman) -{ - LNK_ChunkPtr *map = push_array_no_zero(arena, LNK_ChunkPtr, cman->total_chunk_count); - lnk_visit_chunks(0, cman->root, lnk_save_chunk_ptr, map); - map[0] = &g_null_chunk; - return map; -} - -internal LNK_ChunkNode * -lnk_chunk_ptr_list_reserve(Arena *arena, LNK_ChunkList *list, U64 count) -{ - LNK_ChunkNode *arr = 0; - if (count) { - arr = push_array(arena, LNK_ChunkNode, count); - LNK_Chunk *chunk_arr = push_array(arena, LNK_Chunk, count); - for (U64 i = 0; i < count; i += 1) { - arr[i].data = &chunk_arr[i]; - SLLQueuePush(list->first, list->last, &arr[i]); - } - list->count += count; - } - return arr; -} - -internal String8Array -lnk_data_arr_from_chunk_ptr_list(Arena *arena, LNK_ChunkList list) -{ - String8Array arr = {0}; - arr.v = push_array(arena, String8, list.count); - for (LNK_ChunkNode *n = list.first; n != 0; n = n->next) { - LNK_ChunkPtr c = n->data; - Assert(c->type == LNK_Chunk_Leaf); - arr.v[arr.count] = c->u.leaf; - arr.count += 1; - } - return arr; -} - -internal String8Array * -lnk_data_arr_from_chunk_ptr_list_arr(Arena *arena, LNK_ChunkList *list_arr, U64 count) -{ - String8Array *result = push_array(arena, String8Array, count); - for (U64 i = 0; i < count; i += 1) { - result[i] = lnk_data_arr_from_chunk_ptr_list(arena, list_arr[i]); - } - return result; -} - -internal String8 -lnk_string_from_chunk_type(LNK_ChunkType type) -{ - switch (type) { - case LNK_Chunk_Null: return str8_lit("Null"); - case LNK_Chunk_Leaf: return str8_lit("Leaf"); - case LNK_Chunk_LeafArray: return str8_lit("LeafArray"); - case LNK_Chunk_List: return str8_lit("List"); - default: InvalidPath; - } - return str8_zero(); -} diff --git a/src/linker/lnk_chunk.h b/src/linker/lnk_chunk.h deleted file mode 100644 index ed53cf36..00000000 --- a/src/linker/lnk_chunk.h +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -//////////////////////////////// - -#define LNK_DEBUG_CHUNKS 1 - -#if LNK_DEBUG_CHUNKS -# define lnk_chunk_set_debugf(a, c, f, ...) do { (c)->debug = push_str8f((a), f, __VA_ARGS__); } while(0) -#else -# define lnk_chunk_set_debugf(a, c, f, ...) (void)(c) -#endif - -//////////////////////////////// - -typedef struct LNK_ChunkRef -{ - U64 sect_id; - U64 chunk_id; -} LNK_ChunkRef; - -typedef enum -{ - LNK_Chunk_Null, - LNK_Chunk_Leaf, - LNK_Chunk_LeafArray, - LNK_Chunk_List, -} LNK_ChunkType; - -typedef struct LNK_Chunk -{ - LNK_ChunkRef ref; - LNK_ChunkType type; - U64 align; - U64 min_size; - B32 is_discarded; - B32 sort_chunk; - String8 sort_idx; - U64 input_idx; - COFF_SectionFlags flags; - struct LNK_Chunk *associate; - union { - String8 leaf; - struct LNK_ChunkList *list; - struct LNK_ChunkArray *arr; - } u; - struct LNK_Obj *obj; -#if LNK_DEBUG_CHUNKS - String8 debug; -#endif -} LNK_Chunk, * LNK_ChunkPtr; - -typedef struct LNK_ChunkNode -{ - struct LNK_ChunkNode *next; - LNK_ChunkPtr data; -} LNK_ChunkNode; - -typedef struct LNK_ChunkArray -{ - U64 count; - LNK_ChunkPtr *v; -} LNK_ChunkArray; - -typedef struct LNK_ChunkList -{ - U64 count; - LNK_ChunkNode *first; - LNK_ChunkNode *last; -} LNK_ChunkList; - -typedef enum LNK_ChunkOpType -{ - LNK_ChunkOp_Null, - LNK_ChunkOp_WriteString, - LNK_ChunkOp_Align, - LNK_ChunkOp_Begin, - LNK_ChunkOp_End, - LNK_ChunkOp_EndVirt, -} LNK_ChunkOpType; - -typedef struct LNK_ChunkOp -{ - struct LNK_ChunkOp *next; - LNK_ChunkOpType type; - union { - String8 string; - LNK_Chunk *chunk; - struct { - U64 val; - U64 x; - } align; - LNK_Chunk *leaf; - } u; -} LNK_ChunkOp; - -typedef struct LNK_ChunkOpList -{ - U64 total_chunk_count; - LNK_ChunkOp *first; - LNK_ChunkOp *last; -} LNK_ChunkOpList; - -typedef struct LNK_ChunkPad -{ - U64 off; - U64 size; -} LNK_ChunkPad; - -typedef struct LNK_ChunkPadArray -{ - U64 count; - LNK_ChunkPad *v; -} LNK_ChunkPadArray; -typedef struct LNK_ChunkPadArrayNode -{ - struct LNK_ChunkPadArrayNode *next; - U64 cap; - LNK_ChunkPadArray data; -} LNK_ChunkPadArrayNode; -typedef struct LNK_ChunkPadArrayList -{ - U64 count; - LNK_ChunkPadArrayNode *first; - LNK_ChunkPadArrayNode *last; -} LNK_ChunkPadArrayList; - -typedef struct LNK_ChunkLayout -{ - U64 total_count; - LNK_Chunk **chunk_ptr_array; // discarded chunks point to g_null_chunk - U64 *chunk_off_array; // discarded chunks have offset set to max_U64 - U64 *chunk_file_size_array; // discarded chunks have offset set to max_U64 - U64 *chunk_virt_size_array; // discarded chunks have offset set to max_U64 - U64 pad_array_count; - LNK_ChunkPadArray *pad_array; -} LNK_ChunkLayout; - -typedef struct LNK_ChunkManager -{ - LNK_Chunk *root; - U64 total_chunk_count; -} LNK_ChunkManager; - -//////////////////////////////// - -typedef struct -{ - U64 offset; - LNK_ChunkLayout *layout; -} LNK_OffsetChunks; - -typedef struct -{ - LNK_ChunkLayout layout; - String8 buffer; - U8 fill_byte; - Rng1U64 *ranges; -} LNK_ChunkLayoutSerializer; - -//////////////////////////////// - -read_only global LNK_Chunk g_null_chunk = { 0, 0, /* is_discarded: */ 1 }; -read_only global LNK_Chunk *g_null_chunk_ptr = &g_null_chunk; - -//////////////////////////////// - -internal LNK_ChunkRef lnk_chunk_ref(U64 sect_id, U64 chunk_id); -internal B32 lnk_chunk_ref_is_equal(LNK_ChunkRef a, LNK_ChunkRef b); - -internal LNK_ChunkNode * lnk_chunk_list_push(Arena *arena, LNK_ChunkList *list, LNK_Chunk *chunk); -internal void lnk_chunk_list_concat_in_place(LNK_ChunkList *list, LNK_ChunkList *to_concat); -internal void lnk_chunk_list_concat_in_place_arr(LNK_ChunkList *list, LNK_ChunkList *arr, U64 count); -internal LNK_ChunkList ** lnk_make_chunk_list_arr_arr(Arena *arena, U64 slot_count, U64 per_count); -internal void lnk_chunk_array_sort(LNK_ChunkArray arr); - -internal LNK_ChunkManager * lnk_chunk_manager_alloc(Arena *arena, U64 id, U64 align); -internal LNK_Chunk * lnk_chunk_push(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index); -internal LNK_Chunk * lnk_chunk_push_leaf(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index, void *raw_ptr, U64 raw_size); -internal LNK_Chunk * lnk_chunk_push_list(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index); -internal LNK_ChunkNode * lnk_chunk_deep_copy(Arena *arena, LNK_Chunk *chunk); -internal LNK_ChunkNode * lnk_merge_chunks(Arena *arena, LNK_ChunkManager *dst_cman, LNK_Chunk *dst, LNK_Chunk *src, U64 *id_map_out, U64 id_map_max); -internal void lnk_chunk_associate(LNK_Chunk *head, LNK_Chunk *associate); -internal B32 lnk_chunk_is_discarded(LNK_Chunk *chunk); -internal U64 lnk_chunk_get_size(LNK_Chunk *chunk); -internal U64 lnk_chunk_list_get_node_count(LNK_Chunk *chunk); - -internal void lnk_chunk_op_list_push_node(LNK_ChunkOpList *list, LNK_ChunkOp *op); - -internal LNK_ChunkOp * lnk_push_chunk_op_begin(Arena *arena, U64 chunk_id); -internal LNK_ChunkOp * lnk_push_chunk_op_end_virt(Arena *arena); -internal LNK_ChunkOp * lnk_push_chunk_op_end_file(Arena *arena); -internal LNK_ChunkOp * lnk_push_chunk_op_align(Arena *arena, U64 align, U64 val); -internal LNK_ChunkOp * lnk_push_chunk_op_write(Arena *arena, String8 string); - -internal LNK_ChunkLayout lnk_layout_from_chunk(Arena *arena, LNK_Chunk *root, U64 total_chunk_count); -internal LNK_ChunkLayout lnk_build_chunk_layout(Arena *arena, LNK_ChunkManager *cman); - -#define LNK_CHUNK_VISITOR_SIG(name) B32 name(U64 sect_id, LNK_Chunk *chunk, void *ud) -typedef LNK_CHUNK_VISITOR_SIG(LNK_ChunkVisitorSig); -internal void lnk_visit_chunks(U64 sect_id, LNK_Chunk *root, LNK_ChunkVisitorSig *cb, void *ud); - -internal LNK_ChunkNode * lnk_chunk_ptr_list_reserve(Arena *arena, LNK_ChunkList *list, U64 count); -internal String8Array lnk_data_arr_from_chunk_ptr_list(Arena *arena, LNK_ChunkList list); -internal String8Array * lnk_data_arr_from_chunk_ptr_list_arr(Arena *arena, LNK_ChunkList *list_arr, U64 count); - -internal String8 lnk_string_from_chunk_type(LNK_ChunkType type); - diff --git a/src/linker/lnk_cmd_line.c b/src/linker/lnk_cmd_line.c index ee7e76bc..ac179a42 100644 --- a/src/linker/lnk_cmd_line.c +++ b/src/linker/lnk_cmd_line.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal String8List diff --git a/src/linker/lnk_cmd_line.h b/src/linker/lnk_cmd_line.h index ea8d3052..1cc65781 100644 --- a/src/linker/lnk_cmd_line.h +++ b/src/linker/lnk_cmd_line.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 956ebf5f..a6e76e38 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// @@ -396,7 +396,11 @@ lnk_get_base_addr(LNK_Config *config) if (config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) { base_addr = coff_default_dll_base_from_machine(config->machine); } else if (config->file_characteristics & PE_ImageFileCharacteristic_EXE) { - base_addr = coff_default_exe_base_from_machine(config->machine); + if ((~config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE) && config->machine == COFF_MachineType_X64) { + base_addr = coff_default_exe_base_from_machine(COFF_MachineType_X86); + } else { + base_addr = coff_default_exe_base_from_machine(config->machine); + } } else { lnk_error(LNK_Error_Cmdl, "image type is not specified."); } @@ -2002,6 +2006,11 @@ lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line) config->opt_lbr = LNK_SwitchState_Yes; } } + + // warn about unused large address aware flag + if ((~config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE) && (config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL)) { + lnk_error(LNK_Warning_NoLargeAddressAwarenessForDll, "/LARGEADDRESSAWARE:NO has no effect when specified together with /DLL"); + } // error check base address flags if (config->flags & LNK_ConfigFlag_Fixed) { diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 3d06b062..ad0160a3 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/lnk_debug_helper.c b/src/linker/lnk_debug_helper.c new file mode 100644 index 00000000..3e964c76 --- /dev/null +++ b/src/linker/lnk_debug_helper.c @@ -0,0 +1,39 @@ +// Copyright (c) 2025 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal String8 +lnk_make_linker_compile3(Arena *arena, COFF_MachineType machine) +{ + String8 comp3_data = cv_make_comp3(arena, + 0, + CV_Language_LINK, + cv_arch_from_coff_machine(machine), + /* ver_fe_major */ 0, + /* ver_fe_minor */ 0, + /* ver_fe_build */ 0, + /* ver_feqfe */ 0, + /* ver_major */ 14, + /* ver_minor */ 36, + /* ver_build */ 32537, + /* ver_qfe */ 0, + str8_lit(BUILD_TITLE)); + return comp3_data; +} + +internal String8 +lnk_make_debug_s(Arena *arena, CV_SymbolList symbol_list) +{ + Temp scratch = scratch_begin(&arena, 1); + + CV_DebugS debug_s = {0}; + String8List *symbol_list_ptr = cv_sub_section_ptr_from_debug_s(&debug_s, CV_C13SubSectionKind_Symbols); + *symbol_list_ptr = cv_data_from_symbol_list(scratch.arena, symbol_list, CV_SymbolAlign); + + String8List debug_s_data_list = cv_data_c13_from_debug_s(scratch.arena, &debug_s, 1); + String8 debug_s_data = str8_list_join(arena, &debug_s_data_list, 0); + + scratch_end(scratch); + return debug_s_data; +} + + diff --git a/src/linker/lnk_debug_helper.h b/src/linker/lnk_debug_helper.h new file mode 100644 index 00000000..a0627d17 --- /dev/null +++ b/src/linker/lnk_debug_helper.h @@ -0,0 +1,8 @@ +// Copyright (c) 2025 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#pragma once + +internal String8 lnk_make_linker_compile3(Arena *arena, COFF_MachineType machine); +internal String8 lnk_make_debug_s(Arena *arena, CV_SymbolList symbol_list); + diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index 7e857d20..708a9e93 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// @@ -9,16 +9,13 @@ THREAD_POOL_TASK_FUNC(lnk_parse_debug_s_task) U64 obj_idx = task_id; LNK_ParseDebugSTaskData *task = raw_task; - LNK_Obj *obj = task->obj_arr[obj_idx]; - LNK_ChunkList sect_list = task->sect_list_arr[obj_idx]; - CV_DebugS *debug_s = &task->debug_s_arr[obj_idx]; - - for (LNK_ChunkNode *node = sect_list.first; node != 0; node = node->next) { - LNK_ChunkPtr chunk = node->data; - Assert(chunk->type == LNK_Chunk_Leaf); + LNK_Obj *obj = task->obj_arr[obj_idx]; + String8List sect_list = task->sect_list_arr[obj_idx]; + CV_DebugS *debug_s = &task->debug_s_arr[obj_idx]; + for (String8Node *node = sect_list.first; node != 0; node = node->next) { // parse & merge sub sections - CV_DebugS ds = cv_parse_debug_s(arena, chunk->u.leaf); + CV_DebugS ds = cv_parse_debug_s(arena, node->string); cv_debug_s_concat_in_place(debug_s, &ds); // make sure there is one string table @@ -38,7 +35,7 @@ THREAD_POOL_TASK_FUNC(lnk_parse_debug_s_task) } internal CV_DebugS * -lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, LNK_ChunkList *sect_list_arr) +lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *sect_list_arr) { ProfBeginFunction(); @@ -113,12 +110,12 @@ THREAD_POOL_TASK_FUNC(lnk_parse_debug_t_task) } internal CV_DebugT * -lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, LNK_ChunkList *debug_t_list_arr) +lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *debug_t_list_arr) { ProfBeginFunction(); // list -> array - String8Array *data_arr_arr = lnk_data_arr_from_chunk_ptr_list_arr(arena->v[0], debug_t_list_arr, obj_count); + String8Array *data_arr_arr = str8_array_from_list_arr(arena->v[0], debug_t_list_arr, obj_count); // validate signatures LNK_CheckDebugTSigTaskData check_sig; @@ -376,26 +373,26 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, String8List lib_dir ProfBegin("Collect CodeView"); // TODO: fix memory leak, we need a Temp wrapper for pool arena B32 collect_discarded_flag = 0; - LNK_ChunkList *debug_s_list_arr = lnk_collect_obj_chunks(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug"), str8_lit("S"), collect_discarded_flag); - LNK_ChunkList *debug_p_list_arr = lnk_collect_obj_chunks(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug"), str8_lit("P"), collect_discarded_flag); - LNK_ChunkList *debug_t_list_arr = lnk_collect_obj_chunks(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug"), str8_lit("T"), collect_discarded_flag); + String8List *debug_s_list_arr = lnk_collect_obj_chunks_parallel(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug"), str8_lit("S"), collect_discarded_flag); + String8List *debug_p_list_arr = lnk_collect_obj_chunks_parallel(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug"), str8_lit("P"), collect_discarded_flag); + String8List *debug_t_list_arr = lnk_collect_obj_chunks_parallel(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug"), str8_lit("T"), collect_discarded_flag); ProfEnd(); if (lnk_get_log_status(LNK_Log_Debug) || PROFILE_TELEMETRY) { U64 total_debug_s_size = 0, total_debug_t_size = 0, total_debug_p_size = 0; for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) { - for (LNK_ChunkNode *chunk = debug_s_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { - total_debug_s_size += chunk->data->u.leaf.size; + for (String8Node *chunk = debug_s_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { + total_debug_s_size += chunk->string.size; } - for (LNK_ChunkNode *chunk = debug_t_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { - total_debug_t_size += chunk->data->u.leaf.size; + for (String8Node *chunk = debug_t_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { + total_debug_t_size += chunk->string.size; } - for (LNK_ChunkNode *chunk = debug_p_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { - total_debug_p_size += chunk->data->u.leaf.size; + for (String8Node *chunk = debug_p_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { + total_debug_p_size += chunk->string.size; } } - ProfNoteV("Total .debug$S Input Size: %M", total_debug_s_size); + ProfNoteV("Total .debug$S Input Size: %M", total_debug_s_size); ProfNoteV("Total .debug$T Input Size: %M", total_debug_t_size); ProfNoteV("Total .debug$P Input Size: %M", total_debug_p_size); @@ -416,10 +413,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, String8List lib_dir str8_serial_push_string(scratch.arena, &srl, raw_null_leaf); String8 null_debug_data = str8_serial_end(tp_arena->v[0], &srl); - LNK_Chunk *null_chunk = push_array(tp_arena->v[0], LNK_Chunk, 1); - null_chunk->type = LNK_Chunk_Leaf; - null_chunk->u.leaf = null_debug_data; - lnk_chunk_list_push(tp_arena->v[0], &debug_t_list_arr[0], null_chunk); + str8_list_push(tp_arena->v[0], &debug_t_list_arr[0], null_debug_data); } ProfBegin("Parse CodeView"); @@ -2971,55 +2965,43 @@ lnk_hash_cv_symbol_ptr_arr(TP_Context *tp, Arena *arena, CV_SymbolPtrArray arr) internal THREAD_POOL_TASK_FUNC(lnk_push_dbi_sec_contrib_task) { - U64 obj_idx = task_id; - LNK_PushDbiSecContribTaskData *task = raw_task; - LNK_Section **sect_id_map = task->sect_id_map; - PDB_DbiModule *mod = task->mod_arr[obj_idx]; - LNK_Obj *obj = &task->obj_arr[obj_idx]; - PDB_DbiSectionContribList *dst_list = &task->sc_list[obj_idx]; - String8 image_data = task->image_data; - // TODO: use chunked lists for SC + // TODO: put back unused sc nodes + // TODO: compute CRC for relocations - // TODO: put back unused nodes - PDB_DbiSectionContribNode *sc_arr = push_array_no_zero(arena, PDB_DbiSectionContribNode, obj->sect_count); - U64 sc_count = 0; + U64 obj_idx = task_id; + LNK_PushDbiSecContribTaskData *task = raw_task; + PDB_DbiModule *mod = task->mod_arr[obj_idx]; + LNK_Obj *obj = &task->obj_arr[obj_idx]; + + COFF_SectionHeader *obj_section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + PDB_DbiSectionContribNode *sc_arr = push_array_no_zero(arena, PDB_DbiSectionContribNode, obj->header.section_count_no_null); + U64 sc_count = 0; - for (U64 chunk_idx = 0; chunk_idx < obj->sect_count; ++chunk_idx) { - LNK_Chunk *chunk = obj->chunk_arr[chunk_idx]; - - if (!chunk || lnk_chunk_is_discarded(chunk)) { + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *obj_sect_header = &obj_section_table[sect_idx]; + + if (obj_sect_header->flags & COFF_SectionFlag_LnkRemove) { continue; } - LNK_Section *sect = lnk_sect_from_chunk_ref(task->sect_id_map, chunk->ref); - if (!sect->has_layout) { - continue; - } - - // query chunk info - ISectOff chunk_sc = lnk_sc_from_chunk_ref (sect_id_map, chunk->ref); - String8 chunk_data = lnk_data_from_chunk_ref (sect_id_map, image_data, chunk->ref); - LNK_Section *chunk_sect = lnk_sect_from_chunk_ref (sect_id_map, chunk->ref); - U64 chunk_size = lnk_file_size_from_chunk_ref(sect_id_map, chunk->ref); - - // compute chunk CRC - U32 data_crc = update_crc32(0, chunk_data.str, chunk_data.size); - U32 reloc_crc = 0; // TODO: compute CRC for relocations block + U64 sect_number = rng_1u64_array_bsearch(task->image_section_file_ranges, obj_sect_header->foff); + String8 sect_data = str8_substr(task->image_data, rng_1u64(obj_sect_header->foff, obj_sect_header->foff + obj_sect_header->fsize)); + U32 sect_off = obj_sect_header->foff - task->image_section_file_ranges.v[sect_number].min; // fill out SC PDB_DbiSectionContribNode *sc = sc_arr + sc_count++; - sc->data.base.sec = safe_cast_u16(chunk_sc.isect); + sc->data.base.sec = (U16)sect_number; sc->data.base.pad0 = 0; - sc->data.base.sec_off = chunk_sc.off; - sc->data.base.size = safe_cast_u32(chunk_size); - sc->data.base.flags = chunk_sect->flags; + sc->data.base.sec_off = sect_off; + sc->data.base.size = obj_sect_header->foff; + sc->data.base.flags = obj_sect_header->flags; sc->data.base.mod = mod->imod; sc->data.base.pad1 = 0; - sc->data.data_crc = data_crc; - sc->data.reloc_crc = reloc_crc; + sc->data.data_crc = update_crc32(0, sect_data.str, sect_data.size); + sc->data.reloc_crc = 0; - dbi_sec_contrib_list_push_node(dst_list, sc); + dbi_sec_contrib_list_push_node(&task->sc_list[obj_idx], sc); } // Mod1::fUpdateSecContrib @@ -3040,36 +3022,33 @@ THREAD_POOL_TASK_FUNC(lnk_build_pdb_public_symbols_defined_task) { ProfBeginFunction(); - LNK_BuildPublicSymbolsTask *task = raw_task; - LNK_Section **sect_id_map = task->sect_id_map; - CV_SymbolList *pub_list = &task->pub_list_arr[task_id]; - LNK_SymbolHashTrieChunkList chunk_list = task->chunk_lists[task_id]; + LNK_BuildPublicSymbolsTask *task = raw_task; + CV_SymbolList *pub_list = &task->pub_list_arr[task_id]; + LNK_SymbolHashTrieChunkList chunk_list = task->chunk_lists[task_id]; for (LNK_SymbolHashTrieChunk *chunk = chunk_list.first; chunk != 0; chunk = chunk->next) { CV_SymbolNode *nodes = push_array_no_zero(arena, CV_SymbolNode, chunk->count); for (U64 i = 0, node_idx = 0; i < chunk->count; ++i) { LNK_Symbol *symbol = chunk->v[i].symbol; + Assert(symbol->type == LNK_Symbol_Defined); - Assert(LNK_Symbol_IsDefined(symbol->type)); - - LNK_DefinedSymbol *defined_symbol = &symbol->u.defined; - if (defined_symbol->value_type == LNK_DefinedSymbolValue_Chunk) { + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { CV_Pub32Flags flags = 0; - if (defined_symbol->flags & LNK_DefinedSymbolFlag_IsFunc || defined_symbol->flags & LNK_DefinedSymbolFlag_IsThunk) { + if (COFF_SymbolType_IsFunc(parsed_symbol.type)) { flags |= CV_Pub32Flag_Function; } - U64 symbol_off = lnk_sect_off_from_symbol(sect_id_map, symbol); - U64 symbol_isect = lnk_isect_from_symbol(sect_id_map, symbol); - - U32 symbol_off32 = safe_cast_u32(symbol_off); - U16 symbol_isect16 = safe_cast_u16(symbol_isect); + ISectOff sc = lnk_sc_from_symbol(symbol); + U16 symbol_isect16 = safe_cast_u16(sc.isect); + U32 symbol_off32 = safe_cast_u32(sc.off); nodes[node_idx].data = cv_make_pub32(arena, flags, symbol_off32, symbol_isect16, symbol->name); cv_symbol_list_push_node(pub_list, &nodes[node_idx]); - ++node_idx; + node_idx += 1; } } } @@ -3098,7 +3077,6 @@ internal void lnk_build_pdb_public_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, - LNK_Section **sect_id_map, PDB_PsiContext *psi) { ProfBeginFunction(); @@ -3106,9 +3084,8 @@ lnk_build_pdb_public_symbols(TP_Context *tp, ProfBegin("Defined"); LNK_BuildPublicSymbolsTask task = {0}; - task.sect_id_map = sect_id_map; task.pub_list_arr = push_array(scratch.arena, CV_SymbolList, tp->worker_count); - task.chunk_lists = symtab->chunk_lists[LNK_SymbolScopeIndex_Defined]; + task.chunk_lists = symtab->chunk_lists[LNK_SymbolScope_Defined]; tp_for_parallel(tp, arena, tp->worker_count, lnk_build_pdb_public_symbols_defined_task, &task); ProfEnd(); @@ -3126,16 +3103,8 @@ internal String8List lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, - Guid guid, - COFF_MachineType machine, - COFF_TimeStamp time_stamp, - U32 age, - U64 page_size, - String8 pdb_name, - String8List lib_dir_list, - String8List natvis_list, + LNK_Config *config, LNK_SymbolTable *symtab, - LNK_Section **sect_id_map, U64 obj_count, LNK_Obj *obj_arr, CV_DebugS *debug_s_arr, @@ -3147,8 +3116,12 @@ lnk_build_pdb(TP_Context *tp, ProfBegin("PDB"); Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data); + COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range); + U64 image_section_table_count = pe.section_count+1; + ProfBegin("Setup PDB Context"); - PDB_Context *pdb = pdb_alloc(page_size, machine, time_stamp, age, guid); + PDB_Context *pdb = pdb_alloc(config->page_size, config->machine, config->time_stamp, config->age, config->guid); ProfEnd(); // move patched type data @@ -3284,27 +3257,29 @@ lnk_build_pdb(TP_Context *tp, ProfBegin("Build DBI Section Headers"); { - LNK_Symbol *coff_sect_array_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScopeFlag_Internal, LNK_COFF_SECT_HEADER_ARRAY_SYMBOL_NAME); - LNK_Chunk *coff_sect_chunk = lnk_chunk_from_symbol(coff_sect_array_symbol); - String8 coff_sect_chunk_data = lnk_data_from_chunk_ref(sect_id_map, image_data, coff_sect_chunk->ref); - U64 coff_sect_count = coff_sect_chunk_data.size / sizeof(COFF_SectionHeader); - COFF_SectionHeader *coff_sect_ptr = (COFF_SectionHeader*)coff_sect_chunk_data.str; - for (COFF_SectionHeader *hdr_ptr = &coff_sect_ptr[0], *opl = hdr_ptr + coff_sect_count; - hdr_ptr < opl; - ++hdr_ptr) { - dbi_push_section(pdb->dbi, hdr_ptr); + for (U64 sect_idx = 1; sect_idx < image_section_table_count; sect_idx += 1) { + dbi_push_section(pdb->dbi, image_section_table[sect_idx]); } } ProfEnd(); ProfBegin("Build Section Contrib Map"); { + + Rng1U64Array image_section_file_ranges = {0}; + image_section_file_ranges.count = image_section_table_count; + image_section_file_ranges.v = push_array(scratch.arena, Rng1U64, image_section_table_count); + for (U64 i = 0; i < image_section_table_count; i += 1) { + COFF_SectionHeader *sect_header = image_section_table[i]; + image_section_file_ranges.v[i] = rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize); + } + LNK_PushDbiSecContribTaskData task = {0}; task.obj_arr = obj_arr; - task.sect_id_map = sect_id_map; task.mod_arr = mod_arr; task.sc_list = push_array(scratch.arena, PDB_DbiSectionContribList, obj_count); task.image_data = image_data; + task.image_section_file_ranges = image_section_file_ranges; tp_for_parallel(tp, tp_arena, obj_count, lnk_push_dbi_sec_contrib_task, &task); dbi_sec_list_concat_arr(&pdb->dbi->sec_contrib_list, obj_count, task.sc_list); @@ -3313,7 +3288,7 @@ lnk_build_pdb(TP_Context *tp, ProfBegin("Build NatVis"); { - String8Array natvis_file_path_arr = str8_array_from_list(scratch.arena, &natvis_list); + String8Array natvis_file_path_arr = str8_array_from_list(scratch.arena, &config->natvis_list); String8Array natvis_file_data_arr = lnk_read_data_from_file_path_parallel(tp, scratch.arena, natvis_file_path_arr); for (U64 i = 0; i < natvis_file_data_arr.count; ++i) { @@ -3341,7 +3316,7 @@ lnk_build_pdb(TP_Context *tp, } ProfEnd(); - lnk_build_pdb_public_symbols(tp, tp_arena, symtab, sect_id_map, pdb->psi); + lnk_build_pdb_public_symbols(tp, tp_arena, symtab, pdb->psi); pdb_build(tp, tp_arena, pdb, string_ht); @@ -4439,16 +4414,16 @@ lnk_type_from_itype(CV_TypeIndex itype, Rng1U64 tpi_itype_range, RDIB_Type **tpi } internal U64 -lnk_virt_off_from_sect_off(U64 sect_idx, U64 sect_off, LNK_SectionArray image_sects, LNK_Obj *obj, CV_SymKind symbol_kind, U64 symbol_offset) +lnk_voff_from_sect_off(U64 sect_idx, U64 sect_off, LNK_SectionArray image_sects, LNK_Obj *obj, CV_SymKind symbol_kind, U64 symbol_offset) { - U64 virt_off = 0; + U64 voff = 0; if (sect_idx < image_sects.count) { - virt_off = image_sects.v[sect_idx].virt_off + sect_off; + voff = image_sects.v[sect_idx]->voff + sect_off; } else { lnk_error_obj(LNK_Error_CvIllSymbolData, obj, "Out of bounds section index 0x%x in S_%S @ 0x%llx.", sect_idx, cv_string_from_sym_kind(symbol_kind), symbol_offset); } - return virt_off; + return voff; } internal Rng1U64 @@ -4456,8 +4431,8 @@ lnk_virt_range_from_sect_off_size(U64 sect_idx, U64 sect_off, U64 size, LNK_Sect { Rng1U64 virt_range = {0}; if (sect_idx < image_sects.count) { - U64 virt_off = image_sects.v[sect_idx].virt_off + sect_off; - virt_range = rng_1u64(virt_off, virt_off + size); + U64 voff = image_sects.v[sect_idx]->voff + sect_off; + virt_range = rng_1u64(voff, voff + size); } else { lnk_error_obj(LNK_Error_CvIllSymbolData, obj, "Out of bounds section index 0x%x in S_%S @ 0x%llx.", sect_idx, cv_string_from_sym_kind(symbol_kind), symbol_offset); @@ -4589,8 +4564,8 @@ THREAD_POOL_TASK_FUNC(lnk_convert_line_tables_to_rdi_task) lnk_error_obj(LNK_Warning_IllData, obj, "Out of bounds section index (%u) in $$LINES; skip line info for \"%S\".", parsed_lines.sec_idx, file_path); continue; } - LNK_Section *sect = &task->image_sects.v[parsed_lines.sec_idx]; - CV_LineArray lines = cv_c13_line_array_from_data(arena, raw_lines, sect->virt_off, parsed_lines); + LNK_Section *sect = task->image_sects.v[parsed_lines.sec_idx]; + CV_LineArray lines = cv_c13_line_array_from_data(arena, raw_lines, sect->voff, parsed_lines); // find source file for this line table String8 normal_path = lnk_normalize_src_file_path(scratch.arena, file_path); @@ -4734,7 +4709,7 @@ THREAD_POOL_TASK_FUNC(lnk_convert_symbols_to_rdi_task) String8 name = str8_cstring_capped(data32 + 1, symbol.data.str + symbol.data.size); RDIB_Type *type = lnk_type_from_itype(data32->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset); RDIB_Type *container_type = lnk_find_container_type(name, task->tpi_itype_range, task->udt_name_buckets, task->udt_name_buckets_cap, task->tpi_itype_map); - U64 data_voff = lnk_virt_off_from_sect_off(data32->sec, data32->off, task->image_sects, obj, symbol.kind, symbol.offset); + U64 data_voff = lnk_voff_from_sect_off(data32->sec, data32->off, task->image_sects, obj, symbol.kind, symbol.offset); B32 is_comp_gen = symbol.kind == CV_SymKind_LDATA32 && name.size == 0 && type == 0; if (!is_comp_gen) { @@ -5330,32 +5305,26 @@ THREAD_POOL_TASK_FUNC(lnk_collect_obj_virtual_ranges_task) RDIB_Unit *dst = &task->units[unit_chunk_idx].v[local_unit_idx]; dst->virt_range_count = 0; - dst->virt_ranges = push_array_no_zero(arena, Rng1U64, obj->sect_count); + dst->virt_ranges = push_array_no_zero(arena, Rng1U64, obj->header.section_count_no_null); - for (U64 chunk_idx = 0; chunk_idx < obj->sect_count; ++chunk_idx) { - LNK_Chunk *chunk = obj->chunk_arr[chunk_idx]; - if (!chunk || lnk_chunk_is_discarded(chunk)) { + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + + if (sect_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + if (sect_header->vsize == 0) { continue; } - LNK_Section *sect = lnk_sect_from_chunk_ref(task->sect_id_map, chunk->ref); - if (!sect->has_layout) { - continue; - } - - U64 chunk_voff = lnk_virt_off_from_chunk_ref(task->sect_id_map, chunk->ref); - U64 chunk_size = lnk_virt_size_from_chunk_ref(task->sect_id_map, chunk->ref); - - if (chunk_size == 0) { - continue; - } - - dst->virt_ranges[dst->virt_range_count] = rng_1u64(chunk_voff, chunk_voff + chunk_size); + dst->virt_ranges[dst->virt_range_count] = rng_1u64(sect_header->voff, sect_header->voff + sect_header->vsize); ++dst->virt_range_count; } // free unused memory - arena_pop(arena, sizeof(dst->virt_ranges[0]) * (obj->sect_count - dst->virt_range_count)); + arena_pop(arena, sizeof(dst->virt_ranges[0]) * (obj->header.section_count_no_null - dst->virt_range_count)); ProfEnd(); } @@ -5368,7 +5337,6 @@ lnk_build_rad_debug_info(TP_Context *tp, String8 image_name, String8 image_data, LNK_SectionArray image_sects, - LNK_Section **sect_id_map, U64 obj_count, LNK_Obj *obj_arr, CV_DebugS *debug_s_arr, @@ -5386,10 +5354,8 @@ lnk_build_rad_debug_info(TP_Context *tp, { U64 image_vsize = 0; for (U64 sect_idx = 0; sect_idx < image_sects.count; sect_idx++) { - LNK_Section *sect = &image_sects.v[sect_idx]; - U64 sect_virt_size = lnk_virt_size_from_chunk_ref(sect_id_map, sect->root->ref); - U64 sect_voff_max = sect->virt_off + sect_virt_size; - image_vsize = Max(image_vsize, sect_voff_max); + LNK_Section *sect = image_sects.v[sect_idx]; + image_vsize = Max(image_vsize, sect->voff + sect->vsize); } input.top_level_info.arch = arch; @@ -5405,18 +5371,15 @@ lnk_build_rad_debug_info(TP_Context *tp, input.sect_count = image_sects.count; input.sections = push_array(scratch.arena, RDIB_BinarySection, image_sects.count); for (U64 sect_idx = 0; sect_idx < image_sects.count; ++sect_idx) { - LNK_Section *src = &image_sects.v[sect_idx]; + LNK_Section *src = image_sects.v[sect_idx]; RDIB_BinarySection *dst = &input.sections[sect_idx]; - U64 sect_virt_size = lnk_virt_size_from_chunk_ref(sect_id_map, src->root->ref); - U64 sect_file_size = lnk_file_size_from_chunk_ref(sect_id_map, src->root->ref); - dst->name = push_str8_copy(scratch.arena, src->name); dst->flags = rdi_binary_section_flags_from_coff_section_flags(src->flags); - dst->voff_first = src->virt_off; - dst->voff_opl = src->virt_off + sect_virt_size; - dst->foff_first = src->file_off; - dst->foff_opl = src->file_off + sect_file_size; + dst->voff_first = src->voff; + dst->voff_opl = src->voff + src->vsize; + dst->foff_first = src->foff; + dst->foff_opl = src->foff + src->fsize; } } ProfEnd(); @@ -5551,7 +5514,6 @@ lnk_build_rad_debug_info(TP_Context *tp, { LNK_ConvertUnitToRDITask task = {0}; task.image_sects = image_sects; - task.sect_id_map = sect_id_map; task.obj_arr = obj_arr; task.debug_s_arr = debug_s_arr; task.ipi = types[CV_TypeIndexSource_IPI]; diff --git a/src/linker/lnk_debug_info.h b/src/linker/lnk_debug_info.h index cca87cff..7188830e 100644 --- a/src/linker/lnk_debug_info.h +++ b/src/linker/lnk_debug_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once @@ -122,9 +122,9 @@ typedef union typedef struct { - LNK_Obj **obj_arr; - LNK_ChunkList *sect_list_arr; - CV_DebugS *debug_s_arr; + LNK_Obj **obj_arr; + String8List *sect_list_arr; + CV_DebugS *debug_s_arr; } LNK_ParseDebugSTaskData; typedef struct @@ -313,10 +313,10 @@ typedef struct typedef struct { LNK_Obj *obj_arr; - LNK_Section **sect_id_map; PDB_DbiModule **mod_arr; PDB_DbiSectionContribList *sc_list; String8 image_data; + Rng1U64Array image_section_file_ranges; } LNK_PushDbiSecContribTaskData; typedef struct @@ -334,7 +334,6 @@ typedef struct typedef struct { - LNK_Section **sect_id_map; LNK_SymbolHashTrieChunkList *chunk_lists; CV_SymbolList *pub_list_arr; @@ -454,7 +453,6 @@ typedef struct typedef struct { LNK_SectionArray image_sects; - LNK_Section **sect_id_map; LNK_Obj *obj_arr; CV_DebugS *debug_s_arr; CV_DebugT ipi; @@ -496,8 +494,8 @@ typedef struct //////////////////////////////// // CodeView -internal CV_DebugS * lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, LNK_ChunkList *sect_list_arr); -internal CV_DebugT * lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, LNK_ChunkList *debug_t_list_arr); +internal CV_DebugS * lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *sect_list_arr); +internal CV_DebugT * lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *debug_t_list_arr); internal CV_SymbolList * lnk_cv_symbol_list_arr_from_debug_s_arr(TP_Context *tp, TP_Arena *arena, U64 obj_count, CV_DebugS *debug_s_arr); internal LNK_PchInfo * lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj *obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols); @@ -557,7 +555,6 @@ internal String8List lnk_build_rad_debug_info(TP_Context *tp, String8 image_name, String8 image_data, LNK_SectionArray image_sects, - LNK_Section **sect_id_map, U64 obj_count, LNK_Obj *obj_arr, CV_DebugS *debug_s_arr, @@ -574,25 +571,13 @@ internal LNK_ProcessedCodeViewC13Data lnk_process_c13_data(TP_Context *tp, TP_Ar internal U64 * lnk_hash_cv_symbol_ptr_arr(TP_Context *tp, Arena *arena, CV_SymbolPtrArray arr); internal CV_SymbolPtrArray lnk_dedup_gsi_symbols(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 obj_count, CV_SymbolList *symbol_list_arr); -internal void lnk_build_pdb_public_symbols(TP_Context *tp, - TP_Arena *arena, - LNK_SymbolTable *symtab, - LNK_Section **sect_id_map, - PDB_PsiContext *psi); +internal void lnk_build_pdb_public_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, PDB_PsiContext *psi); internal String8List lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, - Guid guid, - COFF_MachineType machine, - COFF_TimeStamp time_stamp, - U32 age, - U64 page_size, - String8 pdb_name, - String8List lib_dir_list, - String8List natvis_list, + LNK_Config *config, LNK_SymbolTable *symtab, - LNK_Section **sect_id_map, U64 obj_count, LNK_Obj *obj_arr, CV_DebugS *debug_s_arr, diff --git a/src/linker/lnk_error.c b/src/linker/lnk_error.c index b907de88..7ff046f0 100644 --- a/src/linker/lnk_error.c +++ b/src/linker/lnk_error.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) static LNK_ErrorMode g_error_mode_arr[LNK_Error_Count]; diff --git a/src/linker/lnk_error.h b/src/linker/lnk_error.h index 761579ea..b528a8ef 100644 --- a/src/linker/lnk_error.h +++ b/src/linker/lnk_error.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once @@ -32,13 +32,13 @@ typedef enum LNK_Error_IO, LNK_Error_LargeAddrAwareRequired, LNK_Error_InvalidPath, + LNK_Error_MultiplyDefinedSymbol, LNK_Error_StopLast, LNK_Error_First, LNK_Error_AlreadyDefinedSymbol, LNK_Error_AlternateNameConflict, LNK_Error_CvPrecomp, - LNK_Error_MultiplyDefinedSymbol, LNK_Error_Natvis, LNK_Error_TooManyFiles, LNK_Error_UndefinedSymbol, @@ -48,6 +48,8 @@ typedef enum LNK_Error_CvIllSymbolData, LNK_Error_IllegalAlternateNameRedifine, LNK_Error_InvalidTypeIndex, + LNK_Error_UndefinedIsWeak, + LNK_Error_WeakCycle, LNK_Error_Last, LNK_Warning_First, @@ -81,6 +83,7 @@ typedef enum LNK_Warning_UnknownSwitch, LNK_Warning_TLSAlign, LNK_Warning_DirectiveSectionWithRelocs, + LNK_Warning_NoLargeAddressAwarenessForDll, LNK_Warning_Last, LNK_Error_Count diff --git a/src/linker/lnk_export_table.c b/src/linker/lnk_export_table.c index d2f9fc10..f2081e08 100644 --- a/src/linker/lnk_export_table.c +++ b/src/linker/lnk_export_table.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) int @@ -61,19 +61,16 @@ lnk_export_table_push_export(LNK_ExportTable *exptab, LNK_SymbolTable *symtab, L LNK_Export *exp = 0; // get export symbol - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, exp_parse->name); + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, exp_parse->name); if (symbol == 0) { lnk_error(LNK_Warning_IllExport, "symbol \"%S\" for export doesn't exist", exp_parse->name); goto exit; } - symbol = lnk_resolve_symbol(symtab, symbol); - if (!LNK_Symbol_IsDefined(symbol->type)) { + if (symbol->type != LNK_Symbol_Defined) { lnk_error(LNK_Warning_IllExport, "unable to resolve symbol \"%S\" for export", exp_parse->name); goto exit; } - LNK_DefinedSymbol *def = &symbol->u.defined; - // NOTE: It is possible to export a global variable as CODE // with following snippet: // int global_bar = 0; @@ -85,13 +82,15 @@ lnk_export_table_push_export(LNK_ExportTable *exptab, LNK_SymbolTable *symtab, L COFF_ImportType type = coff_import_header_type_from_string(exp_parse->type); switch (type) { case COFF_ImportHeader_Code: { - B32 is_export_data = !(def->flags & (LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk)); + COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + B32 is_export_data = !COFF_SymbolType_IsFunc(defn.type); if (is_export_data) { lnk_error(LNK_Error_IllExport, "export \"%S\" is DATA but has specifier CODE", exp_parse->name); } } break; case COFF_ImportHeader_Data: { - B32 is_export_code = !!(def->flags & (LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk)); + COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + B32 is_export_code = COFF_SymbolType_IsFunc(defn.type); if (is_export_code) { lnk_error(LNK_Error_IllExport, "export \"%S\" is CODE but has specifier DATA", exp_parse->name); } @@ -106,7 +105,6 @@ lnk_export_table_push_export(LNK_ExportTable *exptab, LNK_SymbolTable *symtab, L } break; } - // error check multiple def exp = lnk_export_table_search(exptab, exp_parse->alias); if (exp) { diff --git a/src/linker/lnk_export_table.h b/src/linker/lnk_export_table.h index 42cd3ed7..57533918 100644 --- a/src/linker/lnk_export_table.h +++ b/src/linker/lnk_export_table.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once @@ -7,7 +7,7 @@ typedef struct LNK_Export { struct LNK_Export *next; String8 name; - LNK_Symbol *symbol; + struct LNK_Symbol *symbol; U32 id; U16 ordinal; COFF_ImportType type; diff --git a/src/linker/lnk_import_table.c b/src/linker/lnk_import_table.c index 6b474897..91282160 100644 --- a/src/linker/lnk_import_table.c +++ b/src/linker/lnk_import_table.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal LNK_ImportTable * @@ -55,7 +55,7 @@ internal LNK_ImportFunc * lnk_import_table_search_func(LNK_ImportDLL *dll, String8 name) { LNK_ImportFunc *func = 0; - hash_table_search_string_raw(dll->func_ht, name, &func); + hash_table_search_string_raw(dll->func_ht, name, (void **)&func); return func; } @@ -288,7 +288,7 @@ lnk_import_table_push_func_delayed(LNK_ImportTable *imptab, LNK_ImportDLL *dll, switch (dll->machine) { case COFF_MachineType_X64: { String8 iat_symbol_name = push_str8f(symtab->arena->v[0], "__imp_%S", header->func_name); - LNK_Symbol *iat_symbol = lnk_make_undefined_symbol(symtab->arena->v[0], iat_symbol_name, LNK_SymbolScopeFlag_Main); + LNK_Symbol *iat_symbol = lnk_make_undefined_symbol(symtab->arena->v[0], iat_symbol_name, LNK_SymbolScope_Main); // emit jmp thunk chunk jmp_thunk_chunk = lnk_emit_indirect_jump_thunk_x64(code_sect, code_table_chunk, iat_symbol); diff --git a/src/linker/lnk_import_table.h b/src/linker/lnk_import_table.h index 14493218..2ba9a9a4 100644 --- a/src/linker/lnk_import_table.h +++ b/src/linker/lnk_import_table.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/lnk_input.c b/src/linker/lnk_input.c new file mode 100644 index 00000000..b586ac56 --- /dev/null +++ b/src/linker/lnk_input.c @@ -0,0 +1,154 @@ +// Copyright (c) 2025 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +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 int +lnk_input_obj_compar(const void *raw_a, const void *raw_b) +{ + const LNK_InputObj **a = (const LNK_InputObj **) raw_a; + const LNK_InputObj **b = (const LNK_InputObj **) raw_b; + int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path); + return cmp; +} + +internal int +lnk_input_obj_compar_is_before(void *raw_a, void *raw_b) +{ + LNK_InputObj **a = raw_a; + LNK_InputObj **b = raw_b; + int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path); + int is_before = cmp < 0; + return is_before; +} + +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_InputImport * +lnk_input_import_list_push(Arena *arena, LNK_InputImportList *list) +{ + LNK_InputImport *node = push_array(arena, LNK_InputImport, 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_InputImport ** +lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list) +{ + LNK_InputImport **result = push_array_no_zero(arena, LNK_InputImport *, list.count); + U64 idx = 0; + for (LNK_InputImport *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_InputImport **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 = raw_a; + LNK_InputImport **b = raw_b; + int cmp = str8_compar_ignore_case(&(*a)->import_header.dll_name, &(*b)->import_header.dll_name); + if (cmp == 0) { + cmp = str8_compar_case_sensitive(&(*a)->import_header.func_name, &(*b)->import_header.func_name); + } + return cmp < 0; +} + +int +lnk_input_import_compar(const void *raw_a, const void *raw_b) +{ + LNK_InputImport * const *a = raw_a; + LNK_InputImport * const *b = raw_b; + int cmp = str8_compar_ignore_case(&(*a)->import_header.dll_name, &(*b)->import_header.dll_name); + if (cmp == 0) { + cmp = str8_compar_case_sensitive(&(*a)->import_header.func_name, &(*b)->import_header.func_name); + } + return cmp; +} + diff --git a/src/linker/lnk_input.h b/src/linker/lnk_input.h new file mode 100644 index 00000000..51ba3d65 --- /dev/null +++ b/src/linker/lnk_input.h @@ -0,0 +1,64 @@ +// 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 +{ + COFF_ParsedArchiveImportHeader import_header; + struct LNK_InputImport *next; +} LNK_InputImport; + +typedef struct LNK_InputImportList +{ + U64 count; + LNK_InputImport *first; + LNK_InputImport *last; +} LNK_InputImportList; + +typedef struct LNK_InputObj +{ + struct LNK_InputObj *next; + B32 is_thin; + B32 has_disk_read_failed; + String8 dedup_id; + String8 path; + String8 data; + String8 lib_path; +} LNK_InputObj; + +typedef struct LNK_InputObjList +{ + U64 count; + LNK_InputObj *first; + LNK_InputObj *last; +} LNK_InputObjList; + +//////////////////////////////// + +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_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_InputImport * 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_InputImport ** lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list); +internal LNK_InputImportList lnk_list_from_input_import_arr(LNK_InputImport **arr, U64 count); + diff --git a/src/linker/lnk_lib.c b/src/linker/lnk_lib.c index 93df67c7..22aad6c8 100644 --- a/src/linker/lnk_lib.c +++ b/src/linker/lnk_lib.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal LNK_LibNode * @@ -208,31 +208,6 @@ lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, S return arr; } -#if 0 -internal LNK_LibNode * -lnk_lib_list_push(Arena *arena, LNK_LibList *list, String8 data, String8 path) -{ - ProfBeginFunction(); - - TP_Arena pool_arena = {0}; - pool_arena.count = 1; - pool_arena.v = &arena; - - String8Array data_arr = {0}; - data_arr.count = 1; - data_arr.v = &data; - - String8Array path_arr = {0}; - path_arr.count = 1; - path_arr.v = &path; - - LNK_LibNodeArray node_arr = lnk_lib_list_push_parallel(&pool_arena, list, data_arr, path_arr); - - ProfEnd(); - return node_arr.v; -} -#endif - //////////////////////////////// internal LNK_LibWriter * @@ -265,14 +240,31 @@ lnk_lib_writer_push_obj(LNK_LibWriter *writer, LNK_Obj *obj) lnk_lib_member_list_push(writer->arena, &writer->member_list, member); // push external symbols - for (LNK_SymbolNode *node = obj->symbol_list.first; node != 0; node = node->next) { - LNK_Symbol *symbol = node->data; - B32 is_extern = symbol->type == LNK_Symbol_DefinedExtern; - if (is_extern) { - LNK_LibSymbol lib_symbol = {0}; - lib_symbol.name = symbol->name; - lib_symbol.member_idx = member_idx; - lnk_lib_symbol_list_push(writer->arena, &writer->symbol_list, lib_symbol); + { + COFF_FileHeaderInfo coff_info = coff_file_header_info_from_data(obj->data); + String8 string_table = str8_substr(obj->data, coff_info.string_table_range); + String8 symbol_table = str8_substr(obj->data, coff_info.symbol_table_range); + + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < coff_info.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + void *symbol_ptr; + if (coff_info.is_big_obj) { + symbol_ptr = &((COFF_Symbol32 *)symbol_table.str)[symbol_idx]; + symbol = coff_parse_symbol32(string_table, symbol_ptr); + } else { + symbol_ptr = &((COFF_Symbol16 *)symbol_table.str)[symbol_idx]; + symbol = coff_parse_symbol16(string_table, symbol_ptr); + } + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + if (symbol.storage_class == COFF_SymStorageClass_External) { + LNK_LibSymbol lib_symbol = {0}; + lib_symbol.name = symbol.name; + lib_symbol.member_idx = member_idx; + lnk_lib_symbol_list_push(writer->arena, &writer->symbol_list, lib_symbol); + } + } } } @@ -590,20 +582,45 @@ lnk_build_import_lib(TP_Context *tp, TP_Arena *arena, COFF_MachineType machine, input->lib_path = lib_name; } - LNK_InputObj **inputs = lnk_array_from_input_obj_list(scratch.arena, input_obj_list); - LNK_SectionTable *sectab = lnk_section_table_alloc(0,0,0); - LNK_ObjList obj_list = {0}; - lnk_obj_list_push_parallel(tp, arena, &obj_list, sectab, 0, machine, input_obj_list.count, inputs); + LNK_InputObj **inputs = lnk_array_from_input_obj_list(scratch.arena, input_obj_list); + LNK_ObjList obj_list = {0}; + lnk_obj_list_push_parallel(tp, arena, &obj_list, machine, input_obj_list.count, inputs); LNK_LibBuild import_lib = lnk_build_lib(scratch.arena, machine, time_stamp, dll_name, obj_list, exptab); B32 emit_second_member = 1; String8List coff_archive_data = lnk_coff_archive_from_lib_build(arena->v[0], &import_lib, emit_second_member, COFF_TimeStamp_Max, 0); // cleanup memory - lnk_section_table_release(§ab); scratch_end(scratch); ProfEnd(); return coff_archive_data; } +internal +THREAD_POOL_TASK_FUNC(lnk_push_lib_symbols_task) +{ + LNK_SymbolPusher *task = raw_task; + LNK_SymbolTable *symtab = task->symtab; + LNK_Lib *lib = &task->u.libs.v[task_id].data; + + String8Node *name_node = lib->symbol_name_list.first; + for (U64 symbol_idx = 0; symbol_idx < lib->symbol_count; ++symbol_idx, name_node = name_node->next) { + LNK_Symbol *symbol = lnk_make_lib_symbol(arena, name_node->string, lib, lib->member_off_arr[symbol_idx]); + + U64 hash = lnk_symbol_hash(symbol->name); + lnk_symbol_table_push_(symtab, arena, worker_id, LNK_SymbolScope_Lib, hash, symbol); + } +} + +internal void +lnk_input_lib_symbols(TP_Context *tp, LNK_SymbolTable *symtab, LNK_LibNodeArray libs) +{ + ProfBeginFunction(); + LNK_SymbolPusher task = {0}; + task.symtab = symtab; + task.u.libs = libs; + tp_for_parallel(tp, symtab->arena, libs.count, lnk_push_lib_symbols_task, &task); + ProfEnd(); +} + diff --git a/src/linker/lnk_lib.h b/src/linker/lnk_lib.h index fe579bb5..6b3f2cb9 100644 --- a/src/linker/lnk_lib.h +++ b/src/linker/lnk_lib.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once @@ -114,7 +114,6 @@ internal void lnk_lib_symbol_array_sort(LNK_LibSymbol *arr, U64 count internal LNK_Lib lnk_lib_from_data(Arena *arena, String8 data, String8 path); 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_LibNode * lnk_lib_list_push(Arena *arena, LNK_LibList *list, String8 data, String8 path); //////////////////////////////// diff --git a/src/linker/lnk_log.c b/src/linker/lnk_log.c index 368387f2..905b26ba 100644 --- a/src/linker/lnk_log.c +++ b/src/linker/lnk_log.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal void diff --git a/src/linker/lnk_log.h b/src/linker/lnk_log.h index cc0d8544..a878803f 100644 --- a/src/linker/lnk_log.h +++ b/src/linker/lnk_log.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index aaf9c583..370ab8b7 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -1,8 +1,6 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -//////////////////////////////// - internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...) { @@ -11,85 +9,242 @@ lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...) va_end(args); } +internal void +lnk_error_multiply_defined_symbol(LNK_Obj *defn_obj, LNK_Obj *conf_obj, String8 symbol_name) +{ + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, defn_obj, "symbol %S is multiply defined in %S", symbol_name, conf_obj->path); +} + //////////////////////////////// -internal void -lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node) +internal LNK_ObjNodeArray +lnk_obj_list_reserve(Arena *arena, LNK_ObjList *list, U64 count) { - 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 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; + LNK_ObjNodeArray arr = {0}; + if (count) { + arr.count = count; + arr.v = push_array(arena, LNK_ObjNode, count); + for (LNK_ObjNode *ptr = arr.v, *opl = arr.v + arr.count; ptr < opl; ++ptr) { + SLLQueuePush(list->first, list->last, ptr); + } + list->count += count; + } else { + MemoryZeroStruct(&arr); } + + return arr; +} + +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; + + // + // parse obj header + // + COFF_FileHeaderInfo coff_info = coff_file_header_info_from_data(input->data); + + // + // set & check machine compatibility + // + { + if (task->machine == COFF_MachineType_Unknown) { + ins_atomic_u32_eval_assign(&task->machine, coff_info.machine); + } + + if (coff_info.machine != COFF_MachineType_Unknown && task->machine != coff_info.machine) { + lnk_error_with_loc(LNK_Error_IncompatibleMachine, input->path, input->lib_path, + "conflicting machine types expected %S but got %S", + coff_string_from_machine_type(task->machine), + coff_string_from_machine_type(coff_info.machine)); + } + } + + // + // extract COFF info + // + String8 raw_coff_section_table = str8_substr(input->data, coff_info.section_table_range); + String8 raw_coff_symbol_table = str8_substr(input->data, coff_info.symbol_table_range); + String8 raw_coff_string_table = str8_substr(input->data, coff_info.string_table_range); + + // + // error check: section table / symbol table / string table + // + if (raw_coff_section_table.size != dim_1u64(coff_info.section_table_range)) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read section header table"); + } + if (raw_coff_symbol_table.size != dim_1u64(coff_info.symbol_table_range)) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read symbol table"); + } + if (raw_coff_string_table.size != dim_1u64(coff_info.string_table_range)) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read string table"); + } + + // + // error check section headers + // + COFF_SectionHeader *coff_section_table = (COFF_SectionHeader *)raw_coff_section_table.str; + for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *coff_sect_header = &coff_section_table[sect_idx]; + + // read name + String8 sect_name = coff_name_from_section_header(raw_coff_string_table, coff_sect_header); + + if (~coff_sect_header->flags & COFF_SectionFlag_CntUninitializedData) { + if (coff_sect_header->fsize > 0) { + Rng1U64 sect_range = rng_1u64(coff_sect_header->foff, coff_sect_header->foff + coff_sect_header->fsize); + + if (contains_1u64(coff_info.header_range, coff_sect_header->foff) || + (coff_sect_header->fsize > 0 && contains_1u64(coff_info.header_range, sect_range.max-1))) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into file header)", sect_name, sect_idx+1); + } + if (contains_1u64(coff_info.section_table_range, coff_sect_header->foff) || + (coff_sect_header->fsize > 0 && contains_1u64(coff_info.section_table_range, sect_range.max-1))) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into section header table)", sect_name, sect_idx+1); + } + if (contains_1u64(coff_info.symbol_table_range, coff_sect_header->foff) || + (coff_sect_header->fsize > 0 && contains_1u64(coff_info.symbol_table_range, sect_range.max-1))) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into symbol table)", sect_name, sect_idx+1); + } + if (dim_1u64(sect_range) != coff_sect_header->fsize) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data", sect_name, sect_idx+1); + } + } + } + } + + // fill out obj + obj->data = input->data; + obj->path = push_str8_copy(arena, input->path); + obj->lib_path = push_str8_copy(arena, input->lib_path); + obj->input_idx = obj_idx; + obj->header = coff_info; +} + +internal LNK_ObjNodeArray +lnk_obj_list_push_parallel(TP_Context *tp, + TP_Arena *arena, + LNK_ObjList *obj_list, + COFF_MachineType machine, + U64 input_count, + LNK_InputObj **inputs) +{ + ProfBeginFunction(); + + LNK_ObjIniter task = {0}; + task.inputs = inputs; + task.obj_id_base = obj_list->count; + task.objs = lnk_obj_list_reserve(arena->v[0], obj_list, input_count); + task.machine = machine; + tp_for_parallel(tp, arena, input_count, lnk_obj_initer, &task); + + ProfEnd(); + return task.objs; +} + +internal LNK_Obj ** +lnk_obj_arr_from_list(Arena *arena, LNK_ObjList list) +{ + LNK_Obj **arr = push_array_no_zero(arena, LNK_Obj *, list.count); + U64 idx = 0; + for (LNK_ObjNode *node = list.first; node != 0; node = node->next, ++idx) { + arr[idx] = &node->data; + } + return arr; +} + +internal COFF_ParsedSymbol +lnk_obj_match_symbol(LNK_Obj *obj, String8 match_name) +{ + COFF_ParsedSymbol result = {0}; + + COFF_FileHeaderInfo coff_info = coff_file_header_info_from_data(obj->data); + + String8 raw_coff_symbol_table = str8_substr(obj->data, coff_info.symbol_table_range); + String8 raw_coff_string_table = str8_substr(obj->data, coff_info.string_table_range); + + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < coff_info.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + void *symbol_ptr; + if (coff_info.is_big_obj) { + symbol_ptr = &((COFF_Symbol32 *)raw_coff_symbol_table.str)[symbol_idx]; + symbol = coff_parse_symbol32(raw_coff_string_table, symbol_ptr); + } else { + symbol_ptr = &((COFF_Symbol16 *)raw_coff_symbol_table.str)[symbol_idx]; + symbol = coff_parse_symbol16(raw_coff_string_table, symbol_ptr); + } + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + + if (str8_match(symbol.name, match_name, 0)) { + result = symbol; + break; + } + } + return result; } -internal void -lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat) +internal MSCRT_FeatFlags +lnk_obj_get_features(LNK_Obj *obj) { - SLLConcatInPlace(list, to_concat); + return lnk_obj_match_symbol(obj, str8_lit("@feat.00")).value; } -internal int -lnk_input_obj_compar(const void *raw_a, const void *raw_b) +internal U32 +lnk_obj_get_comp_id(LNK_Obj *obj) { - const LNK_InputObj **a = (const LNK_InputObj **) raw_a; - const LNK_InputObj **b = (const LNK_InputObj **) raw_b; - int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path); - return cmp; + return lnk_obj_match_symbol(obj, str8_lit("@comp.id")).value; } -internal int -lnk_input_obj_compar_is_before(void *raw_a, void *raw_b) +internal U32 +lnk_obj_get_vol_md(LNK_Obj *obj) { - LNK_InputObj **a = raw_a; - LNK_InputObj **b = raw_b; - int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path); - int is_before = cmp < 0; - return is_before; + return lnk_obj_match_symbol(obj, str8_lit("@vol.md")).value; } -internal LNK_InputObjList -lnk_list_from_input_obj_arr(LNK_InputObj **arr, U64 count) +internal COFF_SectionHeader * +lnk_coff_section_header_from_section_number(LNK_Obj *obj, U64 section_number) { - LNK_InputObjList list = {0}; - for (U64 i = 0; i < count; ++i) { - SLLQueuePush(list.first, list.last, arr[i]); - ++list.count; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + COFF_SectionHeader *section_header = §ion_table[section_number-1]; + return section_header; +} + +internal COFF_ParsedSymbol +lnk_parsed_symbol_from_coff_symbol_idx(LNK_Obj *obj, U64 symbol_idx) +{ + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + String8 symbol_table = str8_substr(obj->data, obj->header.symbol_table_range); + + COFF_ParsedSymbol result = {0}; + if (obj->header.is_big_obj) { + result = coff_parse_symbol32(string_table, (COFF_Symbol32 *)symbol_table.str + symbol_idx); + } else { + result = coff_parse_symbol16(string_table, (COFF_Symbol16 *)symbol_table.str + symbol_idx); } - return list; + + return result; } -internal LNK_InputObjList -lnk_input_obj_list_from_string_list(Arena *arena, String8List list) +internal String8List * +lnk_collect_obj_chunks_parallel(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8 name, String8 postfix, B32 collect_discarded) { - 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; + NotImplemented; + return 0; } -//////////////////////////////// +internal String8List +lnk_collect_obj_chunks(Arena *arena, LNK_Obj *obj, String8 name, String8 postfix, B32 collect_discarded) +{ + NotImplemented; + String8List result = {0}; + return result; +} internal void lnk_parse_msvc_linker_directive(Arena *arena, LNK_Obj *obj, LNK_DirectiveInfo *directive_info, String8 buffer) @@ -138,935 +293,91 @@ lnk_parse_msvc_linker_directive(Arena *arena, LNK_Obj *obj, LNK_DirectiveInfo *d scratch_end(scratch); } -//////////////////////////////// - -internal LNK_Obj ** -lnk_obj_arr_from_list(Arena *arena, LNK_ObjList list) -{ - LNK_Obj **arr = push_array_no_zero(arena, LNK_Obj *, list.count); - U64 idx = 0; - for (LNK_ObjNode *node = list.first; node != 0; node = node->next, ++idx) { - arr[idx] = &node->data; - } - return arr; -} - -internal LNK_ObjNodeArray -lnk_obj_list_reserve(Arena *arena, LNK_ObjList *list, U64 count) -{ - LNK_ObjNodeArray arr = {0}; - if (count) { - arr.count = count; - arr.v = push_array(arena, LNK_ObjNode, count); - for (LNK_ObjNode *ptr = arr.v, *opl = arr.v + arr.count; ptr < opl; ++ptr) { - SLLQueuePush(list->first, list->last, ptr); - } - list->count += count; - } else { - MemoryZeroStruct(&arr); - } - - return arr; -} - -internal LNK_ChunkList -lnk_obj_search_chunks(Arena *arena, LNK_Obj *obj, String8 name, String8 postfix, B32 collect_discarded) -{ - LNK_ChunkList list = {0}; - for (U64 sect_idx = 0; sect_idx < obj->chunk_count; ++sect_idx) { - String8 obj_sect_name = obj->sect_name_arr[sect_idx]; - String8 obj_sect_sort = obj->sect_sort_arr[sect_idx]; - - B32 is_match = str8_match(obj_sect_name, name, 0) && - str8_match(obj_sect_sort, postfix, 0); - - if (is_match) { - LNK_ChunkPtr chunk = obj->chunk_arr[sect_idx]; - - if (!collect_discarded && lnk_chunk_is_discarded(chunk)) { - continue; - } - - LNK_ChunkNode *node = push_array_no_zero(arena, LNK_ChunkNode, 1); - node->next = 0; - node->data = chunk; - - SLLQueuePush(list.first, list.last, node); - ++list.count; - } - } - return list; -} - internal -THREAD_POOL_TASK_FUNC(lnk_collect_obj_chunks_task) +THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table) { - U64 obj_idx = task_id; - LNK_CollectObjChunksTaskData *task = raw_task; - LNK_Obj *obj = task->obj_arr[obj_idx]; - LNK_ChunkList *list_ptr = &task->list_arr[obj_idx]; - *list_ptr = lnk_obj_search_chunks(arena, obj, task->name, task->postfix, task->collect_discarded); -} - -internal LNK_ChunkList * -lnk_collect_obj_chunks(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8 name, String8 postfix, B32 collect_discarded) -{ - LNK_CollectObjChunksTaskData task_data = {0}; - task_data.obj_arr = obj_arr; - task_data.name = name; - task_data.postfix = postfix; - task_data.list_arr = push_array_no_zero(arena->v[0], LNK_ChunkList, obj_count); - task_data.collect_discarded = collect_discarded; - tp_for_parallel(tp, arena, obj_count, lnk_collect_obj_chunks_task, &task_data); - return task_data.list_arr; -} - -internal -THREAD_POOL_TASK_FUNC(lnk_symbol_collector) -{ - LNK_SymbolCollector *task = raw_task; - Rng1U64 range = task->range_arr[task_id]; - LNK_SymbolList *list = &task->out_arr[task_id]; - for (U64 obj_idx = range.min; obj_idx < range.max; ++obj_idx) { - LNK_Obj *obj = &task->in_arr.v[obj_idx].data; - for (LNK_SymbolNode *node = obj->symbol_list.first; node != 0; node = node->next) { - if (node->data->type == task->type) { - lnk_symbol_list_push(arena, list, node->data); - } - } - } -} - -internal LNK_SymbolList -lnk_run_symbol_collector(TP_Context *tp, TP_Arena *arena, LNK_ObjNodeArray arr, LNK_SymbolType symbol_type) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); - - LNK_SymbolCollector task_data; - task_data.type = symbol_type; - task_data.range_arr = tp_divide_work(scratch.arena, arr.count, tp->worker_count); - task_data.in_arr = arr; - task_data.out_arr = push_array(scratch.arena, LNK_SymbolList, tp->worker_count); - - tp_for_parallel(tp, arena, tp->worker_count, lnk_symbol_collector, &task_data); - - LNK_SymbolList list = {0}; - for (U64 ithread = 0; ithread < tp->worker_count; ++ithread) { - lnk_symbol_list_concat_in_place(&list, &task_data.out_arr[ithread]); - } - - scratch_end(scratch); - ProfEnd(); - return list; -} - -internal void -lnk_sect_defn_list_push_node(LNK_SectDefnList *list, LNK_SectDefn *node) -{ - SLLQueuePush(list->first, list->last, node); - ++list->count; -} - -internal LNK_SectDefn * -lnk_sect_defn_list_push(Arena *arena, LNK_SectDefnList *list, LNK_Obj *obj, String8 name, U64 idx, COFF_SectionFlags flags) -{ - LNK_SectDefn *node = push_array_no_zero(arena, LNK_SectDefn, 1); - node->next = 0; - node->obj = obj; - node->name = name; - node->idx = idx; - node->flags = flags; - lnk_sect_defn_list_push_node(list, node); - return node; -} - -internal void -lnk_sect_defn_list_concat_in_place(LNK_SectDefnList *list, LNK_SectDefnList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal void -lnk_sect_defn_list_concat_in_place_arr(LNK_SectDefnList *list, LNK_SectDefnList *to_concat_arr, U64 count) -{ - SLLConcatInPlaceArray(list, to_concat_arr, count); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_obj_initer) -{ - Temp scratch = scratch_begin(&arena, 1); - - LNK_ObjIniter *task = raw_task; - LNK_InputObj *input = task->inputs[task_id]; - LNK_Obj *obj = &task->obj_node_arr[task_id].data; - U64 obj_idx = task->obj_id_base + task_id; + LNK_InputCoffSymbolTable *task = raw_task; + LNK_Obj *obj = &task->objs.v[task_id].data; // // parse obj header // - COFF_FileHeaderInfo coff_info = coff_file_header_info_from_data(input->data); - - // - // set & check machine compatibility - // - { - if (task->machine == COFF_MachineType_Unknown) { - ins_atomic_u32_eval_assign(&task->machine, coff_info.machine); - } - - if (coff_info.machine != COFF_MachineType_Unknown && task->machine != coff_info.machine) { - lnk_error_with_loc(LNK_Error_IncompatibleMachine, input->path, input->lib_path, - "conflicting machine types expected %S but got %S", - coff_string_from_machine_type(task->machine), - coff_string_from_machine_type(coff_info.machine)); - } - } + COFF_FileHeaderInfo header = coff_file_header_info_from_data(obj->data); // // extract COFF info // - String8 raw_coff_section_table = str8_substr(input->data, coff_info.section_table_range); - String8 raw_coff_symbol_table = str8_substr(input->data, coff_info.symbol_table_range); - String8 raw_coff_string_table = str8_substr(input->data, coff_info.string_table_range); + String8 raw_coff_section_table = str8_substr(obj->data, header.section_table_range); + String8 raw_coff_symbol_table = str8_substr(obj->data, header.symbol_table_range); + String8 raw_coff_string_table = str8_substr(obj->data, header.string_table_range); - // - // error check: section table / symbol table / string table - // - if (raw_coff_section_table.size != dim_1u64(coff_info.section_table_range)) { - lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read section header table"); - } - if (raw_coff_symbol_table.size != dim_1u64(coff_info.symbol_table_range)) { - lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read symbol table"); - } - if (raw_coff_string_table.size != dim_1u64(coff_info.string_table_range)) { - lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read string table"); - } + COFF_ParsedSymbol symbol = {0}; + for (U64 symbol_idx = 0; symbol_idx < header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + // read symbol + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - U64 chunk_count = coff_info.section_count_no_null + /* :common_block */ 1; - String8 *sect_name_arr = push_array_no_zero(arena, String8, chunk_count); - String8 *sect_sort_arr = push_array_no_zero(arena, String8, chunk_count); - LNK_Chunk *chunk_arr = push_array(arena, LNK_Chunk, chunk_count); - LNK_ChunkPtr *chunk_ptr_arr = push_array_no_zero(arena, LNK_ChunkPtr, chunk_count); - - for (U64 chunk_idx = 0; chunk_idx < chunk_count; chunk_idx += 1) { - chunk_ptr_arr[chunk_idx] = &chunk_arr[chunk_idx]; - } - - // - // setup :common_block - // - - U64 common_block_idx = chunk_count - 1; - sect_name_arr[common_block_idx] = str8_lit(".bss"); - sect_sort_arr[common_block_idx] = str8_lit("~"); - - LNK_Chunk *master_common_block = &chunk_arr[common_block_idx]; - master_common_block->ref = lnk_chunk_ref(0,0); // :chunk_ref_assign - master_common_block->align = 1; - master_common_block->is_discarded = 0; - master_common_block->sort_chunk = 0; - master_common_block->type = LNK_Chunk_List; - master_common_block->sort_idx = sect_sort_arr[common_block_idx]; - master_common_block->input_idx = LNK_MakeChunkInputIdx(obj_idx, common_block_idx); - master_common_block->flags = LNK_BSS_SECTION_FLAGS; - master_common_block->associate = 0; - master_common_block->u.list = push_array(arena, LNK_ChunkList, 1); - master_common_block->obj = obj; - lnk_chunk_set_debugf(arena, master_common_block, "obj[%llx] master common block", obj_idx); - - // - // parse section table - // - COFF_SectionHeader *coff_section_table = (COFF_SectionHeader *)raw_coff_section_table.str; - for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *coff_sect_header = &coff_section_table[sect_idx]; - - // read name - String8 sect_name = coff_name_from_section_header(raw_coff_string_table, coff_sect_header); - - // parse name - coff_parse_section_name(sect_name, §_name_arr[sect_idx], §_sort_arr[sect_idx]); - - // find contents - String8 sect_data; - if (coff_sect_header->flags & COFF_SectionFlag_CntUninitializedData) { - sect_data = str8(0, coff_sect_header->fsize); - } else { - if (coff_sect_header->fsize > 0) { - Rng1U64 sect_range = rng_1u64(coff_sect_header->foff, coff_sect_header->foff + coff_sect_header->fsize); - sect_data = str8_substr(input->data, sect_range); - - if (contains_1u64(coff_info.header_range, coff_sect_header->foff) || - (coff_sect_header->fsize > 0 && contains_1u64(coff_info.header_range, sect_range.max-1))) { - lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into file header)", sect_name, sect_idx+1); - } - if (contains_1u64(coff_info.section_table_range, coff_sect_header->foff) || - (coff_sect_header->fsize > 0 && contains_1u64(coff_info.section_table_range, sect_range.max-1))) { - lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into section header table)", sect_name, sect_idx+1); - } - if (contains_1u64(coff_info.symbol_table_range, coff_sect_header->foff) || - (coff_sect_header->fsize > 0 && contains_1u64(coff_info.symbol_table_range, sect_range.max-1))) { - lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into symbol table)", sect_name, sect_idx+1); - } - if (dim_1u64(sect_range) != coff_sect_header->fsize) { - lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data", sect_name, sect_idx+1); - } - } else { - sect_data = str8_zero(); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + switch (interp) { + case COFF_SymbolValueInterp_Regular: { + if (symbol.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); + U64 hash = lnk_symbol_hash(symbol.name); + lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, hash, defn); } - } + } break; + case COFF_SymbolValueInterp_Weak: { + LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); + U64 hash = lnk_symbol_hash(symbol.name); + lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, hash, defn); - // fill out chunk - LNK_Chunk *chunk = &chunk_arr[sect_idx]; - chunk->align = coff_align_size_from_section_flags(coff_sect_header->flags); - chunk->is_discarded = !!(coff_sect_header->flags & COFF_SectionFlag_LnkRemove); - chunk->sort_chunk = 1; - chunk->type = LNK_Chunk_Leaf; - chunk->sort_idx = sect_sort_arr[sect_idx]; - chunk->input_idx = LNK_MakeChunkInputIdx(obj_idx, sect_idx); - chunk->flags = coff_sect_header->flags; - chunk->u.leaf = sect_data; - chunk->obj = obj; - lnk_chunk_set_debugf(arena, chunk, "obj[%llx] sect[%llx]", obj_idx, sect_idx); - } - - // - // :function_pad_min - // - U64 function_pad_min; - if (task->function_pad_min) { - function_pad_min = *task->function_pad_min; - } else { - function_pad_min = lnk_get_default_function_pad_min(coff_info.machine); - } - - // - // convert from COFF - // - void *coff_symbol_table = raw_coff_symbol_table.str; - LNK_SymbolArray symbol_arr = lnk_symbol_array_from_coff(arena, obj, input->path, input->lib_path, coff_info.is_big_obj, function_pad_min, coff_info.section_count_no_null, coff_section_table, coff_info.symbol_count, coff_symbol_table, raw_coff_string_table, chunk_ptr_arr, master_common_block); - LNK_SymbolList symbol_list = lnk_symbol_list_from_array(arena, symbol_arr); - LNK_RelocList *reloc_list_arr = lnk_reloc_list_array_from_coff(arena, coff_info.machine, input->data, coff_info.section_count_no_null, coff_section_table, chunk_ptr_arr, symbol_arr); - - // fill out obj - obj->data = input->data; - obj->path = push_str8_copy(arena, input->path); - obj->lib_path = push_str8_copy(arena, input->lib_path); - obj->input_idx = obj_idx; - obj->machine = coff_info.machine; - obj->chunk_count = chunk_count; - obj->sect_count = coff_info.section_count_no_null; - obj->sect_name_arr = sect_name_arr; - obj->sect_sort_arr = sect_sort_arr; - obj->chunk_arr = chunk_ptr_arr; - obj->symbol_list = symbol_list; - obj->sect_reloc_list_arr = reloc_list_arr; - - scratch_end(scratch); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_obj_new_sect_scanner) -{ - LNK_ObjNewSectScanner *task = raw_task; - - Rng1U64 range = task->range_arr[task_id]; - HashTable *ht = hash_table_init(arena, 128); - - for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) { - LNK_Obj *obj = &task->obj_node_arr[obj_idx].data; - - for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) { - String8 sect_name = obj->sect_name_arr[chunk_idx]; - COFF_SectionFlags sect_flags = obj->chunk_arr[chunk_idx]->flags & ~COFF_SectionFlags_LnkFlags; - - KeyValuePair *is_present = hash_table_search_string(ht, sect_name); - if (is_present) { - if (lnk_is_error_code_active(LNK_Warning_SectionFlagsConflict)) { - LNK_SectDefn *defn = is_present->value_raw; - if (defn->flags != sect_flags) { - lnk_sect_defn_list_push(arena, &task->defn_arr[task_id], obj, sect_name, chunk_idx, sect_flags); - } - } - } else { - LNK_SectDefn *defn = lnk_sect_defn_list_push(arena, &task->defn_arr[task_id], obj, sect_name, chunk_idx, sect_flags); - hash_table_push_string_raw(arena, ht, sect_name, defn); + lnk_symbol_list_push(arena, &task->weak_lists[task_id], defn); + } break; + case COFF_SymbolValueInterp_Common: { + LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); + U64 hash = lnk_symbol_hash(symbol.name); + lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, hash, defn); + } break; + case COFF_SymbolValueInterp_Abs: { + if (symbol.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); + U64 hash = lnk_symbol_hash(symbol.name); + lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, hash, defn); } + } break; + case COFF_SymbolValueInterp_Undefined: { + LNK_Symbol *s = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); + if (s == 0) { + LNK_Symbol *undef = lnk_make_undefined_symbol(arena, symbol.name, obj); + lnk_symbol_list_push(arena, &task->undef_lists[worker_id], undef); + } + } break; + case COFF_SymbolValueInterp_Debug: { + // not used + } break; + default: { InvalidPath; } break; } } } -LNK_CHUNK_VISITOR_SIG(lnk_chunk_get_count_cb) -{ - U64 *counter = (U64 *)ud; - *counter += 1; - return 0; -} - -internal -THREAD_POOL_TASK_FUNC(lnk_chunk_counter) -{ - U64 obj_idx = task_id; - LNK_ChunkCounter *task = raw_task; - LNK_Obj *obj = &task->obj_arr[obj_idx].data; - for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) { - String8 name = obj->sect_name_arr[chunk_idx]; - LNK_Chunk *chunk = obj->chunk_arr[chunk_idx]; - LNK_Section *sect = lnk_section_table_search(task->sectab, name); - - U64 count = 0; - lnk_visit_chunks(0, chunk, lnk_chunk_get_count_cb, &count); - - task->chunk_counts[sect->id][obj_idx] += count; - } -} - -internal -LNK_CHUNK_VISITOR_SIG(lnk_chunk_ref_assign) -{ - LNK_ChunkRefAssign *ctx = ud; - - // alloc chunk id - U64 chunk_id = *ctx->chunk_id; - *ctx->chunk_id += 1; - - // set chunk ref - chunk->ref = lnk_chunk_ref(sect_id, chunk_id); - - // keep visiting chunks - return 0; -} - -internal -THREAD_POOL_TASK_FUNC(lnk_chunk_ref_assigner) -{ - LNK_ChunkRefAssigner *task = raw_task; - Rng1U64 range = task->range_arr[task_id]; - - for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) { - LNK_Obj *obj = &task->obj_arr[obj_idx].data; - - for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) { - String8 name = obj->sect_name_arr[chunk_idx]; - String8 sort = obj->sect_sort_arr[chunk_idx]; - LNK_Chunk *chunk = obj->chunk_arr[chunk_idx]; - - // :find_chunk_section - LNK_Section *sect = lnk_section_table_search(task->sectab, name); - - // :chunk_ref_assign - LNK_ChunkRefAssign ctx = {0}; - ctx.cman = sect->cman; - ctx.chunk_id = &task->chunk_ids[sect->id][obj_idx]; - lnk_visit_chunks(sect->id, chunk, lnk_chunk_ref_assign, &ctx); - - // push to section chunk list - LNK_ChunkList **chunk_list_arr_arr = sort.size ? task->chunk_list_arr_arr : task->nosort_chunk_list_arr_arr; - lnk_chunk_list_push(arena, &chunk_list_arr_arr[sect->id][task_id], chunk); - } - } -} - -internal LNK_ObjNodeArray -lnk_obj_list_push_parallel(TP_Context *tp, - TP_Arena *arena, - LNK_ObjList *obj_list, - LNK_SectionTable *sectab, - U64 *function_pad_min, - COFF_MachineType machine, - U64 input_count, - LNK_InputObj **inputs) +internal LNK_SymbolInputResult +lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs) { ProfBeginFunction(); Temp scratch = scratch_begin(arena->v, arena->count); - - U64 obj_id_base = obj_list->count; - LNK_ObjNodeArray obj_arr = lnk_obj_list_reserve(arena->v[0], obj_list, input_count); - - ProfBegin("Obj Initer"); - { - LNK_ObjIniter task = {0}; - task.inputs = inputs; - task.obj_id_base = obj_id_base; - task.obj_node_arr = obj_arr.v; - task.function_pad_min = function_pad_min; - task.machine = machine; - tp_for_parallel(tp, arena, input_count, lnk_obj_initer, &task); - } - ProfEnd(); - - if (sectab) { - ProfBegin("Section Table Update"); - { - TP_Temp temp = tp_temp_begin(arena); - LNK_ObjNewSectScanner task; - task.range_arr = tp_divide_work(arena->v[0], obj_arr.count, tp->worker_count); - task.obj_node_arr = obj_arr.v; - task.defn_arr = push_array(arena->v[0], LNK_SectDefnList, tp->worker_count); - task.conf_arr = push_array(arena->v[0], LNK_SectDefnList, tp->worker_count); - tp_for_parallel(tp, arena, tp->worker_count, lnk_obj_new_sect_scanner, &task); + LNK_InputCoffSymbolTable task = {0}; + task.symtab = symtab; + task.objs = objs; + task.weak_lists = push_array(scratch.arena, LNK_SymbolList, tp->worker_count); + task.undef_lists = push_array(scratch.arena, LNK_SymbolList, tp->worker_count); + tp_for_parallel(tp, arena, objs.count, lnk_input_coff_symbol_table, &task); + LNK_SymbolInputResult result = {0}; + SLLConcatInPlaceArray(&result.weak_symbols, task.weak_lists, tp->worker_count); + SLLConcatInPlaceArray(&result.undef_symbols, task.undef_lists, tp->worker_count); - LNK_SectDefnList defn_list = {0}; - LNK_SectDefnList conf_list = {0}; - lnk_sect_defn_list_concat_in_place_arr(&defn_list, task.defn_arr, tp->worker_count); - lnk_sect_defn_list_concat_in_place_arr(&conf_list, task.conf_arr, tp->worker_count); - - - HashTable *ht = hash_table_init(arena->v[0], 128); - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != 0; sect_node = sect_node->next) { - LNK_Section *sect = §_node->data; - hash_table_push_string_u64(arena->v[0], ht, sect->name, sect->flags); - } - - - LNK_SectDefnList new_list = {0}; - for (LNK_SectDefn *curr = defn_list.first, *next; curr != 0; curr = next) { - next = curr->next; - curr->next = 0; - - KeyValuePair *is_present = hash_table_search_string(ht, curr->name); - if (is_present) { - if (lnk_is_error_code_active(LNK_Warning_SectionFlagsConflict)) { - COFF_SectionFlags flags = is_present->value_u64; - if (flags != curr->flags) { - lnk_sect_defn_list_push_node(&conf_list, curr); - } else { - // section is present or is in new_list - } - } - } else { - lnk_sect_defn_list_push_node(&new_list, curr); - hash_table_push_string_u64(arena->v[0], ht, curr->name, curr->flags); - } - } - - - for (LNK_SectDefn *defn = conf_list.first; defn != 0; defn = defn->next) { - KeyValuePair *is_present = hash_table_search_string(ht, defn->name); - if (!is_present) { - InvalidPath; - } - U64 sect_number = (defn->idx + 1); - COFF_SectionFlags expected_flags = is_present->value_u64; - String8 expected_flags_str = coff_string_from_section_flags(scratch.arena, expected_flags); - String8 current_flags_str = coff_string_from_section_flags(scratch.arena, defn->flags); - lnk_error_obj(LNK_Warning_SectionFlagsConflict, defn->obj, "detected section flags conflict in %S(No. %X); expected {%S} but got {%S}", defn->name, sect_number, expected_flags_str, current_flags_str); - } - - - // push new sections for :find_chunk_section - for (LNK_SectDefn *curr = new_list.first; curr != 0; curr = curr->next) { - lnk_section_table_push(sectab, curr->name, curr->flags & ~COFF_SectionFlags_LnkFlags); - } - - tp_temp_end(temp); - } - ProfEnd(); - - ProfBegin("Count Chunks Per Section"); - U64 **chunk_ids; - { - U64 **chunk_counts = push_array_no_zero(scratch.arena, U64 *, sectab->id_max); - for (U64 sect_idx = 0; sect_idx < sectab->id_max; sect_idx += 1) { - chunk_counts[sect_idx] = push_array(scratch.arena, U64, obj_arr.count); - } - - LNK_ChunkCounter task = {0}; - task.sectab = sectab; - task.obj_arr = obj_arr.v; - task.chunk_counts = chunk_counts; - tp_for_parallel(tp, 0, obj_arr.count, lnk_chunk_counter, &task); - - chunk_ids = chunk_counts; - for (U64 sect_idx = 1; sect_idx < sectab->id_max; sect_idx += 1) { - LNK_Section *sect = lnk_section_table_search_id(sectab, sect_idx); - if (!sect) continue; - for (U64 obj_idx = 0; obj_idx < obj_arr.count; obj_idx += 1) { - U64 chunk_id_base = sect->cman->total_chunk_count; - sect->cman->total_chunk_count += chunk_counts[sect_idx][obj_idx]; - chunk_ids[sect_idx][obj_idx] = chunk_id_base; - } - } - } - ProfEnd(); - - ProfBegin("Assign Chunk Refs"); - { - LNK_ChunkRefAssigner task; - task.sectab = sectab; - task.range_arr = tp_divide_work(scratch.arena, obj_arr.count, tp->worker_count); - task.chunk_ids = chunk_ids; - task.obj_arr = obj_arr.v; - task.nosort_chunk_list_arr_arr = lnk_make_chunk_list_arr_arr(scratch.arena, sectab->id_max, tp->worker_count); - task.chunk_list_arr_arr = lnk_make_chunk_list_arr_arr(scratch.arena, sectab->id_max, tp->worker_count); - tp_for_parallel(tp, arena, tp->worker_count, lnk_chunk_ref_assigner, &task); - - // merge chunks - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != 0; sect_node = sect_node->next) { - LNK_Section *sect = §_node->data; - lnk_chunk_list_concat_in_place_arr(sect->nosort_chunk->u.list, task.nosort_chunk_list_arr_arr[sect->id], tp->worker_count); - lnk_chunk_list_concat_in_place_arr(sect->root->u.list, task.chunk_list_arr_arr[sect->id], tp->worker_count); - } - } - ProfEnd(); - } - - ProfEnd(); scratch_end(scratch); - return obj_arr; -} - -internal LNK_SymbolArray -lnk_symbol_array_from_coff(Arena *arena, - LNK_Obj *obj, - String8 obj_path, - String8 lib_path, - B32 is_big_obj, - U64 function_pad_min, - U64 sect_count, - COFF_SectionHeader *section_table, - U64 symbol_count, - void *symbol_table, - String8 string_table, - LNK_ChunkPtr *chunk_table, - LNK_Chunk *master_common_block) -{ - if (function_pad_min) { - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - // read symbol - if (is_big_obj) { - symbol = coff_parse_symbol32(string_table, &((COFF_Symbol32 *)symbol_table)[symbol_idx]); - } else { - symbol = coff_parse_symbol16(string_table, &((COFF_Symbol16 *)symbol_table)[symbol_idx]); - } - - // is this a function symbol? - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) { - if (symbol.section_number == 0 || symbol.section_number > sect_count) { - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "out ouf bounds section index in symbol \"%S (%u)\"", symbol.name, symbol.section_number); - } - - U32 section_offset = 0; - if (symbol.storage_class == COFF_SymStorageClass_External || symbol.storage_class == COFF_SymStorageClass_Static) { - section_offset = symbol.value; - } - if (section_offset > section_table[symbol.section_number-1].fsize) { - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "out of bounds section offset in symbol \"%S (%u)\"", symbol.name, section_offset); - } - - LNK_Chunk *chunk = chunk_table[symbol.section_number-1]; - if (section_offset > 0) { - // convert leaf to list - // - // there is no way to know up front how many splits we have, - // so lazily convert chunks when see two or more functions - // in a section - if (chunk->type == LNK_Chunk_Leaf) { - // make a list chunk - LNK_Chunk *chunk_list = push_array(arena, LNK_Chunk, 1); - chunk_list->type = LNK_Chunk_List; - chunk_list->align = chunk->align; - chunk_list->is_discarded = chunk->is_discarded; - chunk_list->sort_idx = chunk->sort_idx; - chunk_list->input_idx = chunk->input_idx; - chunk_list->flags = chunk->flags; - chunk_list->u.list = push_array(arena, LNK_ChunkList, 1); - chunk_list->obj = obj; - lnk_chunk_set_debugf(arena, chunk_list, "function chunk list for %S", symbol.name); - - // update properties on first chunk - chunk->min_size = function_pad_min; - chunk->sort_chunk = 0; - chunk->sort_idx = str8_zero(); - chunk->input_idx = 0; - - // push leaf to list - lnk_chunk_list_push(arena, chunk_list->u.list, chunk); - - // set list as target chunk - chunk = chunk_list; - - // set list chunk to be head of this section - chunk_table[symbol.section_number-1] = chunk_list; - } - - // find chunk that is near symbol - U64 offset_cursor = 0; - LNK_ChunkNode *current = chunk->u.list->last; - for (LNK_ChunkNode *c = chunk->u.list->first; c != 0; c = c->next) { - Assert(c->data->type == LNK_Chunk_Leaf); - if (offset_cursor + c->data->u.leaf.size >= section_offset) { - current = c; - break; - } - offset_cursor += c->data->u.leaf.size; - } - Assert(current->data->type == LNK_Chunk_Leaf); - - if (offset_cursor < section_offset) { - // bifurcate chunk at symbol offset - U64 split_pos = section_offset - offset_cursor; - Rng1U64 left_data_range = rng_1u64(0, split_pos); - Rng1U64 right_data_range = rng_1u64(left_data_range.max, current->data->u.leaf.size); - String8 left_data = str8_substr(current->data->u.leaf, left_data_range); - String8 right_data = str8_substr(current->data->u.leaf, right_data_range); - - // create new chunk - LNK_Chunk *split_chunk = push_array(arena, LNK_Chunk, 1); - split_chunk->type = LNK_Chunk_Leaf; - split_chunk->align = current->data->align; - split_chunk->min_size = function_pad_min; - split_chunk->is_discarded = current->data->is_discarded; - split_chunk->flags = current->data->flags; - split_chunk->u.leaf = right_data; - split_chunk->obj = obj; - lnk_chunk_set_debugf(arena, split_chunk, "chunk split on function %S sect %x split pos %#llx", symbol.name, symbol.section_number, split_pos); - - LNK_ChunkNode *split_node = push_array(arena, LNK_ChunkNode, 1); - split_node->data = split_chunk; - - // update split chunk data - current->data->u.leaf = left_data; - - // insert split chunk after current chunk - if (split_node->next == 0) { - chunk->u.list->last = split_node; - } - split_node->next = current->next; - current->next = split_node; - chunk->u.list->count += 1; - } - } - } - } - } - - LNK_SymbolArray symbol_array = {0}; - symbol_array.count = symbol_count; - symbol_array.v = push_array(arena, LNK_Symbol, symbol_array.count); - - COFF_ParsedSymbol parsed_symbol; - for (U64 symbol_idx = 0; symbol_idx < symbol_count; symbol_idx += (1 + parsed_symbol.aux_symbol_count)) { - void *aux_symbols; - if (is_big_obj) { - COFF_Symbol32 *ptr = &((COFF_Symbol32 *)symbol_table)[symbol_idx]; - parsed_symbol = coff_parse_symbol32(string_table, ptr); - aux_symbols = parsed_symbol.aux_symbol_count ? ptr+1 : 0; - } else { - COFF_Symbol16 *ptr = (COFF_Symbol16 *)symbol_table + symbol_idx; - parsed_symbol = coff_parse_symbol16(string_table, ptr); - aux_symbols = parsed_symbol.aux_symbol_count ? ptr+1 : 0; - } - - if (symbol_idx + parsed_symbol.aux_symbol_count + 1 > symbol_count) { - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "symbol %S (No. %llx) has out of bounds aux symbol count %llu", parsed_symbol.name, symbol_idx, parsed_symbol.aux_symbol_count); - } - - COFF_SymbolValueInterpType interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); - switch (interp) { - case COFF_SymbolValueInterp_Regular: { - if (parsed_symbol.section_number == 0 || parsed_symbol.section_number > sect_count) { - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "symbol %S (No. %llx) has out ouf bounds section index %x", parsed_symbol.name, symbol_idx, parsed_symbol.section_number); - } - - U32 section_offset = 0; - if (parsed_symbol.storage_class == COFF_SymStorageClass_External || - parsed_symbol.storage_class == COFF_SymStorageClass_Static) { - section_offset = parsed_symbol.value; - } - - if (section_offset > section_table[parsed_symbol.section_number-1].fsize) { - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "symbol %S (No. %llx) has out of bounds section offset %x into section %x", parsed_symbol.name, symbol_idx, section_offset, parsed_symbol.section_number); - } - - LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Static; - if (parsed_symbol.storage_class == COFF_SymStorageClass_External) { - visibility = LNK_DefinedSymbolVisibility_Extern; - } - LNK_DefinedSymbolFlags flags = 0; - if (COFF_SymbolType_IsFunc(parsed_symbol.type)) { - flags |= LNK_DefinedSymbolFlag_IsFunc; - } - - - LNK_Chunk *chunk = chunk_table[parsed_symbol.section_number-1]; - COFF_ComdatSelectType selection = COFF_ComdatSelect_Any; - U64 check_sum = 0; - - - B32 is_comdat = (section_table[parsed_symbol.section_number-1].flags & COFF_SectionFlag_LnkCOMDAT) && - parsed_symbol.value == 0 && - parsed_symbol.aux_symbol_count > 0 && - parsed_symbol.type.u.lsb == COFF_SymType_Null && - parsed_symbol.storage_class == COFF_SymStorageClass_Static; - if (is_comdat) { - COFF_SymbolSecDef *secdef = aux_symbols; - - selection = secdef->selection; - check_sum = secdef->check_sum; - - // create association link between chunks - if (secdef->selection == COFF_ComdatSelect_Associative) { - U32 secdef_number = secdef->number_lo; - - // promote secdef number to 32 bits - if (is_big_obj) { - secdef_number |= (U32)secdef->number_hi << 16; - } - - // associate chunks - if (secdef_number > 0 && secdef_number <= sect_count) { - LNK_Chunk *head_chunk = chunk_table[secdef_number-1]; - LNK_Chunk *associate_chunk = chunk_table[parsed_symbol.section_number-1]; - lnk_chunk_associate(head_chunk, associate_chunk); - } else { - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "symbol %S (No. %llx) has out of bounds section definition number %u", parsed_symbol.name, symbol_idx, secdef_number); - } - } - } - - - if (chunk->type == LNK_Chunk_List) { - LNK_Chunk *closest_chunk = chunk->u.list->last->data; - U64 offset_cursor = 0; - for (LNK_ChunkNode *c = chunk->u.list->first; c != 0; c = c->next) { - if (offset_cursor + c->data->u.leaf.size > section_offset) { - closest_chunk = c->data; - break; - } - offset_cursor += c->data->u.leaf.size; - } - Assert(section_offset >= offset_cursor); - section_offset -= offset_cursor; - chunk = closest_chunk; - } - Assert(chunk->type == LNK_Chunk_Leaf); - - lnk_init_defined_symbol_chunk(&symbol_array.v[symbol_idx], parsed_symbol.name, visibility, flags, chunk, section_offset, selection, check_sum); - symbol_array.v[symbol_idx].obj = obj; - } break; - case COFF_SymbolValueInterp_Weak: { - if (parsed_symbol.aux_symbol_count == 0) { - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "weak symbol \"%S (%u)\" must at least one aux symbol", parsed_symbol.name, symbol_idx); - } - - COFF_SymbolWeakExt *weak_ext = aux_symbols; - if (weak_ext->tag_index >= symbol_count) { - lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "weak symbol \"%S (%u)\" points to out of bounds symbol", parsed_symbol.name, symbol_idx); - } - - LNK_Symbol *symbol = &symbol_array.v[symbol_idx]; - LNK_Symbol *fallback_symbol = &symbol_array.v[weak_ext->tag_index]; - lnk_init_weak_symbol(symbol, parsed_symbol.name, weak_ext->characteristics, fallback_symbol); - - symbol->obj = obj; - fallback_symbol->obj = obj; - } break; - case COFF_SymbolValueInterp_Undefined: { - LNK_Symbol *symbol = &symbol_array.v[symbol_idx]; - lnk_init_undefined_symbol(symbol, parsed_symbol.name, LNK_SymbolScopeFlag_Main); - symbol->obj = obj; - } break; - case COFF_SymbolValueInterp_Common: { - // :common_block - // - // TODO: sort chunks on size to reduce bss usage - LNK_Chunk *chunk = push_array(arena, LNK_Chunk, 1); - chunk->align = Min(32, u64_up_to_pow2(parsed_symbol.value)); // link.exe caps align at 32 bytes - chunk->type = LNK_Chunk_Leaf; - chunk->flags = master_common_block->flags; - chunk->u.leaf = str8(0, parsed_symbol.value); - chunk->obj = obj; - lnk_chunk_set_debugf(arena, chunk, "common block %S", parsed_symbol.name); - lnk_chunk_list_push(arena, master_common_block->u.list, chunk); - - LNK_DefinedSymbolFlags flags = 0; - if (COFF_SymbolType_IsFunc(parsed_symbol.type)) { - flags |= LNK_DefinedSymbolFlag_IsFunc; - } - - LNK_Symbol *symbol = &symbol_array.v[symbol_idx]; - lnk_init_defined_symbol_chunk(symbol, parsed_symbol.name, LNK_DefinedSymbolVisibility_Extern, flags, chunk, 0, COFF_ComdatSelect_Largest, 0); - symbol->obj = obj; - } break; - case COFF_SymbolValueInterp_Abs: { - // Never code or data, synthetic symbol. COFF spec says bits in value are used - // as flags in symbol @feat.00, other symbols like @comp.id and @vol.md are undocumented. - // LLVM uses undocumented mask 0x4800 on @feat.00 to tell if object was compiled with /guard:cf. - - LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Static; - if (parsed_symbol.storage_class == COFF_SymStorageClass_External) { - visibility = LNK_DefinedSymbolVisibility_Extern; - } - - LNK_Symbol *symbol = &symbol_array.v[symbol_idx]; - lnk_init_defined_symbol_va(symbol, parsed_symbol.name, visibility, 0, parsed_symbol.value); - symbol->obj = obj; - } break; - case COFF_SymbolValueInterp_Debug: { - } break; - } - } - - return symbol_array; -} - -internal LNK_RelocList * -lnk_reloc_list_array_from_coff(Arena *arena, COFF_MachineType machine, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, LNK_ChunkPtr *chunk_ptr_arr, LNK_SymbolArray symbol_array) -{ - LNK_RelocList *reloc_list_arr = push_array_no_zero(arena, LNK_RelocList, sect_count); - for (U64 sect_idx = 0; sect_idx < sect_count; ++sect_idx) { - COFF_SectionHeader *coff_sect_header = &coff_sect_arr[sect_idx]; - COFF_RelocInfo coff_reloc_info = coff_reloc_info_from_section_header(coff_data, coff_sect_header); - COFF_Reloc *coff_reloc_v = (COFF_Reloc *)(coff_data.str + coff_reloc_info.array_off); - LNK_Chunk *sect_chunk = chunk_ptr_arr[sect_idx]; - reloc_list_arr[sect_idx] = lnk_reloc_list_from_coff_reloc_array(arena, machine, sect_chunk, symbol_array, coff_reloc_v, coff_reloc_info.count); - } - return reloc_list_arr; -} - -internal MSCRT_FeatFlags -lnk_obj_get_features(LNK_Obj *obj) -{ - MSCRT_FeatFlags result = 0; - LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@feat.00"), 0); - if (sym) { - Assert(LNK_Symbol_IsDefined(sym->type)); - Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA); - result = sym->u.defined.u.va; - } - return result; -} - -internal U32 -lnk_obj_get_comp_id(LNK_Obj *obj) -{ - U32 result = 0; - LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@comp.id"), 0); - if (sym) { - Assert(LNK_Symbol_IsDefined(sym->type)); - Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA); - result = sym->u.defined.u.va; - } - return result; -} - -internal U32 -lnk_obj_get_vol_md(LNK_Obj *obj) -{ - U32 result = 0; - LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@vol.md"), 0); - if (sym) { - Assert(LNK_Symbol_IsDefined(sym->type)); - Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA); - result = sym->u.defined.u.va; - } + ProfEnd(); return result; } diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index ca5c48e0..c0c15ea5 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -1,27 +1,37 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once //////////////////////////////// -typedef struct LNK_InputObj +typedef struct LNK_Obj { - struct LNK_InputObj *next; - B32 is_thin; - B32 has_disk_read_failed; - String8 dedup_id; - String8 path; - String8 data; - String8 lib_path; -} LNK_InputObj; + String8 data; + String8 path; + String8 lib_path; + U64 input_idx; + COFF_FileHeaderInfo header; +} LNK_Obj; -typedef struct LNK_InputObjList +typedef struct LNK_ObjNode { - U64 count; - LNK_InputObj *first; - LNK_InputObj *last; -} LNK_InputObjList; + struct LNK_ObjNode *next; + LNK_Obj data; +} LNK_ObjNode; + +typedef struct LNK_ObjList +{ + U64 count; + LNK_ObjNode *first; + LNK_ObjNode *last; +} LNK_ObjList; + +typedef struct LNK_ObjNodeArray +{ + U64 count; + LNK_ObjNode *v; +} LNK_ObjNodeArray; //////////////////////////////// @@ -46,133 +56,29 @@ typedef struct LNK_DirectiveInfo //////////////////////////////// -#define LNK_MakeChunkInputIdx(obj_idx, sect_idx) (((U64)(obj_idx) << 32) | (U64)((sect_idx) & max_U32)) - -typedef struct LNK_Obj +typedef struct LNK_SymbolInputResult { - String8 data; - String8 path; - String8 lib_path; - U64 input_idx; - COFF_MachineType machine; - U64 chunk_count; - U64 sect_count; - String8 *sect_name_arr; - String8 *sect_sort_arr; - LNK_RelocList *sect_reloc_list_arr; - LNK_ChunkPtr *chunk_arr; - LNK_SymbolList symbol_list; -} LNK_Obj; - -typedef struct LNK_ObjNode -{ - struct LNK_ObjNode *next; - LNK_Obj data; -} LNK_ObjNode; - -typedef struct LNK_ObjList -{ - U64 count; - LNK_ObjNode *first; - LNK_ObjNode *last; -} LNK_ObjList; - -typedef struct LNK_ObjNodeArray -{ - U64 count; - LNK_ObjNode *v; -} LNK_ObjNodeArray; + LNK_SymbolList weak_symbols; + LNK_SymbolList undef_symbols; +} LNK_SymbolInputResult; //////////////////////////////// -typedef struct LNK_SectDefn -{ - struct LNK_SectDefn *next; - LNK_Obj *obj; - String8 name; - COFF_SectionFlags flags; - U64 idx; -} LNK_SectDefn; - -typedef struct -{ - U64 count; - LNK_SectDefn *first; - LNK_SectDefn *last; -} LNK_SectDefnList; - typedef struct { LNK_InputObj **inputs; - LNK_ObjNode *obj_node_arr; + LNK_ObjNodeArray objs; U64 obj_id_base; - LNK_SectDefnList *defn_arr; - LNK_SectionTable *sectab; - U64 *function_pad_min; U32 machine; } LNK_ObjIniter; typedef struct { - Rng1U64 *range_arr; - LNK_ObjNode *obj_node_arr; - LNK_SectDefnList *defn_arr; - LNK_SectDefnList *conf_arr; -} LNK_ObjNewSectScanner; - -typedef struct -{ - LNK_SectionTable *sectab; - LNK_ObjNode *obj_arr; - U64 **chunk_counts; -} LNK_ChunkCounter; - -typedef struct -{ - LNK_ChunkManager *cman; - U64 *chunk_id; -} LNK_ChunkRefAssign; - -typedef struct -{ - LNK_SectionTable *sectab; - Rng1U64 *range_arr; - U64 **chunk_ids; - LNK_ObjNode *obj_arr; - LNK_ChunkList **nosort_chunk_list_arr_arr; - LNK_ChunkList **chunk_list_arr_arr; -} LNK_ChunkRefAssigner; - -typedef struct -{ - LNK_SymbolType type; - LNK_ObjNodeArray in_arr; - LNK_SymbolList *out_arr; - Rng1U64 *range_arr; -} LNK_SymbolCollector; - -typedef struct -{ - LNK_Obj **obj_arr; - String8 name; - String8 postfix; - B32 collect_discarded; - LNK_ChunkList *list_arr; -} LNK_CollectObjChunksTaskData; - -typedef struct -{ - Rng1U64 *range_arr; - LNK_ObjNodeArray in_arr; - String8List *out_arr; -} LNK_DefaultLibCollector; - -typedef struct -{ - LNK_ObjNode *in_arr; - String8List *out_arr; - Rng1U64 *range_arr; -} LNK_ManifestDependencyCollector; + LNK_SymbolTable *symtab; + LNK_ObjNodeArray objs; + LNK_SymbolList *weak_lists; + LNK_SymbolList *undef_lists; +} LNK_InputCoffSymbolTable; //////////////////////////////// @@ -180,29 +86,31 @@ internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...); //////////////////////////////// -internal void lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node); -internal void lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat); -internal LNK_InputObj * lnk_input_obj_list_push(Arena *arena, LNK_InputObjList *list); -internal LNK_InputObj ** lnk_array_from_input_obj_list(Arena *arena, LNK_InputObjList list); -internal LNK_InputObjList lnk_input_obj_list_from_string_list(Arena *arena, String8List list); -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_Obj ** lnk_obj_arr_from_list(Arena *arena, LNK_ObjList list); internal LNK_ObjNodeArray lnk_obj_list_reserve(Arena *arena, LNK_ObjList *list, U64 count); -internal LNK_ChunkList * lnk_collect_obj_chunks(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8 name, String8 postfix, B32 collect_discarded); -internal LNK_ObjNodeArray lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *tp_arena, LNK_ObjList *obj_list, LNK_SectionTable *sectab, U64 *function_pad_min, COFF_MachineType machine, U64 input_count, LNK_InputObj **inputs); +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 LNK_Chunk * lnk_sect_chunk_array_from_coff(Arena *arena, U64 obj_id, String8 obj_path, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, String8 *sect_name_arr, String8 *sect_postfix_arr); -internal LNK_SymbolArray lnk_symbol_array_from_coff(Arena *arena, LNK_Obj *obj, String8 obj_path, String8 lib_path, B32 is_big_obj, U64 function_pad_min, U64 sect_count, COFF_SectionHeader *section_table, U64 symbol_count, void *symbol_table, String8 string_table, LNK_ChunkPtr *chunk_table, LNK_Chunk *master_common_block); -internal LNK_RelocList lnk_reloc_list_from_coff_reloc_array(Arena *arena, COFF_MachineType machine, LNK_Chunk *chunk, LNK_SymbolArray symbol_array, COFF_Reloc *reloc_v, U64 reloc_count); -internal LNK_RelocList * lnk_reloc_list_array_from_coff(Arena *arena, COFF_MachineType machine, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, LNK_ChunkPtr *chunk_ptr_arr, LNK_SymbolArray symbol_array); -internal LNK_DirectiveInfo lnk_directive_info_from_sections(Arena *arena, String8 obj_path, String8 lib_path, U64 chunk_count, LNK_RelocList *reloc_list_arr, String8 *sect_name_arr, LNK_Chunk *chunk_arr); +internal LNK_Obj ** lnk_obj_arr_from_list(Arena *arena, LNK_ObjList list); + +//////////////////////////////// internal U32 lnk_obj_get_features(LNK_Obj *obj); internal U32 lnk_obj_get_comp_id(LNK_Obj *obj); internal U32 lnk_obj_get_vol_md(LNK_Obj *obj); +internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff(LNK_Obj *obj, void *coff_symbol); +internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff_symbol_idx(LNK_Obj *obj, U64 symbol_idx); + +//////////////////////////////// + +internal String8List * lnk_collect_obj_chunks_parallel(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8 name, String8 postfix, B32 collect_discarded); +internal String8List lnk_collect_obj_chunks(Arena *arena, LNK_Obj *obj, String8 name, String8 postfix, B32 collect_discarded); + +//////////////////////////////// + +internal void lnk_parse_msvc_linker_directive(Arena *arena, LNK_Obj *obj, LNK_DirectiveInfo *directive_info, String8 buffer); + +//////////////////////////////// + +internal LNK_SymbolList lnk_run_symbol_collector(TP_Context *tp, TP_Arena *arena, LNK_ObjNodeArray arr, LNK_SymbolType symbol_type); + diff --git a/src/linker/lnk_reloc.c b/src/linker/lnk_reloc.c deleted file mode 100644 index f78df7e6..00000000 --- a/src/linker/lnk_reloc.c +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal LNK_Reloc * -lnk_reloc_list_reserve(Arena *arena, LNK_RelocList *list, U64 count) -{ - LNK_Reloc *arr = NULL; - if (count) { - arr = push_array(arena, LNK_Reloc, count); - for (LNK_Reloc *ptr = arr, *opl = arr + count; ptr < opl; ++ptr) { - SLLQueuePush(list->first, list->last, ptr); - } - list->count += count; - } - return arr; -} - -internal LNK_Reloc * -lnk_reloc_list_push(Arena *arena, LNK_RelocList *list) -{ - LNK_Reloc *node = push_array(arena, LNK_Reloc, 1); - SLLQueuePush(list->first, list->last, node); - list->count += 1; - return node; -} - -internal LNK_RelocList -lnk_reloc_list_copy(Arena *arena, LNK_RelocList *list) -{ - LNK_RelocList result = {0}; - for (LNK_Reloc *n = list->first; n != NULL; n = n->next) { - LNK_Reloc *r = lnk_reloc_list_push(arena, &result); - r->chunk = n->chunk; - r->type = n->type; - r->apply_off = n->apply_off; - r->symbol = n->symbol; - } - return result; -} - -internal void -lnk_reloc_list_concat_in_place(LNK_RelocList *list, LNK_RelocList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal void -lnk_reloc_list_concat_in_place_arr(LNK_RelocList *list, LNK_RelocList *arr, U64 count) -{ - SLLConcatInPlaceArray(list, arr, count); -} - -internal LNK_RelocList ** -lnk_make_reloc_list_arr_arr(Arena *arena, U64 slot_count, U64 per_count) -{ - LNK_RelocList **arr_arr = push_array_no_zero(arena, LNK_RelocList *, slot_count); - for (U64 i = 0; i < slot_count; i += 1) { - arr_arr[i] = push_array(arena, LNK_RelocList, per_count); - } - return arr_arr; -} - -internal LNK_RelocList -lnk_reloc_list_from_coff_reloc_array(Arena *arena, COFF_MachineType machine, LNK_Chunk *chunk, LNK_SymbolArray symbol_array, COFF_Reloc *reloc_v, U64 reloc_count) -{ - LNK_RelocList reloc_list = {0}; - - LNK_Reloc *reloc_arr = lnk_reloc_list_reserve(arena, &reloc_list, reloc_count); - LNK_Reloc *reloc_ptr = reloc_arr; - LNK_Reloc *reloc_opl = reloc_arr + reloc_count; - COFF_Reloc *coff_reloc_ptr = reloc_v; - - for (; reloc_ptr < reloc_opl; ++reloc_ptr, ++coff_reloc_ptr) { - LNK_RelocType type = lnk_ext_reloc_type_from_coff(machine, coff_reloc_ptr->type); - LNK_Chunk *reloc_chunk = chunk; - U64 apply_off = coff_reloc_ptr->apply_off; - LNK_Symbol *symbol = symbol_array.v + coff_reloc_ptr->isymbol; - - if (chunk->type == LNK_Chunk_List) { - reloc_chunk = chunk->u.list->last->data; - U64 cursor = 0; - for (LNK_ChunkNode *c = chunk->u.list->first; c != 0; c = c->next) { - Assert(c->data->type == LNK_Chunk_Leaf); - if (coff_reloc_ptr->apply_off < cursor + c->data->u.leaf.size) { - reloc_chunk = c->data; - break; - } - cursor += c->data->u.leaf.size; - } - apply_off = coff_reloc_ptr->apply_off - cursor; - } - - Assert(reloc_chunk->type == LNK_Chunk_Leaf); - Assert(coff_reloc_ptr->isymbol < symbol_array.count); - reloc_ptr->chunk = reloc_chunk; - reloc_ptr->type = type; - reloc_ptr->apply_off = apply_off; - reloc_ptr->symbol = symbol; - } - return reloc_list; -} - -internal LNK_Reloc ** -lnk_reloc_array_from_list(Arena *arena, LNK_RelocList list) -{ - LNK_Reloc **arr = push_array_no_zero(arena, LNK_Reloc *, list.count); - U64 count = 0; - for (LNK_Reloc *node = list.first; node != 0; node = node->next) { - Assert(count < list.count); - arr[count++] = node; - } - return arr; -} - -internal LNK_RelocType -lnk_ext_reloc_type_from_coff(COFF_MachineType machine, U32 type) -{ - LNK_RelocType result = LNK_Reloc_NULL; - switch (machine) { - case COFF_MachineType_Unknown: break; - case COFF_MachineType_X64: { - switch (type) { - case COFF_Reloc_X64_Abs: result = LNK_Reloc_NULL; break; - case COFF_Reloc_X64_Addr64: result = LNK_Reloc_ADDR_64; break; - case COFF_Reloc_X64_Addr32: result = LNK_Reloc_ADDR_32; break; - case COFF_Reloc_X64_Addr32Nb: result = LNK_Reloc_VIRT_OFF_32; break; - case COFF_Reloc_X64_Rel32: result = LNK_Reloc_REL32; break; - case COFF_Reloc_X64_Rel32_1: result = LNK_Reloc_REL32_1; break; - case COFF_Reloc_X64_Rel32_2: result = LNK_Reloc_REL32_2; break; - case COFF_Reloc_X64_Rel32_3: result = LNK_Reloc_REL32_3; break; - case COFF_Reloc_X64_Rel32_4: result = LNK_Reloc_REL32_4; break; - case COFF_Reloc_X64_Rel32_5: result = LNK_Reloc_REL32_5; break; - case COFF_Reloc_X64_Section: result = LNK_Reloc_SECT_IDX; break; - case COFF_Reloc_X64_SecRel: result = LNK_Reloc_SECT_REL; break; - case COFF_Reloc_X64_SecRel7: lnk_not_implemented("TODO: COFF_Reloc_X64_SecRel7"); break; - case COFF_Reloc_X64_Token: lnk_not_implemented("TODO: COFF_Reloc_X64_Token"); break; - case COFF_Reloc_X64_SRel32: lnk_not_implemented("TODO: COFF_Reloc_X64_SRel32"); break; - case COFF_Reloc_X64_Pair: lnk_not_implemented("TODO: COFF_Reloc_X64_Pair"); break; - case COFF_Reloc_X64_SSpan32: lnk_not_implemented("TODO: COFF_Reloc_X64_SSpan32"); break; - default: lnk_invalid_path("unknown relocation type 0x%X", type); - } - } break; - default: lnk_not_implemented("TODO: define remap for coff reloc types"); break; - } - return result; -} - -internal U32 -lnk_ext_reloc_type_to_coff(COFF_MachineType machine, LNK_RelocType type) -{ - U32 result = 0; - switch (machine) { - case COFF_MachineType_X64: { - switch (type) { - case LNK_Reloc_NULL: result = COFF_Reloc_X64_Abs; break; - case LNK_Reloc_ADDR_64: result = COFF_Reloc_X64_Addr64; break; - case LNK_Reloc_ADDR_32: result = COFF_Reloc_X64_Addr32; break; - case LNK_Reloc_VIRT_OFF_32: result = COFF_Reloc_X64_Addr32Nb; break; - case LNK_Reloc_REL32: result = COFF_Reloc_X64_Rel32; break; - case LNK_Reloc_REL32_1: result = COFF_Reloc_X64_Rel32_1; break; - case LNK_Reloc_REL32_2: result = COFF_Reloc_X64_Rel32_2; break; - case LNK_Reloc_REL32_3: result = COFF_Reloc_X64_Rel32_3; break; - case LNK_Reloc_REL32_4: result = COFF_Reloc_X64_Rel32_4; break; - case LNK_Reloc_REL32_5: result = COFF_Reloc_X64_Rel32_5; break; - case LNK_Reloc_SECT_IDX: result = COFF_Reloc_X64_Section; break; - case LNK_Reloc_SECT_REL: result = COFF_Reloc_X64_SecRel; break; - default: InvalidPath; - } - } break; - default: lnk_not_implemented("TODO: support for machine 0x%X", machine); break; - } - return result; -} - - diff --git a/src/linker/lnk_reloc.h b/src/linker/lnk_reloc.h deleted file mode 100644 index 11e69e3f..00000000 --- a/src/linker/lnk_reloc.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -typedef enum -{ - LNK_Reloc_NULL, - LNK_Reloc_ADDR_16, - LNK_Reloc_ADDR_32, - LNK_Reloc_ADDR_64, - LNK_Reloc_CHUNK_SIZE_FILE_16, - LNK_Reloc_CHUNK_SIZE_FILE_32, - LNK_Reloc_CHUNK_SIZE_VIRT_32, - LNK_Reloc_FILE_ALIGN_32, - LNK_Reloc_FILE_OFF_15, - LNK_Reloc_FILE_OFF_32, - LNK_Reloc_FILE_OFF_64, - LNK_Reloc_REL32, - LNK_Reloc_REL32_1, - LNK_Reloc_REL32_2, - LNK_Reloc_REL32_3, - LNK_Reloc_REL32_4, - LNK_Reloc_REL32_5, - LNK_Reloc_SECT_REL, - LNK_Reloc_SECT_IDX, - LNK_Reloc_VIRT_ALIGN_32, - LNK_Reloc_VIRT_OFF_32, -} LNK_RelocType; - -typedef struct LNK_Reloc -{ - struct LNK_Reloc *next; - LNK_Chunk *chunk; - LNK_RelocType type; - U64 apply_off; - struct LNK_Symbol *symbol; -} LNK_Reloc; - -typedef struct LNK_RelocList -{ - U64 count; - LNK_Reloc *first; - LNK_Reloc *last; -} LNK_RelocList; - -internal LNK_Reloc * lnk_reloc_list_reserve(Arena *arena, LNK_RelocList *list, U64 count); -internal LNK_Reloc * lnk_reloc_list_push(Arena *arena, LNK_RelocList *list); -internal LNK_RelocList lnk_reloc_list_copy(Arena *arena, LNK_RelocList *list); -internal void lnk_reloc_list_concat_in_place(LNK_RelocList *list, LNK_RelocList *to_concat); -internal void lnk_reloc_list_concat_in_place_arr(LNK_RelocList *list, LNK_RelocList *arr, U64 count); -internal LNK_RelocList ** lnk_make_reloc_list_arr_arr(Arena *arena, U64 slot_count, U64 per_count); -internal LNK_Reloc ** lnk_reloc_array_from_list(Arena *arena, LNK_RelocList list); -internal LNK_RelocType lnk_ext_reloc_type_from_coff(COFF_MachineType machine, U32 type); -internal U32 lnk_ext_reloc_type_to_coff(COFF_MachineType machine, LNK_RelocType type); - diff --git a/src/linker/lnk_section_table.c b/src/linker/lnk_section_table.c index 4da0b857..529a25bf 100644 --- a/src/linker/lnk_section_table.c +++ b/src/linker/lnk_section_table.c @@ -1,236 +1,6 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal LNK_SectionNode * -lnk_section_list_remove(LNK_SectionList *list, String8 name) -{ - LNK_SectionNode *section = lnk_section_list_search_node(list, name); - - if (list->count > 0) { - if (list->first == section) { - list->first = list->first->next; - list->count -= 1; - - if (list->last == section) { - list->last = NULL; - } - } else { - for (LNK_SectionNode *curr = list->first, *prev = NULL; curr != NULL; prev = curr, curr = curr->next) { - if (curr == section) { - prev->next = curr->next; - list->count -= 1; - - if (list->last == curr) { - list->last = prev; - } - - break; - } - } - } - } - return section; -} - -internal LNK_SectionNode * -lnk_section_list_search_node(LNK_SectionList *list, String8 name) -{ - LNK_SectionNode *node; - for (node = list->first; node != 0; node = node->next) { - if (str8_match(node->data.name, name, 0)) { - break; - } - } - return node; -} - -internal LNK_Section * -lnk_section_list_search(LNK_SectionList *list, String8 name) -{ - LNK_SectionNode *node = lnk_section_list_search_node(list, name); - return node != NULL ? &node->data : NULL; -} - -internal LNK_SectionArray -lnk_section_array_from_list(Arena *arena, LNK_SectionList list) -{ - LNK_SectionArray result; - result.count = 0; - result.v = push_array_no_zero(arena, LNK_Section, list.count); - for (LNK_SectionNode *node = list.first; node != 0; node = node->next) { - result.v[result.count] = node->data; - result.count += 1; - } - return result; -} - -internal LNK_SectionPtrArray -lnk_section_ptr_array_from_list(Arena *arena, LNK_SectionList list) -{ - LNK_SectionPtrArray result; - result.count = 0; - result.v = push_array_no_zero(arena, LNK_Section *, list.count); - for (LNK_SectionNode *node = list.first; node != 0; node = node->next) { - result.v[result.count] = &node->data; - result.count += 1; - } - return result; -} - -internal String8 -lnk_make_section_sort_index(Arena *arena, String8 name, COFF_SectionFlags flags, U64 section_index) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - // pack sections with run-time data closer - String8List sort_index_list = {0}; - if (flags & COFF_SectionFlag_MemDiscardable) { - str8_list_pushf(scratch.arena, &sort_index_list, "b"); - } else { - str8_list_pushf(scratch.arena, &sort_index_list, "a"); - } - - if (str8_match_lit(".null", name, 0)) { - // null section always first - str8_list_pushf(scratch.arena, &sort_index_list, "a"); - } else if (str8_match_lit(".rsrc", name, 0)) { - // section with resource data must be last because during runtime windows might append pages - str8_list_pushf(scratch.arena, &sort_index_list, "c"); - } else { - str8_list_pushf(scratch.arena, &sort_index_list, "b"); - } - - // sort sections based on the contents - if (flags & COFF_SectionFlag_CntCode) { - str8_list_pushf(scratch.arena, &sort_index_list, "a"); - if (str8_match_lit(".text", name, 0)) { - str8_list_pushf(scratch.arena, &sort_index_list, "a"); - } else { - str8_list_pushf(scratch.arena, &sort_index_list, "b"); - } - } else if (flags & COFF_SectionFlag_CntInitializedData) { - str8_list_pushf(scratch.arena, &sort_index_list, "b"); - if (str8_match_lit(".data", name, 0)) { - str8_list_pushf(scratch.arena, &sort_index_list, "a"); - } else if (str8_match_lit(".rdata", name, 0)) { - str8_list_pushf(scratch.arena, &sort_index_list, "b"); - } else if (str8_match_lit(".tls", name, 0)) { - str8_list_pushf(scratch.arena, &sort_index_list, "c"); - } else { - str8_list_pushf(scratch.arena, &sort_index_list, "d"); - } - } else if (flags & COFF_SectionFlag_CntUninitializedData) { - str8_list_pushf(scratch.arena, &sort_index_list, "c"); - } else { - str8_list_pushf(scratch.arena, &sort_index_list, "d"); - } - - // sort sections based on read/write access so final section layout looks cleaner - if (flags & COFF_SectionFlag_MemRead && ~flags & COFF_SectionFlag_MemWrite) { - str8_list_pushf(scratch.arena, &sort_index_list, "a"); - } else { - str8_list_pushf(scratch.arena, &sort_index_list, "b"); - } - - String8 order_index = str8_from_bits_u32(scratch.arena, safe_cast_u32(section_index)); - str8_list_push(scratch.arena, &sort_index_list, order_index); - - String8 result = str8_list_join(arena, &sort_index_list, 0); - scratch_end(scratch); - ProfEnd(); - return result; -} - -internal void -lnk_section_associate_chunks(LNK_Section *sect, LNK_Chunk *head, LNK_Chunk *associate) -{ - lnk_chunk_associate(head, associate); -} - -internal LNK_Chunk * -lnk_section_push_chunk_raw(LNK_Section *sect, LNK_Chunk *parent, void *raw_ptr, U64 raw_size, String8 sort_index) -{ - return lnk_chunk_push_leaf(sect->arena, sect->cman, parent, sort_index, raw_ptr, raw_size); -} - -internal LNK_Chunk * -lnk_section_push_chunk_data(LNK_Section *sect, LNK_Chunk *parent, String8 data, String8 sort_index) -{ - return lnk_section_push_chunk_raw(sect, parent, data.str, data.size, sort_index); -} - -internal LNK_Chunk * -lnk_section_push_chunk_u32(LNK_Section *sect, LNK_Chunk *parent, U32 value, String8 sort_index) -{ - U32 *ptr = push_array_no_zero(sect->arena, U32, 1); - *ptr = value; - return lnk_section_push_chunk_raw(sect, parent, ptr, sizeof(*ptr), sort_index); -} - -internal LNK_Chunk * -lnk_section_push_chunk_u64(LNK_Section *sect, LNK_Chunk *parent, U32 value, String8 sort_index) -{ - U64 *ptr = push_array_no_zero(sect->arena, U64, 1); - *ptr = value; - return lnk_section_push_chunk_raw(sect, parent, ptr, sizeof(*ptr), sort_index); -} - -internal LNK_Chunk * -lnk_section_push_chunk_bss(LNK_Section *sect, LNK_Chunk *parent, U64 size, String8 sort_index) -{ - return lnk_section_push_chunk_raw(sect, parent, 0, size, sort_index); -} - -internal LNK_Chunk * -lnk_section_push_chunk_list(LNK_Section *sect, LNK_Chunk *parent, String8 sort_index) -{ - return lnk_chunk_push_list(sect->arena, sect->cman, parent, sort_index); -} - -internal LNK_Reloc * -lnk_section_push_reloc(LNK_Section *sect, LNK_Chunk *chunk, LNK_RelocType type, U64 apply_off, LNK_Symbol *symbol) -{ - Assert(symbol); - LNK_Reloc *reloc = lnk_reloc_list_push(sect->arena, §->reloc_list); - reloc->chunk = chunk; - reloc->type = type; - reloc->apply_off = apply_off; - reloc->symbol = symbol; - return reloc; -} - -internal LNK_Reloc * -lnk_section_push_reloc_undefined(LNK_Section *sect, LNK_Chunk *chunk, LNK_RelocType type, U64 apply_off, String8 undefined_symbol_name, LNK_SymbolScopeFlags scope_flags) -{ - LNK_Symbol *symbol = lnk_make_undefined_symbol(sect->arena, undefined_symbol_name, scope_flags); - LNK_Reloc *reloc = lnk_section_push_reloc(sect, chunk, type, apply_off, symbol); - return reloc; -} - -internal void -lnk_section_merge(LNK_Section *dst, LNK_Section *src) -{ - ProfBeginFunction(); - - // set merge info - src->is_merged = 1; - src->merge_sect_id = dst->id; - src->id_map = push_array_no_zero(src->arena, U64, src->cman->total_chunk_count); - - // put source root in a wrapper list so it has unique sort index otherwise - // after we merge sections sort indices might conflict - LNK_Chunk *src_root_wrapper = lnk_section_push_chunk_list(dst, dst->cman->root, str8(0,0)); - - // merge roots - lnk_merge_chunks(dst->arena, dst->cman, src_root_wrapper, src->cman->root, src->id_map, src->cman->total_chunk_count); - - // copy relocations - lnk_reloc_list_concat_in_place(&dst->reloc_list, &src->reloc_list); - - ProfEnd(); -} - internal U8 lnk_code_align_byte_from_machine(COFF_MachineType machine) { @@ -247,35 +17,103 @@ lnk_code_align_byte_from_machine(COFF_MachineType machine) return align_byte; } -internal void -lnk_section_build_data(LNK_Section *sect, COFF_MachineType machine) +internal U16 +lnk_default_align_from_machine(COFF_MachineType machine) { - if (sect->is_loose) { - if (sect->has_layout) { - sect->layout = lnk_build_chunk_layout(sect->arena, sect->cman); - } else { - sect->layout.total_count = sect->cman->total_chunk_count; - sect->layout.chunk_ptr_array = lnk_make_chunk_id_map(sect->arena, sect->cman); - sect->layout.chunk_off_array = 0; - sect->layout.chunk_file_size_array = 0; - sect->layout.chunk_virt_size_array = 0; - sect->layout.pad_array_count = 0; - sect->layout.pad_array = 0; - } - sect->is_loose = 0; + U16 align = 0; + switch (machine) { + case COFF_MachineType_Unknown: break; + case COFF_MachineType_X64: { + align = 16; + } break; + default: { NotImplemented; } break; + } + return align; +} + +internal LNK_SectionContrib * +lnk_section_contrib_chunk_push(LNK_SectionContribChunk *chunk, U64 count) +{ + Assert(chunk->count + count <= chunk->cap); + LNK_SectionContrib *result = &chunk->v[chunk->count]; + chunk->count += count; + return result; +} + +internal LNK_SectionContribChunk * +lnk_section_contrib_chunk_list_push_chunk(Arena *arena, LNK_SectionContribChunkList *list, U64 cap) +{ + LNK_SectionContribChunk *chunk = push_array(arena, LNK_SectionContribChunk, 1); + chunk->count = 0; + chunk->cap = cap; + chunk->v = push_array(arena, LNK_SectionContrib, cap); + SLLQueuePush(list->first, list->last, chunk); + list->chunk_count += 1; + return chunk; +} + +internal void +lnk_section_contrib_chunk_list_concat_in_place(LNK_SectionContribChunkList *list, LNK_SectionContribChunkList *to_concat) +{ + if (list->chunk_count == 0) { + *list = *to_concat; + } else { + list->last->next = to_concat->first; + list->last = to_concat->last; + list->chunk_count += to_concat->chunk_count; + MemoryZeroStruct(to_concat); } } +internal void +lnk_section_list_remove(LNK_SectionList *list, LNK_SectionNode *node) +{ + if (list->count > 0) { + if (list->first == node) { + list->first = list->first->next; + list->count -= 1; + + if (list->last == node) { + list->last = 0; + } + } else { + for (LNK_SectionNode *curr = list->first, *prev = 0; curr != 0; prev = curr, curr = curr->next) { + if (curr == node) { + prev->next = curr->next; + list->count -= 1; + + if (list->last == curr) { + list->last = prev; + } + + break; + } + } + } + } +} + +internal LNK_SectionArray +lnk_section_array_from_list(Arena *arena, LNK_SectionList list) +{ + LNK_SectionArray result; + result.count = 0; + result.v = push_array_no_zero(arena, LNK_Section *, list.count); + for (LNK_SectionNode *node = list.first; node != 0; node = node->next) { + result.v[result.count] = &node->data; + result.count += 1; + } + return result; +} + internal LNK_SectionTable * -lnk_section_table_alloc(U64 section_virt_off, U64 sect_align, U64 file_align) +lnk_section_table_alloc(void) { ProfBeginFunction(); Arena *arena = arena_alloc(); LNK_SectionTable *sectab = push_array(arena, LNK_SectionTable, 1); sectab->arena = arena; - sectab->section_virt_off = section_virt_off; - sectab->sect_align = sect_align; - sectab->file_align = file_align; + sectab->sect_ht = hash_table_init(arena, 256); ProfEnd(); return sectab; } @@ -286,131 +124,106 @@ lnk_section_table_release(LNK_SectionTable **st_ptr) ProfBeginFunction(); LNK_SectionTable *sectab = *st_ptr; arena_release(sectab->arena); - *st_ptr = NULL; + *st_ptr = 0; ProfEnd(); } +internal String8 +lnk_make_name_with_flags(Arena *arena, String8 name, COFF_SectionFlags flags) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List l = {0}; + str8_list_push(scratch.arena, &l, name); + str8_list_push(scratch.arena, &l, str8_struct(&flags)); + String8 name_with_flags = str8_list_join(arena, &l, 0); + scratch_end(scratch); + return name_with_flags; +} + internal LNK_Section * lnk_section_table_push(LNK_SectionTable *sectab, String8 name, COFF_SectionFlags flags) { ProfBeginFunction(); + + LNK_SectionNode *sect_node = push_array(sectab->arena, LNK_SectionNode, 1); + LNK_Section *sect = §_node->data; + sect->arena = arena_alloc(); + sect->id = sectab->id_max++; + sect->name = push_str8_copy(sect->arena, name); + sect->flags = flags; + sect->has_layout = 1; + LNK_SectionList *sect_list = §ab->list; - - LNK_SectionNode *sect_node = push_array(sectab->arena, LNK_SectionNode, 1); - String8 sort_index = lnk_make_section_sort_index(sectab->arena, name, flags, sectab->id_max); - - B32 found = 0; - for (LNK_SectionNode *curr = sect_list->first, *prev = NULL; curr != NULL; prev = curr, curr = curr->next) { - LNK_Section *sect = &curr->data; - int cmp = str8_compar_case_sensitive(&sort_index, §->sort_index); - if (cmp < 0) { - if (prev == NULL) { - SLLQueuePushFront(sect_list->first, sect_list->last, sect_node); - } else { - prev->next = sect_node; - sect_node->next = curr; - } - found = 1; - break; - } - } - - if (!found) { - SLLQueuePush(sect_list->first, sect_list->last, sect_node); - } + SLLQueuePush(sect_list->first, sect_list->last, sect_node); sect_list->count += 1; - U64 sect_id = sectab->id_max; - sectab->id_max += 1; - - LNK_Section *sect = §_node->data; - sect->arena = arena_alloc(); - sect->id = sect_id; - sect->name = push_str8_copy(sect->arena, name); - sect->sort_index = sort_index; - sect->flags = flags; - sect->cman = lnk_chunk_manager_alloc(sect->arena, sect_id, sectab->file_align); - sect->root = sect->cman->root; - sect->nosort_chunk = lnk_chunk_push_list(sect->arena, sect->cman, sect->root, str8(0,0)); - sect->nosort_chunk->sort_chunk = 0; - sect->emit_header = 1; - sect->has_layout = 1; - sect->is_loose = 1; + String8 name_with_flags = lnk_make_name_with_flags(sectab->arena, name, flags); + hash_table_push_string_raw(sectab->arena, sectab->sect_ht, name_with_flags, sect); - lnk_chunk_set_debugf(sect->arena, sect->root, "root chunk for %S", name); - lnk_chunk_set_debugf(sect->arena, sect->nosort_chunk, "nosort chunk for %S", name); - ProfEnd(); return sect; } -internal LNK_Section * -lnk_section_table_push_null(LNK_SectionTable *sectab) -{ - LNK_SectionList *list = §ab->list; - SLLQueuePushFront(list->first, list->last, sectab->null_sect); - list->count += 1; - return §ab->null_sect->data; -} - -LNK_CHUNK_VISITOR_SIG(lnk_chunk_has_leaf) -{ - B32 stop = 0; - if (chunk->type == LNK_Chunk_Leaf) { - B32 has_data = !lnk_chunk_is_discarded(chunk) && chunk->u.leaf.size > 0; - if (has_data) { - B32 *no_data = (B32*)ud; - *no_data = 0; - stop = 1; - } - } - return stop; -} - -LNK_CHUNK_VISITOR_SIG(lnk_chunk_mark_discarded) -{ - chunk->is_discarded = 1; - B32 stop = 0; - return stop; -} - internal void -lnk_section_table_remove(LNK_SectionTable *sectab, LNK_SymbolTable *symtab, String8 name) +lnk_section_table_remove(LNK_SectionTable *sectab, String8 name) { ProfBeginFunction(); - // remove node from list - LNK_SectionNode *sect_node = lnk_section_list_remove(§ab->list, name); - LNK_Section *sect = §_node->data; - - // remove symbol for section root chunk - lnk_symbol_table_remove(symtab, LNK_SymbolScopeIndex_Internal, sect->symbol_name); - - // mark chunks as discarded - lnk_visit_chunks(sect->id, sect->root, lnk_chunk_mark_discarded, NULL); - - // push to empties - SLLQueuePush(sectab->empties_list.first, sectab->empties_list.last, sect_node); - sectab->empties_list.count += 1; + // find node + LNK_SectionNode *sect_n; + for (sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + if (str8_match(sect_n->data.name, name, 0)) { + break; + } + } + + // remove node + lnk_section_list_remove(§ab->list, sect_n); + + // push to free list + SLLQueuePush(sectab->free_list.first, sectab->free_list.last, sect_n); + sectab->free_list.count += 1; ProfEnd(); } internal LNK_Section * -lnk_section_table_search(LNK_SectionTable *sectab, String8 name) +lnk_section_table_search(LNK_SectionTable *sectab, String8 name, COFF_SectionFlags flags) { - return lnk_section_list_search(§ab->list, name); + Temp scratch = scratch_begin(0,0); + + String8 name_with_flags = lnk_make_name_with_flags(scratch.arena, name, flags); + LNK_Section *section = 0; + hash_table_search_string_raw(sectab->sect_ht, name_with_flags, §ion); + + scratch_end(scratch); + return section; } -internal LNK_Section * -lnk_section_table_search_id(LNK_SectionTable *sectab, U64 id) +internal LNK_SectionArray +lnk_section_table_search_many(Arena *arena, LNK_SectionTable *sectab, String8 name) { - for (LNK_SectionNode *node = sectab->list.first; node != NULL; node = node->next) { - if (node->data.id == id) { - return &node->data; + U64 match_count = 0; + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + if (str8_match(sect_n->data.name, name, 0)) { + match_count += 1; } } - return NULL; + + LNK_SectionArray result = {0}; + + if (match_count > 0) { + result.count = 0; + result.v = push_array(arena, LNK_Section *, match_count); + + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + if (str8_match(sect_n->data.name, name, 0)) { + result.v[result.count++] = §_n->data; + } + } + } + + return result; } internal void @@ -419,12 +232,13 @@ lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_l ProfBeginFunction(); Temp scratch = scratch_begin(0, 0); LNK_Section **src_dst = push_array(scratch.arena, LNK_Section *, sectab->id_max); - for (LNK_MergeDirectiveNode *merge_node = merge_list.first; merge_node != NULL; merge_node = merge_node->next) { + for (LNK_MergeDirectiveNode *merge_node = merge_list.first; merge_node != 0; merge_node = merge_node->next) { LNK_MergeDirective *merge = &merge_node->data; // are we trying to merge section that was already merged? - LNK_Section *merge_sect = lnk_section_list_search(§ab->merge_list, merge->src); - if (merge_sect) { + LNK_Section *merge_sect = 0; + hash_table_search_string_raw(sectab->sect_ht, merge->src, &merge_sect); + if (merge_sect && merge_sect->is_merged) { LNK_Section *dst = src_dst[merge_sect->id]; B32 is_ambiguous_merge = !str8_match(dst->name, merge->dst, 0); if (is_ambiguous_merge) { @@ -436,59 +250,41 @@ lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_l } // find source seciton - LNK_Section *src = lnk_section_table_search(sectab, merge->src); - if (src == NULL) { - lnk_error(LNK_Warning_IllData, "Can't find section \"%S\" to merge with \"%S\"", merge->src, merge->dst); - // TODO: supplement obj path if applicable + LNK_SectionArray src_matches = lnk_section_table_search_many(scratch.arena, sectab, merge->src); + if (src_matches.count == 0) { continue; } - - // handle case where destination section doesn't exist - LNK_Section *dst = lnk_section_table_search(sectab, merge->dst); - if (dst == NULL) { - src->name = push_str8_copy(src->arena, merge->dst); - src_dst[src->id] = src; - continue; - } - - // update map - src_dst[src->id] = dst; - - // merge section with destination - lnk_section_merge(dst, src); - - // remove from output section list - LNK_SectionNode *src_node = lnk_section_list_remove(§ab->list, src->name); - - // push section to merged list - SLLQueuePush(sectab->merge_list.first, sectab->merge_list.last, src_node); - sectab->merge_list.count += 1; - } - scratch_end(scratch); - ProfEnd(); -} -internal void -lnk_section_table_remove_empties(LNK_SectionTable *sectab, LNK_SymbolTable *symtab) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - String8List name_list = {0}; - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != NULL; sect_node = sect_node->next) { - LNK_Section *sect = §_node->data; - - B32 no_data = 1; - lnk_visit_chunks(sect->id, sect->root, lnk_chunk_has_leaf, (void*)&no_data); - - if (no_data) { - String8 name = push_str8_copy(scratch.arena, sect->name); - str8_list_push(scratch.arena, &name_list, name); + LNK_Section *dst; + { + LNK_SectionArray dst_matches = lnk_section_table_search_many(scratch.arena, sectab, merge->dst); + + if (dst_matches.count > 1) { + lnk_error(LNK_Warning_AmbiguousMerge, "unable to merge %S=%S, too many dest sections (%llu)", merge->src, merge->dst, dst_matches.count); + continue; + } + + // handle case where destination section doesn't exist + if (dst_matches.count == 0) { + dst = lnk_section_table_push(sectab, merge->dst, src_matches.v[0]->flags); + } else { + dst = dst_matches.v[0]; + } + } + + for (U64 src_idx = 0; src_idx < src_matches.count; src_idx += 1) { + LNK_Section *src = src_matches.v[src_idx]; + + // update map + src_dst[src->id] = dst; + + // merge section with destination + lnk_section_contrib_chunk_list_concat_in_place(&dst->contribs, &src->contribs); + src->is_merged = 1; + + // remove from output section list + lnk_section_table_remove(sectab, src->name); } - } - - for (String8Node *name = name_list.first; name != NULL; name = name->next) { - lnk_section_table_remove(sectab, symtab, name->string); } scratch_end(scratch); ProfEnd(); @@ -499,12 +295,12 @@ lnk_section_table_get_output_sections(Arena *arena, LNK_SectionTable *sectab) { LNK_SectionArray result = {0}; result.count = 0; - result.v = push_array(arena, LNK_Section, sectab->list.count); + result.v = push_array(arena, LNK_Section *, sectab->list.count); for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != 0; sect_node = sect_node->next) { - if (sect_node->data.emit_header && sect_node->data.has_layout) { + if (sect_node->data.has_layout) { Assert(result.count < sectab->list.count); - result.v[result.count] = sect_node->data; + result.v[result.count] = §_node->data; result.count += 1; } } @@ -515,390 +311,53 @@ lnk_section_table_get_output_sections(Arena *arena, LNK_SectionTable *sectab) return result; } -internal -THREAD_POOL_TASK_FUNC(lnk_section_data_builder) -{ - LNK_SectionDataBuilder *task = raw_task; - Rng1U64 range = task->range_arr[task_id]; - for (U64 sect_idx = range.min; sect_idx < range.max; ++sect_idx) { - lnk_section_build_data(task->sect_arr[sect_idx], task->machine); - } -} - internal void -lnk_section_table_build_data(TP_Context *tp, LNK_SectionTable *sectab, COFF_MachineType machine) +lnk_finalize_section_layout(LNK_SectionTable *sectab, LNK_Section *sect, U64 file_align) { - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - LNK_SectionPtrArray sect_arr = lnk_section_ptr_array_from_list(scratch.arena, sectab->list); - - LNK_SectionDataBuilder task = {0}; - task.machine = machine; - task.range_arr = tp_divide_work(scratch.arena, sect_arr.count, tp->worker_count); - task.sect_arr = sect_arr.v; - tp_for_parallel(tp, 0, tp->worker_count, lnk_section_data_builder, &task); - - scratch_end(scratch); - ProfEnd(); -} - -internal void -lnk_section_table_assign_virtual_offsets(LNK_SectionTable *sectab) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, sectab); - U64 cursor = sectab->section_virt_off; - Assert(cursor >= 0x1000); - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != NULL; sect_node = sect_node->next) { - if (sect_node == sectab->null_sect) continue; - LNK_Section *sect = §_node->data; - if (!sect->has_layout) continue; - sect->virt_off = cursor; - U64 sect_size = lnk_virt_size_from_chunk_ref(sect_id_map, sect->root->ref); - cursor += sect_size; - cursor = AlignPow2(cursor, sectab->sect_align); - } - scratch_end(scratch); - ProfEnd(); -} - -internal void -lnk_section_table_assign_file_offsets(LNK_SectionTable *sectab) -{ - ProfBeginFunction(); U64 cursor = 0; - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != NULL; sect_node = sect_node->next) { - LNK_Section *sect = §_node->data; - if (sect->flags & COFF_SectionFlag_CntUninitializedData) { - continue; + for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + LNK_SectionContrib *sc = &sc_chunk->v[sc_idx]; + + cursor = AlignPow2(cursor, sc->align); + + // store section contribution start offset + U64 sc_off = cursor; + + // compute contrib size + U64 sc_size = 0; + for (String8Node *data_n = sc->data_list; data_n != 0; data_n = data_n->next) { + sc_size += data_n->string.size; + } + + cursor += sc_size; + + // assign offset and size + sc->u.off = sc_off; + sc->u.size = sc_size; } - if (!sect->has_layout) continue; - sect->file_off = cursor; - U64 root_size = sect->layout.chunk_file_size_array[sect->root->ref.chunk_id]; - cursor += root_size; } - ProfEnd(); + + if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { + sect->fsize = AlignPow2(cursor, file_align); + } + sect->vsize = cursor; } internal void -lnk_section_table_assign_indices(LNK_SectionTable *sectab) +lnk_assign_section_virtual_space(LNK_Section *sect, U64 sect_align, U64 *voff_cursor) { - ProfBeginFunction(); - U64 isect = 0; - for (LNK_SectionNode *sect_node = sectab->list.first; sect_node != NULL; sect_node = sect_node->next) { - LNK_Section *sect = §_node->data; - if (sect->emit_header) { - sect->isect = isect++; - } - } - ProfEnd(); + sect->voff = *voff_cursor; + *voff_cursor += sect->vsize; + *voff_cursor = AlignPow2(*voff_cursor, sect_align); } -internal String8 -lnk_section_table_serialize(TP_Context *tp, Arena *arena, LNK_SectionTable *sectab, COFF_MachineType machine) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - U64 image_size = 0; - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - LNK_Section *sect = §_n->data; - if (sect->has_layout) { - U64 root_size = sect->layout.chunk_file_size_array[sect->root->ref.chunk_id]; - image_size += root_size; - } - } - - U8 *image_buffer = push_array_no_zero(arena, U8, image_size); - String8 image = str8(image_buffer, image_size); - U64 image_cursor = 0; - - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - LNK_Section *sect = §_n->data; - if (sect->has_layout) { - if (sect->flags & COFF_SectionFlag_CntUninitializedData) { - continue; - } - - U64 sect_size = sect->layout.chunk_file_size_array[sect->root->ref.chunk_id]; - String8 sect_data = str8_substr(image, rng_1u64(image_cursor, image_cursor + sect_size)); - - U8 fill_byte = 0; - if (sect->flags & COFF_SectionFlag_CntCode) { - fill_byte = lnk_code_align_byte_from_machine(machine); - } - - lnk_serialize_chunk_layout(tp, sect->layout, sect_data, fill_byte); - - image_cursor += sect_size; - } - } - - scratch_end(scratch); - ProfEnd(); - return image; -} - -internal LNK_ChunkPtr ** -lnk_chunk_id_map_from_section_table(Arena *arena, LNK_SectionTable *sectab) -{ - ProfBeginFunction(); - LNK_ChunkPtr **chunk_id_map = push_array(arena, LNK_ChunkPtr *, sectab->id_max); - for (LNK_SectionNode *node = sectab->list.first; node != 0; node = node->next) { - LNK_Section *sect = &node->data; - chunk_id_map[sect->id] = lnk_make_chunk_id_map(arena, sect->cman); - } - if (sectab->list.first->data.id != 0) { - chunk_id_map[0] = push_array(arena, LNK_ChunkPtr, 1); - chunk_id_map[0][0] = g_null_chunk_ptr; - } - ProfEnd(); - return chunk_id_map; -} - -internal LNK_Section ** -lnk_sect_id_map_from_section_table(Arena *arena, LNK_SectionTable *sectab) -{ - ProfBeginFunction(); - LNK_Section **map = push_array(arena, LNK_Section *, sectab->id_max); - LNK_SectionList *list_arr[] = { §ab->list, §ab->merge_list, §ab->empties_list }; - for (U64 list_idx = 0; list_idx < ArrayCount(list_arr); ++list_idx) { - for (LNK_SectionNode *sect_node = list_arr[list_idx]->first; sect_node != NULL; sect_node = sect_node->next) { - LNK_Section *sect = §_node->data; - Assert(sect->id < sectab->id_max); - Assert(map[sect->id] == NULL); - map[sect->id] = sect; - } - } - if (map[0] == NULL) { - LNK_Section *sect = push_array(arena, LNK_Section, 1); - sect->layout.chunk_off_array = push_array(arena, U64, 1); - sect->layout.chunk_file_size_array = push_array(arena, U64, 1); - sect->layout.chunk_virt_size_array = push_array(arena, U64, 1); - map[0] = sect; - } - ProfEnd(); - return map; -} - -internal LNK_ChunkRef -lnk_get_final_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = chunk_ref; - if (sect_id_map[chunk_ref.sect_id]->is_merged) { - final_chunk_ref.sect_id = sect_id_map[chunk_ref.sect_id]->merge_sect_id; - final_chunk_ref.chunk_id = sect_id_map[chunk_ref.sect_id]->id_map[chunk_ref.chunk_id]; - // we don't support sections that were merged more than once. - Assert(!sect_id_map[final_chunk_ref.sect_id]->is_merged); - } - return final_chunk_ref; -} - -internal LNK_Section * -lnk_sect_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef input_chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, input_chunk_ref); - LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id]; - return sect; -} - -internal LNK_Chunk * -lnk_chunk_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkPtr **chunk_id_map, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref); - LNK_Chunk *chunk = chunk_id_map[final_chunk_ref.sect_id][final_chunk_ref.chunk_id]; - return chunk; -} - -internal U64 -lnk_isect_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref) -{ - LNK_Section *sect = lnk_sect_from_chunk_ref(sect_id_map, chunk_ref); - U64 isect = sect->isect; - return isect; -} - -internal U64 -lnk_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref); - LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id]; - U64 off = sect->layout.chunk_off_array[final_chunk_ref.chunk_id]; - return off; -} - -internal U64 -lnk_virt_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref); - LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id]; - U64 off = sect->layout.chunk_off_array[final_chunk_ref.chunk_id]; - U64 virt_off = off + sect->virt_off; - return virt_off; -} - -internal U64 -lnk_file_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref); - LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id]; - U64 off = sect->layout.chunk_off_array[final_chunk_ref.chunk_id]; - U64 file_off = off + sect->file_off; - return file_off; -} - -internal U64 -lnk_virt_size_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref); - LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id]; - U64 virt_size = sect->layout.chunk_virt_size_array[final_chunk_ref.chunk_id]; - return virt_size; -} - -internal U64 -lnk_file_size_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref); - LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id]; - U64 file_size = sect->layout.chunk_file_size_array[final_chunk_ref.chunk_id]; - return file_size; -} - -internal String8 -lnk_data_from_chunk_ref(LNK_Section **sect_id_map, String8 image_data, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref); - LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id]; - String8 chunk_data; - if (sect->has_layout) { - U64 chunk_size = lnk_file_size_from_chunk_ref(sect_id_map, chunk_ref); - U64 chunk_foff = lnk_file_off_from_chunk_ref(sect_id_map, chunk_ref); - chunk_data = str8_substr(image_data, r1u64(chunk_foff, chunk_foff + chunk_size)); - } else { - LNK_Chunk *chunk = sect->layout.chunk_ptr_array[final_chunk_ref.chunk_id]; - Assert(chunk->type == LNK_Chunk_Leaf); - chunk_data = chunk->u.leaf; - } - - return chunk_data; -} - -internal String8 -lnk_data_from_chunk_ref_no_pad(LNK_Section **sect_id_map, String8 image_data, LNK_ChunkRef chunk_ref) -{ - LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref); - LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id]; - - String8 chunk_data; - if (sect->has_layout) { - U64 chunk_size = lnk_virt_size_from_chunk_ref(sect_id_map, chunk_ref); - U64 chunk_foff = lnk_file_off_from_chunk_ref(sect_id_map, chunk_ref); - chunk_data = str8_substr(image_data, r1u64(chunk_foff, chunk_foff + chunk_size)); - } else { - LNK_Chunk *chunk = sect->layout.chunk_ptr_array[final_chunk_ref.chunk_id]; - Assert(chunk->type == LNK_Chunk_Leaf); - chunk_data = chunk->u.leaf; - } - - return chunk_data; -} - -internal ISectOff -lnk_sc_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref) -{ - ISectOff sc = {0}; - sc.isect = lnk_isect_from_chunk_ref(sect_id_map, chunk_ref); - sc.off = lnk_off_from_chunk_ref(sect_id_map, chunk_ref); - return sc; -} - -internal U64 -lnk_virt_off_from_reloc(LNK_Section **sect_id_map, LNK_Reloc *reloc) -{ - U64 virt_off = lnk_virt_off_from_chunk_ref(sect_id_map, reloc->chunk->ref); - virt_off += reloc->apply_off; - return virt_off; -} - -internal U64 -lnk_isect_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol) -{ - Assert(LNK_Symbol_IsDefined(symbol->type)); - LNK_ChunkRef symbol_chunk_ref = symbol->u.defined.u.chunk->ref; - U64 symbol_isect = lnk_isect_from_chunk_ref(sect_id_map, symbol_chunk_ref); - return symbol_isect; -} - -internal U64 -lnk_sect_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol) -{ - Assert(LNK_Symbol_IsDefined(symbol->type)); - LNK_ChunkRef symbol_chunk_ref = symbol->u.defined.u.chunk->ref; - U64 chunk_off = lnk_off_from_chunk_ref(sect_id_map, symbol_chunk_ref); - U64 symbol_off = chunk_off + symbol->u.defined.u.chunk_offset; - return symbol_off; -} - -internal U64 -lnk_virt_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol) -{ - Assert(LNK_Symbol_IsDefined(symbol->type)); - LNK_ChunkRef symbol_chunk_ref = symbol->u.defined.u.chunk->ref; - U64 chunk_voff = lnk_virt_off_from_chunk_ref(sect_id_map, symbol_chunk_ref); - U64 symbol_voff = chunk_voff + symbol->u.defined.u.chunk_offset; - return symbol_voff; -} - -internal U64 -lnk_file_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol) -{ - Assert(LNK_Symbol_IsDefined(symbol->type)); - LNK_ChunkRef symbol_chunk_ref = symbol->u.defined.u.chunk->ref; - U64 chunk_foff = lnk_file_off_from_chunk_ref(sect_id_map, symbol_chunk_ref); - U64 symbol_foff = chunk_foff + symbol->u.defined.u.chunk_offset; - return symbol_foff; -} - -internal U64 -lnk_virt_size_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol) -{ - Assert(LNK_Symbol_IsDefined(symbol->type)); - U64 symbol_chunk_virt_size = lnk_virt_size_from_chunk_ref(sect_id_map, symbol->u.defined.u.chunk->ref); - return symbol_chunk_virt_size; -} - -internal U64 -lnk_file_size_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol) -{ - Assert(LNK_Symbol_IsDefined(symbol->type)); - U64 symbol_chunk_file_size = lnk_file_size_from_chunk_ref(sect_id_map, symbol->u.defined.u.chunk->ref); - return symbol_chunk_file_size; -} - -#if LNK_DEBUG_CHUNKS internal void -lnk_dump_chunks(LNK_SectionTable *sectab) +lnk_assign_section_file_space(LNK_Section *sect, U64 *foff_cursor) { - Temp scratch = scratch_begin(0, 0); - LNK_ChunkPtr **chunk_id_map = lnk_chunk_id_map_from_section_table(scratch.arena, sectab); - LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, sectab); - for (U64 sect_id = 0; sect_id < sectab->id_max; ++sect_id) { - LNK_Section *sect = sect_id_map[sect_id]; - if (!sect) continue; - if (sect->is_merged) continue; - if (str8_match_lit(".text", sect->name, 0)) { - for (U64 chunk_id = 0; chunk_id < sect->cman->total_chunk_count; ++chunk_id) { - LNK_ChunkRef chunk_ref = { sect_id, chunk_id }; - LNK_Chunk *chunk = lnk_chunk_from_chunk_ref(sect_id_map, chunk_id_map, chunk_ref); - U64 chunk_foff = sect->file_off + sect->layout.chunk_off_array[chunk_id]; - printf("%llu {%04llX,%04llX} 0x%08llX %.*s\n", chunk_foff, sect_id, chunk_id, chunk_foff, str8_varg(chunk->debug)); - } - } + if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { + sect->foff = *foff_cursor; + *foff_cursor += sect->fsize; } - scratch_end(scratch); } -#endif diff --git a/src/linker/lnk_section_table.h b/src/linker/lnk_section_table.h index 770b934b..8884e5d0 100644 --- a/src/linker/lnk_section_table.h +++ b/src/linker/lnk_section_table.h @@ -1,43 +1,84 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once +typedef struct LNK_SectionContrib +{ + U16 align; + union { + String8Node *data_list; + U64 bss_size; + }; + union { + struct { + U16 sort_idx_size; + U32 obj_idx; + U8 *sort_idx; + }; + struct { + U16 sect_idx; + U32 off; + U32 size; + }; + } u; +} LNK_SectionContrib; + +typedef struct LNK_CommonBlockContrib +{ + struct LNK_Symbol *symbol; + union { + U32 size; + U32 offset; + } u; +} LNK_CommonBlockContrib; + +typedef struct LNK_SectionContribChunk +{ + struct LNK_SectionContribChunk *next; + U64 count; + U64 cap; + LNK_SectionContrib *v; +} LNK_SectionContribChunk; + +typedef struct LNK_SectionContribChunkList +{ + U64 chunk_count; + LNK_SectionContribChunk *first; + LNK_SectionContribChunk *last; +} LNK_SectionContribChunkList; + +typedef struct LNK_SectionDefinition +{ + String8 name; + COFF_SectionFlags flags; + U64 contribs_count; + struct LNK_Obj *obj; + U64 obj_sect_idx; +} LNK_SectionDefinition; + typedef struct LNK_Section { - Arena *arena; - U64 id; - String8 name; - String8 symbol_name; - COFF_SectionFlags flags; - String8 sort_index; + Arena *arena; + U64 id; + String8 name; + COFF_SectionFlags flags; + B32 has_layout; + B32 is_merged; - LNK_ChunkManager *cman; - LNK_Chunk *root; + LNK_SectionContribChunkList contribs; - // overwhelming number of chunks don't have sort index and grouping - // them speeds up sort step - LNK_Chunk *nosort_chunk; - - LNK_RelocList reloc_list; - - B32 emit_header; // TODO: this is a hack to make reloc serializer work in resource converter - B32 has_layout; - B32 is_loose; - - B32 is_merged; - U64 merge_sect_id; - U64 *id_map; - - U64 isect; - U64 virt_off; - U64 file_off; - LNK_ChunkLayout layout; + U64 voff; + U64 vsize; + U64 fsize; + U64 foff; + U64 sect_idx; } LNK_Section; typedef struct LNK_SectionNode { struct LNK_SectionNode *next; + struct LNK_SectionNode *prev; LNK_Section data; } LNK_SectionNode; @@ -49,103 +90,38 @@ typedef struct LNK_SectionList } LNK_SectionList; typedef struct LNK_SectionArray -{ - U64 count; - LNK_Section *v; -} LNK_SectionArray; - -typedef struct LNK_SectionPtrArray { U64 count; LNK_Section **v; -} LNK_SectionPtrArray; +} LNK_SectionArray; typedef struct LNK_SectionTable { Arena *arena; - U64 section_virt_off; - U64 sect_align; - U64 file_align; U64 id_max; + U64 next_sect_idx; LNK_SectionList list; - LNK_SectionList merge_list; - LNK_SectionList empties_list; - LNK_SectionNode *null_sect; + LNK_SectionList free_list; + HashTable *sect_ht; // name -> LNK_Section * } LNK_SectionTable; //////////////////////////////// -typedef struct -{ - COFF_MachineType machine; - Rng1U64 *range_arr; - LNK_Section **sect_arr; -} LNK_SectionDataBuilder; +internal U8 lnk_code_align_byte_from_machine(COFF_MachineType machine); +internal U16 lnk_default_align_from_machine(COFF_MachineType machine); //////////////////////////////// -internal LNK_SectionNode * lnk_section_list_remove(LNK_SectionList *list, String8 name); -internal LNK_SectionNode * lnk_section_list_search_node(LNK_SectionList *list, String8 name); -internal LNK_Section * lnk_section_list_search(LNK_SectionList *list, String8 name); +internal void lnk_section_list_remove(LNK_SectionList *list, LNK_SectionNode *node); +internal LNK_SectionArray lnk_section_array_from_list(Arena *arena, LNK_SectionList list); -internal LNK_SectionArray lnk_section_array_from_list(Arena *arena, LNK_SectionList list); -internal LNK_SectionPtrArray lnk_section_ptr_array_from_list(Arena *arena, LNK_SectionList list); +//////////////////////////////// -internal void lnk_section_associate_chunks(LNK_Section *sect, LNK_Chunk *head, LNK_Chunk *associate); - -internal LNK_Reloc * lnk_section_push_reloc(LNK_Section *sect, LNK_Chunk *chunk, LNK_RelocType type, U64 apply_off, LNK_Symbol *symbol); -internal LNK_Reloc * lnk_section_push_reloc_undefined(LNK_Section *sect, LNK_Chunk *chunk, LNK_RelocType type, U64 apply_off, String8 undefined_symbol_name, LNK_SymbolScopeFlags scope_flags); - -internal void lnk_section_merge(LNK_Section *dst, LNK_Section *src); -internal void lnk_section_build_data(LNK_Section *sect, COFF_MachineType machine); - -internal String8 lnk_make_section_sort_index(Arena *arena, String8 name, COFF_SectionFlags flags, U64 section_index); - -internal LNK_Chunk * lnk_section_push_chunk_raw(LNK_Section *sect, LNK_Chunk *parent, void *data_ptr, U64 data_size, String8 sort_index); -internal LNK_Chunk * lnk_section_push_chunk_data(LNK_Section *sect, LNK_Chunk *parent, String8 data, String8 sort_index); -internal LNK_Chunk * lnk_section_push_chunk_u32(LNK_Section *sect, LNK_Chunk *parent, U32 value, String8 sort_index); -internal LNK_Chunk * lnk_section_push_chunk_u64(LNK_Section *sect, LNK_Chunk *parent, U32 value, String8 sort_index); -internal LNK_Chunk * lnk_section_push_chunk_bss(LNK_Section *sect, LNK_Chunk *parent, U64 size, String8 sort_index); -internal LNK_Chunk * lnk_section_push_chunk_list(LNK_Section *sect, LNK_Chunk *parent, String8 sort_index); - -internal LNK_SectionTable * lnk_section_table_alloc(U64 section_virt_off, U64 sect_align, U64 file_align); +internal LNK_SectionTable * lnk_section_table_alloc(void); internal void lnk_section_table_release(LNK_SectionTable **st_ptr); internal LNK_Section * lnk_section_table_push(LNK_SectionTable *sectab, String8 name, COFF_SectionFlags flags); -internal LNK_Section * lnk_section_table_push_null(LNK_SectionTable *sectab); -internal void lnk_section_table_remove(LNK_SectionTable *sectab, LNK_SymbolTable *symtab, String8 name); -internal LNK_Section * lnk_section_table_search(LNK_SectionTable *sectab, String8 name); -internal LNK_Section * lnk_section_table_search_id(LNK_SectionTable *sectab, U64 id); +internal void lnk_section_table_remove(LNK_SectionTable *sectab, String8 name); +internal LNK_Section * lnk_section_table_search(LNK_SectionTable *sectab, String8 name, COFF_SectionFlags flags); internal void lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_list); -internal void lnk_section_table_remove_empties(LNK_SectionTable *sectab, LNK_SymbolTable *symtab); -internal void lnk_section_table_build_data(TP_Context *tp, LNK_SectionTable *sectab, COFF_MachineType machine); -internal void lnk_section_table_assign_virtual_offsets(LNK_SectionTable *sectab); -internal void lnk_section_table_assign_file_offsets(LNK_SectionTable *sectab); -internal void lnk_section_table_assign_indices(LNK_SectionTable *sectab); -internal String8 lnk_section_table_serialize(TP_Context *tp, Arena *arena, LNK_SectionTable *sectab, COFF_MachineType machine); - -internal LNK_ChunkPtr ** lnk_chunk_id_map_from_section_table(Arena *arena, LNK_SectionTable *sectab); -internal LNK_Section ** lnk_sect_id_map_from_section_table(Arena *arena, LNK_SectionTable *sectab); -internal LNK_ChunkRef lnk_get_final_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal LNK_Section * lnk_sect_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal LNK_Chunk * lnk_chunk_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkPtr **chunk_id_map, LNK_ChunkRef chunk_ref); -internal U64 lnk_isect_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal U64 lnk_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal U64 lnk_virt_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal U64 lnk_file_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal U64 lnk_virt_size_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal U64 lnk_file_size_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal String8 lnk_data_from_chunk_ref(LNK_Section **sect_id_map, String8 image_data, LNK_ChunkRef chunk_ref); -internal String8 lnk_data_from_chunk_ref_no_pad(LNK_Section **sect_id_map, String8 image_data, LNK_ChunkRef chunk_ref); -internal ISectOff lnk_sc_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref); -internal U64 lnk_virt_off_from_reloc(LNK_Section **sect_id_map, LNK_Reloc *reloc); -internal U64 lnk_isect_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol); -internal U64 lnk_sect_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol); -internal U64 lnk_virt_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol); -internal U64 lnk_file_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol); -internal U64 lnk_virt_size_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol); -internal U64 lnk_file_size_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol); - -#if LNK_DEBUG_CHUNKS -internal void lnk_dump_chunks(LNK_SectionTable *sectab); -#endif +internal LNK_SectionArray lnk_section_table_get_output_sections(Arena *arena, LNK_SectionTable *sectab); diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index ef027153..1e09f419 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -1,128 +1,40 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal void -lnk_init_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolType type) -{ - symbol->name = name; - symbol->type = type; -} - -internal void -lnk_init_defined_symbol(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags) -{ - switch (visibility) { - case LNK_DefinedSymbolVisibility_Static: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedStatic); break; - case LNK_DefinedSymbolVisibility_Extern: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedExtern); break; - case LNK_DefinedSymbolVisibility_Internal: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedInternal); break; - } - LNK_DefinedSymbol *def = &symbol->u.defined; - def->flags = flags; - def->value_type = LNK_DefinedSymbolValue_Null; -} - -internal void -lnk_init_defined_symbol_chunk(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum) -{ - lnk_init_defined_symbol(symbol, name, visibility, flags); - LNK_DefinedSymbol *def = &symbol->u.defined; - def->value_type = LNK_DefinedSymbolValue_Chunk; - def->u.chunk = chunk; - def->u.chunk_offset = offset; - def->u.check_sum = check_sum; - def->u.selection = selection; -} - -internal void -lnk_init_defined_symbol_va(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va) -{ - lnk_init_defined_symbol(symbol, name, visibility, flags); - LNK_DefinedSymbol *def = &symbol->u.defined; - def->value_type = LNK_DefinedSymbolValue_VA; - def->u.va = va; -} - -internal void -lnk_init_undefined_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolScopeFlags scope_flags) -{ - lnk_init_symbol(symbol, name, LNK_Symbol_Undefined); - symbol->u.undefined.scope_flags = scope_flags; -} - -internal void -lnk_init_weak_symbol(LNK_Symbol *symbol, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback) -{ - lnk_init_symbol(symbol, name, LNK_Symbol_Weak); - symbol->u.weak.scope_flags = LNK_SymbolScopeFlag_Defined; - symbol->u.weak.lookup_type = lookup; - symbol->u.weak.fallback_symbol = fallback; -} - -internal void -lnk_init_lazy_symbol(LNK_Symbol *symbol, String8 name, LNK_Lib *lib, U64 member_offset) -{ - lnk_init_symbol(symbol, name, LNK_Symbol_Lazy); - symbol->u.lazy.lib = lib; - symbol->u.lazy.member_offset = member_offset; -} +//////////////////////////////// internal LNK_Symbol * -lnk_make_defined_symbol(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags) +lnk_make_defined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx) { - LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1); - lnk_init_defined_symbol(symbol, name, visibility, flags); - return symbol; -} - -internal LNK_Symbol * -lnk_make_defined_symbol_chunk(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum) -{ - LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1); - lnk_init_defined_symbol_chunk(symbol, name, visibility, flags, chunk, offset, selection, check_sum); + LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1); + symbol->name = name; + symbol->type = LNK_Symbol_Defined; + symbol->u.defined.obj = obj; + symbol->u.defined.symbol_idx = symbol_idx; return symbol; } internal LNK_Symbol * -lnk_make_defined_symbol_va(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va) +lnk_make_lib_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset) { - LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1); - lnk_init_defined_symbol_va(symbol, name, visibility, flags, va); - return symbol; -} - -internal LNK_Symbol * -lnk_make_undefined_symbol(Arena *arena, String8 name, LNK_SymbolScopeFlags flags) -{ - LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1); - lnk_init_undefined_symbol(symbol, name, flags); + LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1); + symbol->name = name; + symbol->type = LNK_Symbol_Lib; + symbol->u.lib.lib = lib; + symbol->u.lib.member_offset = member_offset; return symbol; } internal LNK_Symbol * -lnk_make_weak_symbol(Arena *arena, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback) +lnk_make_undefined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj) { - LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1); - lnk_init_weak_symbol(symbol, name, lookup, fallback); + LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1); + symbol->name = name; + symbol->type = LNK_Symbol_Undefined; + symbol->u.undef.obj = obj; return symbol; } -internal LNK_Symbol * -lnk_make_lazy_symbol(Arena *arena, String8 name, LNK_Lib *lib, U64 member_offset) -{ - LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1); - lnk_init_lazy_symbol(symbol, name, lib, member_offset); - return symbol; -} - -internal LNK_Chunk * -lnk_chunk_from_symbol(LNK_Symbol *symbol) -{ - if (LNK_Symbol_IsDefined(symbol->type) && symbol->u.defined.value_type == LNK_DefinedSymbolValue_Chunk) { - return symbol->u.defined.u.chunk; - } - return 0; -} - //////////////////////////////// internal void @@ -147,24 +59,6 @@ lnk_symbol_list_concat_in_place(LNK_SymbolList *list, LNK_SymbolList *to_concat) SLLConcatInPlace(list, to_concat); } -internal LNK_SymbolNode * -lnk_symbol_list_search_node(LNK_SymbolList list, String8 name, StringMatchFlags flags) -{ - for (LNK_SymbolNode *node = list.first; node != 0; node = node->next) { - if (str8_match(node->data->name, name, flags)) { - return node; - } - } - return 0; -} - -internal LNK_Symbol * -lnk_symbol_list_search(LNK_SymbolList list, String8 name, StringMatchFlags flags) -{ - LNK_SymbolNode *node = lnk_symbol_list_search_node(list, name, flags); - return node ? node->data : 0; -} - internal LNK_SymbolList lnk_symbol_list_from_array(Arena *arena, LNK_SymbolArray arr) { @@ -205,6 +99,48 @@ lnk_symbol_array_from_list(Arena *arena, LNK_SymbolList list) //////////////////////////////// +internal ISectOff +lnk_sc_from_symbol(LNK_Symbol *symbol) +{ + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + + ISectOff sc = {0}; + sc.isect = parsed_symbol.section_number; + sc.off = parsed_symbol.value; + + return sc; +} + +internal U64 +lnk_isect_from_symbol(LNK_Symbol *symbol) +{ + return lnk_sc_from_symbol(symbol).isect; +} + +internal U64 +lnk_sect_off_from_symbol(LNK_Symbol *symbol) +{ + return lnk_sc_from_symbol(symbol).off; +} + +internal U64 +lnk_virt_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol) +{ + ISectOff sc = lnk_sc_from_symbol(symbol); + U64 voff = section_table[sc.isect]->voff + sc.off; + return voff; +} + +internal U64 +lnk_file_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol) +{ + ISectOff sc = lnk_sc_from_symbol(symbol); + U64 foff = section_table[sc.isect]->foff + sc.off; + return foff; +} + +//////////////////////////////// + internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_chunk_list_push(Arena *arena, LNK_SymbolHashTrieChunkList *list, U64 cap) { @@ -229,120 +165,148 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) B32 can_replace = 0; - // lazy vs lazy - if (dst->type == LNK_Symbol_Lazy && src->type == LNK_Symbol_Lazy) { + // lib vs lib + if (dst->type == LNK_Symbol_Lib && src->type == LNK_Symbol_Lib) { // link.exe picks symbol from lib that is discovered first - LNK_Lib *dst_lib = dst->u.lazy.lib; - LNK_Lib *src_lib = src->u.lazy.lib; - can_replace = dst_lib->input_idx > src_lib->input_idx; - } - // lazy vs weak - else if (dst->type == LNK_Symbol_Lazy && (LNK_Symbol_IsDefined(src->type) || src->type == LNK_Symbol_Weak)) { - can_replace = 1; - } - // weak vs strong - else if (dst->type == LNK_Symbol_Weak && LNK_Symbol_IsDefined(src->type)) { - can_replace = 1; - } - // weak vs weak - else if (dst->type == LNK_Symbol_Weak && src->type == LNK_Symbol_Weak) { - B32 is_fallback_same = str8_match(dst->u.weak.fallback_symbol->name, src->u.weak.fallback_symbol->name, 0); - if (is_fallback_same) { - if (src->obj && !dst->obj) { - can_replace = 1; - } else if (src->obj && dst->obj) { - can_replace = src->obj->input_idx < dst->obj->input_idx; - } - } else { - lnk_error(LNK_Error_MultiplyDefinedSymbol, "multiply defined weak symbol %S, symbol defined in:", src->name); - lnk_supplement_error("%S", dst->obj->path); - lnk_supplement_error("%S", src->obj->path); - } + can_replace = src->u.lib.lib->input_idx < dst->u.lib.lib->input_idx; } else if (dst->type == LNK_Symbol_Import) { + AssertAlways(src->type != LNK_Symbol_Import); can_replace = 1; } - // defined VA vs defined chunk - else if (LNK_Symbol_IsDefined(dst->type) && dst->u.defined.value_type == LNK_DefinedSymbolValue_VA && - LNK_Symbol_IsDefined(src->type)) { - can_replace = 1; - } - // defined chunk vs defined chunk - else if (LNK_Symbol_IsDefined(dst->type) && dst->u.defined.value_type == LNK_DefinedSymbolValue_Chunk && - LNK_Symbol_IsDefined(src->type) && src->u.defined.value_type == LNK_DefinedSymbolValue_Chunk) { - LNK_DefinedSymbol *dst_defn = &dst->u.defined; - LNK_DefinedSymbol *src_defn = &src->u.defined; + // defined vs defined + else if (dst->type == LNK_Symbol_Defined && src->type == LNK_Symbol_Defined) { + LNK_Obj *dst_obj = dst->u.defined.obj; + LNK_Obj *src_obj = src->u.defined.obj; - Assert(dst_defn->u.chunk->is_discarded == 0); - Assert(dst_defn->u.chunk->type == LNK_Chunk_Leaf); - Assert(src_defn->u.chunk->type == LNK_Chunk_Leaf); + COFF_ParsedSymbol dst_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dst->u.defined.obj, dst->u.defined.symbol_idx); + COFF_ParsedSymbol src_parsed = lnk_parsed_symbol_from_coff_symbol_idx(src->u.defined.obj, src->u.defined.symbol_idx); - COFF_ComdatSelectType dst_select = dst_defn->u.selection; - COFF_ComdatSelectType src_select = src_defn->u.selection; + COFF_SymbolValueInterpType dst_interp = coff_interp_symbol(dst_parsed.section_number, dst_parsed.value, dst_parsed.storage_class); + COFF_SymbolValueInterpType src_interp = coff_interp_symbol(src_parsed.section_number, src_parsed.value, src_parsed.storage_class); - // handle objs compiled with /GR- and /GR - if ((src_select == COFF_ComdatSelect_Any && dst_select == COFF_ComdatSelect_Largest) || - (src_select == COFF_ComdatSelect_Largest && dst_select == COFF_ComdatSelect_Any)) { - dst_select = COFF_ComdatSelect_Largest; - src_select = COFF_ComdatSelect_Largest; + if (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs) { + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx); } + // abs vs regular + else if ((dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Regular) || + (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs)) { + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx); + } + // abs vs common + else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Common) { + if (dst->u.defined.obj->input_idx < src->u.defined.obj->input_idx) { + can_replace = 1; + } else { + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx); + } + } + // common vs abs + else if (dst_interp == COFF_SymbolValueInterp_Common && src_interp == COFF_SymbolValueInterp_Abs) { + if (dst->u.defined.obj->input_idx < src->u.defined.obj->input_idx) { + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx); + } + } + // weak vs weak + else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Weak) { + } + // weak vs abs + else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Abs) { + can_replace = 1; + } + // abs vs weak + else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Weak) { + can_replace = 0; + } + // weak vs regular,common,abs + else if (dst_interp == COFF_SymbolValueInterp_Weak && + (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common || src_interp == COFF_SymbolValueInterp_Abs)) { + can_replace = 1; + } + // regular,common vs regular,common + else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Common) && + (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common)) { + COFF_ComdatSelectType dst_select; + U32 dst_section_length; + U32 dst_check_sum; + if (dst_interp == COFF_SymbolValueInterp_Regular) { + coff_parse_secdef(dst_parsed, dst_obj->header.is_big_obj, &dst_select, 0, &dst_section_length, &dst_check_sum); + } else { + dst_select = COFF_ComdatSelect_Largest; + dst_section_length = dst_parsed.value; + dst_check_sum = 0; + } - if (src_select == dst_select) { - LNK_Chunk *dst_chunk = dst_defn->u.chunk; - LNK_Chunk *src_chunk = src_defn->u.chunk; - U64 dst_chunk_size = lnk_chunk_get_size(dst_chunk); - U64 src_chunk_size = lnk_chunk_get_size(src_chunk); + COFF_ComdatSelectType src_select; + U32 src_section_length; + U32 src_check_sum; + if (src_interp == COFF_SymbolValueInterp_Regular) { + coff_parse_secdef(src_parsed, src_obj->header.is_big_obj, &src_select, 0, &src_section_length, &src_check_sum); + } else { + src_select = COFF_ComdatSelect_Largest; + src_section_length = src_parsed.value; + src_check_sum = 0; + } - switch (src_select) { - case COFF_ComdatSelect_Null: - case COFF_ComdatSelect_Any: { - if (src_chunk_size == dst_chunk_size) { - can_replace = src_chunk->input_idx < dst_chunk->input_idx; - } else { - // both COMDATs are valid but to get smaller exe pick smallest - can_replace = src_chunk_size < dst_chunk_size; - } - } break; - case COFF_ComdatSelect_NoDuplicates: { - lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path); - } break; - case COFF_ComdatSelect_SameSize: { - if (dst_chunk_size != src_chunk_size) { - lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path); - } - } break; - case COFF_ComdatSelect_ExactMatch: { - if (dst_defn->u.check_sum != src_defn->u.check_sum) { - lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path); - } - } break; - case COFF_ComdatSelect_Largest: { - if (dst_chunk_size == src_chunk_size) { - if (dst_defn->u.chunk->u.leaf.str == 0 && src_defn->u.chunk->u.leaf.size > 0) { - // handle communal variable - // - // MSVC CRT relies on this behaviour (e.g. __scrt_ucrt_dll_is_in_use in ucrt_detection.c) - can_replace = 1; + // handle objs compiled with /GR- and /GR + if ((src_select == COFF_ComdatSelect_Any && dst_select == COFF_ComdatSelect_Largest) || + (src_select == COFF_ComdatSelect_Largest && dst_select == COFF_ComdatSelect_Any)) { + dst_select = COFF_ComdatSelect_Largest; + src_select = COFF_ComdatSelect_Largest; + } + + if (src_select == dst_select) { + switch (src_select) { + case COFF_ComdatSelect_Null: + case COFF_ComdatSelect_Any: { + if (src_section_length == dst_section_length) { + can_replace = src_obj->input_idx < dst_obj->input_idx; } else { - can_replace = src_chunk->input_idx < dst_chunk->input_idx; + // both COMDATs are valid but to get smaller exe pick smallest + can_replace = src_section_length < dst_section_length; } - } else { - can_replace = dst_chunk_size < src_chunk_size; + } break; + case COFF_ComdatSelect_NoDuplicates: { + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src_obj, "multiply defined symbol %S in %S.", dst->name, dst_obj->path); + } break; + case COFF_ComdatSelect_SameSize: { + if (dst_section_length != src_section_length) { + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src_obj, "multiply defined symbol %S in %S.", dst->name, dst_obj->path); + } + } break; + case COFF_ComdatSelect_ExactMatch: { + if (dst_check_sum != src_check_sum) { + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src_obj, "multiply defined symbol %S in %S.", dst->name, dst_obj->path); + } + } break; + case COFF_ComdatSelect_Largest: { + if (dst_section_length == src_section_length) { + if (dst_interp == COFF_SymbolValueInterp_Common) { + // handle communal variable + // + // MSVC CRT relies on this behaviour (e.g. __scrt_ucrt_dll_is_in_use in ucrt_detection.c) + can_replace = 1; + } else { + can_replace = src_obj->input_idx < dst_obj->input_idx; + } + } else { + can_replace = dst_section_length < src_section_length; + } + } break; + case COFF_ComdatSelect_Associative: { + // ignore + } break; + default: { InvalidPath; } } - } break; - case COFF_ComdatSelect_Associative: { - // ignore - } break; - default: { - lnk_error_obj(LNK_Error_InvalidPath, src->obj, "unknown COMDAT selection %#x", src->obj, src_select); - } break; + } else { + String8 src_select_str = coff_string_from_comdat_select_type(src_select); + String8 dst_select_str = coff_string_from_comdat_select_type(dst_select); + lnk_error_obj(LNK_Warning_UnresolvedComdat, src_obj, + "%S: COMDAT selection conflict detected, current selection %S, leader selection %S from %S", + src->name, src_select_str, dst_select_str, dst_obj); } } else { - String8 src_select_str = coff_string_from_comdat_select_type(src_defn->u.selection); - String8 dst_select_str = coff_string_from_comdat_select_type(dst_defn->u.selection); - lnk_error_obj(LNK_Warning_UnresolvedComdat, src->obj, - "%S: COMDAT selection conflict detected, current selection %S, leader selection %S from %S", - src->name, src_select_str, dst_select_str, dst->obj->path); + lnk_error(LNK_Error_InvalidPath, "unable to find a suitable replacement logic for symbol combination"); } } else { lnk_error(LNK_Error_InvalidPath, "unable to find a suitable replacement logic for symbol combination"); @@ -355,33 +319,12 @@ internal void lnk_on_symbol_replace(LNK_Symbol *dst, LNK_Symbol *src) { Assert(dst != src); - - if (dst->type == LNK_Symbol_Lazy && src->type == LNK_Symbol_Lazy) { - dst->u.lazy = src->u.lazy; - } else if (LNK_Symbol_IsDefined(dst->type)) { - LNK_DefinedSymbol *dst_defined = &dst->u.defined; - - if (dst_defined->value_type == LNK_DefinedSymbolValue_Chunk) { - // discard chunk from output - dst_defined->u.chunk->is_discarded = 1; - - if (LNK_Symbol_IsDefined(src->type)) { - LNK_DefinedSymbol *src_defined = &src->u.defined; - - if (src_defined->value_type == LNK_DefinedSymbolValue_Chunk) { - // static symbols that are not part of obj's symbol table might point to discarded chunk - dst_defined->u.chunk->ref = src_defined->u.chunk->ref; - - // copy offset because after folding COMDATS we might end - // up with larger sized chunk and, for instance, a vftable - // might have a function pointer preceeding lead symbol - dst_defined->u.chunk = src_defined->u.chunk; - dst_defined->u.chunk_offset = src_defined->u.chunk_offset; - } - } else { - InvalidPath; - } - } + if (dst->type == LNK_Symbol_Lib && src->type == LNK_Symbol_Lib) { + dst->u.lib = src->u.lib; + } else if (dst->type == LNK_Symbol_Defined && src->type == LNK_Symbol_Defined) { + dst->u.defined = src->u.defined; + } else { + InvalidPath; } } @@ -501,38 +444,28 @@ lnk_symbol_table_init(TP_Arena *arena) { LNK_SymbolTable *symtab = push_array(arena->v[0], LNK_SymbolTable, 1); symtab->arena = arena; - for (U64 i = 0; i < LNK_SymbolScopeIndex_Count; ++i) { + for (U64 i = 0; i < LNK_SymbolScope_Count; ++i) { symtab->chunk_lists[i] = push_array(arena->v[0], LNK_SymbolHashTrieChunkList, arena->count); } return symtab; } internal LNK_Symbol * -lnk_symbol_table_search_hash(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, U64 hash, String8 name) +lnk_symbol_table_search_hash(LNK_SymbolTable *symtab, LNK_SymbolScope scope, U64 hash, String8 name) { - LNK_Symbol *result = 0; - while (scope_flags) { - LNK_SymbolScopeIndex scope_idx = ctz64(scope_flags); - scope_flags &= scope_flags - 1; - - LNK_SymbolHashTrie *match = lnk_symbol_hash_trie_search(symtab->scopes[scope_idx], hash, name); - if (match) { - result = match->symbol; - break; - } - } - return result; + LNK_SymbolHashTrie *trie = lnk_symbol_hash_trie_search(symtab->scopes[scope], hash, name); + return trie ? trie->symbol : 0; } internal LNK_Symbol * -lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope, String8 name) +lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name) { U64 hash = lnk_symbol_hash(name); return lnk_symbol_table_search_hash(symtab, scope, hash, name); } internal LNK_Symbol * -lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, char *fmt, ...) +lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *fmt, ...) { Temp scratch = scratch_begin(0, 0); @@ -541,16 +474,16 @@ lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_fla String8 name = push_str8fv(scratch.arena, fmt, args); va_end(args); - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, scope_flags, name); + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, scope, name); scratch_end(scratch); return symbol; } internal void -lnk_symbol_table_push_(LNK_SymbolTable *symtab, Arena *arena, LNK_SymbolHashTrieChunkList *chunk_list, LNK_SymbolScopeIndex scope_idx, U64 hash, LNK_Symbol *symbol) +lnk_symbol_table_push_(LNK_SymbolTable *symtab, Arena *arena, U64 worker_id, LNK_SymbolScope scope, U64 hash, LNK_Symbol *symbol) { - lnk_symbol_hash_trie_insert_or_replace(arena, chunk_list, &symtab->scopes[scope_idx], hash, symbol); + lnk_symbol_hash_trie_insert_or_replace(arena, &symtab->chunk_lists[scope][worker_id], &symtab->scopes[scope], hash, symbol); } internal void @@ -558,29 +491,10 @@ lnk_symbol_table_push_hash(LNK_SymbolTable *symtab, U64 hash, LNK_Symbol *symbol { switch (symbol->type) { case LNK_Symbol_Null: break; - - case LNK_Symbol_Import: - case LNK_Symbol_DefinedExtern: { - lnk_symbol_table_push_(symtab, symtab->arena->v[0], &symtab->chunk_lists[LNK_SymbolScopeIndex_Defined][0], LNK_SymbolScopeIndex_Defined, hash, symbol); - } break; - - case LNK_Symbol_DefinedInternal: { - lnk_symbol_table_push_(symtab, symtab->arena->v[0], &symtab->chunk_lists[LNK_SymbolScopeIndex_Internal][0], LNK_SymbolScopeIndex_Internal, hash, symbol); - } break; - - case LNK_Symbol_Weak: { - lnk_symbol_table_push_(symtab, symtab->arena->v[0], &symtab->chunk_lists[LNK_SymbolScopeIndex_Weak][0], LNK_SymbolScopeIndex_Weak, hash, symbol); - } break; - - case LNK_Symbol_Lazy: { - lnk_symbol_table_push_(symtab, symtab->arena->v[0], &symtab->chunk_lists[LNK_SymbolScopeIndex_Lib][0], LNK_SymbolScopeIndex_Lib, hash, symbol); - } break; - - // symbols not supported - case LNK_Symbol_Undefined: - case LNK_Symbol_DefinedStatic: { - InvalidPath; - } break; + case LNK_Symbol_Defined: + case LNK_Symbol_Import: { lnk_symbol_table_push_(symtab, symtab->arena->v[0], 0, LNK_SymbolScope_Defined, hash, symbol); } break; + case LNK_Symbol_Lib: { lnk_symbol_table_push_(symtab, symtab->arena->v[0], 0, LNK_SymbolScope_Lib, hash, symbol); } break; + default: { InvalidPath; } break; } } @@ -592,7 +506,7 @@ lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_Symbol *symbol) } internal void -lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope, String8 name) +lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name) { U64 hash = lnk_symbol_hash(name); LNK_SymbolHashTrie *trie = lnk_symbol_hash_trie_search(symtab->scopes[scope], hash, name); @@ -601,141 +515,4 @@ lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope, Str } } -internal LNK_Symbol * -lnk_symbol_table_push_defined_chunk(LNK_SymbolTable *symtab, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum) -{ - LNK_Symbol *symbol = lnk_make_defined_symbol_chunk(symtab->arena->v[0], name, visibility, flags, chunk, offset, selection, check_sum); - lnk_symbol_table_push(symtab, symbol); - return symbol; -} -internal LNK_Symbol * -lnk_symbol_table_push_defined(LNK_SymbolTable *symtab, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags) -{ - LNK_Symbol *symbol = lnk_make_defined_symbol(symtab->arena->v[0], name, visibility, flags); - lnk_symbol_table_push(symtab, symbol); - return symbol; -} - -internal LNK_Symbol * -lnk_symbol_table_push_defined_va(LNK_SymbolTable *symtab, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va) -{ - LNK_Symbol *symbol = lnk_make_defined_symbol_va(symtab->arena->v[0], name, visibility, flags, va); - lnk_symbol_table_push(symtab, symbol); - return symbol; -} - -internal LNK_Symbol * -lnk_symbol_table_push_weak(LNK_SymbolTable *symtab, String8 weak_name, COFF_WeakExtType lookup, String8 strong_name) -{ - weak_name = push_str8_copy(symtab->arena->v[0], weak_name); - strong_name = push_str8_copy(symtab->arena->v[0], strong_name); - LNK_Symbol *strong_symbol = lnk_make_undefined_symbol(symtab->arena->v[0], strong_name, LNK_SymbolScopeFlag_Main); - LNK_Symbol *weak_symbol = lnk_make_weak_symbol(symtab->arena->v[0], weak_name, COFF_WeakExt_SearchAlias, strong_symbol); - lnk_symbol_table_push(symtab, weak_symbol); - return weak_symbol; -} - -internal LNK_Symbol * -lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_Symbol *resolve_symbol) -{ - LNK_Symbol *symbol = resolve_symbol; - B32 run_resolver; - do { - run_resolver = 0; - switch (symbol->type) { - case LNK_Symbol_Null: break; - case LNK_Symbol_Undefined: { - LNK_UndefinedSymbol *undef_symbol = &symbol->u.undefined; - LNK_Symbol *def = lnk_symbol_table_search(symtab, undef_symbol->scope_flags, symbol->name); - if (def) { - symbol = def; - run_resolver = 1; - } - } break; - case LNK_Symbol_Weak: { - LNK_WeakSymbol *weak = &symbol->u.weak; - LNK_Symbol *def = lnk_symbol_table_search(symtab, weak->scope_flags, symbol->name); - if (def) { - Assert(LNK_Symbol_IsDefined(def->type)); - symbol = def; - } else { - symbol = symbol->u.weak.fallback_symbol; - } - run_resolver = 1; - } break; - case LNK_Symbol_DefinedExtern: { - // search for defined symbol because we don't update symbol pointers in relocations - // whenver we replace them in the symbol table - symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Defined, symbol->name); - Assert(symbol); - } break; - case LNK_Symbol_DefinedStatic: - case LNK_Symbol_DefinedInternal: { - // symbol resolved - } break; - default: NotImplemented; - } - } while (run_resolver); - return symbol; -} - -#if 0 - -internal void -lnk_symbol_hash_trie_debug(LNK_SymbolHashTrie *root) -{ - Temp scratch = scratch_begin(0,0); - - struct Stack { - struct Stack *next; - U64 i; - LNK_SymbolHashTrie *trie; - }; - - struct Stack *stack = push_array(scratch.arena, struct Stack, 1); - stack->i = 0; - stack->trie = root; - - U64 cur_depth = 1; - U64 max_depth = 0; - - char *dashes = "--------------------------------"; - - FILE *f = fopen("trie.txt", "w"); - - while (stack) { - for (; stack->i < ArrayCount(stack->trie->child); ++stack->i) { - - if (stack->i == 0 && stack->trie->symbol) { - fprintf(f, "%.*s%.*s\n", (int)cur_depth, dashes, str8_varg(stack->trie->symbol->name)); - } - - if (stack->trie->child[stack->i] != 0) { - struct Stack *frame = push_array(scratch.arena, struct Stack, 1); - frame->i = 0; - frame->trie = stack->trie->child[stack->i]; - - stack->i += 1; - SLLStackPush(stack, frame); - - cur_depth += 1; - max_depth = Max(cur_depth, max_depth); - - break; - } - } - - if (stack->i >= ArrayCount(stack->trie->child)) { - cur_depth -= 1; - SLLStackPop(stack); - } - } - - fprintf(f, "Max Depth: %llu\n", max_depth); - fclose(f); - - scratch_end(scratch); -} - -#endif diff --git a/src/linker/lnk_symbol_table.h b/src/linker/lnk_symbol_table.h index a6be9f8a..a12d3a91 100644 --- a/src/linker/lnk_symbol_table.h +++ b/src/linker/lnk_symbol_table.h @@ -1,109 +1,49 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once -typedef enum -{ - LNK_SymbolScopeIndex_Defined, - LNK_SymbolScopeIndex_Internal, // symbols defined by linker - LNK_SymbolScopeIndex_Weak, - LNK_SymbolScopeIndex_Lib, - LNK_SymbolScopeIndex_Count -} LNK_SymbolScopeIndex; +//////////////////////////////// -enum -{ - LNK_SymbolScopeFlag_Defined = 1, - LNK_SymbolScopeFlag_Internal = 2, - LNK_SymbolScopeFlag_Weak = 4, - LNK_SymbolScopeFlag_Lib = 8, - - LNK_SymbolScopeFlag_Main = LNK_SymbolScopeFlag_Defined | LNK_SymbolScopeFlag_Weak, - LNK_SymbolScopeFlag_All = LNK_SymbolScopeFlag_Defined | LNK_SymbolScopeFlag_Weak | LNK_SymbolScopeFlag_Lib | LNK_SymbolScopeFlag_Internal -}; -typedef U64 LNK_SymbolScopeFlags; - -typedef enum -{ - LNK_DefinedSymbolVisibility_Static, - LNK_DefinedSymbolVisibility_Extern, - LNK_DefinedSymbolVisibility_Internal, -} LNK_DefinedSymbolVisibility; - -enum -{ - LNK_DefinedSymbolFlag_IsFunc = (1 << 0), - LNK_DefinedSymbolFlag_IsThunk = (1 << 1), -}; -typedef U64 LNK_DefinedSymbolFlags; - -typedef enum -{ - LNK_DefinedSymbolValue_Null, - LNK_DefinedSymbolValue_Chunk, - LNK_DefinedSymbolValue_VA -} LNK_DefinedSymbolValueType; - -typedef struct LNK_DefinedSymbol -{ - LNK_DefinedSymbolFlags flags; - LNK_DefinedSymbolValueType value_type; - union { - struct { - LNK_Chunk *chunk; - U64 chunk_offset; - U32 check_sum; - COFF_ComdatSelectType selection; - }; - U64 va; - } u; -} LNK_DefinedSymbol; - -typedef struct LNK_WeakSymbol -{ - LNK_SymbolScopeFlags scope_flags; - COFF_WeakExtType lookup_type; - struct LNK_Symbol *fallback_symbol; -} LNK_WeakSymbol; - -typedef struct LNK_UndefinedSymbol -{ - LNK_SymbolScopeFlags scope_flags; -} LNK_UndefinedSymbol; - -typedef struct LNK_LazySymbol -{ - struct LNK_Lib *lib; - U64 member_offset; -} LNK_LazySymbol; - -#define LNK_Symbol_IsDefined(type) ((type) == LNK_Symbol_DefinedStatic || (type) == LNK_Symbol_DefinedExtern || (type) == LNK_Symbol_DefinedInternal) typedef enum { LNK_Symbol_Null, - LNK_Symbol_DefinedStatic, - LNK_Symbol_DefinedExtern, - LNK_Symbol_DefinedInternal, - LNK_Symbol_Weak, - LNK_Symbol_Lazy, - LNK_Symbol_Undefined, + LNK_Symbol_Defined, LNK_Symbol_Import, + LNK_Symbol_Lib, + LNK_Symbol_Undefined, } LNK_SymbolType; +typedef struct LNK_SymbolDefined +{ + struct LNK_Obj *obj; + U32 symbol_idx; +} LNK_SymbolDefined; + +typedef struct LNK_SymbolLib +{ + struct LNK_Lib *lib; + U64 member_offset; +} LNK_SymbolLib; + +typedef struct LNK_SymbolUndefined +{ + struct LNK_Obj *obj; +} LNK_SymbolUndefined; + typedef struct LNK_Symbol { - String8 name; - LNK_SymbolType type; - struct LNK_Obj *obj; + String8 name; + LNK_SymbolType type; union { - LNK_DefinedSymbol defined; - LNK_WeakSymbol weak; - LNK_UndefinedSymbol undefined; - LNK_LazySymbol lazy; + LNK_SymbolDefined defined; + LNK_SymbolLib lib; + LNK_SymbolUndefined undef; } u; } LNK_Symbol; +//////////////////////////////// + typedef struct LNK_SymbolNode { struct LNK_SymbolNode *next; @@ -129,6 +69,15 @@ typedef struct LNK_SymbolArray LNK_Symbol *v; } LNK_SymbolArray; +//////////////////////////////// + +typedef enum +{ + LNK_SymbolScope_Defined, + LNK_SymbolScope_Lib, + LNK_SymbolScope_Count +} LNK_SymbolScope; + typedef struct LNK_SymbolHashTrie { String8 *name; @@ -154,8 +103,8 @@ typedef struct LNK_SymbolHashTrieChunkList typedef struct LNK_SymbolTable { TP_Arena *arena; - LNK_SymbolHashTrie *scopes[LNK_SymbolScopeIndex_Count]; - LNK_SymbolHashTrieChunkList *chunk_lists[LNK_SymbolScopeIndex_Count]; + LNK_SymbolHashTrie *scopes[LNK_SymbolScope_Count]; + LNK_SymbolHashTrieChunkList *chunk_lists[LNK_SymbolScope_Count]; } LNK_SymbolTable; //////////////////////////////// @@ -170,26 +119,14 @@ typedef struct //////////////////////////////// -global read_only LNK_Symbol g_null_symbol = { str8_lit_comp("NULL"), LNK_Symbol_DefinedStatic }; +global read_only LNK_Symbol g_null_symbol = { str8_lit_comp("NULL"), LNK_Symbol_Null }; global read_only LNK_Symbol *g_null_symbol_ptr = &g_null_symbol; //////////////////////////////// -internal void lnk_init_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolType type); -internal void lnk_init_defined_symbol(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags); -internal void lnk_init_defined_symbol_chunk(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum); -internal void lnk_init_defined_symbol_va(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va); -internal void lnk_init_undefined_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolScopeFlags scope_flags); -internal void lnk_init_weak_symbol(LNK_Symbol *symbol, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback); - -internal LNK_Symbol * lnk_make_defined_symbol(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags); -internal LNK_Symbol * lnk_make_defined_symbol_chunk(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum); -internal LNK_Symbol * lnk_make_defined_symbol_va(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va); -internal LNK_Symbol * lnk_make_undefined_symbol(Arena *arena, String8 name, LNK_SymbolScopeFlags scope_flags); -internal LNK_Symbol * lnk_make_weak_symbol(Arena *arena, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback); -internal LNK_Symbol * lnk_make_lazy_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset); - -internal LNK_Chunk * lnk_chunk_from_symbol(LNK_Symbol *symbol); +internal LNK_Symbol * lnk_make_defined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx); +internal LNK_Symbol * lnk_make_lib_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset); +internal LNK_Symbol * lnk_make_undefined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj); //////////////////////////////// @@ -202,6 +139,14 @@ internal LNK_SymbolArray lnk_symbol_array_from_list(Arena *arena, LNK_Symbol //////////////////////////////// +internal ISectOff lnk_sc_from_symbol (LNK_Symbol *symbol); +internal U64 lnk_isect_from_symbol (LNK_Symbol *symbol); +internal U64 lnk_sect_off_from_symbol(LNK_Symbol *symbol); +internal U64 lnk_virt_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol); +internal U64 lnk_file_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol); + +//////////////////////////////// + internal void lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunks, LNK_SymbolHashTrie **trie, U64 hash, LNK_Symbol *symbol); internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_search(LNK_SymbolHashTrie *trie, U64 hash, String8 name); internal void lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie); @@ -211,12 +156,10 @@ internal void lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *tr internal U64 lnk_symbol_hash(String8 string); internal LNK_SymbolTable * lnk_symbol_table_init(TP_Arena *arena); -internal LNK_Symbol * lnk_symbol_table_search_hash(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope, U64 hash, String8 name); -internal LNK_Symbol * lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope, String8 name); -internal LNK_Symbol * lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope, char *fmt, ...); +internal LNK_Symbol * lnk_symbol_table_search_hash(LNK_SymbolTable *symtab, LNK_SymbolScope scope, U64 hash, String8 name); +internal LNK_Symbol * lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name); +internal LNK_Symbol * lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *fmt, ...); internal void lnk_symbol_table_push_hash(LNK_SymbolTable *symtab, U64 hash, LNK_Symbol *symbol); internal void lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_Symbol *symbol); -internal void lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope, String8 name); - -internal LNK_Symbol * lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_Symbol *resolve_symbol); +internal void lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name); diff --git a/src/linker/lnk_timer.c b/src/linker/lnk_timer.c index 61872279..51460d58 100644 --- a/src/linker/lnk_timer.c +++ b/src/linker/lnk_timer.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) global LNK_Timer g_timers[LNK_Timer_Count]; diff --git a/src/linker/lnk_timer.h b/src/linker/lnk_timer.h index 41b81053..ade95ae2 100644 --- a/src/linker/lnk_timer.h +++ b/src/linker/lnk_timer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/pdb_ext/msf_builder.c b/src/linker/pdb_ext/msf_builder.c index 5ae0036d..5a1cf862 100644 --- a/src/linker/pdb_ext/msf_builder.c +++ b/src/linker/pdb_ext/msf_builder.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal U64 diff --git a/src/linker/pdb_ext/msf_builder.h b/src/linker/pdb_ext/msf_builder.h index c9bda071..a2d10322 100644 --- a/src/linker/pdb_ext/msf_builder.h +++ b/src/linker/pdb_ext/msf_builder.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/pdb_ext/pdb.c b/src/linker/pdb_ext/pdb.c index f0b3447c..3ddd666f 100644 --- a/src/linker/pdb_ext/pdb.c +++ b/src/linker/pdb_ext/pdb.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal U32 diff --git a/src/linker/pdb_ext/pdb.h b/src/linker/pdb_ext/pdb.h index 3101a935..6b31f4ee 100644 --- a/src/linker/pdb_ext/pdb.h +++ b/src/linker/pdb_ext/pdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/pdb_ext/pdb_builder.c b/src/linker/pdb_ext/pdb_builder.c index ffabb1c6..103a2b4c 100644 --- a/src/linker/pdb_ext/pdb_builder.c +++ b/src/linker/pdb_ext/pdb_builder.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// diff --git a/src/linker/pdb_ext/pdb_builder.h b/src/linker/pdb_ext/pdb_builder.h index 7042bf17..8ca8ed93 100644 --- a/src/linker/pdb_ext/pdb_builder.h +++ b/src/linker/pdb_ext/pdb_builder.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/pdb_ext/pdb_helpers.c b/src/linker/pdb_ext/pdb_helpers.c index 501ded65..0f8dcc28 100644 --- a/src/linker/pdb_ext/pdb_helpers.c +++ b/src/linker/pdb_ext/pdb_helpers.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal U64 diff --git a/src/linker/pdb_ext/pdb_helpers.h b/src/linker/pdb_ext/pdb_helpers.h index 315ff665..7ba2d47b 100644 --- a/src/linker/pdb_ext/pdb_helpers.h +++ b/src/linker/pdb_ext/pdb_helpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/rdi/rdi_builder.c b/src/linker/rdi/rdi_builder.c index 6d7145f1..623f0a9f 100644 --- a/src/linker/rdi/rdi_builder.c +++ b/src/linker/rdi/rdi_builder.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal RDIB_DataModel diff --git a/src/linker/rdi/rdi_builder.h b/src/linker/rdi/rdi_builder.h index d0b3ef33..50bb1965 100644 --- a/src/linker/rdi/rdi_builder.h +++ b/src/linker/rdi/rdi_builder.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/rdi/rdi_coff.c b/src/linker/rdi/rdi_coff.c index 2646860c..57072743 100644 --- a/src/linker/rdi/rdi_coff.c +++ b/src/linker/rdi/rdi_coff.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal RDI_Arch diff --git a/src/linker/rdi/rdi_coff.h b/src/linker/rdi/rdi_coff.h index d946e237..c68e4dc5 100644 --- a/src/linker/rdi/rdi_coff.h +++ b/src/linker/rdi/rdi_coff.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/rdi/rdi_cv.c b/src/linker/rdi/rdi_cv.c index 330161d0..0f8e9de2 100644 --- a/src/linker/rdi/rdi_cv.c +++ b/src/linker/rdi/rdi_cv.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal RDI_Arch diff --git a/src/linker/rdi/rdi_cv.h b/src/linker/rdi/rdi_cv.h index 0201c688..fb6ca4da 100644 --- a/src/linker/rdi/rdi_cv.h +++ b/src/linker/rdi/rdi_cv.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once diff --git a/src/linker/thread_pool/thread_pool.c b/src/linker/thread_pool/thread_pool.c index 434729d8..066efdd4 100644 --- a/src/linker/thread_pool/thread_pool.c +++ b/src/linker/thread_pool/thread_pool.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal void diff --git a/src/linker/thread_pool/thread_pool.h b/src/linker/thread_pool/thread_pool.h index e37f903c..f03e2a1e 100644 --- a/src/linker/thread_pool/thread_pool.h +++ b/src/linker/thread_pool/thread_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools +// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once