added /RAD_WRITE_TEMP_FILES

When enabled linker writes image and debug info to temporary files
and renames them after all writes are done.
This commit is contained in:
Nikita Smith
2025-02-10 16:18:59 -08:00
parent 8a8a8bcc8d
commit bbce89eada
10 changed files with 148 additions and 47 deletions
+16 -11
View File
@@ -358,7 +358,7 @@ lnk_manifest_from_inputs(Arena *arena,
// write linker manifest to temp file
String8 linker_manifest_path = push_str8f(scratch.arena, "%S.manifest.temp", manifest_name);
lnk_write_data_to_file_path(linker_manifest_path, linker_manifest);
lnk_write_data_to_file_path(linker_manifest_path, str8_zero(), linker_manifest);
// push linker manifest
str8_list_push(scratch.arena, &input_manifest_path_list, linker_manifest_path);
@@ -3079,7 +3079,7 @@ lnk_write_thread(void *raw_ctx)
{
ProfBeginFunction();
LNK_WriteThreadContext *ctx = raw_ctx;
lnk_write_data_to_file_path(ctx->path, ctx->data);
lnk_write_data_to_file_path(ctx->path, ctx->temp_path, ctx->data);
ProfEnd();
}
@@ -3254,7 +3254,7 @@ lnk_run(int argc, char **argv)
LNK_ObjList obj_list = {0};
LNK_LibList lib_index[LNK_InputSource_Count] = {0};
String8 image_data = str8_zero();
OS_Handle image_write_thread = {0};
OS_Handle image_write_thread = {0};
// init state machine
struct StateList state_list = {0};
@@ -3763,7 +3763,7 @@ lnk_run(int argc, char **argv)
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);
lnk_write_data_to_file_path(config->manifest_name, manifest_data);
lnk_write_data_to_file_path(config->manifest_name, str8_zero(), manifest_data);
temp_end(temp);
ProfEnd();
} break;
@@ -4080,10 +4080,14 @@ lnk_run(int argc, char **argv)
}
}
LNK_WriteThreadContext *ctx = push_array(scratch.arena, LNK_WriteThreadContext, 1);
ctx->path = config->image_name;
ctx->data = image_data;
image_write_thread = os_thread_launch(lnk_write_thread, ctx, 0);
// write image file in background
{
LNK_WriteThreadContext *ctx = push_array(scratch.arena, LNK_WriteThreadContext, 1);
ctx->path = config->image_name;
ctx->temp_path = config->temp_image_name;
ctx->data = image_data;
image_write_thread = os_thread_launch(lnk_write_thread, ctx, 0);
}
if (lnk_get_log_status(LNK_Log_InputObj)) {
U64 total_input_size = 0;
@@ -4110,7 +4114,7 @@ lnk_run(int argc, char **argv)
ProfBegin("Build Imp Lib");
lnk_timer_begin(LNK_Timer_Lib);
String8List lib_list = lnk_build_import_lib(tp, tp_arena, config->machine, config->time_stamp, config->imp_lib_name, config->image_name, exptab);
lnk_write_data_list_to_file_path(config->imp_lib_name, lib_list);
lnk_write_data_list_to_file_path(config->imp_lib_name, str8_zero(), lib_list);
lnk_timer_end(LNK_Timer_Lib);
ProfEnd();
} break;
@@ -4143,7 +4147,8 @@ lnk_run(int argc, char **argv)
input.parsed_symbols,
types);
lnk_write_data_list_to_file_path(config->rad_debug_name, rdi_data);
lnk_write_data_list_to_file_path(config->rad_debug_name, config->temp_rad_debug_name, rdi_data);
lnk_timer_end(LNK_Timer_Rdi);
}
@@ -4177,7 +4182,7 @@ lnk_run(int argc, char **argv)
input.parsed_symbols,
types);
lnk_write_data_list_to_file_path(config->pdb_name, pdb_data);
lnk_write_data_list_to_file_path(config->pdb_name, config->temp_pdb_name, pdb_data);
lnk_timer_end(LNK_Timer_Pdb);
}
+1
View File
@@ -224,6 +224,7 @@ typedef struct
typedef struct
{
String8 path;
String8 temp_path;
String8 data;
} LNK_WriteThreadContext;
+13 -2
View File
@@ -152,17 +152,17 @@ global read_only struct
{ LNK_CmdSwitch_Rad_PdbHashTypeNames, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." },
{ LNK_CmdSwitch_Rad_SectVirtOff, "RAD_SECT_VIRT_OFF", ":#", "Set RVA where section data is placed in memory. For internal use only." },
{ LNK_CmdSwitch_Rad_SharedThreadPool, "RAD_SHARED_THREAD_POOL", "[:STRING]", "Default value \"" LNK_DEFAULT_THREAD_POOL_NAME "\"" },
{ LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." },
{ LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." },
{ LNK_CmdSwitch_Rad_SuppressError, "RAD_SUPPRESS_ERROR", ":#", "" },
{ LNK_CmdSwitch_Rad_SymbolTableCapDefined, "RAD_SYMBOL_TABLE_CAP_DEFINED", ":#", "Number of buckets allocated in the symbol table for defined symbols." },
{ LNK_CmdSwitch_Rad_SymbolTableCapInternal, "RAD_SYMBOL_TABLE_CAP_INTERNAL", ":#", "Number of buckets allocated in the symbol table for internal symbols." },
{ LNK_CmdSwitch_Rad_SymbolTableCapLib, "RAD_SYMBOL_TABLE_CAP_LIB", ":#", "Number of buckets allocated in the symbol table for library symbols." },
{ LNK_CmdSwitch_Rad_SymbolTableCapWeak, "RAD_SYMBOL_TABLE_CAP_WEAK", ":#", "Number of buckets allocated in the symbol table for weak symbols." },
{ LNK_CmdSwitch_Rad_TargetOs, "RAD_TARGET_OS", ":{WINDOWS,LINUX,MAC}" },
{ LNK_CmdSwitch_Rad_WriteTempFiles, "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, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." },
{ LNK_CmdSwitch_Rad_Version, "RAD_VERSION", "", "Print version and exit." },
{ LNK_CmdSwitch_Rad_Workers, "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, "HELP", "", "" },
{ LNK_CmdSwitch_Help, "?", "", "" },
};
@@ -1754,6 +1754,10 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam
}
} break;
case LNK_CmdSwitch_Rad_WriteTempFiles: {
lnk_cmd_switch_parse_flag(obj_path, lib_path, cmd_switch, value_strings, &config->write_temp_files);
} break;
case LNK_CmdSwitch_Rad_TimeStamp: {
lnk_cmd_switch_parse_u32(obj_path, lib_path, cmd_switch, value_strings, &config->time_stamp, 0);
} break;
@@ -2054,6 +2058,13 @@ lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line)
// :Rad_DebugAltPath
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) {
config->temp_image_name = push_str8f(arena, "%S.tmp%x", config->image_name, config->time_stamp);
config->temp_pdb_name = push_str8f(arena, "%S.tmp%x", config->pdb_name, config->time_stamp);
config->temp_rad_debug_name = push_str8f(arena, "%S.tmp%x", config->rad_debug_name, config->time_stamp);
}
if (lnk_get_log_status(LNK_Log_Debug)) {
String8 full_cmd_line = str8_list_join(scratch.arena, &raw_cmd_line, &(StringJoin){ .sep = str8_lit_comp(" ") });
fprintf(stderr, "--------------------------------------------------------------------------------\n");
+5
View File
@@ -155,6 +155,7 @@ typedef enum
LNK_CmdSwitch_Rad_SymbolTableCapInternal,
LNK_CmdSwitch_Rad_SymbolTableCapWeak,
LNK_CmdSwitch_Rad_SymbolTableCapLib,
LNK_CmdSwitch_Rad_WriteTempFiles,
LNK_CmdSwitch_Rad_TargetOs,
LNK_CmdSwitch_Rad_TimeStamp,
LNK_CmdSwitch_Rad_Version,
@@ -364,6 +365,10 @@ typedef struct LNK_Config
U64 symbol_table_cap_lib;
B32 build_imp_lib;
B32 build_exp;
LNK_SwitchState write_temp_files;
String8 temp_image_name;
String8 temp_pdb_name;
String8 temp_rad_debug_name;
} LNK_Config;
typedef enum
+1 -1
View File
@@ -2676,7 +2676,7 @@ lnk_replace_type_names_with_hashes(TP_Context *tp, TP_Arena *arena, CV_DebugT de
if (task.make_map) {
String8List map = {0};
str8_list_concat_in_place_array(&map, task.maps, tp->worker_count);
lnk_write_data_list_to_file_path(map_name, map);
lnk_write_data_list_to_file_path(map_name, str8_zero(), map);
tp_arena_release(&task.map_arena);
}
+56 -17
View File
@@ -10,6 +10,15 @@ lnk_open_file_read(char *path, uint64_t path_size, void *handle_buffer, uint64_t
return !os_handle_match(handle, os_handle_zero());
}
shared_function int
lnk_open_file_write_rename(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max)
{
OS_Handle handle = os_file_open(OS_AccessFlag_Write|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite|OS_AccessFlag_ShareDelete, str8((U8*)path, path_size));
Assert(sizeof(handle) <= handle_buffer_max);
MemoryCopy(handle_buffer, &handle, sizeof(handle));
return !os_handle_match(handle, os_handle_zero());
}
shared_function int
lnk_open_file_write(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max)
{
@@ -26,6 +35,14 @@ lnk_close_file(void *raw_handle)
os_file_close(handle);
}
shared_function int
lnk_rename_file(void *raw_handle, char *new_file_path, uint64_t new_file_path_size)
{
OS_Handle handle = *(OS_Handle *)raw_handle;
B32 is_renamed = os_rename_file_by_handle(handle, str8((U8*)new_file_path, new_file_path_size));
return (int)is_renamed;
}
shared_function uint64_t
lnk_size_from_file(void *raw_handle)
{
@@ -163,34 +180,56 @@ lnk_read_data_from_file_path_parallel(TP_Context *tp, Arena *arena, String8Array
}
internal void
lnk_write_data_list_to_file_path(String8 path, String8List data)
lnk_write_data_list_to_file_path(String8 path, String8 temp_path, String8List data)
{
ProfBeginV("Write %M to %S", data.total_size, path);
B32 is_written = 0;
U64 bytes_written = 0;
OS_Handle handle;
if (lnk_open_file_write((char*)path.str, path.size, &handle, sizeof(handle))) {
U64 offset = 0;
for (String8Node *data_n = data.first; data_n != 0; data_n = data_n->next) {
U64 write_size = lnk_write_file(&handle, offset, data_n->string.str, data_n->string.size);
if (write_size != data_n->string.size) {
break;
B32 open_with_rename = (temp_path.size > 0);
OS_Handle file_handle = {0};
if (open_with_rename) {
lnk_open_file_write_rename((char*)temp_path.str, temp_path.size, &file_handle, sizeof(file_handle));
} else {
lnk_open_file_write((char*)path.str, path.size, &file_handle, sizeof(file_handle));
}
if (!os_handle_match(file_handle, os_handle_zero())) {
// write data nodes
{
for (String8Node *data_n = data.first; data_n != 0; data_n = data_n->next) {
U64 write_size = lnk_write_file(&file_handle, bytes_written, data_n->string.str, data_n->string.size);
if (write_size != data_n->string.size) {
break;
}
bytes_written += data_n->string.size;
}
offset += data_n->string.size;
}
lnk_close_file(&handle);
B32 is_write_complete = (bytes_written == data.total_size);
is_written = (offset == data.total_size);
if (is_written) {
if (is_write_complete) {
// rename file to original name
if (open_with_rename) {
if (lnk_rename_file(&file_handle, (char*)path.str, path.size)) {
lnk_log(LNK_Log_IO_Write, "Renamed %S -> %S", temp_path, path);
} else {
lnk_error(LNK_Error_IO, "failed to rename %S -> %S", temp_path, path);
}
}
}
// log write
if (is_write_complete) {
if (lnk_get_log_status(LNK_Log_IO_Write)) {
lnk_log(LNK_Log_IO_Write, "File \"%S\" %M written", path, data.total_size);
}
} else {
lnk_error(LNK_Error_IO, "incomplete write occurred, %M written, expected %M, file %S",
offset, data.total_size, path);
lnk_error(LNK_Error_IO, "incomplete write, %M written, expected %M, file %S", bytes_written, data.total_size, path);
}
// clean up handle
lnk_close_file(&file_handle);
} else {
lnk_error(LNK_Error_NoAccess, "don't have access to write to %S", path);
}
@@ -199,12 +238,12 @@ lnk_write_data_list_to_file_path(String8 path, String8List data)
}
internal void
lnk_write_data_to_file_path(String8 path, String8 data)
lnk_write_data_to_file_path(String8 path, String8 temp_path, String8 data)
{
Temp scratch = scratch_begin(0,0);
String8List data_list = {0};
str8_list_push(scratch.arena, &data_list, data);
lnk_write_data_list_to_file_path(path, data_list);
lnk_write_data_list_to_file_path(path, temp_path, data_list);
scratch_end(scratch);
}
+4 -2
View File
@@ -17,7 +17,9 @@ typedef struct
shared_function int lnk_open_file_read(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max);
shared_function int lnk_open_file_write(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max);
shared_function int lnk_open_file_write_rename(char *path, uint64_t path_size, void *handle_buffer, uint64_t handle_buffer_max);
shared_function void lnk_close_file(void *raw_handle);
shared_function int lnk_rename_file(void *raw_handle, char *new_file_path, uint64_t new_file_path_size);
shared_function uint64_t lnk_size_from_file(void *raw_handle);
shared_function uint64_t lnk_read_file(void *raw_handle, void *buffer, uint64_t buffer_max);
shared_function uint64_t lnk_write_file(void *raw_handle, uint64_t offset, void *buffer, uint64_t buffer_size);
@@ -27,8 +29,8 @@ shared_function uint64_t lnk_write_file(void *raw_handle, uint64_t offset, void
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 void lnk_write_data_list_to_file_path(String8 path, String8List list);
internal void lnk_write_data_to_file_path(String8 path, String8 data);
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);
internal String8List lnk_file_search(Arena *arena, String8List dir_list, String8 file_path);
+10
View File
@@ -151,6 +151,16 @@ os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range)
return result;
}
internal B32
os_rename_file(String8 orig_name, String8 new_name)
{
Temp scratch = scratch_begin(0,0);
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareWrite|OS_AccessFlag_ShareDelete, orig_name);
B32 is_renamed = os_rename_file_by_handle(file, new_name);
os_file_close(file);
return is_renamed;
}
////////////////////////////////
//~ rjf: Process Launcher Helpers
+10 -7
View File
@@ -38,13 +38,14 @@ struct OS_ProcessInfo
typedef U32 OS_AccessFlags;
enum
{
OS_AccessFlag_Read = (1<<0),
OS_AccessFlag_Write = (1<<1),
OS_AccessFlag_Execute = (1<<2),
OS_AccessFlag_Append = (1<<3),
OS_AccessFlag_ShareRead = (1<<4),
OS_AccessFlag_ShareWrite = (1<<5),
OS_AccessFlag_Inherited = (1<<6),
OS_AccessFlag_Read = (1<<0),
OS_AccessFlag_Write = (1<<1),
OS_AccessFlag_Execute = (1<<2),
OS_AccessFlag_Append = (1<<3),
OS_AccessFlag_ShareRead = (1<<4),
OS_AccessFlag_ShareWrite = (1<<5),
OS_AccessFlag_ShareDelete = (1<<6),
OS_AccessFlag_Inherited = (1<<7),
};
////////////////////////////////
@@ -206,6 +207,8 @@ internal U64 os_file_write(OS_Handle file, Rng1U64 rng, void *data);
internal B32 os_file_set_times(OS_Handle file, DateTime time);
internal FileProperties os_properties_from_file(OS_Handle file);
internal OS_FileID os_id_from_file(OS_Handle file);
internal B32 os_rename_file_by_handle(OS_Handle file, String8 new_name);
internal B32 os_rename_file(String8 orig_name, String8 new_name);
internal B32 os_delete_file_at_path(String8 path);
internal B32 os_copy_file_path(String8 dst, String8 src);
internal String8 os_full_path_from_path(Arena *arena, String8 path);
+32 -7
View File
@@ -321,13 +321,14 @@ os_file_open(OS_AccessFlags flags, String8 path)
DWORD share_mode = 0;
DWORD creation_disposition = OPEN_EXISTING;
SECURITY_ATTRIBUTES security_attributes = {sizeof(security_attributes), 0, 0};
if(flags & OS_AccessFlag_Read) {access_flags |= GENERIC_READ;}
if(flags & OS_AccessFlag_Write) {access_flags |= GENERIC_WRITE;}
if(flags & OS_AccessFlag_Execute) {access_flags |= GENERIC_EXECUTE;}
if(flags & OS_AccessFlag_ShareRead) {share_mode |= FILE_SHARE_READ;}
if(flags & OS_AccessFlag_ShareWrite) {share_mode |= FILE_SHARE_WRITE|FILE_SHARE_DELETE;}
if(flags & OS_AccessFlag_Write) {creation_disposition = CREATE_ALWAYS;}
if(flags & OS_AccessFlag_Append) {creation_disposition = OPEN_ALWAYS; access_flags |= FILE_APPEND_DATA; }
if(flags & OS_AccessFlag_Read) {access_flags |= GENERIC_READ;}
if(flags & OS_AccessFlag_Write) {access_flags |= GENERIC_WRITE;}
if(flags & OS_AccessFlag_Execute) {access_flags |= GENERIC_EXECUTE;}
if(flags & OS_AccessFlag_ShareRead) {share_mode |= FILE_SHARE_READ;}
if(flags & OS_AccessFlag_ShareWrite) {share_mode |= FILE_SHARE_WRITE|FILE_SHARE_DELETE;}
if(flags & OS_AccessFlag_ShareDelete) {share_mode |= FILE_SHARE_DELETE; access_flags |= DELETE;}
if(flags & OS_AccessFlag_Write) {creation_disposition = CREATE_ALWAYS;}
if(flags & OS_AccessFlag_Append) {creation_disposition = OPEN_ALWAYS; access_flags |= FILE_APPEND_DATA; }
if(flags & OS_AccessFlag_Inherited)
{
security_attributes.bInheritHandle = 1;
@@ -469,6 +470,30 @@ os_id_from_file(OS_Handle file)
return result;
}
internal B32
os_rename_file_by_handle(OS_Handle file, String8 new_name)
{
Temp scratch = scratch_begin(0,0);
HANDLE handle = (HANDLE)file.u64[0];
String16 new_name16 = str16_from_8(scratch.arena, new_name);
U64 file_rename_info_size = sizeof(FILE_RENAME_INFO);
U64 buffer_size = file_rename_info_size + sizeof(WCHAR)*new_name16.size;
U8 *buffer = push_array(scratch.arena, U8, buffer_size);
FILE_RENAME_INFO *rename_info = (FILE_RENAME_INFO *)buffer;
rename_info->ReplaceIfExists = 1;
rename_info->FileNameLength = new_name16.size * sizeof(new_name16.str[0]);
MemoryCopy(rename_info->FileName, new_name16.str, new_name16.size * sizeof(new_name16.str[0]));
BOOL is_renamed = SetFileInformationByHandle(handle, FileRenameInfo, buffer, buffer_size);
scratch_end(scratch);
return is_renamed;
}
internal B32
os_delete_file_at_path(String8 path)
{