produce image layout map

This commit is contained in:
Nikita Smith
2025-04-02 11:14:33 -07:00
parent 02885b0b86
commit c6ba475427
5 changed files with 198 additions and 5 deletions
+177 -1
View File
@@ -3139,6 +3139,169 @@ LNK_CHUNK_VISITOR_SIG(lnk_max_tls_align)
return 0;
}
global LNK_Section **g_rad_sort_sect_id_map = 0;
typedef struct
{
U64 off;
LNK_Chunk *chunk;
} LNK_ChunkOffPair;
internal int
lnk_map_sort_on_chunk_file_off(void *raw_a, void *raw_b)
{
LNK_Chunk **a = raw_a;
LNK_Chunk **b = raw_b;
U64 a_file_off = lnk_virt_off_from_chunk_ref(g_rad_sort_sect_id_map, (*a)->ref);
U64 b_file_off = lnk_virt_off_from_chunk_ref(g_rad_sort_sect_id_map, (*b)->ref);
int is_before = a_file_off < b_file_off;
return is_before;
}
internal int
lnk_map_sort_on_chunk_off(void *raw_a, void *raw_b)
{
LNK_ChunkOffPair *a = raw_a;
LNK_ChunkOffPair *b = raw_b;
int is_before = a->off < b->off;
return is_before;
}
internal U64
lnk_chunk_off_pair_array_bsearch(LNK_ChunkOffPair *arr, U64 count, U64 value)
{
if(count > 1 && arr[0].off <= value && value < arr[count-1].off)
{
U64 l = 0;
U64 r = count - 1;
for(; l <= r; )
{
U64 m = l + (r - l) / 2;
if(arr[m].off == value)
{
return m;
}
else if(arr[m].off < value)
{
l = m + 1;
}
else
{
r = m - 1;
}
}
}
else if (count == 1 && arr[0].off == value)
{
return 0;
}
return max_U64;
}
internal String8List
lnk_build_rad_chunk_map(Arena *arena, String8 image_data, U64 thread_count, LNK_ObjList objs, LNK_SectionTable *st, LNK_SymbolTable *symtab)
{
Temp scratch = scratch_begin(&arena, 1);
LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, st);
String8List map = {0};
str8_list_pushf(arena, &map, "# CHUNKS\n");
for (LNK_SectionNode *sect_n = st->list.first; sect_n != 0; sect_n = sect_n->next) {
LNK_Section *sect = &sect_n->data;
if (sect->has_layout) {
LNK_Chunk **chunks = push_array_no_zero(scratch.arena, LNK_Chunk *, sect->layout.total_count);
MemoryCopyTyped(chunks, sect->layout.chunk_ptr_array, sect->layout.total_count);
g_rad_sort_sect_id_map = sect_id_map;
radsort(chunks, sect->layout.total_count, lnk_map_sort_on_chunk_file_off);
str8_list_pushf(arena, &map, "%S\n", sect->name);
str8_list_pushf(arena, &map, "%-16s %-8s %-8s %-16s %-8s %s\n", "Sect:Offset", "VirtSize", "FileSize", "Blake3", "ChunkRef", "Source");
for (U64 chunk_idx = 0; chunk_idx < sect->layout.total_count; ++chunk_idx) {
LNK_Chunk *chunk = chunks[chunk_idx];
if (chunk->type == LNK_Chunk_Leaf && chunk != g_null_chunk_ptr) {
Temp temp = temp_begin(scratch.arena);
ISectOff sc = lnk_sc_from_chunk_ref(sect_id_map, chunk->ref);
U64 virt_size = lnk_virt_size_from_chunk_ref(sect_id_map, chunk->ref);
U64 file_size = lnk_file_size_from_chunk_ref(sect_id_map, chunk->ref);
String8 chunk_data = lnk_data_from_chunk_ref(sect_id_map, image_data, chunk->ref);
U128 chunk_hash = {0};
if (~sect->flags & COFF_SectionFlag_CntUninitializedData) {
blake3_hasher hasher; blake3_hasher_init(&hasher);
blake3_hasher_update(&hasher, chunk_data.str, chunk_data.size);
blake3_hasher_finalize(&hasher, (U8 *)&chunk_hash, sizeof(chunk_hash));
}
String8 address_str = push_str8f(temp.arena, "%04x:%08x", sc.isect, sc.off);
String8 virt_size_str = push_str8f(temp.arena, "%08x", virt_size);
String8 file_size_str = push_str8f(temp.arena, "%08x", file_size);
String8 chunk_hash_str = push_str8f(temp.arena, "%08x%08x", chunk_hash.u64[0], chunk_hash.u64[1]);
String8 chunk_ref_str = push_str8f(temp.arena, "{%llx,%llx}", chunk->ref.sect_id, chunk->ref.chunk_id);
String8 source_str = str8_lit("\?\?\?");
if (chunk->obj) {
if (chunk->obj->lib_path.size) {
String8 lib_name = chunk->obj->lib_path;
lib_name = str8_skip_last_slash(lib_name);
lib_name = str8_chop_last_dot(lib_name);
String8 obj_name = chunk->obj->path;
obj_name = str8_skip_last_slash(obj_name);
source_str = push_str8f(temp.arena, "%S:%S", lib_name, obj_name);
} else {
source_str = push_str8f(temp.arena, "%S", chunk->obj->path);
}
}
str8_list_pushf(arena, &map, "%-16S %-8S %-8S %-16S %-8S %S\n", address_str, virt_size_str, file_size_str, chunk_hash_str, chunk_ref_str, source_str);
temp_end(temp);
}
}
str8_list_pushf(arena, &map, "\n");
}
}
str8_list_pushf(arena, &map, "# SYMBOLS\n");
str8_list_pushf(arena, &map, "%-8s %s\n", "ChunkRef", "Symbol");
for (LNK_ObjNode *obj_n = objs.first; obj_n != 0; obj_n = obj_n->next) {
LNK_Obj *obj = &obj_n->data;
for (LNK_SymbolNode *symbol_n = obj->symbol_list.first; symbol_n != 0; symbol_n = symbol_n->next) {
LNK_Symbol *symbol = symbol_n->data;
if (LNK_Symbol_IsDefined(symbol->type)) {
if (symbol->u.defined.value_type == LNK_DefinedSymbolValue_Chunk) {
LNK_Chunk *chunk = symbol->u.defined.u.chunk;
String8 chunk_ref_str = push_str8f(scratch.arena, "{%llx,%llx}", chunk->ref.sect_id, chunk->ref.chunk_id);
String8 lib_name = obj->lib_path;
lib_name = str8_skip_last_slash(lib_name);
lib_name = str8_chop_last_dot(lib_name);
String8 obj_name = obj->path;
obj_name = str8_skip_last_slash(obj_name);
str8_list_pushf(arena, &map, "%-8S (%S%s%S) %S\n",
chunk_ref_str,
lib_name, lib_name.size ? ":" : "", obj_name,
symbol->name);
}
}
}
}
//str8_list_pushf(arena, &map, "%16s %30s %23s %10s\n\n", "Address", "Publics by Value", "Rva+Base", "Lib:Object");
scratch_end(scratch);
return map;
}
internal void
lnk_run(int argc, char **argv)
{
@@ -3167,6 +3330,7 @@ lnk_run(int argc, char **argv)
State_BuildBaseRelocs,
State_FinalizeImage,
State_BuildImpLib,
State_BuildRadChunkMap,
State_BuildDebugInfo,
};
struct StateNode {
@@ -3254,10 +3418,11 @@ lnk_run(int argc, char **argv)
B32 report_unresolved_symbols = 1;
B32 check_unused_delay_loads = !!(config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll);
B32 build_imp_lib = config->build_imp_lib;
B32 build_rad_chunk_map = (config->rad_chunk_map == LNK_SwitchState_Yes);
LNK_ObjList obj_list = {0};
LNK_LibList lib_index[LNK_InputSource_Count] = {0};
String8 image_data = str8_zero();
OS_Handle image_write_thread = {0};
OS_Handle image_write_thread = {0};
// init state machine
struct StateList state_list = {0};
@@ -4121,6 +4286,12 @@ lnk_run(int argc, char **argv)
lnk_timer_end(LNK_Timer_Lib);
ProfEnd();
} break;
case State_BuildRadChunkMap: {
ProfBegin("RAD Chunk Map");
String8List map = lnk_build_rad_chunk_map(scratch.arena, image_data, config->worker_count, obj_list, st, symtab);
lnk_write_data_list_to_file_path(config->rad_chunk_map_name, config->temp_rad_chunk_map_name, map);
ProfEnd();
} break;
case State_BuildDebugInfo: {
ProfBegin("Debug Info");
lnk_timer_begin(LNK_Timer_Debug);
@@ -4319,6 +4490,11 @@ lnk_run(int argc, char **argv)
continue;
}
}
if (build_rad_chunk_map) {
build_rad_chunk_map = 0;
state_list_push(scratch.arena, state_list, State_BuildRadChunkMap);
continue;
}
if (build_debug_info) {
build_debug_info = 0;
state_list_push(scratch.arena, state_list, State_BuildDebugInfo);
+2 -1
View File
@@ -40,6 +40,7 @@ typedef struct LNK_Chunk
struct LNK_ChunkList *list;
struct LNK_ChunkArray *arr;
} u;
struct LNK_Obj *obj;
#if LNK_DEBUG_CHUNKS
String8 debug;
#endif
@@ -115,7 +116,7 @@ typedef struct LNK_ChunkPadArrayNode
} LNK_ChunkPadArrayNode;
typedef struct LNK_ChunkPadArrayList
{
U64 count;
U64 count;
LNK_ChunkPadArrayNode *first;
LNK_ChunkPadArrayNode *last;
} LNK_ChunkPadArrayList;
+10 -3
View File
@@ -132,6 +132,7 @@ global read_only struct
{ LNK_CmdSwitch_Rad_Age, "RAD_AGE", ":#", "Age embeded in EXE and PDB, used to validate incremental build. Default is 1." },
{ LNK_CmdSwitch_Rad_BuildInfo, "RAD_BUILD_INFO", "", "Print build info and exit." },
{ LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "" },
{ LNK_CmdSwitch_Rad_ChunkMap, "RAD_CHUNK_MAP", ":FILENAME", "Emit file with the output image's layout description." },
{ LNK_CmdSwitch_Rad_Debug, "RAD_DEBUG", "[:NO]", "Emit RAD debug info file." },
{ LNK_CmdSwitch_Rad_DebugAltPath, "RAD_DEBUGALTPATH", "", "" },
{ LNK_CmdSwitch_Rad_DebugName, "RAD_DEBUG_NAME", ":FILENAME", "Sets file name for RAD debug info file." },
@@ -1522,6 +1523,11 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam
lnk_cmd_switch_set_flag_64(obj_path, lib_path, cmd_switch, value_strings, &config->flags, LNK_ConfigFlag_CheckUnusedDelayLoadDll);
} break;
case LNK_CmdSwitch_Rad_ChunkMap: {
lnk_cmd_switch_parse_string_copy(arena, obj_path, lib_path, cmd_switch, value_strings, &config->rad_chunk_map_name);
config->rad_chunk_map = LNK_SwitchState_Yes;
} break;
case LNK_CmdSwitch_Rad_Debug: {
lnk_cmd_switch_parse_flag(obj_path, lib_path, cmd_switch, value_strings, &config->rad_debug);
} break;
@@ -2060,9 +2066,10 @@ lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line)
// 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);
config->temp_rad_chunk_map_name = push_str8f(arena, "%S.tmp%x", config->rad_chunk_map_name, config->time_stamp);
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)) {
+4
View File
@@ -128,6 +128,7 @@ typedef enum
LNK_CmdSwitch_Rad_Age,
LNK_CmdSwitch_Rad_BuildInfo,
LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll,
LNK_CmdSwitch_Rad_ChunkMap,
LNK_CmdSwitch_Rad_Debug,
LNK_CmdSwitch_Rad_DebugName,
LNK_CmdSwitch_Rad_DebugAltPath,
@@ -355,6 +356,8 @@ typedef struct LNK_Config
String8 manifest_ui_access;
String8List manifest_dependency_list;
LNK_SwitchState rad_debug;
LNK_SwitchState rad_chunk_map;
String8 rad_chunk_map_name;
String8 rad_debug_name;
String8 rad_debug_alt_path;
String8List include_symbol_list;
@@ -369,6 +372,7 @@ typedef struct LNK_Config
String8 temp_image_name;
String8 temp_pdb_name;
String8 temp_rad_debug_name;
String8 temp_rad_chunk_map_name;
} LNK_Config;
typedef enum
+5
View File
@@ -410,6 +410,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer)
chunk->input_idx = LNK_MakeChunkInputIdx(obj_idx, sect_idx);
chunk->flags = coff_sect->flags;
chunk->u.leaf = data;
chunk->obj = obj;
lnk_chunk_set_debugf(arena, chunk, "%S: name: %S, obj_idx: 0x%llX isect: 0x%llX", cached_path, sect_name_arr[sect_idx], obj_idx, sect_idx);
}
@@ -425,6 +426,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer)
master_common_block->flags = LNK_BSS_SECTION_FLAGS;
master_common_block->associate = 0;
master_common_block->u.list = push_array(arena, LNK_ChunkList, 1);
master_common_block->obj = obj;
lnk_chunk_set_debugf(arena, master_common_block, "%S: master common block", cached_path);
LNK_ChunkPtr *chunk_ptr_arr = push_array_no_zero(arena, LNK_ChunkPtr, chunk_count);
@@ -787,6 +789,7 @@ lnk_symbol_array_from_coff(Arena *arena,
chunk_list->input_idx = chunk->input_idx;
chunk_list->flags = chunk->flags;
chunk_list->u.list = push_array(arena, LNK_ChunkList, 1);
chunk_list->obj = obj;
lnk_chunk_set_debugf(arena, chunk_list, "%S: function chunk list for %S", obj_path, symbol.name);
// update properties on first chunk
@@ -834,6 +837,7 @@ lnk_symbol_array_from_coff(Arena *arena,
split_chunk->is_discarded = current->data->is_discarded;
split_chunk->flags = current->data->flags;
split_chunk->u.leaf = right_data;
split_chunk->obj = obj;
lnk_chunk_set_debugf(arena, split_chunk, "%S: chunk split on function %S SECT%x SPLIT_POS %#llx", obj_path, symbol.name, symbol.section_number, split_pos);
LNK_ChunkNode *split_node = push_array(arena, LNK_ChunkNode, 1);
@@ -985,6 +989,7 @@ lnk_symbol_array_from_coff(Arena *arena,
chunk->type = LNK_Chunk_Leaf;
chunk->flags = master_common_block->flags;
chunk->u.leaf = str8(0, parsed_symbol.value);
chunk->obj = obj;
lnk_chunk_set_debugf(arena, chunk, "%S: common block %S", obj_path, parsed_symbol.name);
lnk_chunk_list_push(arena, master_common_block->u.list, chunk);