From 46663688e21a46be71e4d6dc6f28de4632203d6e Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Thu, 19 Jun 2025 15:12:18 -0700 Subject: [PATCH] on read memory map files --- src/linker/lnk.c | 16 +- src/linker/lnk.h | 2 +- src/linker/lnk_config.c | 532 ++++++++++++++++++------------------ src/linker/lnk_config.h | 102 ++++--- src/linker/lnk_debug_info.c | 6 +- src/linker/lnk_debug_info.h | 2 +- src/linker/lnk_io.c | 108 +++++--- src/linker/lnk_io.h | 14 +- 8 files changed, 405 insertions(+), 377 deletions(-) diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 6a79c0a8..bc70f354 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -203,6 +203,7 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SymbolTableCapWeak, "0x3ffff"); lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SymbolTableCapLib, "0x3ffff"); lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_DebugAltPath, "%%_RAD_RDI_PATH%%"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_MemoryMapFiles, ""); #if BUILD_DEBUG lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Log, "debug"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Log, "io_write"); @@ -435,6 +436,7 @@ lnk_merge_manifest_files(String8 mt_path, String8 out_name, String8List manifest internal String8 lnk_manifest_from_inputs(Arena *arena, + LNK_IO_Flags io_flags, String8 mt_path, String8 manifest_name, B32 manifest_uac, @@ -468,7 +470,7 @@ lnk_manifest_from_inputs(Arena *arena, lnk_merge_manifest_files(mt_path, merged_manifest_path, unique_input_manifest_paths); // read mt.exe output from disk - manifest_data = lnk_read_data_from_file_path(arena, merged_manifest_path); + manifest_data = lnk_read_data_from_file_path(arena, io_flags, merged_manifest_path); if (manifest_data.size == 0) { lnk_error(LNK_Error_Mt, "unable to find mt.exe output manifest on disk, expected path \"%S\"", merged_manifest_path); } @@ -1455,7 +1457,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) U64 thin_inputs_count = 0; LNK_InputObj **thin_inputs = lnk_thin_array_from_input_obj_list(scratch.arena, unique_obj_input_list, &thin_inputs_count); String8Array thin_input_paths = lnk_path_array_from_input_obj_array(scratch.arena, thin_inputs, thin_inputs_count); - String8Array thin_input_datas = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], thin_input_paths); + String8Array thin_input_datas = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], config->io_flags, thin_input_paths); for (U64 thin_input_idx = 0; thin_input_idx < thin_inputs_count; thin_input_idx += 1) { thin_inputs[thin_input_idx]->has_disk_read_failed = thin_input_datas.v[thin_input_idx].size == 0; thin_inputs[thin_input_idx]->data = thin_input_datas.v[thin_input_idx]; @@ -1696,7 +1698,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) ProfBegin("Disk Read Libs"); String8Array paths = str8_array_from_list(temp.arena, &unique_input_lib_list); - String8Array datas = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], paths); + String8Array datas = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], config->io_flags, paths); ProfEnd(); ProfBegin("Lib Init"); @@ -2047,7 +2049,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) ProfBegin("Embed Manifest"); // TODO: currently we convert manifest to res and parse res again, this unnecessary instead push manifest // resource to the tree directly - String8 manifest_data = lnk_manifest_from_inputs(scratch.arena, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, input_manifest_path_list, manifest_dep_list); + String8 manifest_data = lnk_manifest_from_inputs(scratch.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, input_manifest_path_list, manifest_dep_list); String8 manifest_res = pe_make_manifest_resource(scratch.arena, *config->manifest_resource_id, manifest_data); str8_list_push(scratch.arena, &res_data_list, manifest_res); str8_list_push(scratch.arena, &res_path_list, str8_lit("* Manifest *")); @@ -2056,7 +2058,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) case LNK_ManifestOpt_WriteToFile: { ProfBeginDynamic("Write Manifest To: %.*s", str8_varg(config->manifest_name)); Temp temp = temp_begin(scratch.arena); - String8 manifest_data = lnk_manifest_from_inputs(temp.arena, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, input_manifest_path_list, manifest_dep_list); + String8 manifest_data = lnk_manifest_from_inputs(temp.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, input_manifest_path_list, manifest_dep_list); lnk_write_data_to_file_path(config->manifest_name, str8_zero(), manifest_data); temp_end(temp); ProfEnd(); @@ -2072,7 +2074,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) ProfBegin("Load .res files from disk"); for (String8Node *node = config->input_list[LNK_Input_Res].first; node != 0; node = node->next) { - String8 res_data = lnk_read_data_from_file_path(scratch.arena, node->string); + String8 res_data = lnk_read_data_from_file_path(scratch.arena, config->io_flags, node->string); if (res_data.size > 0) { if (pe_is_res(res_data)) { str8_list_push(scratch.arena, &res_data_list, res_data); @@ -5066,7 +5068,7 @@ lnk_run(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) // // CodeView // - LNK_CodeViewInput input = lnk_make_code_view_input(tp, tp_arena, config->lib_dir_list, link_ctx.objs_count, link_ctx.objs); + LNK_CodeViewInput input = lnk_make_code_view_input(tp, tp_arena, config->io_flags, config->lib_dir_list, link_ctx.objs_count, link_ctx.objs); CV_DebugT *types = lnk_import_types(tp, tp_arena, &input); // diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 68d49ce8..16438043 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -188,7 +188,7 @@ internal U128 lnk_blake3_hash_parallel(TP_Context *tp, U64 chunk_count, String8 internal String8 lnk_make_linker_manifest(Arena *arena, B32 manifest_uac, String8 manifest_level, String8 manifest_ui_access, String8List manifest_dependency_list); internal void lnk_merge_manifest_files(String8 mt_path, String8 out_name, String8List manifest_path_list); -internal String8 lnk_manifest_from_inputs(Arena *arena, String8 mt_path, String8 manifest_name, B32 manifest_uac, String8 manifest_level, String8 manifest_ui_access, String8List input_manifest_path_list, String8List deps_list); +internal String8 lnk_manifest_from_inputs(Arena *arena, LNK_IO_Flags io_flags, String8 mt_path, String8 manifest_name, B32 manifest_uac, String8 manifest_level, String8 manifest_ui_access, String8List input_manifest_path_list, String8List deps_list); // --- Internal Objs ----------------------------------------------------------- diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 92c3f3b0..338afe57 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -1,10 +1,8 @@ // Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -//////////////////////////////// -// Enum <-> String - -global read_only LNK_CmdSwitch g_cmd_switch_map[] = { +global read_only LNK_CmdSwitch g_cmd_switch_map[] = +{ { LNK_CmdSwitch_Null, 0, "", "", "" }, { LNK_CmdSwitch_NotImplemented, 0, "NOT_IMPLEMENTED", "", "" }, { LNK_CmdSwitch_Align, 0, "ALIGN", ":#", "" }, @@ -123,40 +121,42 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { { LNK_CmdSwitch_NotImplemented, 0, "WX", "", "" }, //- internal switches - { LNK_CmdSwitch_Rad_Age, 0, "RAD_AGE", ":#", "Age embeded in EXE and PDB, used to validate incremental build. Default is 1." }, - { LNK_CmdSwitch_Rad_BuildInfo, 0, "RAD_BUILD_INFO", "", "Print build info and exit." }, - { LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, 0, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Map, 0, "RAD_MAP", ":FILENAME", "Emit file with the output image's layout description." }, - { LNK_CmdSwitch_Rad_Debug, 0, "RAD_DEBUG", "[:NO]", "Emit RAD debug info file." }, - { LNK_CmdSwitch_Rad_DebugAltPath, 0, "RAD_DEBUGALTPATH", "", "" }, - { LNK_CmdSwitch_Rad_DebugName, 0, "RAD_DEBUG_NAME", ":FILENAME", "Sets file name for RAD debug info file." }, - { LNK_CmdSwitch_Rad_DelayBind, 0, "RAD_DELAY_BIND", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_DoMerge, 0, "RAD_DO_MERGE", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_EnvLib, 0, "RAD_ENV_LIB", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Exe, 0, "RAD_EXE", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Guid, 0, "RAD_GUID", ":{IMAGEBLAKE3|XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX}", "" }, - { LNK_CmdSwitch_Rad_LargePages, 0, "RAD_LARGE_PAGES", "[:NO]", "Disabled by default on Windows." }, - { LNK_CmdSwitch_Rad_LinkVer, 0, "RAD_LINK_VER", ":##,##", "" }, - { LNK_CmdSwitch_Rad_Log, 0, "RAD_LOG", ":{ALL,INPUT_OBJ,INPUT_LIB,IO,LINK_STATS,TIMERS}", "" }, - { LNK_CmdSwitch_Rad_MtPath, 0, "RAD_MT_PATH", ":EXEPATH", "Exe path to manifest tool, default: " LNK_MANIFEST_MERGE_TOOL_NAME }, - { LNK_CmdSwitch_Rad_OsVer, 0, "RAD_OS_VER", ":##,##", "" }, - { LNK_CmdSwitch_Rad_PageSize, 0, "RAD_PAGE_SIZE", ":#", "Must be power of two." }, - { LNK_CmdSwitch_Rad_PathStyle, 0, "RAD_PATH_STYLE", ":{WindowsAbsolute|UnixAbsolute}", "" }, - { LNK_CmdSwitch_Rad_PdbHashTypeNameLength, 0, "RAD_PDB_HASH_TYPE_NAME_LENGTH", ":#", "Number of hash bytes to use to replace type name. Default 8 bytes (Max 16)." }, - { LNK_CmdSwitch_Rad_PdbHashTypeNameMap, 0, "RAD_PDB_HASH_TYPE_NAME_MAP", ":FILENAME", "Produce map file with hash -> type name mappings." }, - { LNK_CmdSwitch_Rad_PdbHashTypeNames, 0, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." }, - { LNK_CmdSwitch_Rad_RemoveSection, 0, "RAD_REMOVE_SECTION", ":NAME", "Removes a section from output 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", ":#", "Sets maximum number of workers in a thread pool." }, - { LNK_CmdSwitch_Rad_SuppressError, 0, "RAD_SUPPRESS_ERROR", ":#", "" }, - { LNK_CmdSwitch_Rad_SymbolTableCapDefined, 0, "RAD_SYMBOL_TABLE_CAP_DEFINED", ":#", "Number of buckets allocated in the symbol table for defined symbols." }, - { LNK_CmdSwitch_Rad_SymbolTableCapInternal, 0, "RAD_SYMBOL_TABLE_CAP_INTERNAL", ":#", "Number of buckets allocated in the symbol table for internal symbols." }, - { LNK_CmdSwitch_Rad_SymbolTableCapLib, 0, "RAD_SYMBOL_TABLE_CAP_LIB", ":#", "Number of buckets allocated in the symbol table for library symbols." }, - { LNK_CmdSwitch_Rad_SymbolTableCapWeak, 0, "RAD_SYMBOL_TABLE_CAP_WEAK", ":#", "Number of buckets allocated in the symbol table for weak symbols." }, - { LNK_CmdSwitch_Rad_TargetOs, 0, "RAD_TARGET_OS", ":{WINDOWS,LINUX,MAC}" }, + { LNK_CmdSwitch_Rad_Age, 0, "RAD_AGE", ":#", "Age embeded in EXE and PDB, used to validate incremental build. Default is 1." }, + { LNK_CmdSwitch_Rad_BuildInfo, 0, "RAD_BUILD_INFO", "", "Print build info and exit." }, + { LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, 0, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Map, 0, "RAD_MAP", ":FILENAME", "Emit file with the output image's layout description." }, + { 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_Debug, 0, "RAD_DEBUG", "[:NO]", "Emit RAD debug info file." }, + { LNK_CmdSwitch_Rad_DebugAltPath, 0, "RAD_DEBUGALTPATH", "", "" }, + { LNK_CmdSwitch_Rad_DebugName, 0, "RAD_DEBUG_NAME", ":FILENAME", "Sets file name for RAD debug info file." }, + { LNK_CmdSwitch_Rad_DelayBind, 0, "RAD_DELAY_BIND", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_DoMerge, 0, "RAD_DO_MERGE", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_EnvLib, 0, "RAD_ENV_LIB", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Exe, 0, "RAD_EXE", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Guid, 0, "RAD_GUID", ":{IMAGEBLAKE3|XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX}", "" }, + { LNK_CmdSwitch_Rad_LargePages, 0, "RAD_LARGE_PAGES", "[:NO]", "Disabled by default on Windows." }, + { LNK_CmdSwitch_Rad_LinkVer, 0, "RAD_LINK_VER", ":##,##", "" }, + { LNK_CmdSwitch_Rad_Log, 0, "RAD_LOG", ":{ALL,INPUT_OBJ,INPUT_LIB,IO,LINK_STATS,TIMERS}", "" }, + { LNK_CmdSwitch_Rad_MtPath, 0, "RAD_MT_PATH", ":EXEPATH", "Exe path to manifest tool, default: " LNK_MANIFEST_MERGE_TOOL_NAME }, + { LNK_CmdSwitch_Rad_OsVer, 0, "RAD_OS_VER", ":##,##", "" }, + { LNK_CmdSwitch_Rad_PageSize, 0, "RAD_PAGE_SIZE", ":#", "Must be power of two." }, + { LNK_CmdSwitch_Rad_PathStyle, 0, "RAD_PATH_STYLE", ":{WindowsAbsolute|UnixAbsolute}", "" }, + { LNK_CmdSwitch_Rad_PdbHashTypeNameLength, 0, "RAD_PDB_HASH_TYPE_NAME_LENGTH", ":#", "Number of hash bytes to use to replace type name. Default 8 bytes (Max 16)." }, + { LNK_CmdSwitch_Rad_PdbHashTypeNameMap, 0, "RAD_PDB_HASH_TYPE_NAME_MAP", ":FILENAME", "Produce map file with hash -> type name mappings." }, + { LNK_CmdSwitch_Rad_PdbHashTypeNames, 0, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." }, + { LNK_CmdSwitch_Rad_RemoveSection, 0, "RAD_REMOVE_SECTION", ":NAME", "Removes a section from output image." }, + { LNK_CmdSwitch_Rad_SectVirtOff, 0, "RAD_SECT_VIRT_OFF", ":#", "Set RVA where section data is placed in memory. For internal use only." }, + { 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", ":#", "Sets maximum number of workers in a thread pool." }, + { LNK_CmdSwitch_Rad_SuppressError, 0, "RAD_SUPPRESS_ERROR", ":#", "" }, + { LNK_CmdSwitch_Rad_SymbolTableCapDefined, 0, "RAD_SYMBOL_TABLE_CAP_DEFINED", ":#", "Number of buckets allocated in the symbol table for defined symbols." }, + { LNK_CmdSwitch_Rad_SymbolTableCapInternal, 0, "RAD_SYMBOL_TABLE_CAP_INTERNAL", ":#", "Number of buckets allocated in the symbol table for internal symbols." }, + { LNK_CmdSwitch_Rad_SymbolTableCapLib, 0, "RAD_SYMBOL_TABLE_CAP_LIB", ":#", "Number of buckets allocated in the symbol table for library symbols." }, + { LNK_CmdSwitch_Rad_SymbolTableCapWeak, 0, "RAD_SYMBOL_TABLE_CAP_WEAK", ":#", "Number of buckets allocated in the symbol table for weak symbols." }, + { LNK_CmdSwitch_Rad_TargetOs, 0, "RAD_TARGET_OS", ":{WINDOWS,LINUX,MAC}" }, { 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." }, - { LNK_CmdSwitch_Rad_TimeStamp, 0, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." }, - { LNK_CmdSwitch_Rad_Version, 0, "RAD_VERSION", "", "Print version and exit." }, + { LNK_CmdSwitch_Rad_TimeStamp, 0, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." }, + { LNK_CmdSwitch_Rad_Version, 0, "RAD_VERSION", "", "Print version and exit." }, { LNK_CmdSwitch_Rad_Workers, 0, "RAD_WORKERS", ":#", "Sets 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_Help, 0, "HELP", "", "" }, @@ -270,8 +270,6 @@ 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, ...) { @@ -307,8 +305,6 @@ lnk_cmd_line_has_switch(LNK_CmdLine cmd_line, LNK_CmdSwitchType cmd_switch) return lnk_cmd_line_has_option_string(cmd_line, cmd_switch_name); } -//////////////////////////////// - internal void lnk_error_cmd_switch(LNK_ErrorCode code, String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, char *fmt, ...) { @@ -362,185 +358,6 @@ lnk_error_invalid_uac_ui_access_param(LNK_ErrorCode error_code, String8 obj_path lnk_error_cmd_switch(error_code, obj_path, lib_path, cmd_switch, "invalid param format, expected \"uiAccess={'true'|'false'}\" but got \"%S\"", input); } -//////////////////////////////// - -internal String8 -lnk_get_image_name(LNK_Config *config) -{ - String8 image_name = config->image_name; - image_name = str8_skip_last_slash(image_name); - image_name = str8_chop_last_dot(image_name); - return image_name; -} - -internal U64 -lnk_get_default_function_pad_min(COFF_MachineType machine) -{ - U64 function_pad_min = 0; - switch (machine) { - case COFF_MachineType_Unknown: break; - case COFF_MachineType_X86: { - function_pad_min = 5; - } break; - case COFF_MachineType_X64: { - function_pad_min = 6; - } break; - default: { - lnk_error_cmd_switch(LNK_Error_Cmdl, - str8_zero(), - str8_zero(), - LNK_CmdSwitch_FunctionPadMin, - "default paramter is not defined for: %S", - coff_string_from_machine_type(machine)); - } break; - } - return function_pad_min; -} - -internal U64 -lnk_get_base_addr(LNK_Config *config) -{ - U64 base_addr = config->user_base_addr; - if (base_addr == 0) { - if (config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) { - base_addr = coff_default_dll_base_from_machine(config->machine); - } else if (config->file_characteristics & PE_ImageFileCharacteristic_EXE) { - if ((~config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE) && config->machine == COFF_MachineType_X64) { - base_addr = coff_default_exe_base_from_machine(COFF_MachineType_X86); - } else { - base_addr = coff_default_exe_base_from_machine(config->machine); - } - } else { - lnk_error(LNK_Error_Cmdl, "image type is not specified."); - } - } - return base_addr; -} - -internal Version -lnk_get_default_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine) -{ - Version ver = make_version(0,0); - switch (subsystem) { - case PE_WindowsSubsystem_WINDOWS_BOOT_APPLICATION: ver = make_version(1,0); break; - - case PE_WindowsSubsystem_WINDOWS_CUI: { - switch (machine) { - case COFF_MachineType_X64: - case COFF_MachineType_X86: ver = make_version(6,0); break; - - case COFF_MachineType_ArmNt: - case COFF_MachineType_Arm64: - case COFF_MachineType_Arm: ver = make_version(6,2); break; - - default: lnk_not_implemented("define subsystem(%S) version for %S", pe_string_from_subsystem(subsystem), coff_string_from_machine_type(machine)); break; - } - } break; - - case PE_WindowsSubsystem_WINDOWS_GUI: { - switch (machine) { - case COFF_MachineType_X64: - case COFF_MachineType_X86: ver = make_version(6,0); break; - - case COFF_MachineType_ArmNt: - case COFF_MachineType_Arm64: - case COFF_MachineType_Arm: ver = make_version(6,2); break; - - default: lnk_not_implemented("define subsystem(%S) version for %S", pe_string_from_subsystem(subsystem), coff_string_from_machine_type(machine)); break; - } - } break; - - case PE_WindowsSubsystem_POSIX_CUI: ver = make_version(19,90); break; - - case PE_WindowsSubsystem_EFI_APPLICATION: - case PE_WindowsSubsystem_EFI_BOOT_SERVICE_DRIVER: - case PE_WindowsSubsystem_EFI_ROM: - case PE_WindowsSubsystem_EFI_RUNTIME_DRIVER: ver = make_version(1,0); break; - - case PE_WindowsSubsystem_NATIVE_WINDOWS: - case PE_WindowsSubsystem_NATIVE: lnk_not_implemented("detect -drive=WDM switch"); break; - - default: lnk_not_implemented("unknown subsystem kind %u", subsystem); break; - } - return ver; -} - -internal Version -lnk_get_min_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine) -{ - Version ver = make_version(0,0); - switch (subsystem) { - case PE_WindowsSubsystem_WINDOWS_BOOT_APPLICATION: ver = make_version(1,0); break; - - case PE_WindowsSubsystem_WINDOWS_CUI: { - switch (machine) { - case COFF_MachineType_X86: ver = make_version(5,1); break; - - case COFF_MachineType_X64: ver = make_version(5,2); break; - - case COFF_MachineType_ArmNt: - case COFF_MachineType_Arm64: - case COFF_MachineType_Arm: ver = make_version(6,2); break; - - default: lnk_not_implemented("define min subsystem(%S) version for %S", pe_string_from_subsystem(subsystem), coff_string_from_machine_type(machine)); break; - } - } break; - - case PE_WindowsSubsystem_WINDOWS_GUI: { - switch (machine) { - case COFF_MachineType_X86: ver = make_version(5,1); break; - - case COFF_MachineType_X64: ver = make_version(5,2); break; - - case COFF_MachineType_ArmNt: - case COFF_MachineType_Arm64: - case COFF_MachineType_Arm: ver = make_version(6,2); break; - - default: lnk_not_implemented("define min subsystem(%S) version for %S", pe_string_from_subsystem(subsystem), coff_string_from_machine_type(machine)); break; - } - } break; - - case PE_WindowsSubsystem_POSIX_CUI: ver = make_version(1,0); break; - - case PE_WindowsSubsystem_EFI_APPLICATION: - case PE_WindowsSubsystem_EFI_BOOT_SERVICE_DRIVER: - case PE_WindowsSubsystem_EFI_ROM: - case PE_WindowsSubsystem_EFI_RUNTIME_DRIVER: ver = make_version(1,0); break; - - case PE_WindowsSubsystem_NATIVE_WINDOWS: - case PE_WindowsSubsystem_NATIVE: lnk_not_implemented("detect -drive=WDM switch"); break; - - default: lnk_not_implemented("unknown subsystem kind %u", subsystem); - } - return ver; -} - -internal B32 -lnk_do_debug_info(LNK_Config *config) -{ - B32 do_debug_info = config->rad_debug == LNK_SwitchState_Yes || - (config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null); - return do_debug_info; -} - -internal B32 -lnk_is_thread_pool_shared(LNK_Config *config) -{ - return config->shared_thread_pool_name.size > 0; -} - -internal B32 -lnk_is_section_removed(LNK_Config *config, String8 section_name) -{ - B32 is_removed = 0; - for (String8Node *name_n = config->remove_sections.first; name_n != 0 && !is_removed; name_n = name_n->next) { - is_removed = str8_match(section_name, name_n->string, 0); - } - return is_removed; -} - -//////////////////////////////// - internal B32 lnk_cmd_switch_parse_version(String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, String8List value_strings, Version *ver_out) { @@ -784,8 +601,6 @@ lnk_cmd_switch_parse_string_copy(Arena *arena, String8 obj_path, String8 lib_pat } } -//////////////////////////////// - internal B32 lnk_parse_alt_name_directive(String8 input, LNK_AltName *alt_out) { @@ -947,7 +762,180 @@ lnk_parse_merge_directive(String8 string, LNK_MergeDirective *out) return is_parse_ok; } -//////////////////////////////// +internal String8 +lnk_get_image_name(LNK_Config *config) +{ + String8 image_name = config->image_name; + image_name = str8_skip_last_slash(image_name); + image_name = str8_chop_last_dot(image_name); + return image_name; +} + +internal U64 +lnk_get_default_function_pad_min(COFF_MachineType machine) +{ + U64 function_pad_min = 0; + switch (machine) { + case COFF_MachineType_Unknown: break; + case COFF_MachineType_X86: { + function_pad_min = 5; + } break; + case COFF_MachineType_X64: { + function_pad_min = 6; + } break; + default: { + lnk_error_cmd_switch(LNK_Error_Cmdl, + str8_zero(), + str8_zero(), + LNK_CmdSwitch_FunctionPadMin, + "default paramter is not defined for: %S", + coff_string_from_machine_type(machine)); + } break; + } + return function_pad_min; +} + +internal U64 +lnk_get_base_addr(LNK_Config *config) +{ + U64 base_addr = config->user_base_addr; + if (base_addr == 0) { + if (config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) { + base_addr = coff_default_dll_base_from_machine(config->machine); + } else if (config->file_characteristics & PE_ImageFileCharacteristic_EXE) { + if ((~config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE) && config->machine == COFF_MachineType_X64) { + base_addr = coff_default_exe_base_from_machine(COFF_MachineType_X86); + } else { + base_addr = coff_default_exe_base_from_machine(config->machine); + } + } else { + lnk_error(LNK_Error_Cmdl, "image type is not specified."); + } + } + return base_addr; +} + +internal Version +lnk_get_default_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine) +{ + Version ver = make_version(0,0); + switch (subsystem) { + case PE_WindowsSubsystem_WINDOWS_BOOT_APPLICATION: ver = make_version(1,0); break; + + case PE_WindowsSubsystem_WINDOWS_CUI: { + switch (machine) { + case COFF_MachineType_X64: + case COFF_MachineType_X86: ver = make_version(6,0); break; + + case COFF_MachineType_ArmNt: + case COFF_MachineType_Arm64: + case COFF_MachineType_Arm: ver = make_version(6,2); break; + + default: lnk_not_implemented("define subsystem(%S) version for %S", pe_string_from_subsystem(subsystem), coff_string_from_machine_type(machine)); break; + } + } break; + + case PE_WindowsSubsystem_WINDOWS_GUI: { + switch (machine) { + case COFF_MachineType_X64: + case COFF_MachineType_X86: ver = make_version(6,0); break; + + case COFF_MachineType_ArmNt: + case COFF_MachineType_Arm64: + case COFF_MachineType_Arm: ver = make_version(6,2); break; + + default: lnk_not_implemented("define subsystem(%S) version for %S", pe_string_from_subsystem(subsystem), coff_string_from_machine_type(machine)); break; + } + } break; + + case PE_WindowsSubsystem_POSIX_CUI: ver = make_version(19,90); break; + + case PE_WindowsSubsystem_EFI_APPLICATION: + case PE_WindowsSubsystem_EFI_BOOT_SERVICE_DRIVER: + case PE_WindowsSubsystem_EFI_ROM: + case PE_WindowsSubsystem_EFI_RUNTIME_DRIVER: ver = make_version(1,0); break; + + case PE_WindowsSubsystem_NATIVE_WINDOWS: + case PE_WindowsSubsystem_NATIVE: lnk_not_implemented("detect -drive=WDM switch"); break; + + default: lnk_not_implemented("unknown subsystem kind %u", subsystem); break; + } + return ver; +} + +internal Version +lnk_get_min_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine) +{ + Version ver = make_version(0,0); + switch (subsystem) { + case PE_WindowsSubsystem_WINDOWS_BOOT_APPLICATION: ver = make_version(1,0); break; + + case PE_WindowsSubsystem_WINDOWS_CUI: { + switch (machine) { + case COFF_MachineType_X86: ver = make_version(5,1); break; + + case COFF_MachineType_X64: ver = make_version(5,2); break; + + case COFF_MachineType_ArmNt: + case COFF_MachineType_Arm64: + case COFF_MachineType_Arm: ver = make_version(6,2); break; + + default: lnk_not_implemented("define min subsystem(%S) version for %S", pe_string_from_subsystem(subsystem), coff_string_from_machine_type(machine)); break; + } + } break; + + case PE_WindowsSubsystem_WINDOWS_GUI: { + switch (machine) { + case COFF_MachineType_X86: ver = make_version(5,1); break; + + case COFF_MachineType_X64: ver = make_version(5,2); break; + + case COFF_MachineType_ArmNt: + case COFF_MachineType_Arm64: + case COFF_MachineType_Arm: ver = make_version(6,2); break; + + default: lnk_not_implemented("define min subsystem(%S) version for %S", pe_string_from_subsystem(subsystem), coff_string_from_machine_type(machine)); break; + } + } break; + + case PE_WindowsSubsystem_POSIX_CUI: ver = make_version(1,0); break; + + case PE_WindowsSubsystem_EFI_APPLICATION: + case PE_WindowsSubsystem_EFI_BOOT_SERVICE_DRIVER: + case PE_WindowsSubsystem_EFI_ROM: + case PE_WindowsSubsystem_EFI_RUNTIME_DRIVER: ver = make_version(1,0); break; + + case PE_WindowsSubsystem_NATIVE_WINDOWS: + case PE_WindowsSubsystem_NATIVE: lnk_not_implemented("detect -drive=WDM switch"); break; + + default: lnk_not_implemented("unknown subsystem kind %u", subsystem); + } + return ver; +} + +internal B32 +lnk_do_debug_info(LNK_Config *config) +{ + B32 do_debug_info = config->rad_debug == LNK_SwitchState_Yes || + (config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null); + return do_debug_info; +} + +internal B32 +lnk_is_thread_pool_shared(LNK_Config *config) +{ + return config->shared_thread_pool_name.size > 0; +} + +internal B32 +lnk_is_section_removed(LNK_Config *config, String8 section_name) +{ + B32 is_removed = 0; + for (String8Node *name_n = config->remove_sections.first; name_n != 0 && !is_removed; name_n = name_n->next) { + is_removed = str8_match(section_name, name_n->string, 0); + } + return is_removed; +} internal void lnk_print_build_info() @@ -999,8 +987,6 @@ lnk_print_help(void) scratch_end(scratch); } -//////////////////////////////// - internal String8 lnk_expand_env_vars_windows(Arena *arena, HashTable *env_vars, String8 string) { @@ -1034,6 +1020,46 @@ lnk_expand_env_vars_windows(Arena *arena, HashTable *env_vars, String8 string) return result; } +internal String8List +lnk_unwrap_rsp(Arena *arena, String8List arg_list) +{ + Temp scratch = scratch_begin(&arena, 1); + + String8List result = {0}; + + for (String8Node *curr = arg_list.first; curr != 0; curr = curr->next) { + B32 is_rsp = str8_match_lit("@", curr->string, StringMatchFlag_RightSideSloppy); + if (is_rsp) { + // remove "@" + String8 name = str8_skip(curr->string, 1); + + if (os_file_path_exists(name)) { + // read rsp from disk + String8 file = lnk_read_data_from_file_path(scratch.arena, 0, name); + + // parse rsp + String8List rsp_args = lnk_arg_list_parse_windows_rules(scratch.arena, file); + + // handle case where rsp references another rsp + String8List list = lnk_unwrap_rsp(arena, rsp_args); + + // push arguments from rsp + list = str8_list_copy(arena, &list); + str8_list_concat_in_place(&result, &list); + } else { + lnk_error(LNK_Error_Cmdl, "unable to find rsp: %S", name); + } + } else { + // push regular argument + String8 str = push_str8_copy(arena, curr->string); + str8_list_push(arena, &result, str); + } + } + + scratch_end(scratch); + return result; +} + internal void lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_name, String8List value_strings, String8 obj_path, String8 lib_path) { @@ -1634,9 +1660,14 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam config->rad_chunk_map = LNK_SwitchState_Yes; } break; + case LNK_CmdSwitch_Rad_MemoryMapFiles: { + lnk_cmd_switch_set_flag_32(obj_path, lib_path, cmd_switch, value_strings, &config->io_flags, LNK_IO_Flags_MemoryMapFiles); + } break; + case LNK_CmdSwitch_Rad_Debug: { lnk_cmd_switch_parse_flag(obj_path, lib_path, cmd_switch, value_strings, &config->rad_debug); } break; + case LNK_CmdSwitch_Rad_DebugName: { // :Rad_DebugAltPath lnk_cmd_switch_parse_string_copy(arena, obj_path, lib_path, cmd_switch, value_strings, &config->rad_debug_name); @@ -1797,6 +1828,17 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } } break; + case LNK_CmdSwitch_Rad_SectVirtOff: { + U64 sect_virt_off; + if (lnk_cmd_switch_parse_u64(obj_path, lib_path, cmd_switch, value_strings, §_virt_off, LNK_ParseU64Flag_CheckUnder32bit)) { + if (sect_virt_off >= 0x1000) { + config->section_virt_off = sect_virt_off; + } else { + lnk_error_cmd_switch(LNK_Error_Cmdl, obj_path, lib_path, cmd_switch, "section virtual offset must be >= 0x1000"); + } + } + } break; + case LNK_CmdSwitch_Rad_SharedThreadPool: { if (value_strings.node_count == 0) { config->shared_thread_pool_name = str8_lit(LNK_DEFAULT_THREAD_POOL_NAME); @@ -1892,46 +1934,6 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam scratch_end(scratch); } -internal String8List -lnk_unwrap_rsp(Arena *arena, String8List arg_list) -{ - Temp scratch = scratch_begin(&arena, 1); - - String8List result = {0}; - - for (String8Node *curr = arg_list.first; curr != 0; curr = curr->next) { - B32 is_rsp = str8_match_lit("@", curr->string, StringMatchFlag_RightSideSloppy); - if (is_rsp) { - // remove "@" - String8 name = str8_skip(curr->string, 1); - - if (os_file_path_exists(name)) { - // read rsp from disk - String8 file = lnk_read_data_from_file_path(scratch.arena, name); - - // parse rsp - String8List rsp_args = lnk_arg_list_parse_windows_rules(scratch.arena, file); - - // handle case where rsp references another rsp - String8List list = lnk_unwrap_rsp(arena, rsp_args); - - // push arguments from rsp - list = str8_list_copy(arena, &list); - str8_list_concat_in_place(&result, &list); - } else { - lnk_error(LNK_Error_Cmdl, "unable to find rsp: %S", name); - } - } else { - // push regular argument - String8 str = push_str8_copy(arena, curr->string); - str8_list_push(arena, &result, str); - } - } - - scratch_end(scratch); - return result; -} - internal LNK_Config * lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd_line) { diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 1567607b..5411058a 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -3,6 +3,29 @@ #pragma once +#if OS_WINDOWS +# define LNK_MANIFEST_MERGE_TOOL_NAME "mt.exe" +#elif OS_LINUX || OS_MAC +# define LNK_MANIFEST_MERGE_TOOL_NAME "llvm-mt" +#else +# error +#endif + +#define LNK_DEFAULT_THREAD_POOL_NAME "RADLINK_THREAD_POOL" + +typedef enum +{ + LNK_ParseU64Flag_CheckUnder32bit = (1 << 0), + LNK_ParseU64Flag_CheckPow2 = (1 << 1), +} LNK_ParseU64Flags; + +typedef enum +{ + LNK_SwitchState_Null, + LNK_SwitchState_No, + LNK_SwitchState_Yes +} LNK_SwitchState; + typedef enum { LNK_CmdSwitch_Null, @@ -128,7 +151,6 @@ typedef enum LNK_CmdSwitch_Rad_Age, LNK_CmdSwitch_Rad_BuildInfo, LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, - LNK_CmdSwitch_Rad_Map, LNK_CmdSwitch_Rad_Debug, LNK_CmdSwitch_Rad_DebugAltPath, LNK_CmdSwitch_Rad_DebugName, @@ -141,6 +163,8 @@ typedef enum LNK_CmdSwitch_Rad_LinkVer, LNK_CmdSwitch_Rad_Log, LNK_CmdSwitch_Rad_Logo, + LNK_CmdSwitch_Rad_Map, + LNK_CmdSwitch_Rad_MemoryMapFiles, LNK_CmdSwitch_Rad_MtPath, LNK_CmdSwitch_Rad_OsVer, LNK_CmdSwitch_Rad_PageSize, @@ -177,13 +201,6 @@ typedef struct LNK_CmdSwitch char *desc; } LNK_CmdSwitch; -typedef enum -{ - LNK_SwitchState_Null, - LNK_SwitchState_No, - LNK_SwitchState_Yes -} LNK_SwitchState; - typedef enum { LNK_Input_Null, @@ -285,16 +302,6 @@ typedef enum LNK_TypeNameHashMode_Full, } LNK_TypeNameHashMode; -#if OS_WINDOWS -# define LNK_MANIFEST_MERGE_TOOL_NAME "mt.exe" -#elif OS_LINUX || OS_MAC -# define LNK_MANIFEST_MERGE_TOOL_NAME "llvm-mt" -#else -# error -#endif - -#define LNK_DEFAULT_THREAD_POOL_NAME "RADLINK_THREAD_POOL" - typedef struct LNK_Config { LNK_ConfigFlags flags; @@ -383,15 +390,10 @@ typedef struct LNK_Config String8 temp_rad_debug_name; String8 temp_rad_chunk_map_name; String8List remove_sections; + LNK_IO_Flags io_flags; } LNK_Config; -typedef enum -{ - LNK_ParseU64Flag_CheckUnder32bit = (1 << 0), - LNK_ParseU64Flag_CheckPow2 = (1 << 1), -} LNK_ParseU64Flags; - -//////////////////////////////// +// --- MSVC Error Codes -------------------------------------------------------- typedef enum { @@ -518,8 +520,7 @@ typedef enum LNK_MsWarningCode_Lnk4286 = 4286, } LNK_MsErrorCode; -//////////////////////////////// -// Enum <-> String +// --- Enum <-> String --------------------------------------------------------- internal String8 lnk_string_cmd_switch_type(LNK_CmdSwitchType type); internal LNK_CmdSwitchType lnk_cmd_switch_type_from_string(String8 string); @@ -528,36 +529,20 @@ 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 +// --- 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); -//////////////////////////////// -// Errors +internal B32 lnk_cmd_line_has_switch(LNK_CmdLine cmd_line, LNK_CmdSwitchType cmd_switch_type); + +// --- Errors ------------------------------------------------------------------ internal void lnk_error_cmd_switch (LNK_ErrorCode code, String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, char *fmt, ...); internal void lnk_error_cmd_switch_invalid_param_count(LNK_ErrorCode code, String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch); internal void lnk_error_cmd_switch_invalid_param (LNK_ErrorCode code, String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, String8 param); -//////////////////////////////// -// Getters - -internal String8 lnk_get_image_name(LNK_Config *config); -internal U64 lnk_get_default_function_pad_min(COFF_MachineType machine); -internal U64 lnk_get_base_addr(LNK_Config *config); -internal Version lnk_get_default_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine); -internal Version lnk_get_min_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine); - -internal B32 lnk_do_debug_info (LNK_Config *config); -internal B32 lnk_is_thread_pool_shared(LNK_Config *config); - -internal B32 lnk_is_section_removed(LNK_Config *config, String8 section_name); - -//////////////////////////////// -// Specialized Parsers +// --- Specialized Parsers ------------------------------------------------------ internal B32 lnk_cmd_switch_parse_version (String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, String8List value_strings, Version *ver_out); internal B32 lnk_cmd_switch_parse_tuple (String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, String8List value_strings, Rng1U64 *tuple_out); @@ -572,17 +557,28 @@ internal void lnk_cmd_switch_set_flag_64 (String8 obj_path, String8 lib_path, internal B32 lnk_cmd_switch_parse_string (String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, String8List value_strings, String8 *string_out); internal void lnk_cmd_switch_parse_string_copy(Arena *arena, String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, String8List value_strings, String8 *string_out); -//////////////////////////////// - internal B32 lnk_parse_alt_name_directive(String8 input, LNK_AltName *alt_out); internal B32 lnk_parse_export_directive_ex(Arena *arena, String8List directive, String8 obj_path, String8 lib_path, PE_ExportParse *export_out); internal B32 lnk_parse_export_directive(Arena *arena, String8 directive, String8 obj_path, String8 lib_path, PE_ExportParse *export_out); - internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data); - internal B32 lnk_parse_merge_directive(String8 string, LNK_MergeDirective *parse_out); -//////////////////////////////// +// --- Getters ----------------------------------------------------------------- + +internal String8 lnk_get_image_name(LNK_Config *config); +internal U64 lnk_get_default_function_pad_min(COFF_MachineType machine); +internal U64 lnk_get_base_addr(LNK_Config *config); +internal Version lnk_get_default_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine); +internal Version lnk_get_min_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine); + +internal B32 lnk_do_debug_info (LNK_Config *config); +internal B32 lnk_is_thread_pool_shared(LNK_Config *config); + +internal B32 lnk_is_section_removed(LNK_Config *config, String8 section_name); + +// --- Config ------------------------------------------------------------------ internal void lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 name, String8List value_list, String8 obj_path, String8 lib_path); +internal LNK_Config * lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd_line); + diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index 031e9a8a..ec930b22 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -360,7 +360,7 @@ lnk_merge_debug_t_and_debug_p(Arena *arena, U64 obj_count, CV_DebugT *debug_t_ar } internal LNK_CodeViewInput -lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, String8List lib_dir_list, U64 obj_count, LNK_Obj **obj_arr) +lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, U64 obj_count, LNK_Obj **obj_arr) { ProfBegin("Extract CodeView"); Temp scratch = scratch_begin(0,0); @@ -675,7 +675,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, String8List lib_dir // read type servers from disk in parallel { ProfBegin("Read External Type Servers"); - String8Array msf_data_arr = lnk_read_data_from_file_path_parallel(tp, scratch.arena, ts_path_arr); + String8Array msf_data_arr = lnk_read_data_from_file_path_parallel(tp, scratch.arena, 0, ts_path_arr); ProfEnd(); MSF_Parsed **msf_parse_arr = lnk_msf_parsed_from_data_parallel(tp_arena, tp, msf_data_arr); @@ -3315,7 +3315,7 @@ lnk_build_pdb(TP_Context *tp, 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, natvis_file_path_arr); + String8Array natvis_file_data_arr = lnk_read_data_from_file_path_parallel(tp, scratch.arena, config->io_flags, natvis_file_path_arr); for (U64 i = 0; i < natvis_file_data_arr.count; ++i) { String8 natvis_file_path = natvis_file_path_arr.v[i]; diff --git a/src/linker/lnk_debug_info.h b/src/linker/lnk_debug_info.h index 4ed75c34..43e208be 100644 --- a/src/linker/lnk_debug_info.h +++ b/src/linker/lnk_debug_info.h @@ -500,7 +500,7 @@ internal CV_DebugT * lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena * internal CV_SymbolList * lnk_cv_symbol_list_arr_from_debug_s_arr(TP_Context *tp, TP_Arena *arena, U64 obj_count, CV_DebugS *debug_s_arr); internal LNK_PchInfo * lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj *obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols); -internal LNK_CodeViewInput lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, String8List lib_dir_list, U64 objs_count, LNK_Obj **objs); +internal LNK_CodeViewInput lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, U64 objs_count, LNK_Obj **objs); internal LNK_LeafRef lnk_leaf_ref(U32 idx, U32 leaf_idx); internal LNK_LeafRef lnk_obj_leaf_ref(U32 obj_idx, U32 leaf_idx); diff --git a/src/linker/lnk_io.c b/src/linker/lnk_io.c index 6b9aee8d..bb67b134 100644 --- a/src/linker/lnk_io.c +++ b/src/linker/lnk_io.c @@ -172,31 +172,13 @@ lnk_log_read(String8 path, U64 size) } internal String8 -lnk_read_data_from_file_path(Arena *arena, String8 path) +lnk_read_data_from_file_path(Arena *arena, LNK_IO_Flags io_flags, String8 path) { - String8 data = str8_zero(); - OS_Handle handle = {0}; - int is_open = lnk_open_file_read((char*)path.str, path.size, &handle, sizeof(handle)); - if (is_open) { - U64 buffer_size = lnk_size_from_file(&handle); - U8 *buffer = push_array_no_zero(arena, U8, buffer_size); - U64 read_size = lnk_read_file(&handle, buffer, buffer_size); - - data = str8(buffer, read_size); - - lnk_close_file(&handle); - - if (read_size != buffer_size) { - lnk_error(LNK_Warning_IllData, "incomplete file read occurred, read %u bytes, expected %u bytes, file %S", path); - } - - if (lnk_get_log_status(LNK_Log_IO_Read)) { - lnk_log_read(path, data.size); - } - } else { - lnk_error(LNK_Error_FileNotFound, "unable to open file %S", path); - } - return data; + Temp scratch = scratch_begin(&arena, 1); + TP_Context *single_thread_ctx = tp_alloc(scratch.arena, 1, 1, str8_zero()); + String8Array data_arr = lnk_read_data_from_file_path_parallel(single_thread_ctx, arena, io_flags, (String8Array){ .count = 1, .v = &path }); + scratch_end(scratch); + return data_arr.v[0]; } internal @@ -232,33 +214,72 @@ THREAD_POOL_TASK_FUNC(lnk_data_from_file_path_task) task->data_arr.v[task_id] = str8(buffer, read_size); } -internal String8Array -lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, String8Array path_arr) +internal +THREAD_POOL_TASK_FUNC(lnk_memory_map_file_task) { - Temp scratch = scratch_begin(&arena,1); + LNK_DiskReader *task = raw_task; +#if _WIN32 + Temp scratch = scratch_begin(&arena, 1); + String16 path16 = str16_from_8(scratch.arena, task->path_arr.v[task_id]); + HANDLE file_handle = CreateFileW(path16.str, GENERIC_READ, 0, 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); + } + scratch_end(scratch); +#else +# error "memory mapping files is not supported on this platform" +#endif +} +internal String8Array +lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, LNK_IO_Flags io_flags, String8Array path_arr) +{ LNK_DiskReader reader = {0}; - reader.path_arr = path_arr; - reader.handle_arr = push_array_no_zero(scratch.arena, OS_Handle, path_arr.count); - reader.size_arr = push_array_no_zero(scratch.arena, U64, path_arr.count); - // open handles and get sizes - tp_for_parallel(tp, 0, path_arr.count, lnk_data_size_from_file_path_task, &reader); + if (io_flags & LNK_IO_Flags_MemoryMapFiles) { + reader.io_flags = io_flags; + reader.path_arr = path_arr; + reader.data_arr.count = path_arr.count; + reader.data_arr.v = push_array(arena, String8, path_arr.count); + tp_for_parallel(tp, 0, path_arr.count, lnk_memory_map_file_task, &reader); + } else { + Temp scratch = scratch_begin(&arena,1); - // compute file buffer size - U64 total_data_size = sum_array_u64(path_arr.count, reader.size_arr); + reader.path_arr = path_arr; + reader.handle_arr = push_array_no_zero(scratch.arena, OS_Handle, path_arr.count); + reader.size_arr = push_array_no_zero(scratch.arena, U64, path_arr.count); - // assign offsets into file buffer - U64 *off_arr = push_array_no_zero(scratch.arena, U64, path_arr.count); - MemoryCopyTyped(off_arr, reader.size_arr, path_arr.count); - counts_to_offsets_array_u64(path_arr.count, off_arr); + // open handles and get sizes + tp_for_parallel(tp, 0, path_arr.count, lnk_data_size_from_file_path_task, &reader); - reader.data_arr = str8_array_reserve(arena, path_arr.count); - reader.off_arr = off_arr; - reader.buffer = push_array_no_zero(arena, U8, total_data_size); + // compute file buffer size + U64 total_data_size = sum_array_u64(path_arr.count, reader.size_arr); - // read files and close handles - tp_for_parallel(tp, 0, path_arr.count, lnk_data_from_file_path_task, &reader); + // assign offsets into file buffer + U64 *off_arr = push_array_no_zero(scratch.arena, U64, path_arr.count); + MemoryCopyTyped(off_arr, reader.size_arr, path_arr.count); + counts_to_offsets_array_u64(path_arr.count, off_arr); + + reader.io_flags = io_flags; + reader.data_arr = str8_array_reserve(arena, path_arr.count); + reader.off_arr = off_arr; + reader.buffer = push_array_no_zero(arena, U8, total_data_size); + + // read files and close handles + tp_for_parallel(tp, 0, path_arr.count, lnk_data_from_file_path_task, &reader); + + scratch_end(scratch); + } String8Array result = {0}; result.count = path_arr.count; @@ -270,7 +291,6 @@ lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, String8Array } } - scratch_end(scratch); return result; } diff --git a/src/linker/lnk_io.h b/src/linker/lnk_io.h index 13b0d777..9ff2a085 100644 --- a/src/linker/lnk_io.h +++ b/src/linker/lnk_io.h @@ -2,8 +2,17 @@ //////////////////////////////// +typedef U32 LNK_IO_Flags; +enum +{ + LNK_IO_Flags_MemoryMapFiles = (1 << 0), +}; + +//////////////////////////////// + typedef struct { + LNK_IO_Flags io_flags; String8Array path_arr; String8Array data_arr; OS_Handle *handle_arr; @@ -30,10 +39,9 @@ internal OS_Handle lnk_file_open_with_rename_permissions(String8 path); internal B32 lnk_file_set_delete_on_close(OS_Handle handle, B32 delete_file); internal B32 lnk_file_rename(OS_Handle handle, String8 new_name); -internal String8 lnk_read_data_from_file_path(Arena *arena, String8 path); -internal String8Array lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, String8Array path_arr); +internal String8 lnk_read_data_from_file_path(Arena *arena, LNK_IO_Flags io_flags, String8 path); +internal String8Array lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, LNK_IO_Flags io_flags, String8Array path_arr); internal void lnk_write_data_list_to_file_path(String8 path, String8 temp_path, String8List list); internal void lnk_write_data_to_file_path(String8 path, String8 temp_path, String8 data); -