From 541d3afefab30bd4e6100fd15fa7b8fe34798a7b Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Fri, 29 May 2026 22:04:20 -0700 Subject: [PATCH] WIP /RAD_TYPE_SERVER env vars replace HashTable with HashMap in config --- src/linker/hash_table.c | 33 ++- src/linker/hash_table.h | 21 +- src/linker/lnk.c | 504 +++++++++++++++++++++++++------- src/linker/lnk.h | 3 +- src/linker/lnk_cmd_line.c | 199 ++++++++++++- src/linker/lnk_cmd_line.h | 56 +++- src/linker/lnk_config.c | 313 ++++++++++++-------- src/linker/lnk_config.h | 47 ++- src/linker/lnk_debug_info.c | 119 ++++---- src/linker/lnk_debug_info.h | 12 +- src/linker/lnk_io.c | 41 ++- src/linker/lnk_io.h | 3 +- src/linker/lnk_log.h | 7 +- src/linker/lnk_obj.c | 33 ++- src/linker/lnk_obj.h | 3 + src/linker/tests/linker_tests.c | 5 +- 16 files changed, 1055 insertions(+), 344 deletions(-) diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index 48534e14..45591cbb 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -680,18 +680,22 @@ hash_map_hash_from_path(String8 path) internal HashMapNode * hash_map_push_string_string(Arena *arena, HashMap *hm, String8 key, String8 value) { + key = push_str8_copy(arena, key); + value = push_str8_copy(arena, value); return hash_map_push(arena, hm, hash_map_hasher(key), (HashMapKeyValue){ .key = { .key_string = key }, .value = { .value_string = value } }, hash_map_match_string); } internal HashMapNode * hash_map_push_string_raw(Arena *arena, HashMap *hm, String8 key, void *value) { + key = push_str8_copy(arena, key); return hash_map_push(arena, hm, hash_map_hasher(key), (HashMapKeyValue){ .key = { .key_string = key }, .value = { .value_raw = value } }, hash_map_match_string); } internal HashMapNode * hash_map_push_string_u64(Arena *arena, HashMap *hm, String8 key, U64 value) { + key = push_str8_copy(arena, key); return hash_map_push(arena, hm, hash_map_hasher(key), (HashMapKeyValue){ .key = { .key_string = key }, .value = { .value_u64 = value } }, hash_map_match_string); } @@ -740,18 +744,22 @@ hash_map_push_raw_u64(Arena *arena, HashMap *hm, void *key, U64 value) internal HashMapNode * hash_map_push_path_u64(Arena *arena, HashMap *hm, String8 path, U64 value) { - return hash_map_push(arena, hm, hash_map_hasher(path), (HashMapKeyValue){ .key = { .key_string = path }, .value = { .value_u64 = value } }, hash_map_match_path); + path = push_str8_copy(arena, path); + return hash_map_push(arena, hm, hash_map_hash_from_path(path), (HashMapKeyValue){ .key = { .key_string = path }, .value = { .value_u64 = value } }, hash_map_match_path); } internal HashMapNode * hash_map_push_path_string(Arena *arena, HashMap *hm, String8 path, String8 value) { - return hash_map_push(arena, hm, hash_map_hasher(path), (HashMapKeyValue){ .key = { .key_string = path }, .value = { .value_string = value } }, hash_map_match_path); + path = push_str8_copy(arena, path); + value = push_str8_copy(arena, value); + return hash_map_push(arena, hm, hash_map_hash_from_path(path), (HashMapKeyValue){ .key = { .key_string = path }, .value = { .value_string = value } }, hash_map_match_path); } internal HashMapNode * hash_map_push_path_raw(Arena *arena, HashMap *hm, String8 path, void *value) { + path = push_str8_copy(arena, path); return hash_map_push(arena, hm, hash_map_hash_from_path(path), (HashMapKeyValue){ .key = { .key_string = path }, .value = { .value_raw = value } }, hash_map_match_path); } @@ -777,6 +785,13 @@ hash_map_search_string_raw(HashMap *hm, String8 key) return n ? n->v.value.value_raw : 0; } +internal String8 * +hash_map_search_string_string(HashMap *hm, String8 key) +{ + HashMapNode *n = hash_map_search(hm, hash_map_hasher(key), (HashMapKey){ .key_string = key }, hash_map_match_string); + return n ? &n->v.value.value_string : 0; +} + internal U32 * hash_map_search_string_u32(HashMap *hm, String8 key) { @@ -791,6 +806,20 @@ hash_map_search_string_u64(HashMap *hm, String8 key) return n ? &n->v.value.value_u64 : 0; } +internal U64 * +hash_map_search_path_u64(HashMap *hm, String8 key) +{ + HashMapNode *n = hash_map_search(hm, hash_map_hash_from_path(key), (HashMapKey){ .key_string = key }, hash_map_match_path); + return n ? &n->v.value.value_u64 : 0; +} + +internal String8 * +hash_map_search_path_string(HashMap *hm, String8 key) +{ + HashMapNode *n = hash_map_search(hm, hash_map_hash_from_path(key), (HashMapKey){ .key_string = key }, hash_map_match_path); + return n ? &n->v.value.value_string : 0; +} + internal void * hash_map_search_path_raw(HashMap *hm, String8 key) { diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index 39e46a1d..f9e89d67 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -175,15 +175,18 @@ internal HashMapNode * hash_map_push_path_u64 (Arena *arena, HashMap *hm, St internal HashMapNode * hash_map_push_path_string (Arena *arena, HashMap *hm, String8 path, String8 value); internal HashMapNode * hash_map_push_path_raw (Arena *arena, HashMap *hm, String8 path, void *value); -internal void * hash_map_search_stringf_raw(HashMap *hm, char *fmt, ...); -internal void * hash_map_search_string_raw(HashMap *hm, String8 key); -internal U32 * hash_map_search_string_u32(HashMap *hm, String8 key); -internal U64 * hash_map_search_string_u64(HashMap *hm, String8 key); -internal void * hash_map_search_path_raw (HashMap *hm, String8 key); -internal void * hash_map_search_u64_raw (HashMap *hm, U64 key); -internal U64 * hash_map_search_u64_u64 (HashMap *hm, U64 key); -internal void * hash_map_search_raw_raw (HashMap *hm, void *key); -internal U64 * hash_map_search_raw_u64 (HashMap *hm, void *key); +internal void * hash_map_search_stringf_raw (HashMap *hm, char *fmt, ...); +internal void * hash_map_search_string_raw (HashMap *hm, String8 key); +internal String8 * hash_map_search_string_string(HashMap *hm, String8 key); +internal U32 * hash_map_search_string_u32 (HashMap *hm, String8 key); +internal U64 * hash_map_search_string_u64 (HashMap *hm, String8 key); +internal U64 * hash_map_search_path_u64 (HashMap *hm, String8 key); +internal String8 * hash_map_search_path_string (HashMap *hm, String8 key); +internal void * hash_map_search_path_raw (HashMap *hm, String8 key); +internal void * hash_map_search_u64_raw (HashMap *hm, U64 key); +internal U64 * hash_map_search_u64_u64 (HashMap *hm, U64 key); +internal void * hash_map_search_raw_raw (HashMap *hm, void *key); +internal U64 * hash_map_search_raw_u64 (HashMap *hm, void *key); internal HashMapKeyValue * key_value_from_hash_map (Arena *arena, HashMap *hm); internal U32 * keys_from_hash_map_u32 (Arena *arena, HashMap *hm); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 2e85f44c..22269a89 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -124,75 +124,106 @@ internal LNK_CmdLine lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line) { + Temp scratch = scratch_begin(&arena, 1); LNK_CmdLine cmd_line = {0}; - // default flags - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Align, "%u", KB(4)); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Debug, "none"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_FileAlign, "%u", 512); + char *default_opts[] = { + "/ALIGN:4096", + "/DEBUG:none", + "/FILEALIGN:512", + "/HIGHENTROPYVA", + "/MANIFESTUAC:\"level='asInvoker' uiAccess='false'\"", + "/NXCOMPAT", + "/LARGEADDRESSAWARE", + "/PDBALTPATH:%_RAD_PDB_PATH%", + "/PDBPAGESIZE:4096", + (char*)str8f(scratch.arena, "/HEAP:%llu,%llu", MB(1), KB(4)).str, + (char*)str8f(scratch.arena, "/STACK:%llu,%llu", MB(1), KB(4)).str, + + "/RAD_BOOT_MODE:LINKER", + //"/RAD_BUILD_EXP", + "/RAD_BUILD_IMPLIB", + "/RAD_TPYE_HASH_ALG:BLAKE3", + "/RAD_AGE:1", + "/RAD_CHECK_UNUSED_DELAY_LOAD_DLL", + "/RAD_DO_MERGE", + "/RAD_ENV_LIB", + "/RAD_EXE", + "/RAD_GUID:imageblake3", + "/RAD_LARGE_PAGES:no", + "/RAD_LINK_VER:14.0", + "/RAD_OS_VER:6.0", + "/RAD_PAGE_SIZE:4096", + "/RAD_PATH_STYLE:system", + "/RAD_PDB_HASH_TYPE_NAMES:NONE", + "/RAD_PDB_HASH_TYPE_NAME_LENGTH:8", + "/RAD_DEBUGALTPATH:%_RAD_RDI_PATH%", + "/RAD_MEMORY_MAP_FILES", + "/RAD_MAP_LINES_FOR_UNRESOLVED_SYMBOLS", + "/RAD_UNRESOLVED_SYMBOL_LIMIT:1000", + "/RAD_UNRESOLVED_SYMBOL_REF_LIMIT:10", + (char*)str8f(scratch.arena, "/RAD_MT_PATH:%s", LNK_MANIFEST_MERGE_TOOL_NAME).str, + (char*)str8f(scratch.arena, "/RAD_DATA_DIR_COUNT:%u", PE_DataDirectoryIndex_COUNT).str, + }; + + char *push_opts[] = { + "/MERGE:.xdata=.rdata", + "/MERGE:.00cfg=.rdata", + // TODO: .tls must be always first contribution in .data section because compiler generates TLS relative movs + //"/MERGE:.tls=.data", + "/MERGE:.idata=.data", + "/MERGE:.didat=.data", + "/MERGE:.edata=.rdata", + "/MERGE:.RAD_LINK_PE_DEBUG_DIR=.rdata", + "/MERGE:.RAD_LINK_PE_DEBUG_DATA=.rdata", + + "/RAD_REMOVE_SECTION:.debug", + "/RAD_REMOVE_SECTION:.gehcont", + "/RAD_REMOVE_SECTION:.gfids", + "/RAD_REMOVE_SECTION:.gxfg", + + (char*)str8f(scratch.arena, "/RAD_WORKERS:%u", get_system_info()->logical_processor_count).str, + + // errors that are too verbose in release build + (char*)str8f(scratch.arena, "/RAD_IGNORE:%d", LNK_Warning_UnknownSwitch * (BUILD_DEBUG ? -1 : 1)).str, + (char*)str8f(scratch.arena, "/RAD_IGNORE:%d", LNK_Warning_UnknownDirective * (BUILD_DEBUG ? -1 : 1)).str, + (char*)str8f(scratch.arena, "/RAD_IGNORE:%d", LNK_Error_InvalidTypeIndex * (BUILD_DEBUG ? -1 : 1)).str, + + #if BUILD_DEBUG + "/RAD_LOG:debug", + "/RAD_LOG:io_write", + #else + (char*)str8f(scratch.arena, "/RAD_IGNORE:%u", LNK_Error_InvalidTypeIndex).str, + #endif + }; + +#define DefaultOpt(...) do { \ + LNK_CmdLine parsed_cmd_line = lnk_cmd_line_from_stringf_windows_rules(arena, __VA_ARGS__); \ + for EachNode(cmd, LNK_CmdOption, parsed_cmd_line.first_option) { \ + if (!lnk_cmd_line_has_switch(user_cmd_line, lnk_cmd_switch_type_from_string(cmd->string))) { \ + String8List value_strings = str8_list_copy(arena, &cmd->value_strings); \ + lnk_cmd_line_push_option_list(arena, &cmd_line, cmd->string, value_strings); \ + } \ + } \ +} while (0) + +#define PushOpt(...) do { \ + LNK_CmdLine parsed_cmd_line = lnk_cmd_line_from_stringf_windows_rules(arena, __VA_ARGS__); \ + lnk_cmd_line_concat_in_place(&cmd_line, &parsed_cmd_line); \ +} while (0) + if (lnk_cmd_line_has_switch(user_cmd_line, LNK_CmdSwitch_Dll)) { - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_SubSystem, "%S", pe_string_from_subsystem(PE_WindowsSubsystem_WINDOWS_GUI)); + DefaultOpt("/SUBSYSTEM:%S", pe_string_from_subsystem(PE_WindowsSubsystem_WINDOWS_GUI)); } - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_HighEntropyVa, ""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_ManifestUac, "\"level='asInvoker' uiAccess='false'\""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_NxCompat, ""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_LargeAddressAware, ""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_PdbAltPath, "%%_RAD_PDB_PATH%%"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_PdbPageSize, "%u", KB(4)); if (!lnk_cmd_line_has_switch(user_cmd_line, LNK_CmdSwitch_Brepro)) { - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_TimeStamp, "%u", get_process_start_time_unix()); + DefaultOpt("/RAD_TIME_STAMP:%u", get_process_start_time_unix()); } - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_TypeHashAlg, "BLAKE3"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_Age, "%u", 1); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, ""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_DoMerge, ""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_EnvLib, ""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_Exe, ""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_Guid, "imageblake3"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_LargePages, "no"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_LinkVer, "14.0"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_OsVer, "6.0"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_PageSize, "%u", KB(4)); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_PathStyle, "system"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_Workers, "%u", get_system_info()->logical_processor_count); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_DebugAltPath, "%%_RAD_RDI_PATH%%"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_MemoryMapFiles, ""); - if (BUILD_DEBUG) { - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_Log, "debug"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_Log, "io_write"); - } else { - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_Ignore, "%u", LNK_Error_InvalidTypeIndex); + for EachIndex(i, ArrayCount(default_opts)) { + DefaultOpt("%s", default_opts[i]); } - // default section merges - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Merge, ".xdata=.rdata"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Merge, ".00cfg=.rdata"); - // TODO: .tls must be always first contribution in .data section because compiler generates TLS relative movs - //lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Merge, ".tls=.data"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Merge, ".idata=.data"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Merge, ".didat=.data"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Merge, ".edata=.rdata"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Merge, ".RAD_LINK_PE_DEBUG_DIR=.rdata"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Merge, ".RAD_LINK_PE_DEBUG_DATA=.rdata"); - - // sections to remove from the image - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".debug"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".gehcont"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".gfids"); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".gxfg"); - - // set limits on unresolved symbol errors - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, ""); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, "1000"); - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, "10"); - - // set default max worker count - if (lnk_cmd_line_has_switch(user_cmd_line, LNK_CmdSwitch_Rad_SharedThreadPool)) { - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, ""); - } - - if ( ! lnk_cmd_line_has_switch(user_cmd_line, LNK_CmdSwitch_Rad_MtPath)) { - lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_MtPath, "%s", LNK_MANIFEST_MERGE_TOOL_NAME); + for EachIndex(i, ArrayCount(push_opts)) { + PushOpt("%s", push_opts[i]); } // when /FORCE is specified on the command line, do not stop on these errors @@ -202,39 +233,37 @@ lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line) } #endif - // errors that are too verbose in release build - U64 debug_opt = BUILD_DEBUG ? -1 : 1; - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_Ignore, "%d", LNK_Warning_UnknownSwitch * debug_opt); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_Ignore, "%d", LNK_Warning_UnknownDirective * debug_opt); - lnk_cmd_line_push_optionf(arena, &cmd_line, LNK_CmdSwitch_Rad_Ignore, "%d", LNK_Error_InvalidTypeIndex * debug_opt); - +#undef DefaultOpt +#undef PushOpt + scratch_end(scratch); return cmd_line; } internal LNK_Config * -lnk_config_from_argcv(Arena *arena, int argc, char **argv) +lnk_config_from_argcv(CmdLine *cmdline) { - Temp scratch = scratch_begin(&arena, 1); + Temp scratch = scratch_begin(0,0); String8List raw_cmd_line = {0}; - for (U64 i = 1; i < argc; i += 1) { str8_list_push(arena, &raw_cmd_line, str8_cstring(argv[i])); } + for (U64 i = 1; i < cmdline->argc; i += 1) { str8_list_push(scratch.arena, &raw_cmd_line, str8_cstring(cmdline->argv[i])); } #if PROFILE_TELEMETRY tmMessage(0, TMMF_ICON_NOTE, "Command Line: %.*s", str8_varg(str8_list_join(scratch.arena, &raw_cmd_line, &(StringJoin){ .sep = str8_lit_comp(" ") }))); #endif // make command line - LNK_CmdLine cmd_line = {0}; + LNK_CmdLine cmd_line_msvc = {0}; { String8List unwrapped_cmd_line = lnk_unwrap_rsp(scratch.arena, raw_cmd_line); LNK_CmdLine user_cmd_line = lnk_cmd_line_parse_windows_rules(scratch.arena, unwrapped_cmd_line); + user_cmd_line.raw_cmd_line = raw_cmd_line; LNK_CmdLine default_cmd_line = lnk_make_default_cmd_line(scratch.arena, user_cmd_line); - lnk_cmd_line_concat_in_place(&cmd_line, &default_cmd_line); - lnk_cmd_line_concat_in_place(&cmd_line, &user_cmd_line); + lnk_cmd_line_concat_in_place(&cmd_line_msvc, &default_cmd_line); + lnk_cmd_line_concat_in_place(&cmd_line_msvc, &user_cmd_line); } // init config - LNK_Config *config = lnk_config_from_cmd_line(raw_cmd_line, cmd_line); + LNK_Config *config = lnk_config_init(cmd_line_msvc); scratch_end(scratch); return config; @@ -1228,6 +1257,20 @@ lnk_array_from_lib_member_list(Arena *arena, LNK_LibMemberRefList list) return result; } +internal LNK_Link * +lnk_link_init(TP_Arena *arena, LNK_Config *config) +{ + LNK_Link *link = push_array(arena->v[0], LNK_Link, 1); + link->arena = arena_alloc(.name = "LINK"); + link->last_symbol_input = &link->objs.first; + link->last_include = &config->include_symbol_list.first; + link->last_default_lib = &config->input_default_lib_list.first; + link->last_obj_lib = &config->input_obj_lib_list.first; + link->last_cmd_lib = &config->input_list[LNK_Input_Lib].first; + link->try_to_resolve_entry_point = 1; + return link; +} + internal LNK_ObjNode * lnk_load_objs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, U64 *objs_count_out) { @@ -1243,7 +1286,7 @@ lnk_load_objs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer * lnk_log(LNK_Log_InputObj, "[ Obj Input Size %M ]", input_size); } - LNK_ObjNode *new_objs = lnk_obj_from_input_many(tp, arena, config->machine, new_input_objs.count, new_input_objs.v); + LNK_ObjNode *new_objs = lnk_obj_from_input_many(tp, arena, config, new_input_objs.count, new_input_objs.v); // if machine type was unspecified on the command line, derive it from obj file if (config->machine == COFF_MachineType_Unknown) { @@ -1727,7 +1770,7 @@ lnk_link_inputs(TP_Context *tp, B32 link_whole_archive = config->whole_archive_all; if ( ! link_whole_archive) { String8 lib_name = str8_chop_last_dot(str8_skip_last_slash(lib->path)); - link_whole_archive = hash_table_search_path(config->whole_archive_ht, lib_name) != 0; + link_whole_archive = hash_map_search_path_u64(&config->whole_archive_ht, lib_name) != 0; } ProfBeginV("Search %S", str8_skip_last_slash(lib->path)); @@ -1911,23 +1954,14 @@ lnk_link_inputs(TP_Context *tp, ProfEnd(); } + internal LNK_LinkResult lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab) { ProfBeginFunction(); Temp scratch = scratch_begin(arena->v, arena->count); - // - // init link context - // - LNK_Link *link = push_array(arena->v[0], LNK_Link, 1); - link->arena = arena_alloc(.name = "LINK"); - link->last_symbol_input = &link->objs.first; - link->last_include = &config->include_symbol_list.first; - link->last_default_lib = &config->input_default_lib_list.first; - link->last_obj_lib = &config->input_obj_lib_list.first; - link->last_cmd_lib = &config->input_list[LNK_Input_Lib].first; - link->try_to_resolve_entry_point = 1; + LNK_Link *link = lnk_link_init(arena, config); // TODO: factor out // input :null_obj String8 null_obj = lnk_make_null_obj(inputer->arena); @@ -5292,21 +5326,34 @@ THREAD_POOL_TASK_FUNC(lnk_p2r_worker) scratch_end(scratch); } +internal LNK_Obj ** +lnk_debug_filter_objs(Arena *arena, LNK_Obj **objs, U64 objs_count, U64 *count_out) +{ + U64 debug_info_objs_count = 0; + LNK_Obj **debug_info_objs = push_array(arena, LNK_Obj *, objs_count); + for EachIndex(obj_idx, objs_count) { + LNK_Obj *obj = objs[obj_idx]; + + // filter out internal objs from debug info output + if (obj->exclude_from_debug_info) { + continue; + } + + debug_info_objs[debug_info_objs_count++] = obj; + } + if (count_out) { + *count_out = debug_info_objs_count; + } + return debug_info_objs; +} + internal void -lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) +lnk_run_linker(TP_Context *tp, TP_Arena *arena, LNK_Config *config) { ProfBeginFunction(); Temp scratch = scratch_begin(arena->v, arena->count); - if (lnk_get_log_status(LNK_Log_Debug)) { - String8 full_cmd_line = str8_list_join(scratch.arena, &config->raw_cmd_line, &(StringJoin){ .sep = str8_lit_comp(" ") }); - lnk_fprintf(stderr, "--------------------------------------------------------------------------------\n"); - lnk_fprintf(stderr, "Command Line: %.*s\n", str8_varg(full_cmd_line)); - lnk_fprintf(stderr, "Work Dir : %.*s\n", str8_varg(config->work_dir)); - lnk_fprintf(stderr, "--------------------------------------------------------------------------------\n"); - } - // // Input Context // @@ -5368,12 +5415,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) lnk_timer_begin(LNK_Timer_Debug); U64 debug_info_objs_count = 0; - LNK_Obj **debug_info_objs = push_array(scratch.arena, LNK_Obj *, objs_count); - for EachIndex(obj_idx, objs_count) { - LNK_Obj *obj = objs[obj_idx]; - if (obj->exclude_from_debug_info) { continue; } - debug_info_objs[debug_info_objs_count++] = obj; - } + LNK_Obj **debug_info_objs = lnk_debug_filter_objs(scratch.arena, objs, objs_count, &debug_info_objs_count); // // CodeView @@ -5401,7 +5443,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) config->pdb_hash_type_name_length, config->pdb_hash_type_name_map); } - pdb_data = lnk_build_pdb(tp, arena, image_ctx.image_data, config, symtab, &cv, cv_types); + pdb_data = lnk_build_pdb(tp, arena, image_ctx.image_data, config, symtab, &cv, cv_types, LNK_PDB_BuilderFlag_All); if (config->debug_mode == LNK_DebugMode_Full) { lnk_write_data_list_to_file_path(config->pdb_name, config->temp_pdb_name, pdb_data); } @@ -5488,7 +5530,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) stripped_cv.debug_s_arr = debug_s_arr; stripped_cv.symbol_input_ranges = push_array(scratch.arena, Rng1U64, tp->worker_count); - String8List pdb_data = lnk_build_pdb(tp, arena, image_ctx.image_data, config, symtab, &stripped_cv, (LNK_MergedTypes){0}); + String8List pdb_data = lnk_build_pdb(tp, arena, image_ctx.image_data, config, symtab, &stripped_cv, (LNK_MergedTypes){0}, LNK_PDB_BuilderFlag_All); lnk_write_data_list_to_file_path(config->pdb_stripped_name, str8f(scratch.arena, "%S.tmp", config->pdb_stripped_name), pdb_data); } @@ -5510,15 +5552,265 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) ProfEnd(); } +internal void +lnk_run_type_server(TP_Context *tp, TP_Arena *arena, LNK_Config *config) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(arena->v, arena->count); + + // generate GUID + Guid type_server_guid = {0}; + { + blake3_hasher hasher = {0}; + blake3_hasher_init(&hasher); + blake3_hasher_update(&hasher, config->pdb_name.str, config->pdb_name.size); + blake3_hasher_update(&hasher, (U8*)&config->time_stamp, sizeof(config->time_stamp)); + blake3_hasher_finalize(&hasher, type_server_guid.v, sizeof(type_server_guid.v)); + } + + // push default type-server switches + lnk_config_pushf(config, "/PDB:\"%S.type_server.pdb\"", str8_chop_last_dot(config->pdb_name)); + lnk_config_pushf(config, "/DEBUG:FULL"); + lnk_config_pushf(config, "/NOD"); + lnk_config_pushf(config, "/RAD_WRITE_TEMP_FILES"); + lnk_config_pushf(config, "/RAD_MEMORY_MAP_FILES:READ_WRITE"); + lnk_config_pushf(config, "/RAD_GUID:%S", string_from_guid(scratch.arena, type_server_guid)); + + // load objs + U64 objs_count = 0; + LNK_Obj **objs = 0; + { + LNK_Inputer *inputer = lnk_inputer_init(); + LNK_Link *link = lnk_link_init(arena, config); + + // input :null_obj + String8 null_obj = lnk_make_null_obj(inputer->arena); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Null *"), null_obj); + + // input objs on command line + for (String8Node *obj_path = config->input_list[LNK_Input_Obj].first; obj_path != 0; obj_path = obj_path->next) { + lnk_inputer_push_obj_thin(inputer, 0, obj_path->string); + } + + LNK_ObjNode *obj_nodes = lnk_load_objs(tp, arena, config, inputer, 0, link, &objs_count); + objs = push_array(scratch.arena, LNK_Obj *, objs_count); + for EachIndex(obj_idx, objs_count) { objs[obj_idx] = &obj_nodes[obj_idx].data; } + } + + // obj filters + { + // internal filter + objs = lnk_debug_filter_objs(scratch.arena, objs, objs_count, &objs_count); + + // command line filter + { + U64 result_count = 0; + LNK_Obj **result = push_array(scratch.arena, LNK_Obj *, objs_count); + for EachIndex(obj_idx, objs_count) { + LNK_Obj *obj = objs[obj_idx]; + + B32 skip = 0; + if (config->type_server_match_obj.node_count) { + skip = 1; + for EachNode(filter_n, String8Node, config->type_server_match_obj.first) { + if (str8_match_wildcard(obj->path, filter_n->string, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive)) { + skip = 0; + break; + } + } + } + if (skip) { continue; } + + result[result_count++] = obj; + } + objs_count = result_count; + objs = result; + } + } + + // compute size of objs compiled with /Z7 + U64 debug_t_total_size = 0; + { + for EachIndex(obj_idx, objs_count) { + LNK_Obj *obj = objs[obj_idx]; + + // register debug info sections + U64 type_sect_indices_count = 0; + U32 type_sect_indices[4] = {0}; + if (obj->debug_t_sect_idx < obj->header.section_count_no_null) { type_sect_indices[type_sect_indices_count++] = obj->debug_t_sect_idx; } + else if (obj->debug_p_sect_idx < obj->header.section_count_no_null) { type_sect_indices[type_sect_indices_count++] = obj->debug_p_sect_idx; } + + for EachIndex(i, type_sect_indices_count) { + U64 section_idx = type_sect_indices[i]; + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + COFF_SectionHeader *debug_t_header = §ion_table[section_idx]; + String8 debug_t_data = str8_substr(obj->data, r1u64(debug_t_header->foff, debug_t_header->foff + debug_t_header->fsize)); + CV_Leaf first_leaf = {0}; + if (cv_read_leaf(debug_t_data, sizeof(CV_Signature), CV_LeafAlign, &first_leaf) >= sizeof(CV_LeafHeader)) { + if ( ! cv_is_leaf_type_server(first_leaf.kind)) { + debug_t_total_size += debug_t_data.size; + } + } + } + } + } + + if (debug_t_total_size) { + // merge & write types + LNK_CodeViewInput cv = lnk_make_code_view_input(tp, arena, config, objs_count, objs); + LNK_MergedTypes cv_types = lnk_merge_types(tp, arena, &cv); + String8List pdb = lnk_build_pdb(tp, arena, str8_zero(), config, 0, &cv, cv_types, LNK_PDB_BuilderFlag_Tpi|LNK_PDB_BuilderFlag_Ipi); + lnk_write_data_list_to_file_path(config->pdb_name, config->temp_pdb_name, pdb); + + typedef struct Edit { + struct Edit *next; + String8 data; + U64 offset; + LNK_Obj *obj; + } Edit; + Edit *edits = 0; + + for EachIndex(obj_idx, objs_count) { + LNK_Obj *obj = objs[obj_idx]; + + // skip objs in libraries + if (obj->link_member) { continue; } + + if (obj->debug_t_sect_idx < obj->header.section_count_no_null) { + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + COFF_SectionHeader *section_header = §ion_table[obj->debug_t_sect_idx]; + + // skip type servers + if (cv_debug_t_is_type_server_ref(&cv.debug_t_arr[obj_idx])) { continue; } + + CV_LeafTypeServer2 ts_info = { .sig70 = config->guid, .age = 1 }; + String8List ts_srl = {0}; + str8_list_push(scratch.arena, &ts_srl, str8_struct(&ts_info)); + str8_list_push(scratch.arena, &ts_srl, push_cstr(scratch.arena, config->pdb_name)); + + // type server srl -> CodeView leaf + String8 ts_string = str8_list_join(scratch.arena, &ts_srl, 0); + String8 ts_leaf = cv_make_leaf(scratch.arena, CV_LeafKind_TYPESERVER2, ts_string, CV_LeafAlign); + + // make type server .debug$T + String8List debug_t_list = {0}; + str8_list_push(scratch.arena, &debug_t_list, str8_struct((&(CV_Signature){ CV_Signature_C13 }))); + str8_list_push(scratch.arena, &debug_t_list, ts_leaf); + String8 debug_t = str8_list_join(scratch.arena, &debug_t_list, 0); + + // realloc seciton if there is not enough room in the section header + if (debug_t.size > section_header->fsize) { + section_header->foff = obj->data.size; + section_header->fsize = debug_t.size; + + // append to the obj + struct Edit *e = push_array(scratch.arena, struct Edit, 1); + e->data = debug_t; + e->offset = obj->data.size; + e->obj = obj; + SLLStackPush(edits, e); + } else { + section_header->fsize = debug_t.size; + + Rng1U64 section_frange = rng_1u64(section_header->foff, section_header->foff + section_header->fsize); + String8 section_data = str8_substr(obj->data, section_frange); + MemoryCopyStr8(section_data.str, debug_t); + } + } + + // discard precompiled headers types, they are already in the type server + if (obj->debug_p_sect_idx < obj->header.section_count_no_null) { + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + COFF_SectionHeader *section_header = §ion_table[obj->debug_p_sect_idx]; + section_header->flags |= COFF_SectionFlag_LnkRemove; + } + + // discard type hashes sectiosn, there is no /Z7 sections in the file + if (obj->debug_h_sect_idx < obj->header.section_count_no_null) { + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + COFF_SectionHeader *section_header = §ion_table[obj->debug_h_sect_idx]; + section_header->flags |= COFF_SectionFlag_LnkRemove; + } + + // edit obj + if (edits) { + #if OS_WINDOWS + AssertAlways(UnmapViewOfFile(obj->data.str)); + #else + NotImplemented; + #endif + File handle = file_open(AccessFlag_Append|AccessFlag_Read|AccessFlag_Write, obj->path); + for EachNode(e, struct Edit, edits) { + AssertAlways(file_write(handle, r1u64(e->offset, e->offset + e->data.size), e->data.str) == e->data.size); + } + file_close(handle); + } + } + } + +#if 0 + // relaunch the binary with the same command line, but with boot mode set to LINKER + if (config->type_server == LNK_SwitchState_Yes) { + String8 curr_cmdl = str8_list_join(scratch.arena, &config->raw_cmd_line, &(StringJoin){.sep=str8_lit(" ")}); + String8 linker_cmdl = str8f(scratch.arena, "%S %S /RAD_TYPE_SERVER:NO /RAD_BOOT_MODE:LINKER", get_process_info()->binary_file_path, curr_cmdl); + + ProcessLaunchParams launch_opts = { + .path = get_process_info()->binary_path, + .inherit_env = 1, + .consoleless = 1, + .cmd_line = lnk_arg_list_parse_windows_rules(scratch.arena, linker_cmdl), + }; + Process process_handle = process_launch(&launch_opts); + + if (process_match(process_handle, process_zero())) { + String8 e = str8f(scratch.arena, "failed to launch radlink(%S) after type server step\n"); + lnk_invalid_path((char*)e.str); + } + } +#endif + + scratch_end(scratch); + ProfEnd(); +} + internal void entry_point(CmdLine *cmdline) { Temp scratch = scratch_begin(0,0); lnk_log_begin(); - LNK_Config *config = lnk_config_from_argcv(scratch.arena, cmdline->argc, cmdline->argv); + + LNK_Config *config = lnk_config_from_argcv(cmdline); 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); - lnk_run(tp, tp_arena, config); + + // detect type server from the environment + { + HashMap env = lnk_env_vars_from_process_info(scratch.arena, get_process_info(), LNK_EnvVarRule_Current); + LNK_EnvVar *type_server_var = lnk_env_var_from_mapf(&env, "RAD_TYPE_SERVER"); + if (type_server_var) { + U64 do_type_server = 0; + if (lnk_env_var_to_u64(&env, type_server_var, &do_type_server)) { + if (do_type_server) { + lnk_config_pushf(config, "/RAD_TYPE_SERVER"); + lnk_log(LNK_Log_Debug, "type server mode was enabled from the environment\n"); + } + } + } + } + + if (lnk_get_log_status(LNK_Log_Debug)) { + String8 full_cmd_line = str8_list_join(scratch.arena, &config->raw_cmd_line, &(StringJoin){ .sep = str8_lit_comp(" ") }); + lnk_fprintf(stderr, "--------------------------------------------------------------------------------\n"); + lnk_fprintf(stderr, "Command Line: %.*s\n", str8_varg(full_cmd_line)); + lnk_fprintf(stderr, "Work Dir : %.*s\n", str8_varg(config->work_dir)); + lnk_fprintf(stderr, "--------------------------------------------------------------------------------\n"); + } + + switch (config->boot_mode) { + case LNK_BootMode_Linker: lnk_run_linker (tp, tp_arena, config); break; + case LNK_BootMode_TypeServer: lnk_run_type_server(tp, tp_arena, config); break; + } + lnk_log_end(); scratch_end(scratch); } diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 5b2d20e3..0c1ff4a3 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -319,7 +319,7 @@ typedef struct // --- Config ----------------------------------------------------------------- -internal LNK_Config * lnk_config_from_argcv(Arena *arena, int argc, char **argv); +internal LNK_Config * lnk_config_from_cmdline(CmdLine *cmdline); // --- Entry Point ------------------------------------------------------------- @@ -375,6 +375,7 @@ internal void lnk_lib_member_ref_list_concat_in_place_array(LNK_L internal int lnk_lib_member_ref_is_before(void *raw_a, void *raw_b); internal LNK_LibMemberRef ** lnk_array_from_lib_member_list(Arena *arena, LNK_LibMemberRefList list); +internal LNK_Link * lnk_link_init (TP_Arena *arena, LNK_Config *config); internal LNK_ObjNode * lnk_load_objs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, U64 *objs_count_out); internal void lnk_load_libs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_Link *link); internal void lnk_link_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link); diff --git a/src/linker/lnk_cmd_line.c b/src/linker/lnk_cmd_line.c index 80f07c53..7e25ffa4 100644 --- a/src/linker/lnk_cmd_line.c +++ b/src/linker/lnk_cmd_line.c @@ -143,6 +143,7 @@ lnk_cmd_line_concat_in_place(LNK_CmdLine *list, LNK_CmdLine *to_concat) internal LNK_CmdLine lnk_cmd_line_parse_windows_rules(Arena *arena, String8List arg_list) { + Temp scratch = scratch_begin(&arena, 1); LNK_CmdLine cmd_line = {0}; cmd_line.raw_cmd_line = str8_list_copy(arena, &arg_list); @@ -161,17 +162,41 @@ lnk_cmd_line_parse_windows_rules(Arena *arena, String8List arg_list) String8 value_string = str8_skip(arg, param_start_pos + 1); // make value list - String8List value_list = str8_split_by_string_chars(arena, value_string, str8_lit(","), 0); + String8List value_list_unowned = str8_split_by_string_chars(scratch.arena, value_string, str8_lit(","), 0); + String8List value_list = str8_list_copy(arena, &value_list_unowned); // push command + option_name = push_str8_copy(arena, option_name); lnk_cmd_line_push_option_list(arena, &cmd_line, option_name, value_list); } else { - str8_list_push(arena, &cmd_line.input_list, arg); + str8_list_push(arena, &cmd_line.input_list, push_str8_copy(arena, arg)); } } + scratch_end(scratch); return cmd_line; } +internal LNK_CmdLine +lnk_cmd_line_from_stringfv_windows_rules(Arena *arena, char *fmt, va_list args) +{ + Temp scratch = scratch_begin(&arena, 1); + String8 string = push_str8fv(scratch.arena, fmt, args); + String8List arg_list = lnk_arg_list_parse_windows_rules(scratch.arena, string); + LNK_CmdLine result = lnk_cmd_line_parse_windows_rules(arena, arg_list); + scratch_end(scratch); + return result; +} + +internal LNK_CmdLine +lnk_cmd_line_from_stringf_windows_rules(Arena *arena, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + LNK_CmdLine result = lnk_cmd_line_from_stringfv_windows_rules(arena, fmt, args); + va_end(args); + return result; +} + internal LNK_CmdOption * lnk_cmd_line_option_from_string(LNK_CmdLine cmd_line, String8 string) { @@ -245,7 +270,159 @@ lnk_data_from_cmd_line(Arena *arena, LNK_CmdLine cmd_line) } internal String8 -lnk_expand_env_vars_windows(Arena *arena, HashTable *env_vars, String8 string) +lnk_env_var_chain_separator_from_rule(LNK_EnvVarRule rule) +{ + String8 result = {0}; + switch (rule) { + case LNK_EnvVarRule_Batch: { result = str8_lit(";"); } break; + case LNK_EnvVarRule_Bash: { result = str8_lit(":"); } break; + default: break; + } + return result; +} + +internal LNK_EnvVar * +lnk_env_var_push(Arena *arena, HashMap *env_vars, String8 key, String8 value, LNK_EnvVarRule rule) +{ + LNK_EnvVar *env_var = lnk_env_var_from_map(env_vars, key); + if (env_var == 0) { + env_var = push_array(arena, LNK_EnvVar, 1); + env_var->raw_value = value; + env_var->rule = rule; + hash_map_push_path_raw(arena, env_vars, key, env_var); + } + return env_var; +} + +internal LNK_EnvVar * +lnk_env_var_push_string(Arena *arena, HashMap *env_vars, String8 string, LNK_EnvVarRule rule) +{ + LNK_EnvVar *result = 0; + + string = str8_skip_chop_whitespace(string); + + // string -> (key, value) + String8 key = {0}; + String8 val = {0}; + switch (rule) { + case LNK_EnvVarRule_Batch: { + if (string.size >= 2) { + if (str8_match_wildcard(string, str8_lit("\"*\""), 0)) { + string = str8_chop(str8_skip(string, 1), 1); + } + } + + U64 sep_idx = str8_find_needle(string, 0, str8_lit("="), 0); + if (sep_idx < string.size) { + // extract key and value + key = str8_skip_chop_whitespace(str8_prefix(string, sep_idx)); + val = str8_skip_chop_whitespace(str8_skip(string, sep_idx+1)); + + // strip quotes + if (str8_match_wildcard(val, str8_lit("\"*\""), 0) || // "*" + str8_match_wildcard(val, str8_lit("\'*\'"), 0)) { // '*' + val = str8_chop(str8_skip(val, 1), 1); + } + } + } break; + + case LNK_EnvVarRule_Bash: { + U64 sep_idx = str8_find_needle(string, 0, str8_lit("="), 0); + if (sep_idx < string.size) { + // extract key and value + key = str8_skip_chop_whitespace(str8_prefix(string, sep_idx)); + val = str8_skip_chop_whitespace(str8_skip(string, sep_idx+1)); + + // strip quotes + if (str8_match_wildcard(val, str8_lit("\"*\""), 0) || // "*" + str8_match_wildcard(val, str8_lit("\'*\'"), 0)) { // '*' + val = str8_chop(str8_skip(val, 1), 1); + } + } + } break; + + default: break; + } + + if (key.size > 0) { + result = lnk_env_var_push(arena, env_vars, key, val, rule); + } + + return result; +} + +internal LNK_EnvVar * +lnk_env_var_push_batch(Arena *arena, HashMap *env_vars, String8 string) +{ + return lnk_env_var_push_string(arena, env_vars, string, LNK_EnvVarRule_Batch); +} + +internal LNK_EnvVar * +lnk_env_var_push_bash(Arena *arena, HashMap *env_vars, String8 string) +{ + return lnk_env_var_push_string(arena, env_vars, string, LNK_EnvVarRule_Bash); +} + +internal LNK_EnvVar * +lnk_env_var_batchf(Arena *arena, HashMap *env_vars, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(arena, fmt, args); + va_end(args); + LNK_EnvVar *result = lnk_env_var_push_string(arena, env_vars, string, LNK_EnvVarRule_Batch); + return result; +} + +internal LNK_EnvVar * +lnk_env_var_bashf(Arena *arena, HashMap *env_vars, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(arena, fmt, args); + va_end(args); + LNK_EnvVar *result = lnk_env_var_push_string(arena, env_vars, string, LNK_EnvVarRule_Bash); + return result; +} + +internal LNK_EnvVar * +lnk_env_var_from_mapf(HashMap *env_vars, char *fmt, ...) +{ + Temp scratch = scratch_begin(0,0); + va_list args; + va_start(args, fmt); + String8 key = push_str8fv(scratch.arena, fmt, args); + va_end(args); + LNK_EnvVar *result = hash_map_search_path_raw(env_vars, key); + scratch_end(scratch); + return result; +} + +internal LNK_EnvVar * +lnk_env_var_from_map(HashMap *env_vars, String8 key) +{ + return hash_map_search_path_raw(env_vars, key); +} + +internal String8List +lnk_value_list_from_env_var(Arena *arena, LNK_EnvVar *env_var) +{ + return str8_split_by_string_chars(arena, env_var->raw_value, lnk_env_var_chain_separator_from_rule(env_var->rule), 0); +} + +internal B32 +lnk_env_var_to_u64(HashMap *env_vars, LNK_EnvVar *env_var, U64 *value_out) +{ + String8 value = str8_skip_chop_whitespace(env_var->raw_value); + if (str8_match_wildcard(value, str8_lit("\"*\""), 0) || + str8_match_wildcard(value, str8_lit("\'*\'"), 0)) { + value = str8_chop(str8_skip(value, 1), 1); + } + return try_u64_from_str8_c_rules(value, value_out); +} + +internal String8 +lnk_expand_env_vars_windows(Arena *arena, HashMap *env_vars, String8 string) { Temp scratch = scratch_begin(&arena, 1); @@ -260,9 +437,9 @@ lnk_expand_env_vars_windows(Arena *arena, HashTable *env_vars, String8 string) if (open < close) { String8 env_var_name = str8_substr(string, rng_1u64(open+1, close)); - BucketNode *match = hash_table_search_path(env_vars, env_var_name); - if (match) { - str8_list_push(scratch.arena, &list, match->v.value_string); + LNK_EnvVar *env_var = lnk_env_var_from_map(env_vars, env_var_name); + if (env_var) { + str8_list_push(scratch.arena, &list, env_var->raw_value); i = close+1; } else { str8_list_pushf(scratch.arena, &list, "%%%S", env_var_name); @@ -277,3 +454,13 @@ lnk_expand_env_vars_windows(Arena *arena, HashTable *env_vars, String8 string) return result; } +internal HashMap +lnk_env_vars_from_process_info(Arena *arena, ProcessInfo *proc_info, LNK_EnvVarRule rule) +{ + HashMap env_vars = {0}; + for EachNode(node, String8Node, proc_info->environment.first) { + lnk_env_var_push_string(arena, &env_vars, str8_copy(arena, node->string), rule); + } + return env_vars; +} + diff --git a/src/linker/lnk_cmd_line.h b/src/linker/lnk_cmd_line.h index b1ad7d16..083a651f 100644 --- a/src/linker/lnk_cmd_line.h +++ b/src/linker/lnk_cmd_line.h @@ -19,18 +19,56 @@ typedef struct LNK_CmdLine String8List raw_cmd_line; } LNK_CmdLine; -internal String8List lnk_arg_list_parse_windows_rules(Arena *arena, String8 string); -internal LNK_CmdLine lnk_cmd_line_parse_windows_rules(Arena *arena, String8List arg_list); -internal LNK_CmdOption * lnk_cmd_line_option_from_string(LNK_CmdLine cmd_line, String8 string); -internal B32 lnk_cmd_line_has_option_string(LNK_CmdLine cmd_line, String8 string); -internal B32 lnk_cmd_line_has_option(LNK_CmdLine cmd_line, char *string); +typedef enum LNK_EnvVarRule +{ + LNK_EnvVarRule_Null, + LNK_EnvVarRule_Batch, + LNK_EnvVarRule_Bash, +#if OS_WINDOWS + LNK_EnvVarRule_Current, +#elif OS_LINUX + LNK_EnvVarRule_Current, +#else +# error "define env var rules" +#endif +} LNK_EnvVarRule; -internal LNK_CmdOption * lnk_cmd_line_push_option(Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value); -internal LNK_CmdOption * lnk_cmd_line_push_option_if_not_present(Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value); +typedef struct LNK_EnvVar +{ + LNK_EnvVarRule rule; + String8 raw_value; + String8List chain_values; +} LNK_EnvVar; + +// --- List Helpers ------------------------------------------------------------ internal void lnk_cmd_line_concat_in_place(LNK_CmdLine *list, LNK_CmdLine *to_concat); -internal String8List lnk_data_from_cmd_line(Arena *arena, LNK_CmdLine cmd_line); +// --- Command Line ------------------------------------------------------------ -internal String8 lnk_expand_env_vars_windows(Arena *arena, HashTable *env_vars, String8 string); +internal String8List lnk_arg_list_parse_windows_rules (Arena *arena, String8 string); +internal LNK_CmdLine lnk_cmd_line_parse_windows_rules (Arena *arena, String8List arg_list); +internal LNK_CmdLine lnk_cmd_line_from_stringfv_windows_rules(Arena *arena, char *fmt, va_list args); +internal LNK_CmdLine lnk_cmd_line_from_stringf_windows_rules (Arena *arena, char *fmt, ...); +internal LNK_CmdOption * lnk_cmd_line_push_option_list (Arena *arena, LNK_CmdLine *cmd_line, String8 string, String8List value_strings); +internal LNK_CmdOption * lnk_cmd_line_push_option (Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value); +internal LNK_CmdOption * lnk_cmd_line_push_option_if_not_present (Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value); +internal String8List lnk_data_from_cmd_line (Arena *arena, LNK_CmdLine cmd_line); +internal LNK_CmdOption * lnk_cmd_line_option_from_string (LNK_CmdLine cmd_line, String8 string); +internal B32 lnk_cmd_line_has_option_string (LNK_CmdLine cmd_line, String8 string); +internal B32 lnk_cmd_line_has_option (LNK_CmdLine cmd_line, char *string); +// --- Env Vars ---------------------------------------------------------------- + +internal HashMap lnk_env_vars_from_process_info(Arena *arena, ProcessInfo *proc_info, LNK_EnvVarRule rule); + +internal LNK_EnvVar * lnk_env_var_from_map (HashMap *env, String8 key); +internal LNK_EnvVar * lnk_env_var_from_mapf (HashMap *env, char *fmt, ...); +internal LNK_EnvVar * lnk_env_var_push (Arena *arena, HashMap *env, String8 key, String8 value, LNK_EnvVarRule rule); +internal LNK_EnvVar * lnk_env_var_push_string (Arena *arena, HashMap *env, String8 string, LNK_EnvVarRule rule); +internal LNK_EnvVar * lnk_env_var_push_batch (Arena *arena, HashMap *env, String8 string); +internal LNK_EnvVar * lnk_env_var_push_bash (Arena *arena, HashMap *env, String8 string); +internal LNK_EnvVar * lnk_env_var_pushf (Arena *arena, HashMap *env, LNK_EnvVarRule rule, char *fmt, ...); +internal String8List lnk_value_list_from_env_var(Arena *arena, LNK_EnvVar *env_var); +internal B32 lnk_env_var_to_u64 (HashMap *env, LNK_EnvVar *var, U64 *value_out); +internal String8 lnk_expand_env_vars_windows(Arena *arena, HashMap *env, String8 string); diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 2e5a8f1a..949fa256 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -63,11 +63,15 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_Rad_Age, 0, "RAD_AGE", ":#", "Age embeded in EXE and PDB, used to validate incremental build. Default is 1." }, { LNK_CmdSwitch_Rad_AltPchDir, 0, "RAD_ALT_PCH_DIR", ":PATH", "Alternative directory to search for PCH object files." }, + //{ LNK_CmdSwitch_Rad_BuildExp, 0, "RAD_BUILD_EXP", "[:NO]", "Build export data." }, { LNK_CmdSwitch_Rad_BuildInfo, 0, "RAD_BUILD_INFO", "", "Print build info and exit." }, + { LNK_CmdSwitch_Rad_BuildImpLib, 0, "RAD_BUILD_IMPLIB", "[:NO]", "Build import library." }, { LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, 0, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "Check for unused delay load dlls." }, + { LNK_CmdSwitch_Rad_DataDirCount, 0, "RAD_DATA_DIR_COUNT", ":#", "Internal default for PE optional header data directory count." }, { LNK_CmdSwitch_Rad_Map, 0, "RAD_MAP", ":FILENAME", "Emit file with the output image's layout description." }, { LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, 0, "RAD_MAP_LINES_FOR_UNRESOLVED_SYMBOLS", "[:NO]", "Use debug info to print source file location for unresolved symbol" }, - { LNK_CmdSwitch_Rad_MemoryMapFiles, 0, "RAD_MEMORY_MAP_FILES", "[:NO]", "When enabled, files are memory-mapped instead of being read entirely on request." }, + { LNK_CmdSwitch_Rad_MemoryMapFiles, 0, "RAD_MEMORY_MAP_FILES", "[:{NO|READ_ONLY|READ_WRITE}]", "When enabled, files are memory-mapped instead of being read entirely on request." }, + { LNK_CmdSwitch_Rad_BootMode, 0, "RAD_BOOT_MODE", "[:LINKER|TYPE_SERVER]", "Overrides default boot program." }, { LNK_CmdSwitch_Rad_Debug, 0, "RAD_DEBUG", "[:NO]", "Emit RAD debug info file." }, { LNK_CmdSwitch_Rad_DebugAltPath, 0, "RAD_DEBUGALTPATH", ":PATH", "Alternative output path fof the RDI." }, { LNK_CmdSwitch_Rad_DebugName, 0, "RAD_DEBUG_NAME", ":FILENAME", "Set file name for RAD debug info file." }, @@ -98,6 +102,10 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, 0, "RAD_UNRESOLVED_SYMBOL_REF_LIMIT", ":#", "Limit number of unresolved symbol references linker reports." }, { LNK_CmdSwitch_Rad_Version, 0, "RAD_VERSION", "", "Print version and exit." }, { LNK_CmdSwitch_Rad_Workers, 0, "RAD_WORKERS", ":#", "Set number of workers created in the pool. Number is capped at 1024. When /RAD_SHARED_THREAD_POOL is specified this number cant exceed /RAD_SHARED_THREAD_POOL_MAX_WORKERS." }, + { LNK_CmdSwitch_Rad_WorkDir, 0, "RAD_WORK_DIR", ":PATH", "Working directory used for stable debug paths." }, + + { LNK_CmdSwitch_RadTypeServer, 0, "RAD_TYPE_SERVER", "", "Boot in type server mode." }, + { LNK_CmdSwitch_RadTypeServer_MatchObj, 0, "RAD_TYPE_SERVER_MATCH_OBJ", ":OBJ_PATH", "Obj paths that match OBJ_PATH have their type server replaced." }, { LNK_CmdSwitch_Help, 0, "HELP", "", "" }, { LNK_CmdSwitch_Help, 0, "?", "", "" }, @@ -210,32 +218,18 @@ lnk_type_name_hash_mode_from_string(String8 name) return LNK_TypeNameHashMode_Null; } -internal LNK_CmdOption * -lnk_cmd_line_push_option_if_not_presentf(Arena *arena, LNK_CmdLine *cmd_line, LNK_CmdSwitchType cmd_switch_type, char *param_fmt, ...) +internal String8List +lnk_cmd_line_values_from_switch(Arena *arena, LNK_CmdLine cmd_line, LNK_CmdSwitchType cmd_switch) { - LNK_CmdOption *opt = 0; - String8 cmd_switch_name = lnk_string_from_cmd_switch_type(cmd_switch_type); - if (!lnk_cmd_line_has_option_string(*cmd_line, cmd_switch_name)) { - va_list param_args; - va_start(param_args, param_fmt); - String8 param_str = push_str8fv(arena, param_fmt, param_args); - va_end(param_args); - - opt = lnk_cmd_line_push_option_string(arena, cmd_line, cmd_switch_name, param_str); - } - return opt; -} - -internal LNK_CmdOption * -lnk_cmd_line_push_optionf(Arena *arena, LNK_CmdLine *cmd_line, LNK_CmdSwitchType cmd_switch, char *param_fmt, ...) -{ - va_list param_args; - va_start(param_args, param_fmt); - String8 param_str = push_str8fv(arena, param_fmt, param_args); - va_end(param_args); + String8List values = {0}; String8 cmd_switch_name = lnk_string_from_cmd_switch_type(cmd_switch); - LNK_CmdOption *opt = lnk_cmd_line_push_option_string(arena, cmd_line, cmd_switch_name, param_str); - return opt; + for EachNode(cmd, LNK_CmdOption, cmd_line.first_option) { + if (str8_match(cmd->string, cmd_switch_name, StringMatchFlag_CaseInsensitive)) { + String8List value_strings = str8_list_copy(arena, &cmd->value_strings); + str8_list_concat_in_place(&values, &value_strings); + } + } + return values; } internal B32 @@ -363,7 +357,7 @@ lnk_cmd_switch_parse_tuple(LNK_Obj *obj, LNK_CmdSwitchType cmd_switch, String8Li tuple_out->v[1] = b; return 1; } else { - lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable ot parse second parameter \"%S\"", value_strings.last->string); + lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse second parameter \"%S\"", value_strings.last->string); } } else { lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse first parameter \"%S\"", value_strings.first->string); @@ -930,7 +924,7 @@ lnk_is_section_removed(LNK_Config *config, String8 section_name) internal B32 lnk_is_dll_delay_load(LNK_Config *config, String8 dll_name) { - return hash_table_search_path_u64(config->delay_load_ht, dll_name, 0); + return hash_map_search_path_u64(&config->delay_load_ht, dll_name) != 0; } internal String8 @@ -954,21 +948,21 @@ internal void lnk_push_disallow_lib(LNK_Config *config, String8 path) { String8 lib_name = lnk_get_lib_name(path); - hash_table_push_path_u64(config->arena, config->disallow_lib_ht, lib_name, 0); + hash_map_push_path_u64(config->arena, &config->disallow_lib_ht, lib_name, 1); } internal B32 lnk_is_lib_disallowed(LNK_Config *config, String8 path) { String8 lib_name = lnk_get_lib_name(path); - return hash_table_search_path(config->disallow_lib_ht, lib_name) != 0; + return hash_map_search_path_u64(&config->disallow_lib_ht, lib_name) != 0; } internal void lnk_include_symbol(LNK_Config *config, String8 name, LNK_Obj *obj) { // is this a duplicate symbol? - if (hash_table_search_string_raw(config->include_symbol_ht, name)) { + if (hash_map_search_string_raw(&config->include_symbol_ht, name)) { return; } @@ -981,7 +975,7 @@ lnk_include_symbol(LNK_Config *config, String8 name, LNK_Obj *obj) SLLQueuePush(config->include_symbol_list.first, config->include_symbol_list.last, node); config->include_symbol_list.count += 1; - hash_table_push_string_raw(config->arena, config->include_symbol_ht, name, node); + hash_map_push_string_raw(config->arena, &config->include_symbol_ht, name, node); } internal void @@ -1117,6 +1111,23 @@ lnk_unwrap_rsp(Arena *arena, String8List arg_list) return result; } +internal void +lnk_apply_write_temp_files(Arena *arena, LNK_Config *config) +{ + if (config->rad_chunk_map_name.size) { + config->temp_rad_chunk_map_name = push_str8f(arena, "%S.tmp%x", config->rad_chunk_map_name, config->time_stamp); + } + if (config->out_path.size) { + config->temp_out_path = push_str8f(arena, "%S.tmp%x", config->out_path, config->time_stamp); + } + if (config->pdb_name.size) { + config->temp_pdb_name = push_str8f(arena, "%S.tmp%x", config->pdb_name, config->time_stamp); + } + if (config->rad_debug_name.size) { + config->temp_rad_debug_name = push_str8f(arena, "%S.tmp%x", config->rad_debug_name, config->time_stamp); + } +} + internal void lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List value_strings, LNK_Obj *obj) { @@ -1153,19 +1164,19 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List if (value_strings.node_count == 1) { LNK_AltName alt_name; if (lnk_parse_alt_name_directive(value_strings.first->string, obj, &alt_name)) { - String8 to_extant = {0}; - if (hash_table_search_string_string(config->alt_name_ht, alt_name.from, &to_extant)) { - if (str8_match(to_extant, alt_name.to, 0)) { + String8 *to_extant = hash_map_search_string_string(&config->alt_name_ht, alt_name.from); + if (to_extant) { + if (str8_match(*to_extant, alt_name.to, 0)) { // ignore, duplicate } else { - lnk_error_obj(LNK_Error_AlternateNameConflict, obj, "conflicting alternative name: existing '%S=%S' vs. new '%S=%S'", alt_name.from, to_extant, alt_name.from, alt_name.to); + lnk_error_obj(LNK_Error_AlternateNameConflict, obj, "conflicting alternative name: existing '%S=%S' vs. new '%S=%S'", alt_name.from, *to_extant, alt_name.from, alt_name.to); } } else { alt_name.from = push_str8_copy(config->arena, alt_name.from); alt_name.to = push_str8_copy(config->arena, alt_name.to); lnk_alt_name_list_push(config->arena, &config->alt_name_list, alt_name); - hash_table_push_string_string(config->arena, config->alt_name_ht, alt_name.from, alt_name.to); + hash_map_push_string_string(config->arena, &config->alt_name_ht, alt_name.from, alt_name.to); } } } else { @@ -1252,9 +1263,9 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List case LNK_CmdSwitch_DelayLoad: { for (String8Node *name_n = value_strings.first; name_n != 0; name_n = name_n->next) { - if (hash_table_search_path_u64(config->delay_load_ht, name_n->string, 0)) { continue; } + if (hash_map_search_path_u64(&config->delay_load_ht, name_n->string)) { continue; } String8 name = push_str8_copy(config->arena, name_n->string); - hash_table_push_path_u64(config->arena, config->delay_load_ht, name, 0); + hash_map_push_path_u64(config->arena, &config->delay_load_ht, name, 1); str8_list_push(config->arena, &config->delay_load_dll_list, name); } } break; @@ -1283,7 +1294,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List PE_ExportParse export_parse = {0}; if (lnk_parse_export_directive_ex(config->arena, value_strings, obj, &export_parse)) { String8 export_name = pe_name_from_export_parse(&export_parse); - PE_ExportParseNode *exp_n = hash_table_search_string_raw(config->export_ht, export_name); + PE_ExportParseNode *exp_n = hash_map_search_string_raw(&config->export_ht, export_name); if (exp_n == 0) { // make sure export is defined @@ -1294,7 +1305,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List // push new export exp_n = pe_export_parse_list_push(config->arena, &config->export_symbol_list, export_parse); - hash_table_push_string_raw(config->arena, config->export_ht, export_name, exp_n); + hash_map_push_string_raw(config->arena, &config->export_ht, export_name, exp_n); } else { B32 is_ambiguous = 1; PE_ExportParse *extant_export = &exp_n->data; @@ -1332,7 +1343,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List break; } - LNK_AltName *current = hash_table_search_string_raw(config->fail_if_mismatch_ht, dir.from); + LNK_AltName *current = hash_map_search_string_raw(&config->fail_if_mismatch_ht, dir.from); if (current) { if ( ! str8_match(current->to, dir.to, 0)) { lnk_error_cmd_switch(LNK_Error_FailIfMismatch, obj, cmd_switch, @@ -1347,7 +1358,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List n->from = push_str8_copy(config->arena, dir.from); n->to = push_str8_copy(config->arena, dir.to); n->obj = obj; - hash_table_push_string_raw(config->arena, config->fail_if_mismatch_ht, n->from, n); + hash_map_push_string_raw(config->arena, &config->fail_if_mismatch_ht, n->from, n); } } break; @@ -1616,10 +1627,10 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List } else { for (String8Node *lib_n = value_strings.first; lib_n != 0; lib_n = lib_n->next) { String8 lib_name = lnk_get_lib_name(lib_n->string); - if (hash_table_search_path_raw(config->disallow_lib_ht, lib_name)) { + if (hash_map_search_path_u64(&config->disallow_lib_ht, lib_name)) { continue; } - hash_table_push_path_raw(config->arena, config->disallow_lib_ht, lib_name, 0); + hash_map_push_path_u64(config->arena, &config->disallow_lib_ht, lib_name, 1); } } } break; @@ -1782,7 +1793,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List String8 lib_name; if (lnk_cmd_switch_parse_string(obj, cmd_switch, value_strings, &lib_name)) { lib_name = str8_chop_last_dot(str8_skip_last_slash(lib_name)); - hash_table_push_path_string(config->arena, config->whole_archive_ht, lib_name, str8_zero()); + hash_map_push_path_u64(config->arena, &config->whole_archive_ht, lib_name, 1); } } } break; @@ -1800,15 +1811,40 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List str8_list_concat_in_place(&config->alt_pch_dirs, &dirs); } break; + //case LNK_CmdSwitch_Rad_BuildExp: { + // LNK_SwitchState state; + // if (lnk_cmd_switch_parse_flag(obj, cmd_switch, value_strings, &state)) { + // config->build_exp = (state == LNK_SwitchState_Yes); + // } + //} break; + case LNK_CmdSwitch_Rad_BuildInfo: { lnk_print_build_info(); abort_self(0); } break; + case LNK_CmdSwitch_Rad_BuildImpLib: { + LNK_SwitchState state; + if (lnk_cmd_switch_parse_flag(obj, cmd_switch, value_strings, &state)) { + config->build_imp_lib = (state == LNK_SwitchState_Yes); + } + } break; + case LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll: { lnk_cmd_switch_set_flag_64(obj, cmd_switch, value_strings, &config->flags, LNK_ConfigFlag_CheckUnusedDelayLoadDll); } break; + case LNK_CmdSwitch_Rad_DataDirCount: { + U64 data_dir_count = 0; + if (lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &data_dir_count, LNK_ParseU64Flag_CheckUnder32bit)) { + if (1 <= data_dir_count && data_dir_count <= PE_DataDirectoryIndex_COUNT) { + config->data_dir_count = data_dir_count; + } else { + lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid data directory count %llu, expected 1 through %u", data_dir_count, PE_DataDirectoryIndex_COUNT); + } + } + } break; + case LNK_CmdSwitch_Rad_Map: { lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->rad_chunk_map_name); config->rad_chunk_map = LNK_SwitchState_Yes; @@ -1819,7 +1855,39 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List } break; case LNK_CmdSwitch_Rad_MemoryMapFiles: { - lnk_cmd_switch_set_flag_32(obj, cmd_switch, value_strings, &config->io_flags, LNK_IO_Flags_MemoryMapFiles); + if (value_strings.node_count == 0) { + config->io_flags &= ~LNK_IO_Flags_MemoryMapFilesReadWrite; + config->io_flags |= LNK_IO_Flags_MemoryMapFilesReadOnly; + } else if (value_strings.node_count == 1) { + String8 value = value_strings.first->string; + if (str8_matchi(value, str8_lit("no"))) { + config->io_flags &= ~(LNK_IO_Flags_MemoryMapFilesReadOnly|LNK_IO_Flags_MemoryMapFilesReadWrite); + } else if (str8_matchi(value, str8_lit("yes")) || str8_matchi(value, str8_lit("read_only"))) { + config->io_flags &= ~LNK_IO_Flags_MemoryMapFilesReadWrite; + config->io_flags |= LNK_IO_Flags_MemoryMapFilesReadOnly; + } else if (str8_matchi(value, str8_lit("read_write"))) { + config->io_flags &= ~LNK_IO_Flags_MemoryMapFilesReadOnly; + config->io_flags |= LNK_IO_Flags_MemoryMapFilesReadWrite; + } else { + lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid parameter: \"%S\", expected NO, READ_ONLY, or READ_WRITE", value); + } + } else { + lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch); + } + } break; + + case LNK_CmdSwitch_Rad_BootMode: { + if (value_strings.node_count == 1) { + if (str8_matchi(value_strings.first->string, str8_lit("linker"))) { + config->boot_mode = LNK_BootMode_Linker; + } else if (str8_matchi(value_strings.first->string, str8_lit("type_server"))) { + config->boot_mode = LNK_BootMode_TypeServer; + } else { + lnk_error_cmd_switch(LNK_Error_Boot, obj, cmd_switch, "unknown value: \"%S\".", value_strings.first->string); + } + } else { + lnk_error_cmd_switch_invalid_param_count(LNK_Error_Boot, obj, cmd_switch); + } } break; case LNK_CmdSwitch_Rad_Debug: { @@ -2049,6 +2117,9 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List case LNK_CmdSwitch_Rad_WriteTempFiles: { lnk_cmd_switch_parse_flag(obj, cmd_switch, value_strings, &config->write_temp_files); + if (config->write_temp_files == LNK_SwitchState_Yes) { + lnk_apply_write_temp_files(config->arena, config); + } } break; case LNK_CmdSwitch_Rad_TimeStamp: { @@ -2086,58 +2157,73 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List } } break; + case LNK_CmdSwitch_Rad_WorkDir: { + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->work_dir); + } break; + case LNK_CmdSwitch_Help: { lnk_print_help(); abort_self(0); } break; + + case LNK_CmdSwitch_RadTypeServer: { + if (lnk_cmd_switch_parse_flag(obj, cmd_switch, value_strings, &config->type_server)) { + if (config->type_server == LNK_SwitchState_Yes) { + config->boot_mode = LNK_BootMode_TypeServer; + } + } + } break; + + case LNK_CmdSwitch_RadTypeServer_MatchObj: { + String8List value_strings_copy = str8_list_copy(config->arena, &value_strings); + str8_list_concat_in_place(&config->type_server_match_obj, &value_strings_copy); + } break; } scratch_end(scratch); } +internal void +lnk_config_pushf(LNK_Config *config, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + LNK_CmdLine cmd_line = lnk_cmd_line_from_stringfv_windows_rules(config->arena, fmt, args); + va_end(args); + + for EachNode(cmd, LNK_CmdOption, cmd_line.first_option) { + lnk_apply_cmd_option_to_config(config, cmd->string, cmd->value_strings, &(LNK_Obj){0}); + } +} + internal LNK_Config * -lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line) +lnk_config_init(LNK_CmdLine cmd_line) { ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); + Temp scratch = scratch_begin(0,0); Arena *arena = arena_alloc(); LNK_Config *config = push_array(arena, LNK_Config, 1); - config->arena = arena; - config->raw_cmd_line = str8_list_copy(arena, &raw_cmd_line); - config->work_dir = get_current_path(arena); - config->build_imp_lib = 1; - config->build_exp = 1; - config->heap_reserve = MB(1); - config->heap_commit = KB(4); - config->stack_reserve = MB(1); - config->stack_commit = KB(4); - config->pdb_hash_type_names = LNK_TypeNameHashMode_None; - config->pdb_hash_type_name_length = 8; - config->data_dir_count = PE_DataDirectoryIndex_COUNT; - config->export_ht = hash_table_init(arena, max_U16/2); - config->alt_name_ht = hash_table_init(arena, 0x100); - config->include_symbol_ht = hash_table_init(arena, 0x100); - config->delay_load_ht = hash_table_init(arena, 0x100); - config->disallow_lib_ht = hash_table_init(arena, 0x100); - config->fail_if_mismatch_ht = hash_table_init(arena, 0x100); - config->whole_archive_ht = hash_table_init(arena, 0x100); + config->arena = arena; + config->raw_cmd_line = str8_list_copy(arena, &cmd_line.raw_cmd_line); + config->work_dir = get_current_path(arena); - // process command line switches - for (LNK_CmdOption *cmd = cmd_line.first_option; cmd != 0; cmd = cmd->next) { + // apply command line switches + for EachNode(cmd, LNK_CmdOption, cmd_line.first_option) { lnk_apply_cmd_option_to_config(config, cmd->string, cmd->value_strings, 0); } + // in shared thread pool mode force fixed number of workers + if (lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Rad_SharedThreadPool) && + !lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers)) { + config->max_worker_count = get_system_info()->logical_processor_count; + } + // :manifest_input if (lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_ManifestInput)) { if (config->manifest_opt == LNK_ManifestOpt_Embed) { - for (LNK_CmdOption *cmd = cmd_line.first_option; cmd != 0; cmd = cmd->next) { - LNK_CmdSwitchType cmd_switch = lnk_cmd_switch_type_from_string(cmd->string); - if (cmd_switch == LNK_CmdSwitch_ManifestInput) { - String8List manifest_list = str8_list_copy(arena, &cmd->value_strings); - str8_list_concat_in_place(&config->input_list[LNK_Input_Manifest], &manifest_list); - } - } + String8List manifest_list = lnk_cmd_line_values_from_switch(arena, cmd_line, LNK_CmdSwitch_ManifestInput); + str8_list_concat_in_place(&config->input_list[LNK_Input_Manifest], &manifest_list); } else { lnk_error_cmd_switch(LNK_Error_Cmdl, 0, LNK_CmdSwitch_ManifestInput, "missing /MANIFEST:EMBED"); } @@ -2153,9 +2239,9 @@ lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line) } // input files - for (String8Node *input_node = cmd_line.input_list.first; input_node != 0; input_node = input_node->next) { + for EachNode(input_node, String8Node, cmd_line.input_list.first) { String8 path = push_str8_copy(arena, input_node->string); - String8 ext = str8_skip_last_dot(path); + String8 ext = str8_skip_last_dot(path); // map file extension to input type LNK_InputType input_type = lnk_input_type_from_string(ext); @@ -2285,57 +2371,46 @@ lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line) config->imp_lib_name = full_path_from_path(arena, config->imp_lib_name); config->manifest_name = full_path_from_path(arena, config->manifest_name); - // collect env vars - HashTable *env_vars = hash_table_init(scratch.arena, 512); + // set up env vars + HashMap env_vars = lnk_env_vars_from_process_info(scratch.arena, get_process_info(), LNK_EnvVarRule_Batch); { -#if OS_WINDOWS - ProcessInfo *process_info = get_process_info(); - for (String8Node *node = process_info->environment.first; node != 0; node = node->next) { - String8List list = str8_split_by_string_chars(scratch.arena, node->string, str8_lit("="), 0); - - String8 key = list.first->string; - String8 val = str8_zero(); - if (list.node_count == 2) { - val = list.last->string; - } else if (list.node_count > 2) { - U64 sep_idx = str8_find_needle(node->string, node->string.size, str8_lit("="), 0); - val = str8_skip(node->string, sep_idx+1); + // define linker env vars + struct { String8 key, value; } key_value_str8_table[] = { + { str8_lit("_pdb"), str8_skip_last_slash(config->pdb_name) }, + { str8_lit("_ext"), str8_skip_last_dot(config->out_path) }, + { str8_lit("_rad_pdb_path"), config->pdb_name }, + { str8_lit("_rad_rdi"), str8_skip_last_slash(config->rad_debug_name) }, + { str8_lit("_radi_rdi_path"), config->rad_debug_name }, + }; + for EachElement(i, key_value_str8_table) { + if (lnk_env_var_from_map(&env_vars, key_value_str8_table[i].key)) { + lnk_log(LNK_Log_Debug, "Env var already exists: %S\n",key_value_str8_table[i].key); } - - hash_table_push_path_string(scratch.arena, env_vars, key, val); - } -#endif - } - - // define linker env vars - hash_table_push_path_string(scratch.arena, env_vars, str8_lit("_pdb"), str8_skip_last_slash(config->pdb_name)); - hash_table_push_path_string(scratch.arena, env_vars, str8_lit("_ext"), str8_skip_last_dot(config->out_path)); - hash_table_push_path_string(scratch.arena, env_vars, str8_lit("_rad_pdb_path"), config->pdb_name); - hash_table_push_path_string(scratch.arena, env_vars, str8_lit("_rad_rdi"), str8_skip_last_slash(config->rad_debug_name)); - hash_table_push_path_string(scratch.arena, env_vars, str8_lit("_rad_rdi_path"), config->rad_debug_name); - - // collect LIB and LIBPATH - if (config->flags & LNK_ConfigFlag_EnvLib) { - BucketNode *lib = hash_table_search_path(env_vars, str8_lit("lib")); - if (lib) { - String8List val_list = str8_split_by_string_chars(scratch.arena, lib->v.value_string, str8_lit(";"), 0); - String8List val_list_copy = str8_list_copy(arena, &val_list); - str8_list_concat_in_place(&config->lib_dir_list, &val_list_copy); + lnk_env_var_batchf(scratch.arena, &env_vars, "%S=%S", key_value_str8_table[i].key, key_value_str8_table[i].value); } - BucketNode *lib_path = hash_table_search_path(env_vars, str8_lit("libpath")); - if (lib_path) { - String8List val_list = str8_split_by_string_chars(scratch.arena, lib->v.value_string, str8_lit(";"), 0); - String8List val_list_copy = str8_list_copy(arena, &val_list); - str8_list_concat_in_place(&config->lib_dir_list, &val_list_copy); + if (config->flags & LNK_ConfigFlag_EnvLib) { + // collect LIB and LIBPATH + struct { String8List *config_list; char *key; } key_str8_list[] = { + { &config->lib_dir_list, "lib" }, + { &config->lib_dir_list, "lib_path" }, + }; + for EachElement(i, key_str8_list) { + LNK_EnvVar *var = lnk_env_var_from_mapf(&env_vars, key_str8_list[i].key); + if (var) { + String8List value = lnk_value_list_from_env_var(arena, var); + String8List value_copy = str8_list_copy(config->arena, &value); + str8_list_concat_in_place(key_str8_list[i].config_list, &value_copy); + } + } } } // :PdbAltPath - config->pdb_alt_path = lnk_expand_env_vars_windows(arena, env_vars, config->pdb_alt_path); + config->pdb_alt_path = lnk_expand_env_vars_windows(arena, &env_vars, config->pdb_alt_path); // :Rad_DebugAltPath - config->rad_debug_alt_path = lnk_expand_env_vars_windows(arena, env_vars, config->rad_debug_alt_path); + config->rad_debug_alt_path = lnk_expand_env_vars_windows(arena, &env_vars, config->rad_debug_alt_path); // create temporary files names if (config->write_temp_files == LNK_SwitchState_Yes) { diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 2f0f34cf..90e5355f 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -3,6 +3,13 @@ #pragma once +// entry points +typedef enum +{ + LNK_BootMode_Linker, + LNK_BootMode_TypeServer, +} LNK_BootMode; + #if OS_WINDOWS # define LNK_MANIFEST_MERGE_TOOL_NAME "mt.exe" #elif OS_LINUX || OS_MAC @@ -88,13 +95,16 @@ typedef enum LNK_CmdSwitch_Version, LNK_CmdSwitch_WholeArchive, - LNK_CmdSwitch_Rad_Age, LNK_CmdSwitch_Rad_AltPchDir, + LNK_CmdSwitch_Rad_BuildExp, LNK_CmdSwitch_Rad_BuildInfo, + LNK_CmdSwitch_Rad_BuildImpLib, LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, + LNK_CmdSwitch_Rad_DataDirCount, LNK_CmdSwitch_Rad_Debug, LNK_CmdSwitch_Rad_DebugAltPath, + LNK_CmdSwitch_Rad_BootMode, LNK_CmdSwitch_Rad_DebugName, LNK_CmdSwitch_Rad_DelayBind, LNK_CmdSwitch_Rad_DoMerge, @@ -110,6 +120,7 @@ typedef enum LNK_CmdSwitch_Rad_Map, LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, LNK_CmdSwitch_Rad_MemoryMapFiles, + LNK_CmdSwitch_Rad_Mode, LNK_CmdSwitch_Rad_MtPath, LNK_CmdSwitch_Rad_OsVer, LNK_CmdSwitch_Rad_PageSize, @@ -126,7 +137,12 @@ typedef enum LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, LNK_CmdSwitch_Rad_Version, LNK_CmdSwitch_Rad_Workers, + LNK_CmdSwitch_Rad_WorkDir, LNK_CmdSwitch_Rad_WriteTempFiles, + + LNK_CmdSwitch_RadTypeServer, + LNK_CmdSwitch_RadTypeServer_MatchObj, + LNK_CmdSwitch_Help, LNK_CmdSwitch_Count @@ -262,9 +278,11 @@ typedef enum LNK_TypeNameHashMode_Full, } LNK_TypeNameHashMode; + typedef struct LNK_Config { Arena *arena; + LNK_BootMode boot_mode; LNK_ConfigFlags flags; LNK_DebugMode debug_mode; B32 ghash; @@ -352,19 +370,21 @@ typedef struct LNK_Config String8 delay_load_helper_name; String8List remove_sections; LNK_IO_Flags io_flags; - HashTable *export_ht; - HashTable *alt_name_ht; - HashTable *include_symbol_ht; - HashTable *delay_load_ht; - HashTable *disallow_lib_ht; - HashTable *fail_if_mismatch_ht; - HashTable *whole_archive_ht; + HashMap export_ht; + HashMap alt_name_ht; + HashMap include_symbol_ht; + HashMap delay_load_ht; + HashMap disallow_lib_ht; + HashMap fail_if_mismatch_ht; + HashMap whole_archive_ht; B32 whole_archive_all; U64 unresolved_symbol_limit; U64 unresolved_symbol_ref_limit; LNK_SwitchState map_lines_for_unresolved_symbols; String8List alt_pch_dirs; LLVM_GHashAlg type_hash_alg; + LNK_SwitchState type_server; + String8List type_server_match_obj; } LNK_Config; // --- MSVC Error Codes -------------------------------------------------------- @@ -503,12 +523,8 @@ internal LNK_InputType lnk_input_type_from_string(String8 string); internal LNK_DebugMode lnk_debug_mode_from_string(String8 string); internal LNK_TypeNameHashMode lnk_type_name_hash_mode_from_string(String8 string); -// --- Command Line Helpers ---------------------------------------------------- - -internal LNK_CmdOption * lnk_cmd_line_push_option_if_not_presentf(Arena *arena, LNK_CmdLine *cmd_line, LNK_CmdSwitchType cmd_switch_type, char *param_fmt, ...); -internal LNK_CmdOption * lnk_cmd_line_push_optionf (Arena *arena, LNK_CmdLine *cmd_line, LNK_CmdSwitchType cmd_switch_type, char *param_fmt, ...); - -internal B32 lnk_cmd_line_has_switch(LNK_CmdLine cmd_line, LNK_CmdSwitchType cmd_switch_type); +internal String8List lnk_cmd_line_values_from_switch(Arena *arena, LNK_CmdLine cmd_line, LNK_CmdSwitchType cmd_switch_type); +internal B32 lnk_cmd_line_has_switch (LNK_CmdLine cmd_line, LNK_CmdSwitchType cmd_switch_type); // --- Errors ------------------------------------------------------------------ @@ -564,6 +580,7 @@ internal void lnk_whole_archive(LNK_Config *config, String8 lib_name); // --- Config ------------------------------------------------------------------ internal void lnk_apply_cmd_option_to_config(LNK_Config *config, String8 name, String8List value_list, struct LNK_Obj *obj); +internal void lnk_config_pushf(LNK_Config *config, char *fmt, ...); -internal LNK_Config * lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line); +internal LNK_Config * lnk_config_init(LNK_CmdLine cmd_line); diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index da6ab1bd..40fa31f8 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -2461,11 +2461,15 @@ THREAD_POOL_TASK_FUNC(lnk_push_dbi_sec_contrib_task) } internal String8List -lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config *config, LNK_SymbolTable *symtab, LNK_CodeViewInput *cv, LNK_MergedTypes cv_types) +lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config *config, LNK_SymbolTable *symtab, LNK_CodeViewInput *cv, LNK_MergedTypes cv_types, LNK_PDB_BuilderFlags builder_flags) { ProfBeginFunction(); Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); + if (builder_flags == LNK_PDB_BuilderFlag_All) { + builder_flags = ~0; + } + LNK_BuildPdb task = { .image_data = image_data, .symtab = symtab, @@ -2495,83 +2499,92 @@ lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config } // push types - pdb_type_server_push_parallel(tp, task.pdb->type_servers[CV_TypeIndexSource_IPI], cv_types.count[CV_TypeIndexSource_IPI], cv_types.v[CV_TypeIndexSource_IPI]); - pdb_type_server_push_parallel(tp, task.pdb->type_servers[CV_TypeIndexSource_TPI], cv_types.count[CV_TypeIndexSource_TPI], cv_types.v[CV_TypeIndexSource_TPI]); + if (builder_flags & LNK_PDB_BuilderFlag_Ipi) { + pdb_type_server_push_parallel(tp, task.pdb->type_servers[CV_TypeIndexSource_IPI], cv_types.count[CV_TypeIndexSource_IPI], cv_types.v[CV_TypeIndexSource_IPI]); + } + if (builder_flags & LNK_PDB_BuilderFlag_Tpi) { + pdb_type_server_push_parallel(tp, task.pdb->type_servers[CV_TypeIndexSource_TPI], cv_types.count[CV_TypeIndexSource_TPI], cv_types.v[CV_TypeIndexSource_TPI]); + } ProfBegin("Merge String Tables"); task.string_ht = cv_dedup_string_tables(tp_arena, tp, cv->obj_count, cv->debug_s_arr); cv_string_hash_table_assign_buffer_offsets(tp, task.string_ht); ProfEnd(); - ProfScope ("Alloc Modules") - for EachIndex(obj_idx, cv->obj_count) { - task.mod_arr[obj_idx] = dbi_push_module(task.pdb->dbi, cv->obj_arr[obj_idx]->path, lnk_obj_get_lib_path(cv->obj_arr[obj_idx])); - } + if (builder_flags & LNK_PDB_BuilderFlag_Modules) { + ProfScope ("Alloc Modules") + for EachIndex(obj_idx, cv->obj_count) + task.mod_arr[obj_idx] = dbi_push_module(task.pdb->dbi, cv->obj_arr[obj_idx]->path, lnk_obj_get_lib_path(cv->obj_arr[obj_idx])); - ProfScope("Move Global Symbols") - tp_for_parallel(tp, 0, tp->worker_count, lnk_move_global_symbols_to_gsi, &task); + ProfScope("Move Global Symbols") + tp_for_parallel(tp, 0, tp->worker_count, lnk_move_global_symbols_to_gsi, &task); - ProfScope("Build GSI and PSI") - pdb_build_gsi_psi(tp, task.pdb); + ProfScope("Build GSI and PSI") + pdb_build_gsi_psi(tp, task.pdb); - ProfScope("Write Modules") - tp_for_parallel(tp, 0, tp->worker_count, lnk_write_pdb_modules, &task); + ProfScope("Write Modules") + tp_for_parallel(tp, 0, tp->worker_count, lnk_write_pdb_modules, &task); + } ProfBegin("Add string tables"); pdb_strtab_add_cv_string_hash_table(&task.pdb->info->strtab, task.string_ht); ProfEnd(); - ProfBegin("Build Section Contrib Map"); - { - ProfBegin("Build DBI Section Headers"); - for (U64 sect_idx = 1; sect_idx < task.image_section_table_count; sect_idx += 1) { - dbi_push_section(task.pdb->dbi, task.image_section_table[sect_idx]); + if (builder_flags & LNK_PDB_BuilderFlag_SC) { + ProfBegin("Build Section Contrib Map"); + { + ProfBegin("Build DBI Section Headers"); + for (U64 sect_idx = 1; sect_idx < task.image_section_table_count; sect_idx += 1) { + dbi_push_section(task.pdb->dbi, task.image_section_table[sect_idx]); + } + ProfEnd(); + + for EachIndex(i, task.image_section_table_count) { + COFF_SectionHeader *sect_header = task.image_section_table[i]; + if (~sect_header->flags & COFF_SectionFlag_CntUninitializedData) { + task.image_section_file_ranges.v[task.image_section_file_ranges.count++] = rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize); + } + task.image_section_virt_ranges.v[i] = rng_1u64(sect_header->voff, sect_header->voff + sect_header->vsize); + } + + task.sc_list = push_array(scratch.arena, PDB_DbiSectionContribList, cv->obj_count); + tp_for_parallel(tp, tp_arena, cv->obj_count, lnk_push_dbi_sec_contrib_task, &task); + dbi_sec_list_concat_arr(&task.pdb->dbi->sec_contrib_list, cv->obj_count, task.sc_list); } ProfEnd(); - - for EachIndex(i, task.image_section_table_count) { - COFF_SectionHeader *sect_header = task.image_section_table[i]; - if (~sect_header->flags & COFF_SectionFlag_CntUninitializedData) { - task.image_section_file_ranges.v[task.image_section_file_ranges.count++] = rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize); - } - task.image_section_virt_ranges.v[i] = rng_1u64(sect_header->voff, sect_header->voff + sect_header->vsize); - } - - task.sc_list = push_array(scratch.arena, PDB_DbiSectionContribList, cv->obj_count); - tp_for_parallel(tp, tp_arena, cv->obj_count, lnk_push_dbi_sec_contrib_task, &task); - dbi_sec_list_concat_arr(&task.pdb->dbi->sec_contrib_list, cv->obj_count, task.sc_list); } - ProfEnd(); - ProfBegin("Build NatVis"); - { - 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, config->io_flags, natvis_file_path_arr); + if (builder_flags & LNK_PDB_BuilderFlag_NATVIS) { + ProfBegin("Build NatVis"); + { + 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, config->io_flags, natvis_file_path_arr); - for EachIndex(i, natvis_file_data_arr.count) { - String8 natvis_file_path = natvis_file_path_arr.v[i]; - String8 natvis_file_data = natvis_file_data_arr.v[i]; + for EachIndex(i, natvis_file_data_arr.count) { + String8 natvis_file_path = natvis_file_path_arr.v[i]; + String8 natvis_file_data = natvis_file_data_arr.v[i]; - // did we read the file? - if (natvis_file_data.size == 0) { - lnk_error(LNK_Warning_FileNotFound, "unable to open natvis file \"%S\"", natvis_file_path); - continue; - } + // did we read the file? + if (natvis_file_data.size == 0) { + lnk_error(LNK_Warning_FileNotFound, "unable to open natvis file \"%S\"", natvis_file_path); + continue; + } - // sanity check file extension or VS wont load NatVis - String8 ext = str8_skip_last_dot(natvis_file_path); - if (!str8_match(ext, str8_lit("natvis"), StringMatchFlag_CaseInsensitive)) { - lnk_error(LNK_Warning_Natvis, "Visual Studio expects .natvis extension: \"%S\"", natvis_file_path); - } + // sanity check file extension or VS wont load NatVis + String8 ext = str8_skip_last_dot(natvis_file_path); + if (!str8_match(ext, str8_lit("natvis"), StringMatchFlag_CaseInsensitive)) { + lnk_error(LNK_Warning_Natvis, "Visual Studio expects .natvis extension: \"%S\"", natvis_file_path); + } - // add natvis to PDB - PDB_SrcError error = pdb_add_src(task.pdb->info, task.pdb->msf, natvis_file_path, natvis_file_data, PDB_SrcComp_NULL); - if (error != PDB_SrcError_OK) { - lnk_error(LNK_Error_Natvis, "%S", pdb_string_from_src_error(error)); + // add natvis to PDB + PDB_SrcError error = pdb_add_src(task.pdb->info, task.pdb->msf, natvis_file_path, natvis_file_data, PDB_SrcComp_NULL); + if (error != PDB_SrcError_OK) { + lnk_error(LNK_Error_Natvis, "%S", pdb_string_from_src_error(error)); + } } } + ProfEnd(); } - ProfEnd(); pdb_build(tp, tp_arena, task.pdb, task.string_ht, 0, cv->is_stripped); diff --git a/src/linker/lnk_debug_info.h b/src/linker/lnk_debug_info.h index d93c66eb..5a16f986 100644 --- a/src/linker/lnk_debug_info.h +++ b/src/linker/lnk_debug_info.h @@ -130,6 +130,16 @@ typedef struct //////////////////////////////// // PDB +typedef enum +{ + LNK_PDB_BuilderFlag_All = 0, + LNK_PDB_BuilderFlag_Tpi = (1<<0), + LNK_PDB_BuilderFlag_Ipi = (1<<1), + LNK_PDB_BuilderFlag_Modules = (1<<2), + LNK_PDB_BuilderFlag_SC = (1<<4), + LNK_PDB_BuilderFlag_NATVIS = (1<<5), +} LNK_PDB_BuilderFlags; + typedef struct { String8 image_data; @@ -180,5 +190,5 @@ internal void lnk_replace_type_names_with_hashes (TP_Context *tp, TP //////////////////////////////// // PDB -internal String8List lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config *config, LNK_SymbolTable *symtab, LNK_CodeViewInput *cv, LNK_MergedTypes cv_types); +internal String8List lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config *config, LNK_SymbolTable *symtab, LNK_CodeViewInput *cv, LNK_MergedTypes cv_types, LNK_PDB_BuilderFlags builder_flags); diff --git a/src/linker/lnk_io.c b/src/linker/lnk_io.c index 20e180c0..499ba728 100644 --- a/src/linker/lnk_io.c +++ b/src/linker/lnk_io.c @@ -215,19 +215,36 @@ THREAD_POOL_TASK_FUNC(lnk_memory_map_file_task) Temp scratch = scratch_begin(&arena, 1); #if OS_WINDOWS String16 path16 = str16_from_8(scratch.arena, task->path_arr.v[task_id]); - HANDLE file_handle = CreateFileW(path16.str, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (file_handle != INVALID_HANDLE_VALUE) { - HANDLE mapping_handle = CreateFileMappingA(file_handle, 0, PAGE_WRITECOPY, 0, 0, 0); - if (mapping_handle != INVALID_HANDLE_VALUE) { - LARGE_INTEGER file_size = {0}; - GetFileSizeEx(file_handle, &file_size); - void *file_data = MapViewOfFile(mapping_handle, FILE_MAP_COPY, 0, 0, file_size.QuadPart); - if (file_data) { - task->data_arr.v[task_id] = str8(file_data, file_size.QuadPart); + if (task->io_flags & LNK_IO_Flags_MemoryMapFilesReadWrite) { + HANDLE file_handle = CreateFileW(path16.str, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (file_handle != INVALID_HANDLE_VALUE) { + HANDLE mapping_handle = CreateFileMappingA(file_handle, 0, PAGE_READWRITE, 0, 0, 0); + if (mapping_handle != INVALID_HANDLE_VALUE) { + LARGE_INTEGER file_size = {0}; + GetFileSizeEx(file_handle, &file_size); + void *file_data = MapViewOfFile(mapping_handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, file_size.QuadPart); + if (file_data) { + task->data_arr.v[task_id] = str8(file_data, file_size.QuadPart); + } + CloseHandle(mapping_handle); } - CloseHandle(mapping_handle); + CloseHandle(file_handle); + } + } else { + HANDLE file_handle = CreateFileW(path16.str, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (file_handle != INVALID_HANDLE_VALUE) { + HANDLE mapping_handle = CreateFileMappingA(file_handle, 0, PAGE_WRITECOPY, 0, 0, 0); + if (mapping_handle != INVALID_HANDLE_VALUE) { + LARGE_INTEGER file_size = {0}; + GetFileSizeEx(file_handle, &file_size); + void *file_data = MapViewOfFile(mapping_handle, FILE_MAP_COPY, 0, 0, file_size.QuadPart); + if (file_data) { + task->data_arr.v[task_id] = str8(file_data, file_size.QuadPart); + } + CloseHandle(mapping_handle); + } + CloseHandle(file_handle); } - CloseHandle(file_handle); } #elif OS_LINUX int fd = open((char *)push_cstr(scratch.arena, task->path_arr.v[task_id]).str, O_RDONLY); @@ -253,7 +270,7 @@ lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, LNK_IO_Flags ProfBeginFunction(); LNK_DiskReader reader = {0}; - if (io_flags & LNK_IO_Flags_MemoryMapFiles) { + if (io_flags & (LNK_IO_Flags_MemoryMapFilesReadWrite|LNK_IO_Flags_MemoryMapFilesReadOnly)) { reader.io_flags = io_flags; reader.path_arr = path_arr; reader.data_arr.count = path_arr.count; diff --git a/src/linker/lnk_io.h b/src/linker/lnk_io.h index 3aff6fe9..ffa35a62 100644 --- a/src/linker/lnk_io.h +++ b/src/linker/lnk_io.h @@ -3,7 +3,8 @@ typedef U32 LNK_IO_Flags; enum { - LNK_IO_Flags_MemoryMapFiles = (1 << 0), + LNK_IO_Flags_MemoryMapFilesReadOnly = (1 << 0), + LNK_IO_Flags_MemoryMapFilesReadWrite = (1 << 1), }; typedef struct diff --git a/src/linker/lnk_log.h b/src/linker/lnk_log.h index dc94d47f..9890037c 100644 --- a/src/linker/lnk_log.h +++ b/src/linker/lnk_log.h @@ -55,6 +55,7 @@ typedef enum LNK_Error_AlternateNameConflict, LNK_Error_RelocationAgainstRemovedSection, LNK_Error_FailIfMismatch, + LNK_Error_Boot, LNK_Error_StopLast, LNK_Error_ContinueFirst, @@ -160,7 +161,7 @@ internal LNK_LogType lnk_log_type_from_string(String8 string); internal LNK_ErrorCodeStatus lnk_get_error_code_status(LNK_ErrorCode code); internal void lnk_internal_error(LNK_InternalError code, char *file, int line, char *fmt, ...); -#define lnk_invalid_path(...) lnk_internal_error(LNK_InternalError_InvalidPath, __FILE__, __LINE__, __VA_ARGS__) -#define lnk_not_implemented(...) lnk_internal_error(LNK_InternalError_NotImplemented, __FILE__, __LINE__, __VA_ARGS__) -#define lnk_incomplete_switch(...) lnk_internal_error(LNK_InternalError_IncompleteSwitch, __FILE__, __LINE__, __VA_ARGS__) +#define lnk_invalid_path(...) lnk_internal_error(LNK_InternalError_InvalidPath, __FILE__, __LINE__, ## __VA_ARGS__) +#define lnk_not_implemented(...) lnk_internal_error(LNK_InternalError_NotImplemented, __FILE__, __LINE__, ## __VA_ARGS__) +#define lnk_incomplete_switch(...) lnk_internal_error(LNK_InternalError_IncompleteSwitch, __FILE__, __LINE__, ## __VA_ARGS__) diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index 220af7f3..99760eb0 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -338,26 +338,51 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) obj->associated_sections = associated_sections; obj->self = &task->objs[task_id]; obj->link_member = input->link_member; + obj->debug_t_sect_idx = ~0; + obj->debug_p_sect_idx = ~0; + obj->debug_h_sect_idx = ~0; +} + +internal +THREAD_POOL_TASK_FUNC(lnk_obj_find_debug_t) +{ + LNK_Obj *obj = &((LNK_ObjNode *)raw_task)[task_id].data; + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + for EachIndex(sect_idx, obj->header.section_count_no_null) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + String8 sect_name = coff_name_from_section_header(str8_zero(), sect_header); + if (str8_match(sect_name, str8_lit(".debug$T"), 0)) { + obj->debug_t_sect_idx = sect_idx; + } else if (str8_match(sect_name, str8_lit(".debug$P"), 0)) { + obj->debug_p_sect_idx = sect_idx; + } else if (str8_match(sect_name, str8_lit(".debug$H"), 0)) { + obj->debug_h_sect_idx = sect_idx; + } + } } internal LNK_ObjNode * -lnk_obj_from_input_many(TP_Context *tp, TP_Arena *arena, COFF_MachineType machine, U64 inputs_count, LNK_Input **inputs) +lnk_obj_from_input_many(TP_Context *tp, TP_Arena *arena, LNK_Config *config, U64 inputs_count, LNK_Input **inputs) { LNK_ObjNode *objs = 0; if (inputs_count) { objs = push_array(arena->v[0], LNK_ObjNode, inputs_count); - tp_for_parallel(tp, arena, inputs_count, lnk_obj_initer, &(LNK_ObjIniter){ .inputs = inputs, .objs = objs, .machine = machine }); + tp_for_parallel(tp, arena, inputs_count, lnk_obj_initer, &(LNK_ObjIniter){ .inputs = inputs, .objs = objs, .machine = config->machine }); + + if (lnk_do_debug_info(config)) { + tp_for_parallel(tp, arena, inputs_count, lnk_obj_find_debug_t, objs); + } } return objs; } internal LNK_ObjNode * -lnk_obj_from_input(Arena *arena, COFF_MachineType machine, LNK_Input *input) +lnk_obj_from_input(Arena *arena, LNK_Config *config, LNK_Input *input) { Temp scratch = scratch_begin(&arena, 1); TP_Context *tp = tp_alloc(scratch.arena, 1, 1, str8_zero()); TP_Arena tp_arena = { .count = 1, .v = &arena }; - LNK_ObjNode *result = lnk_obj_from_input_many(tp, &tp_arena, machine, 1, &input); + LNK_ObjNode *result = lnk_obj_from_input_many(tp, &tp_arena, config, 1, &input); scratch_end(scratch); return result; } diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index b852bce7..84248631 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -9,6 +9,9 @@ typedef struct LNK_Obj { String8 path; String8 data; + U32 debug_t_sect_idx; + U32 debug_p_sect_idx; + U32 debug_h_sect_idx; U32 input_idx; COFF_FileHeaderInfo header; U32 *comdats; diff --git a/src/linker/tests/linker_tests.c b/src/linker/tests/linker_tests.c index 0b0e5e0d..90e7bb37 100644 --- a/src/linker/tests/linker_tests.c +++ b/src/linker/tests/linker_tests.c @@ -5583,9 +5583,8 @@ TEST(cyclic_type) String8 output = g_errors; B32 is_cycle_detected = 0; while (output.size && !is_cycle_detected) { - is_cycle_detected = t_match_linef(&output, - "Error(043): %S: LF_POINTER(type_index: 0x1000) forward refs member type index 0x1001 (leaf struct offset: 0x0)", - t_make_file_path(arena, str8_lit("cycle.obj"))); + String8 line = str8_chop_line(&output); + is_cycle_detected = str8_match_wildcard(line, str8_lit("Error(*): *: LF_POINTER(type_index: *) forward refs member type index * (leaf struct offset: *)"), StringMatchFlag_CaseInsensitive); t_chop_line(&output); } T_Ok(is_cycle_detected);