From 765b61152d4cb4af709dab2789dc36f89dd63b42 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Mon, 22 Jun 2026 22:11:39 -0700 Subject: [PATCH] sort import library member refs by input index --- src/linker/lnk.c | 39 ++++++++++++++++++++++++++++++++------- src/linker/lnk_config.c | 5 +++++ src/linker/lnk_config.h | 8 +++++--- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/linker/lnk.c b/src/linker/lnk.c index db55faa2..2850b08b 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -168,6 +168,7 @@ lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line) "/RAD_MAP_LINES_FOR_UNRESOLVED_SYMBOLS", "/RAD_UNRESOLVED_SYMBOL_LIMIT:1000", "/RAD_UNRESOLVED_SYMBOL_REF_LIMIT:10", + "/RAD_SORT_IMPORTS", (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, }; @@ -1247,11 +1248,27 @@ lnk_lib_member_ref_list_concat_in_place_array(LNK_LibMemberRefList *list, LNK_Li static LNK_LibMemberInfo *g_sort_lib_member_context; -internal int +force_inline int lnk_lib_member_ref_is_before(void *raw_a, void *raw_b) { LNK_LibMemberRef **a = raw_a, **b = raw_b; - return lnk_symbol_is_before(g_sort_lib_member_context[(*a)->member_idx].link, g_sort_lib_member_context[(*b)->member_idx].link); + return lnk_symbol_is_before(g_sort_lib_member_context[(*a)->member_idx].link, + g_sort_lib_member_context[(*b)->member_idx].link); +} + +force_inline int +lnk_import_ref_is_before(void *raw_a, void *raw_b) +{ + LNK_LibMemberRef **a_ptr = raw_a, **b_ptr = raw_b; + LNK_LibMemberRef *a = *a_ptr, *b = *b_ptr; + int cmp = u64_compar(&a->lib->input_idx, &b->lib->input_idx); + if (cmp == 0) { + cmp = u32_compar(&a->member_idx, &b->member_idx); + } +#if LNK_PARANOID + if (a != b) Assert(cmp != 0); +#endif + return cmp < 0; } internal LNK_LibMemberRef ** @@ -2002,12 +2019,20 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer // make imports // { - HashMap static_imports_hm = {0}; - HashMap delayed_imports_hm = {0}; - String8List delayed_dll_names = {0}; - String8List static_dll_names = {0}; + HashMap static_imports_hm = {0}; + HashMap delayed_imports_hm = {0}; + String8List delayed_dll_names = {0}; + String8List static_dll_names = {0}; - for EachNode(member_ref, LNK_LibMemberRef, link->imports.first) { + LNK_LibMemberRef **import_member_refs = lnk_array_from_lib_member_list(scratch.arena, link->imports); + + // optionally sort import library member refs by input index + if (config->sort_imports == LNK_SwitchState_Yes) { + radsort(import_member_refs, link->imports.count, lnk_import_ref_is_before); + } + + for EachIndex(import_member_idx, link->imports.count) { + LNK_LibMemberRef *member_ref = import_member_refs[import_member_idx]; LNK_Lib *lib = member_ref->lib; U64 member_idx = member_ref->member_idx; LNK_LibMemberInfo *member_infos = hash_map_search_raw_raw(&link->lib_member_infos_hm, lib); diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index c3e17e6b..84495620 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -92,6 +92,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_Rad_RemoveSection, 0, "RAD_REMOVE_SECTION", ":NAME", "Removes a section from the image." }, { LNK_CmdSwitch_Rad_SharedThreadPool, 0, "RAD_SHARED_THREAD_POOL", "[:STRING]", "Default value \"" LNK_DEFAULT_THREAD_POOL_NAME "\"" }, { LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, 0, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Set maximum number of workers in a thread pool." }, + { LNK_CmdSwitch_Rad_SortImports, 0, "RAD_SORT_IMPORTS", "[:NO]", "Sort static and delayed import tables by their order of appearance in libs, without assuming link order." }, { LNK_CmdSwitch_Rad_Ignore, 0, "RAD_IGNORE", ":#", "Ignore the specified RAD linker warning." }, { LNK_CmdSwitch_Rad_ImageAltPath, 0, "RAD_IMAGEALTPATH", ":FILENAME", "Alternative name for the image" }, { LNK_CmdSwitch_Rad_WriteTempFiles, 0, "RAD_WRITE_TEMP_FILES", "[:NO]", "When speicifed linker writes image and debug info to temporary files and renames after link is done." }, @@ -2081,6 +2082,10 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List } } break; + case LNK_CmdSwitch_Rad_SortImports: { + lnk_cmd_switch_parse_flag(obj, cmd_switch, value_strings, &config->sort_imports); + } break; + case LNK_CmdSwitch_Rad_Ignore: { S64List error_code_list = {0}; if ( ! lnk_cmd_switch_parse_s64_list(scratch.arena, obj, cmd_switch, value_strings, &error_code_list, 0)) { diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 267124c8..a781d664 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -96,14 +96,14 @@ typedef enum LNK_CmdSwitch_WholeArchive, LNK_CmdSwitch_Rad_Age, + LNK_CmdSwitch_Rad_BootMode, LNK_CmdSwitch_Rad_BuildExp, - LNK_CmdSwitch_Rad_BuildInfo, LNK_CmdSwitch_Rad_BuildImpLib, + LNK_CmdSwitch_Rad_BuildInfo, 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, @@ -130,13 +130,14 @@ typedef enum LNK_CmdSwitch_Rad_RemoveSection, LNK_CmdSwitch_Rad_SharedThreadPool, LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, + LNK_CmdSwitch_Rad_SortImports, LNK_CmdSwitch_Rad_TimeStamp, LNK_CmdSwitch_Rad_TypeHashAlg, LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, LNK_CmdSwitch_Rad_Version, - LNK_CmdSwitch_Rad_Workers, LNK_CmdSwitch_Rad_WorkDir, + LNK_CmdSwitch_Rad_Workers, LNK_CmdSwitch_Rad_WriteTempFiles, LNK_CmdSwitch_RadTypeServer, @@ -385,6 +386,7 @@ typedef struct LNK_Config LLVM_GHashAlg type_hash_alg; String8 type_server_name; LNK_SwitchState type_server; + LNK_SwitchState sort_imports; } LNK_Config; // --- MSVC Error Codes --------------------------------------------------------