mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-17 09:32:22 -07:00
5613 lines
232 KiB
C
5613 lines
232 KiB
C
// Copyright (c) 2025 Epic Games Tools
|
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_parse_debug_s_task)
|
|
{
|
|
U64 obj_idx = task_id;
|
|
LNK_ParseDebugSTaskData *task = raw_task;
|
|
|
|
LNK_Obj *obj = task->obj_arr[obj_idx];
|
|
String8List sect_list = task->sect_list_arr[obj_idx];
|
|
CV_DebugS *debug_s = &task->debug_s_arr[obj_idx];
|
|
|
|
for (String8Node *node = sect_list.first; node != 0; node = node->next) {
|
|
// parse & merge sub sections
|
|
CV_DebugS ds = cv_parse_debug_s(arena, node->string);
|
|
cv_debug_s_concat_in_place(debug_s, &ds);
|
|
|
|
// make sure there is one string table
|
|
String8List string_data_list = cv_sub_section_from_debug_s(*debug_s, CV_C13SubSectionKind_StringTable);
|
|
if (string_data_list.node_count > 1) {
|
|
// TODO: print section index
|
|
lnk_error_obj(LNK_Warning_IllData, obj, ".debug$S has %u string table sub-sections defined, picking first sub-section", string_data_list.node_count);
|
|
}
|
|
|
|
// make sure there is one file checksum table
|
|
String8List checksum_data_list = cv_sub_section_from_debug_s(*debug_s, CV_C13SubSectionKind_FileChksms);
|
|
if (checksum_data_list.node_count > 1) {
|
|
// TODO: print section index
|
|
lnk_error_obj(LNK_Warning_IllData, obj, ".debug$S has %u file checksum sub-sections defined, picking first sub-section", checksum_data_list.node_count);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal CV_DebugS *
|
|
lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *sect_list_arr)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_ParseDebugSTaskData task_data = {0};
|
|
task_data.obj_arr = obj_arr;
|
|
task_data.sect_list_arr = sect_list_arr;
|
|
task_data.debug_s_arr = push_array(arena->v[0], CV_DebugS, obj_count);
|
|
|
|
tp_for_parallel(tp, arena, obj_count, lnk_parse_debug_s_task, &task_data);
|
|
|
|
ProfEnd();
|
|
return task_data.debug_s_arr;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_check_debug_t_sig_and_get_data_task)
|
|
{
|
|
U64 obj_idx = task_id;
|
|
LNK_CheckDebugTSigTaskData *task = raw_task;
|
|
|
|
String8Array data_arr = task->data_arr_arr[obj_idx];
|
|
LNK_Obj *obj = task->obj_arr[obj_idx];
|
|
|
|
for (String8 *data_ptr = &data_arr.v[0], *data_opl = data_arr.v + data_arr.count;
|
|
data_ptr < data_opl;
|
|
++data_ptr) {
|
|
if (data_ptr->size == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (data_ptr->size < sizeof(CV_Signature)) {
|
|
// TODO: print section index
|
|
lnk_error_obj(LNK_Error_IllData, obj, ".debug$T must have at least 4 bytes for CodeView signature");
|
|
}
|
|
|
|
CV_Signature *sig_ptr = (CV_Signature *)data_ptr->str;
|
|
switch (*sig_ptr) {
|
|
default: {
|
|
lnk_error_obj(LNK_Warning_IllData, obj, "unknown CodeView type signature in section (TODO: print section index)");
|
|
*data_ptr = str8(0,0);
|
|
} break;
|
|
case CV_Signature_C6: {
|
|
lnk_not_implemented("TODO: C6 types");
|
|
*data_ptr = str8(0,0);
|
|
} break;
|
|
case CV_Signature_C7: {
|
|
lnk_not_implemented("TODO: C7 types");
|
|
*data_ptr = str8(0,0);
|
|
} break;
|
|
case CV_Signature_C11: {
|
|
lnk_not_implemented("TODO: C11 types");
|
|
*data_ptr = str8(0,0);
|
|
} break;
|
|
case CV_Signature_C13: {
|
|
data_ptr->str += sizeof(CV_Signature);
|
|
data_ptr->size -= sizeof(CV_Signature);
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_parse_debug_t_task)
|
|
{
|
|
ProfBeginFunction();
|
|
U64 obj_idx = task_id;
|
|
LNK_ParseDebugTTaskData *task = raw_task;
|
|
String8Array data_arr = task->data_arr_arr[obj_idx];
|
|
CV_DebugT *debug_t = &task->debug_t_arr[obj_idx];
|
|
*debug_t = cv_debug_t_from_data_arr(arena, data_arr, CV_LeafAlign);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal CV_DebugT *
|
|
lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *debug_t_list_arr)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
// list -> array
|
|
String8Array *data_arr_arr = str8_array_from_list_arr(arena->v[0], debug_t_list_arr, obj_count);
|
|
|
|
// validate signatures
|
|
LNK_CheckDebugTSigTaskData check_sig;
|
|
check_sig.obj_arr = obj_arr;
|
|
check_sig.data_arr_arr = data_arr_arr;
|
|
tp_for_parallel(tp, 0, obj_count, lnk_check_debug_t_sig_and_get_data_task, &check_sig);
|
|
|
|
// parse debug types
|
|
LNK_ParseDebugTTaskData parse;
|
|
parse.data_arr_arr = data_arr_arr;
|
|
parse.debug_t_arr = push_array_no_zero(arena->v[0], CV_DebugT, obj_count);
|
|
tp_for_parallel(tp, arena, obj_count, lnk_parse_debug_t_task, &parse);
|
|
|
|
ProfEnd();
|
|
return parse.debug_t_arr;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_parse_cv_symbols_task)
|
|
{
|
|
LNK_ParseCVSymbolsTaskData *task = raw_task;
|
|
LNK_CodeViewSymbolsInput *input = &task->inputs[task_id];
|
|
cv_parse_symbol_sub_section(arena, input->symbol_list, 0, input->raw_symbols, CV_SymbolAlign);
|
|
}
|
|
|
|
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)
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
String8 work_dir = os_get_current_path(scratch.arena);
|
|
|
|
HashTable *debug_p_ht = hash_table_init(scratch.arena, obj_count);
|
|
CV_LeafHeader **endprecomp_arr = push_array(scratch.arena, CV_LeafHeader *, obj_count);
|
|
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
CV_DebugT *debug_p = &debug_p_arr[obj_idx];
|
|
CV_DebugT *debug_t = &debug_t_arr[obj_idx];
|
|
|
|
if (debug_t->count && debug_p->count) {
|
|
lnk_error_obj(LNK_Warning_MultipleDebugTAndDebugP,
|
|
&obj_arr[obj_idx],
|
|
"multiple sections with debug types detected, obj must have either .debug$T or .debug$P (using .debug$T for type server)");
|
|
continue;
|
|
}
|
|
|
|
if (debug_p->count) {
|
|
String8 obj_path = obj_arr[obj_idx].path;
|
|
obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, obj_path, work_dir);
|
|
if (hash_table_search_path(debug_p_ht, obj_path)) {
|
|
lnk_error_obj(LNK_Warning_DuplicateObjPath, &obj_arr[obj_idx], "duplicate obj path %S", obj_path);
|
|
} else {
|
|
hash_table_push_path_u64(scratch.arena, debug_p_ht, obj_path, obj_idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
LNK_PchInfo* pch_arr = push_array_no_zero(arena, LNK_PchInfo, obj_count);
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
CV_DebugT debug_t = debug_t_arr[obj_idx];
|
|
if (cv_debug_t_is_pch(debug_t)) {
|
|
CV_Leaf precomp_leaf = cv_debug_t_get_leaf(debug_t, 0);
|
|
CV_PrecompInfo precomp = cv_precomp_info_from_leaf(precomp_leaf);
|
|
|
|
String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, precomp.obj_name, work_dir);
|
|
|
|
// map obj name in LF_PRECOMP to obj index
|
|
U64 debug_p_obj_idx;
|
|
if (!hash_table_search_path_u64(debug_p_ht, obj_path, &debug_p_obj_idx)) {
|
|
lnk_error_obj(LNK_Error_PrecompObjNotFound, &obj_arr[obj_idx], "LF_PRECOMP references non-existent obj %S", obj_path);
|
|
lnk_exit(LNK_Error_PrecompObjNotFound);
|
|
}
|
|
|
|
// get LF_PRECOMP
|
|
CV_DebugT debug_p = debug_p_arr[debug_p_obj_idx];
|
|
CV_Leaf endprecomp_leaf = cv_debug_t_get_leaf(debug_p, precomp.leaf_count);
|
|
CV_LeafEndPreComp *endprecomp = (CV_LeafEndPreComp*) endprecomp_leaf.data.str;
|
|
|
|
// error check LF_PRECOMP
|
|
if (precomp.start_index > CV_MinComplexTypeIndex) {
|
|
lnk_error_obj(LNK_Warning_AtypicalStartIndex, &obj_arr[obj_idx], "atypical start index 0x%X in LF_PRECOMP", precomp.start_index);
|
|
}
|
|
if (precomp.start_index < CV_MinComplexTypeIndex) {
|
|
lnk_error_obj(LNK_Error_InvalidStartIndex, &obj_arr[obj_idx], "invalid start index 0x%X in LF_PRECOMP; must be >= 0x%X", precomp.start_index, CV_MinComplexTypeIndex);
|
|
}
|
|
if (precomp.leaf_count > debug_p.count) {
|
|
lnk_error_obj(LNK_Error_InvalidPrecompLeafCount, &obj_arr[obj_idx], "leaf count %u LF_PRECOMP exceeds leaf count %u in .debug$P in %S", precomp.leaf_count, debug_p.count, obj_arr[debug_p_obj_idx].path);
|
|
}
|
|
|
|
// error check LF_ENDPRECOMP
|
|
if (endprecomp_leaf.kind != CV_LeafKind_ENDPRECOMP) {
|
|
lnk_error_obj(LNK_Error_EndprecompNotFound, &obj_arr[obj_idx], "unable to find LF_ENDPRECOMP @ 0x%X in %S", precomp.leaf_count, obj_arr[debug_p_obj_idx].path);
|
|
}
|
|
if (endprecomp_leaf.data.size != sizeof(CV_LeafEndPreComp)) {
|
|
lnk_error_obj(LNK_Error_IllData, &obj_arr[obj_idx], "invalid size 0x%X for LF_ENDPRECOMP", endprecomp_leaf.data.size);
|
|
}
|
|
if (endprecomp->sig != precomp.sig) {
|
|
lnk_error_obj(LNK_Error_PrecompSigMismatch, &obj_arr[obj_idx], "signature mismatch between LF_PRECOMP(0x%X) and LF_ENDPRECOMP(0x%X); precomp obj %S", precomp.sig, endprecomp->sig, obj_arr[debug_p_obj_idx].path);
|
|
}
|
|
{ // check against S_OBJNAME sig in precompiled obj $$SYMBOLS
|
|
CV_SymbolList symbol_list = parsed_symbols[debug_p_obj_idx].v[0];
|
|
if (symbol_list.count) {
|
|
CV_ObjInfo obj_info = cv_obj_info_from_symbol(symbol_list.first->data);
|
|
if (obj_info.sig != 0 && obj_info.sig != precomp.sig) {
|
|
lnk_error_obj(LNK_Error_PrecompSigMismatch, &obj_arr[obj_idx], "signature mismatch between LF_PRECOMP(0x%X) and S_OBJNAME(0x%X) in %S", precomp.sig, obj_info.sig, &obj_arr[debug_p_obj_idx].path);
|
|
}
|
|
} else {
|
|
lnk_error_obj(LNK_Warning_PrecompObjSymbolsNotFound, &obj_arr[obj_idx], "symbols not found, unable to chceck LF_PRECOMP signature against S_OBJ");
|
|
}
|
|
}
|
|
|
|
// see :pch_check
|
|
LNK_PchInfo *pch = &pch_arr[obj_idx];
|
|
pch->ti_lo = precomp.start_index;
|
|
pch->ti_hi = precomp.start_index + precomp.leaf_count;
|
|
pch->debug_p_obj_idx = debug_p_obj_idx;
|
|
|
|
// [start_index, start_index+type_index_count)
|
|
debug_t_arr[obj_idx].count -= 1;
|
|
debug_t_arr[obj_idx].v += 1;
|
|
|
|
endprecomp_arr[debug_p_obj_idx] = cv_debug_t_get_leaf_header(debug_p, precomp.leaf_count);
|
|
} else {
|
|
LNK_PchInfo *pch = &pch_arr[obj_idx];
|
|
pch->ti_lo = CV_MinComplexTypeIndex;
|
|
pch->ti_hi = CV_MinComplexTypeIndex;
|
|
pch->debug_p_obj_idx = 0; // :null_obj
|
|
}
|
|
}
|
|
|
|
// remove LF_ENDPRECOMP
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
if (endprecomp_arr[obj_idx]) {
|
|
endprecomp_arr[obj_idx]->kind = CV_LeafKind_NOTYPE;
|
|
endprecomp_arr[obj_idx]->size = sizeof(CV_LeafKind);
|
|
}
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
return pch_arr;
|
|
}
|
|
|
|
internal void
|
|
lnk_do_debug_info_discard(CV_DebugS *debug_s_arr, CV_SymbolListArray *parsed_symbols, U64 obj_idx)
|
|
{
|
|
// remove symbols
|
|
for (U64 i = 0; i < parsed_symbols[obj_idx].count; ++i) {
|
|
MemoryZeroStruct(&parsed_symbols[obj_idx].v[i]);
|
|
}
|
|
|
|
// remove inline sites
|
|
String8List *inlineelines_ptr = cv_sub_section_ptr_from_debug_s(&debug_s_arr[obj_idx], CV_C13SubSectionKind_InlineeLines);
|
|
MemoryZeroStruct(inlineelines_ptr);
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_msf_parsed_from_data_task)
|
|
{
|
|
ProfBeginFunction();
|
|
LNK_MsfParsedFromDataTask *task = raw_task;
|
|
// TODO: pick Info, TPI and IPI to flattten to make sure we don't waste compute on throw-away streams
|
|
task->msf_parse_arr[task_id] = msf_parsed_from_data(arena, task->data_arr.v[task_id]);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal MSF_Parsed **
|
|
lnk_msf_parsed_from_data_parallel(TP_Arena *arena, TP_Context *tp, String8Array data_arr)
|
|
{
|
|
ProfBeginFunction();
|
|
LNK_MsfParsedFromDataTask task = {0};
|
|
task.data_arr = data_arr;
|
|
task.msf_parse_arr = push_array_no_zero(arena->v[0], MSF_Parsed *, data_arr.count);
|
|
tp_for_parallel(tp, arena, data_arr.count, lnk_msf_parsed_from_data_task, &task);
|
|
ProfEnd();
|
|
return task.msf_parse_arr;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_get_external_leaves_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
U64 ts_idx = task_id;
|
|
LNK_GetExternalLeavesTask *task = raw_task;
|
|
MSF_Parsed *msf_parse = task->msf_parse_arr[ts_idx];
|
|
|
|
task->external_ti_ranges[ts_idx] = push_array(arena, Rng1U64, CV_TypeIndexSource_COUNT);
|
|
task->external_leaves[ts_idx] = push_array(arena, CV_DebugT, CV_TypeIndexSource_COUNT);
|
|
task->is_corrupted[ts_idx] = 1;
|
|
|
|
if (msf_parse) {
|
|
PDB_OpenTypeServerError tpi_error = PDB_OpenTypeServerError_UNKNOWN;
|
|
PDB_OpenTypeServerError ipi_error = PDB_OpenTypeServerError_UNKNOWN;
|
|
|
|
PDB_TypeServerParse tpi_parse, ipi_parse;
|
|
if (PDB_FixedStream_Tpi < msf_parse->stream_count && PDB_FixedStream_Ipi < msf_parse->stream_count) {
|
|
tpi_error = pdb_type_server_parse_from_data(msf_parse->streams[PDB_FixedStream_Tpi], &tpi_parse);
|
|
ipi_error = pdb_type_server_parse_from_data(msf_parse->streams[PDB_FixedStream_Ipi], &ipi_parse);
|
|
}
|
|
|
|
if (tpi_error == PDB_OpenTypeServerError_OK && ipi_error == PDB_OpenTypeServerError_OK) {
|
|
task->is_corrupted[ts_idx] = 0;
|
|
|
|
task->external_ti_ranges[ts_idx][CV_TypeIndexSource_NULL] = rng_1u64(0,0);
|
|
task->external_ti_ranges[ts_idx][CV_TypeIndexSource_TPI ] = tpi_parse.ti_range;
|
|
task->external_ti_ranges[ts_idx][CV_TypeIndexSource_IPI ] = ipi_parse.ti_range;
|
|
|
|
MemoryZeroStruct(&task->external_leaves[ts_idx][CV_TypeIndexSource_NULL]);
|
|
task->external_leaves[ts_idx][CV_TypeIndexSource_TPI] = cv_debug_t_from_data(arena, tpi_parse.leaf_data, PDB_LEAF_ALIGN);
|
|
task->external_leaves[ts_idx][CV_TypeIndexSource_IPI] = cv_debug_t_from_data(arena, ipi_parse.leaf_data, PDB_LEAF_ALIGN);
|
|
} else {
|
|
if (tpi_error != PDB_OpenTypeServerError_OK) {
|
|
lnk_error(LNK_Error_UnableToOpenTypeServer, "failed to open TPI in %S, reson %S", task->ts_info_arr[ts_idx].name, pdb_string_from_open_type_server_error(tpi_error));
|
|
}
|
|
if (ipi_error != PDB_OpenTypeServerError_OK) {
|
|
lnk_error(LNK_Error_UnableToOpenTypeServer, "failed to open IPI in %S, reason %S", task->ts_info_arr[ts_idx].name, pdb_string_from_open_type_server_error(ipi_error));
|
|
}
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal CV_DebugT *
|
|
lnk_merge_debug_t_and_debug_p(Arena *arena, U64 obj_count, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr)
|
|
{
|
|
CV_DebugT *result = push_array_no_zero(arena, CV_DebugT, obj_count);
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
CV_DebugT *debug_p = &debug_p_arr[obj_idx];
|
|
CV_DebugT *debug_t = &debug_t_arr[obj_idx];
|
|
if (debug_p->count) {
|
|
Assert(!debug_t->count);
|
|
result[obj_idx] = *debug_p;
|
|
} else if (debug_t->count) {
|
|
Assert(!debug_p->count);
|
|
result[obj_idx] = *debug_t;
|
|
} else {
|
|
MemoryZeroStruct(&result[obj_idx]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
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 obj_count, LNK_Obj **obj_arr)
|
|
{
|
|
ProfBegin("Extract CodeView");
|
|
Temp scratch = scratch_begin(0,0);
|
|
|
|
// gather debug info sections from objs
|
|
ProfBegin("Collect CodeView");
|
|
// TODO: fix memory leak, we need a Temp wrapper for pool arena
|
|
B32 collect_discarded_flag = 0;
|
|
String8List *debug_s_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$S"), collect_discarded_flag);
|
|
String8List *debug_p_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$P"), collect_discarded_flag);
|
|
String8List *debug_t_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$T"), collect_discarded_flag);
|
|
ProfEnd();
|
|
|
|
if (lnk_get_log_status(LNK_Log_Debug) || PROFILE_TELEMETRY) {
|
|
U64 total_debug_s_size = 0, total_debug_t_size = 0, total_debug_p_size = 0;
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
for (String8Node *chunk = debug_s_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) {
|
|
total_debug_s_size += chunk->string.size;
|
|
}
|
|
for (String8Node *chunk = debug_t_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) {
|
|
total_debug_t_size += chunk->string.size;
|
|
}
|
|
for (String8Node *chunk = debug_p_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) {
|
|
total_debug_p_size += chunk->string.size;
|
|
}
|
|
}
|
|
|
|
ProfNoteV("Total .debug$S Input Size: %M", total_debug_s_size);
|
|
ProfNoteV("Total .debug$T Input Size: %M", total_debug_t_size);
|
|
ProfNoteV("Total .debug$P Input Size: %M", total_debug_p_size);
|
|
|
|
if (lnk_get_log_status(LNK_Log_Debug)) {
|
|
lnk_log(LNK_Log_Debug, "[Total .debug$S Input Size %M]", total_debug_s_size);
|
|
lnk_log(LNK_Log_Debug, "[Total .debug$T Input Size %M]", total_debug_t_size);
|
|
lnk_log(LNK_Log_Debug, "[Total .debug$P Input Size %M]", total_debug_p_size);
|
|
}
|
|
}
|
|
|
|
// TODO: temp hack, remove when we have null obj with .debug$T
|
|
{
|
|
String8 raw_null_leaf = cv_serialize_raw_leaf(scratch.arena, CV_LeafKind_NOTYPE, str8(0,0), 1);
|
|
|
|
String8List srl = {0};
|
|
str8_serial_begin(scratch.arena, &srl);
|
|
str8_serial_push_u32(scratch.arena, &srl, CV_Signature_C13);
|
|
str8_serial_push_string(scratch.arena, &srl, raw_null_leaf);
|
|
String8 null_debug_data = str8_serial_end(tp_arena->v[0], &srl);
|
|
|
|
str8_list_push(tp_arena->v[0], &debug_t_list_arr[0], null_debug_data);
|
|
}
|
|
|
|
ProfBegin("Parse CodeView");
|
|
CV_DebugS *debug_s_arr = lnk_parse_debug_s_sections(tp, tp_arena, obj_count, obj_arr, debug_s_list_arr);
|
|
CV_DebugT *debug_p_arr = lnk_parse_debug_t_sections(tp, tp_arena, obj_count, obj_arr, debug_p_list_arr);
|
|
CV_DebugT *debug_t_arr = lnk_parse_debug_t_sections(tp, tp_arena, obj_count, obj_arr, debug_t_list_arr);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Sort Type Servers");
|
|
|
|
U64 external_count = 0, internal_count = 0;
|
|
LNK_Obj *sorted_obj_arr = push_array_no_zero(tp_arena->v[0], LNK_Obj, obj_count);
|
|
CV_DebugS *sorted_debug_s_arr = push_array_no_zero(tp_arena->v[0], CV_DebugS, obj_count);
|
|
CV_DebugT *sorted_debug_t_arr = push_array_no_zero(tp_arena->v[0], CV_DebugT, obj_count);
|
|
CV_DebugT *sorted_debug_p_arr = push_array_no_zero(tp_arena->v[0], CV_DebugT, obj_count);
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
B32 is_type_server = cv_debug_t_is_type_server(debug_t_arr[obj_idx]);
|
|
if (is_type_server) {
|
|
Assert(internal_count + external_count < obj_count);
|
|
U64 slot_idx = (obj_count - external_count - 1);
|
|
++external_count;
|
|
|
|
// TODO: report error: somehow obj was compiled with /Zi and /Yc
|
|
Assert(debug_p_arr[obj_idx].count == 0);
|
|
|
|
sorted_obj_arr[slot_idx] = *obj_arr[obj_idx];
|
|
sorted_debug_s_arr[slot_idx] = debug_s_arr[obj_idx];
|
|
sorted_debug_t_arr[slot_idx] = debug_t_arr[obj_idx];
|
|
MemoryZeroStruct(&sorted_debug_p_arr[slot_idx]);
|
|
} else {
|
|
Assert(internal_count + external_count < obj_count);
|
|
U64 slot_idx = internal_count;
|
|
++internal_count;
|
|
|
|
sorted_obj_arr[slot_idx] = *obj_arr[obj_idx];
|
|
sorted_debug_s_arr[slot_idx] = debug_s_arr[obj_idx];
|
|
sorted_debug_t_arr[slot_idx] = debug_t_arr[obj_idx];
|
|
sorted_debug_p_arr[slot_idx] = debug_p_arr[obj_idx];
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
|
|
// setup pointers to arrays
|
|
LNK_Obj *internal_obj_arr = sorted_obj_arr;
|
|
LNK_Obj *external_obj_arr = sorted_obj_arr + internal_count;
|
|
CV_DebugS *internal_debug_s_arr = sorted_debug_s_arr;
|
|
CV_DebugS *external_debug_s_arr = sorted_debug_s_arr + internal_count;
|
|
CV_DebugT *internal_debug_t_arr = sorted_debug_t_arr;
|
|
CV_DebugT *external_debug_t_arr = sorted_debug_t_arr + internal_count;
|
|
CV_DebugT *internal_debug_p_arr = sorted_debug_p_arr;
|
|
CV_DebugT *external_debug_p_arr = sorted_debug_p_arr + internal_count;
|
|
|
|
ProfBegin("Parse Symbols");
|
|
|
|
ProfBegin("Count Symbol Inputs");
|
|
U64 internal_total_symbol_input_count = 0;
|
|
U64 external_total_symbol_input_count = 0;
|
|
for (U64 obj_idx = 0; obj_idx < internal_count; ++obj_idx) {
|
|
String8List raw_symbols = cv_sub_section_from_debug_s(internal_debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols);
|
|
internal_total_symbol_input_count += raw_symbols.node_count;
|
|
}
|
|
for (U64 obj_idx = 0; obj_idx < external_count; ++obj_idx) {
|
|
String8List raw_symbols = cv_sub_section_from_debug_s(external_debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols);
|
|
external_total_symbol_input_count += raw_symbols.node_count;
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Prepare Symbol Inputs");
|
|
U64 total_symbol_input_count = internal_total_symbol_input_count + external_total_symbol_input_count;
|
|
LNK_CodeViewSymbolsInput *symbol_inputs = push_array_no_zero(tp_arena->v[0], LNK_CodeViewSymbolsInput, total_symbol_input_count);
|
|
CV_SymbolListArray *parsed_symbols = push_array_no_zero(tp_arena->v[0], CV_SymbolListArray, obj_count);
|
|
{
|
|
CV_SymbolList *reserved_lists = push_array(tp_arena->v[0], CV_SymbolList, total_symbol_input_count);
|
|
for (U64 obj_idx = 0, input_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
String8List raw_symbols = cv_sub_section_from_debug_s(sorted_debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols);
|
|
|
|
// init parse output
|
|
if (raw_symbols.node_count > 0) {
|
|
parsed_symbols[obj_idx].count = raw_symbols.node_count;
|
|
parsed_symbols[obj_idx].v = reserved_lists + input_idx;
|
|
} else {
|
|
parsed_symbols[obj_idx].count = 0;
|
|
parsed_symbols[obj_idx].v = 0;
|
|
}
|
|
|
|
// init worker input
|
|
for (String8Node *data_n = raw_symbols.first; data_n != 0; data_n = data_n->next, ++input_idx) {
|
|
Assert(input_idx < total_symbol_input_count);
|
|
LNK_CodeViewSymbolsInput *in = &symbol_inputs[input_idx];
|
|
in->obj_idx = obj_idx;
|
|
in->symbol_list = &reserved_lists[input_idx];
|
|
in->raw_symbols = data_n->string;
|
|
}
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Symbol Parse");
|
|
LNK_ParseCVSymbolsTaskData task = {0};
|
|
task.inputs = symbol_inputs;
|
|
tp_for_parallel(tp, tp_arena, total_symbol_input_count, lnk_parse_cv_symbols_task, &task);
|
|
ProfEnd();
|
|
|
|
// TODO: do we rely on this behaviour?
|
|
//
|
|
// :zero_out_symbol_sub_section
|
|
ProfBegin("Zero-out Symbols Sub-sections");
|
|
for (U64 i = 0; i < obj_count; ++i) {
|
|
CV_DebugS *debug_s = &sorted_debug_s_arr[i];
|
|
String8List *symbols_ptr = cv_sub_section_ptr_from_debug_s(debug_s, CV_C13SubSectionKind_Symbols);
|
|
MemoryZeroStruct(symbols_ptr);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfEnd();
|
|
|
|
CV_SymbolListArray *internal_parsed_symbols = parsed_symbols;
|
|
CV_SymbolListArray *external_parsed_symbols = parsed_symbols + internal_count;
|
|
LNK_CodeViewSymbolsInput *internal_symbol_inputs = symbol_inputs;
|
|
LNK_CodeViewSymbolsInput *external_symbol_inputs = symbol_inputs + internal_count;
|
|
|
|
LNK_PchInfo *pch_arr = lnk_setup_pch(tp_arena->v[0],
|
|
internal_count,
|
|
internal_obj_arr,
|
|
internal_debug_t_arr,
|
|
internal_debug_p_arr,
|
|
internal_parsed_symbols);
|
|
|
|
CV_DebugT *merged_debug_t_p_arr = lnk_merge_debug_t_and_debug_p(tp_arena->v[0], internal_count, internal_debug_t_arr, internal_debug_p_arr);
|
|
|
|
ProfBegin("Analyze & Read External Type Server Files");
|
|
String8Array ts_path_arr;
|
|
Rng1U64 **external_ti_ranges;
|
|
CV_DebugT **external_leaves;
|
|
U64 *obj_to_ts_idx_arr = push_array_no_zero(tp_arena->v[0], U64, external_count);
|
|
U64List *ts_to_obj_arr = push_array(tp_arena->v[0], U64List, external_count);
|
|
{
|
|
HashTable *type_server_path_ht = hash_table_init(scratch.arena, 256);
|
|
HashTable *ignored_path_ht = hash_table_init(scratch.arena, 256);
|
|
CV_TypeServerInfoList ts_info_list = {0};
|
|
|
|
// push null
|
|
CV_TypeServerInfoNode *null_ts_info = push_array(scratch.arena, CV_TypeServerInfoNode, 1);
|
|
SLLQueuePush(ts_info_list.first, ts_info_list.last, null_ts_info);
|
|
++ts_info_list.count;
|
|
|
|
for (U64 obj_idx = 0; obj_idx < external_count; ++obj_idx) {
|
|
// first leaf always type server
|
|
CV_DebugT debug_t = external_debug_t_arr[obj_idx];
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, 0);
|
|
CV_TypeServerInfo ts = cv_type_server_info_from_leaf(leaf);
|
|
|
|
// search disk for type server
|
|
String8List match_list = lnk_file_search(scratch.arena, lib_dir_list, ts.name);
|
|
|
|
// chop file name from path and search on it
|
|
//
|
|
// TODO: check if ts.name is a path and in that case do file search
|
|
if (match_list.node_count == 0) {
|
|
String8 file_name = str8_skip_last_slash(ts.name);
|
|
match_list = lnk_file_search(scratch.arena, lib_dir_list, file_name);
|
|
}
|
|
|
|
B32 do_debug_info_discard = 0;
|
|
|
|
// too many matches?
|
|
if (match_list.node_count > 1) {
|
|
if (!hash_table_search_path(ignored_path_ht, ts.name)) {
|
|
hash_table_push_path_u64(scratch.arena, ignored_path_ht, ts.name, 0);
|
|
lnk_error_obj(LNK_Warning_MultipleExternalTypeServers, obj_arr[obj_idx], "located multiple external type servers:");
|
|
lnk_supplement_error_list(match_list);
|
|
}
|
|
do_debug_info_discard = 1;
|
|
}
|
|
// no match?
|
|
else if (match_list.node_count == 0) {
|
|
if (!hash_table_search_path(ignored_path_ht, ts.name)) {
|
|
hash_table_push_string_u64(scratch.arena, ignored_path_ht, ts.name, 0);
|
|
lnk_error_obj(LNK_Warning_MissingExternalTypeServer, obj_arr[obj_idx], "unable to open external type server %S", ts.name);
|
|
}
|
|
do_debug_info_discard = 1;
|
|
}
|
|
|
|
// external type server is missing, discard parts of debug info that need types
|
|
if (do_debug_info_discard) {
|
|
lnk_do_debug_info_discard(external_debug_s_arr, external_parsed_symbols, obj_idx);
|
|
continue;
|
|
}
|
|
|
|
String8 path = match_list.first->string;
|
|
{
|
|
struct HT_Value {
|
|
CV_TypeServerInfo ts;
|
|
LNK_Obj *obj;
|
|
U64 ts_idx;
|
|
};
|
|
|
|
// was this type server queued?
|
|
KeyValuePair *is_path_queued = hash_table_search_path(type_server_path_ht, path);
|
|
if (is_path_queued) {
|
|
struct HT_Value *present = is_path_queued->value_raw;
|
|
|
|
// make sure type servers sigs match
|
|
if (MemoryMatchStruct(&ts.sig, &present->ts.sig)) {
|
|
// wire obj to type server data
|
|
obj_to_ts_idx_arr[obj_idx] = present->ts_idx;
|
|
|
|
// wire type server to obj
|
|
u64_list_push(tp_arena->v[0], &ts_to_obj_arr[present->ts_idx], obj_idx);
|
|
} else {
|
|
lnk_error_obj(LNK_Error_ExternalTypeServerConflict,
|
|
obj_arr[obj_idx],
|
|
"external type server signature conflicts with type server loaded from '%S'",
|
|
present->obj->path);
|
|
}
|
|
} else {
|
|
U64 ts_idx = ts_info_list.count;
|
|
|
|
// when we search matches on disk we store path on scratch,
|
|
// make path copy in case we need it for error reporting
|
|
path = push_str8_copy(tp_arena->v[0], path);
|
|
|
|
// fill out type server info we read from obj
|
|
CV_TypeServerInfoNode *ts_info_node = push_array(scratch.arena, CV_TypeServerInfoNode, 1);
|
|
ts_info_node->data = ts;
|
|
ts_info_node->data.name = path;
|
|
|
|
// push to type server info list
|
|
SLLQueuePush(ts_info_list.first, ts_info_list.last, ts_info_node);
|
|
ts_info_list.count += 1;
|
|
|
|
// wire obj to type server
|
|
obj_to_ts_idx_arr[obj_idx] = ts_idx;
|
|
|
|
// wire type server to obj
|
|
u64_list_push(tp_arena->v[0], &ts_to_obj_arr[ts_idx], obj_idx);
|
|
|
|
// fill out value
|
|
struct HT_Value *value = push_array(scratch.arena, struct HT_Value, 1);
|
|
value->ts = ts;
|
|
value->obj = obj_arr[obj_idx];
|
|
value->ts_idx = ts_idx;
|
|
|
|
// update hash table
|
|
hash_table_push_path_raw(scratch.arena, type_server_path_ht, path, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// type server info list -> array
|
|
ts_path_arr.count = ts_info_list.count;
|
|
ts_path_arr.v = push_array(tp_arena->v[0], String8, ts_info_list.count);
|
|
CV_TypeServerInfo *ts_info_arr = push_array(scratch.arena, CV_TypeServerInfo, ts_info_list.count);
|
|
{
|
|
U64 idx = 0;
|
|
for (CV_TypeServerInfoNode *n = ts_info_list.first; n != 0; n = n->next, ++idx) {
|
|
ts_path_arr.v[idx] = n->data.name;
|
|
ts_info_arr[idx] = n->data;
|
|
}
|
|
}
|
|
|
|
// 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, 0, ts_path_arr);
|
|
ProfEnd();
|
|
|
|
MSF_Parsed **msf_parse_arr = lnk_msf_parsed_from_data_parallel(tp_arena, tp, msf_data_arr);
|
|
|
|
ProfBegin("Error check type servers");
|
|
for (U64 ts_idx = 0; ts_idx < msf_data_arr.count; ++ts_idx) {
|
|
MSF_Parsed *msf_parse = msf_parse_arr[ts_idx];
|
|
|
|
B32 do_debug_info_discard = 0;
|
|
|
|
if (!msf_parse) {
|
|
do_debug_info_discard = 1;
|
|
} else {
|
|
PDB_InfoParse info_parse = {0};
|
|
pdb_info_parse_from_data(msf_parse->streams[PDB_FixedStream_Info], &info_parse);
|
|
if (!MemoryMatchStruct(&info_parse.guid, &ts_info_arr[ts_idx].sig)) {
|
|
Temp scratch = scratch_begin(0,0);
|
|
String8 expected_sig_str = string_from_guid(scratch.arena, ts_info_arr[ts_idx].sig);
|
|
String8 on_disk_sig_str = string_from_guid(scratch.arena, info_parse.guid);
|
|
lnk_error(LNK_Warning_MismatchedTypeServerSignature, "%S: signature mismatch in type server read from disk, expected %S, got %S",
|
|
ts_info_arr[ts_idx].name, expected_sig_str, on_disk_sig_str);
|
|
scratch_end(scratch);
|
|
|
|
do_debug_info_discard = 1;
|
|
}
|
|
}
|
|
|
|
if (do_debug_info_discard) {
|
|
U64List obj_idx_list = ts_to_obj_arr[ts_idx];
|
|
for (U64Node *obj_idx_n = obj_idx_list.first; obj_idx_n != 0; obj_idx_n = obj_idx_n->next) {
|
|
lnk_do_debug_info_discard(external_debug_s_arr, external_parsed_symbols, obj_idx_n->data);
|
|
}
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBeginDynamic("Open External Type Servers [Count %llu]", ts_path_arr.count);
|
|
LNK_GetExternalLeavesTask task = {0};
|
|
task.ts_info_arr = ts_info_arr;
|
|
task.msf_parse_arr = msf_parse_arr;
|
|
task.external_ti_ranges = push_array_no_zero(tp_arena->v[0], Rng1U64 *, msf_data_arr.count);
|
|
task.external_leaves = push_array_no_zero(tp_arena->v[0], CV_DebugT *, msf_data_arr.count);
|
|
task.is_corrupted = push_array_no_zero(scratch.arena, B8, msf_data_arr.count);
|
|
tp_for_parallel(tp, tp_arena, msf_data_arr.count, lnk_get_external_leaves_task, &task);
|
|
ProfEnd();
|
|
|
|
String8List unopen_type_server_list = {0};
|
|
|
|
// discard debug info that depends on the missing type server
|
|
for (U64 ts_idx = 1; ts_idx < msf_data_arr.count; ++ts_idx) {
|
|
if (task.is_corrupted[ts_idx]) {
|
|
U64List obj_idx_list = ts_to_obj_arr[ts_idx];
|
|
for (U64Node *node = obj_idx_list.first; node != 0; node = node->next) {
|
|
lnk_do_debug_info_discard(external_debug_s_arr, external_parsed_symbols, node->data);
|
|
}
|
|
}
|
|
}
|
|
|
|
// format error
|
|
for (U64 ts_idx = 1; ts_idx < msf_data_arr.count; ++ts_idx) {
|
|
if (task.is_corrupted[ts_idx]) {
|
|
U64List obj_idx_list = ts_to_obj_arr[ts_idx];
|
|
str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t%S\n", ts_path_arr.v[ts_idx]);
|
|
str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t\tDependent obj(s):\n");
|
|
for (U64Node *obj_idx_node = obj_idx_list.first; obj_idx_node != 0; obj_idx_node = obj_idx_node->next) {
|
|
String8 obj_path = external_obj_arr[obj_idx_node->data].path;
|
|
str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t\t\t%S\n", obj_path);
|
|
}
|
|
}
|
|
}
|
|
if (unopen_type_server_list.node_count) {
|
|
String8List error_msg_list = { 0 };
|
|
str8_list_pushf(scratch.arena, &error_msg_list, "unable to open external type server(s):\n");
|
|
str8_list_concat_in_place(&error_msg_list, &unopen_type_server_list);
|
|
String8 error_msg = str8_list_join(scratch.arena, &error_msg_list, 0);
|
|
lnk_error(LNK_Error_UnableToOpenTypeServer, "%S", error_msg);
|
|
}
|
|
|
|
// output
|
|
external_ti_ranges = task.external_ti_ranges;
|
|
external_leaves = task.external_leaves;
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
// fill out result
|
|
LNK_CodeViewInput cv = {0};
|
|
cv.count = obj_count;
|
|
cv.internal_count = internal_count;
|
|
cv.external_count = external_count;
|
|
cv.type_server_count = ts_path_arr.count;
|
|
cv.type_server_path_arr = ts_path_arr.v;
|
|
cv.ts_to_obj_arr = ts_to_obj_arr;
|
|
cv.obj_arr = sorted_obj_arr;
|
|
cv.pch_arr = pch_arr;
|
|
cv.debug_s_arr = sorted_debug_s_arr;
|
|
cv.debug_p_arr = sorted_debug_p_arr;
|
|
cv.debug_t_arr = sorted_debug_t_arr;
|
|
cv.merged_debug_t_p_arr = merged_debug_t_p_arr;
|
|
cv.total_symbol_input_count = total_symbol_input_count;
|
|
cv.symbol_inputs = symbol_inputs;
|
|
cv.parsed_symbols = parsed_symbols;
|
|
cv.internal_obj_arr = internal_obj_arr;
|
|
cv.external_obj_arr = external_obj_arr;
|
|
cv.internal_debug_s_arr = internal_debug_s_arr;
|
|
cv.external_debug_s_arr = external_debug_s_arr;
|
|
cv.internal_debug_t_arr = internal_debug_t_arr;
|
|
cv.external_debug_t_arr = external_debug_t_arr;
|
|
cv.internal_debug_p_arr = internal_debug_p_arr;
|
|
cv.external_debug_p_arr = external_debug_p_arr;
|
|
cv.internal_total_symbol_input_count = internal_total_symbol_input_count;
|
|
cv.internal_symbol_inputs = internal_symbol_inputs;
|
|
cv.internal_parsed_symbols = internal_parsed_symbols;
|
|
cv.external_total_symbol_input_count = external_total_symbol_input_count;
|
|
cv.external_symbol_inputs = external_symbol_inputs;
|
|
cv.external_parsed_symbols = external_parsed_symbols;
|
|
cv.external_ti_ranges = external_ti_ranges;
|
|
cv.external_leaves = external_leaves;
|
|
cv.external_obj_to_ts_idx_arr = obj_to_ts_idx_arr;
|
|
cv.external_obj_range = rng_1u64(internal_count, internal_count + external_count);
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return cv;
|
|
}
|
|
|
|
internal LNK_LeafRef
|
|
lnk_leaf_ref(U32 enc_loc_idx, U32 enc_leaf_idx)
|
|
{
|
|
LNK_LeafRef ref;
|
|
ref.enc_loc_idx = enc_loc_idx;
|
|
ref.enc_leaf_idx = enc_leaf_idx;
|
|
return ref;
|
|
}
|
|
|
|
internal LNK_LeafRef
|
|
lnk_obj_leaf_ref(U32 obj_idx, U32 leaf_idx)
|
|
{
|
|
return lnk_leaf_ref(obj_idx, leaf_idx);
|
|
}
|
|
|
|
internal LNK_LeafRef
|
|
lnk_ts_leaf_ref(CV_TypeIndexSource ti_source, U32 ts_idx, U32 leaf_idx)
|
|
{
|
|
ts_idx |= LNK_LeafRefFlag_LocIdxExternal;
|
|
|
|
if (ti_source == CV_TypeIndexSource_IPI) {
|
|
leaf_idx |= LNK_LeafRefFlag_LeafIdxIPI;
|
|
}
|
|
|
|
return lnk_leaf_ref(ts_idx, leaf_idx);
|
|
}
|
|
|
|
internal int
|
|
lnk_leaf_ref_compare(LNK_LeafRef a, LNK_LeafRef b)
|
|
{
|
|
int cmp = 0;
|
|
if (a.enc_loc_idx < b.enc_loc_idx) {
|
|
cmp = -1;
|
|
} else if (a.enc_loc_idx > b.enc_loc_idx) {
|
|
cmp = +1;
|
|
} else {
|
|
if (a.enc_leaf_idx < b.enc_leaf_idx) {
|
|
cmp = -1;
|
|
} else if (a.enc_leaf_idx > b.enc_leaf_idx) {
|
|
cmp = +1;
|
|
}
|
|
}
|
|
return cmp;
|
|
}
|
|
|
|
internal int
|
|
lnk_leaf_ref_is_before(void *raw_a, void *raw_b)
|
|
{
|
|
LNK_LeafRef **a = raw_a;
|
|
LNK_LeafRef **b = raw_b;
|
|
int is_before;
|
|
if ((*a)->enc_loc_idx == (*b)->enc_loc_idx) {
|
|
is_before = (*a)->enc_leaf_idx < (*b)->enc_leaf_idx;
|
|
} else {
|
|
is_before = (*a)->enc_loc_idx < (*b)->enc_loc_idx;
|
|
}
|
|
return is_before;
|
|
}
|
|
|
|
internal LNK_LeafLocType
|
|
lnk_loc_type_from_leaf_ref(LNK_LeafRef leaf_ref)
|
|
{
|
|
if (leaf_ref.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal) {
|
|
return LNK_LeafLocType_External;
|
|
}
|
|
return LNK_LeafLocType_Internal;
|
|
}
|
|
|
|
internal LNK_LeafLocType
|
|
lnk_loc_type_from_obj_idx(LNK_CodeViewInput *input, U64 obj_idx)
|
|
{
|
|
if (input->external_obj_range.min <= obj_idx && obj_idx < input->external_obj_range.max) {
|
|
return LNK_LeafLocType_External;
|
|
}
|
|
return LNK_LeafLocType_Internal;
|
|
}
|
|
|
|
internal U64
|
|
lnk_loc_idx_from_obj_idx(LNK_CodeViewInput *input, U64 obj_idx)
|
|
{
|
|
if (input->external_obj_range.min <= obj_idx && obj_idx < input->external_obj_range.max) {
|
|
return input->external_obj_to_ts_idx_arr[obj_idx - input->external_obj_range.min];
|
|
}
|
|
return obj_idx;
|
|
}
|
|
|
|
internal CV_TypeIndex
|
|
lnk_ti_lo_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref)
|
|
{
|
|
CV_TypeIndex ti_lo;
|
|
|
|
LNK_LeafLocType loc_type = lnk_loc_type_from_leaf_ref(leaf_ref);
|
|
switch (loc_type) {
|
|
case LNK_LeafLocType_Internal: {
|
|
ti_lo = CV_MinComplexTypeIndex;
|
|
} break;
|
|
case LNK_LeafLocType_External: {
|
|
U64 ts_idx = leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal;
|
|
CV_TypeIndexSource ti_source = (leaf_ref.enc_loc_idx & LNK_LeafRefFlag_LeafIdxIPI) ? CV_TypeIndexSource_IPI : CV_TypeIndexSource_TPI;
|
|
ti_lo = input->external_ti_ranges[ts_idx][ti_source].min;
|
|
} break;
|
|
default: ti_lo = 0; break;
|
|
}
|
|
|
|
return ti_lo;
|
|
}
|
|
|
|
internal CV_TypeIndex
|
|
lnk_ti_lo_from_loc(LNK_CodeViewInput *input, LNK_LeafLocType loc_type, U64 loc_idx, CV_TypeIndexSource ti_source)
|
|
{
|
|
CV_TypeIndex ti_lo = 0;
|
|
if (loc_type == LNK_LeafLocType_Internal) {
|
|
ti_lo = CV_MinComplexTypeIndex;
|
|
} else if (loc_type == LNK_LeafLocType_External) {
|
|
ti_lo = input->external_ti_ranges[loc_idx][ti_source].min;
|
|
}
|
|
return ti_lo;
|
|
}
|
|
|
|
internal String8
|
|
lnk_data_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref)
|
|
{
|
|
String8 data;
|
|
|
|
LNK_LeafLocType loc_type = lnk_loc_type_from_leaf_ref(leaf_ref);
|
|
switch (loc_type) {
|
|
case LNK_LeafLocType_Internal: {
|
|
U32 obj_idx = leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal;
|
|
U32 leaf_idx = leaf_ref.enc_leaf_idx;
|
|
CV_DebugT debug_t = input->merged_debug_t_p_arr[obj_idx];
|
|
data = cv_debug_t_get_raw_leaf(debug_t, leaf_idx);
|
|
} break;
|
|
|
|
case LNK_LeafLocType_External: {
|
|
U64 ts_idx = leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal;
|
|
U64 leaf_idx = leaf_ref.enc_leaf_idx & ~LNK_LeafRefFlag_LeafIdxIPI;
|
|
CV_TypeIndexSource ti_source = leaf_ref.enc_leaf_idx & LNK_LeafRefFlag_LeafIdxIPI ? CV_TypeIndexSource_IPI : CV_TypeIndexSource_TPI;
|
|
CV_DebugT debug_t = input->external_leaves[ts_idx][ti_source];
|
|
data = cv_debug_t_get_raw_leaf(debug_t, leaf_idx);
|
|
} break;
|
|
|
|
default: data = str8(0,0); break;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
internal CV_TypeIndex
|
|
lnk_type_index_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref)
|
|
{
|
|
CV_TypeIndex type_index = 0;
|
|
LNK_LeafLocType loc_type = lnk_loc_type_from_leaf_ref(leaf_ref);
|
|
switch (loc_type) {
|
|
case LNK_LeafLocType_Internal: {
|
|
LNK_PchInfo pch_info = input->pch_arr[leaf_ref.enc_loc_idx];
|
|
type_index = pch_info.ti_hi + leaf_ref.enc_leaf_idx;
|
|
} break;
|
|
case LNK_LeafLocType_External: {
|
|
CV_TypeIndex lo = lnk_ti_lo_from_leaf_ref(input, leaf_ref);
|
|
type_index = lo + leaf_ref.enc_leaf_idx & ~LNK_LeafRefFlag_LeafIdxIPI;
|
|
} break;
|
|
default: InvalidPath;
|
|
}
|
|
return type_index;
|
|
}
|
|
|
|
internal CV_Leaf
|
|
lnk_cv_leaf_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref)
|
|
{
|
|
String8 raw_leaf = lnk_data_from_leaf_ref(input, leaf_ref);
|
|
CV_Leaf leaf;
|
|
cv_deserial_leaf(raw_leaf, 0, 1, &leaf);
|
|
return leaf;
|
|
}
|
|
|
|
internal U128
|
|
lnk_hash_from_leaf_ref(LNK_LeafHashes *hashes, LNK_LeafRef leaf_ref)
|
|
{
|
|
LNK_LeafLocType loc_type;
|
|
CV_TypeIndexSource ti_source;
|
|
if (leaf_ref.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal) {
|
|
loc_type = LNK_LeafLocType_External;
|
|
ti_source = (leaf_ref.enc_leaf_idx & LNK_LeafRefFlag_LeafIdxIPI) ? CV_TypeIndexSource_IPI : CV_TypeIndexSource_TPI;
|
|
} else {
|
|
loc_type = LNK_LeafLocType_Internal;
|
|
ti_source = CV_TypeIndexSource_TPI;
|
|
}
|
|
|
|
U32 loc_idx = leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal;
|
|
U32 leaf_idx = leaf_ref.enc_leaf_idx & ~LNK_LeafRefFlag_LeafIdxIPI;
|
|
U128 hash = hashes->v[loc_type][loc_idx][ti_source].v[leaf_idx];
|
|
|
|
return hash;
|
|
}
|
|
|
|
internal LNK_LeafRef
|
|
lnk_leaf_ref_from_loc_idx_and_ti(LNK_CodeViewInput *input,
|
|
LNK_LeafLocType loc_type,
|
|
CV_TypeIndexSource ti_source,
|
|
U64 loc_idx,
|
|
CV_TypeIndex obj_ti)
|
|
{
|
|
LNK_LeafRef leaf_ref;
|
|
|
|
switch (loc_type) {
|
|
case LNK_LeafLocType_External: {
|
|
U64 ts_idx = loc_idx;
|
|
|
|
CV_TypeIndex ti_lo = input->external_ti_ranges[ts_idx][ti_source].min;
|
|
Assert(obj_ti >= ti_lo);
|
|
|
|
// encode leaf index for type server
|
|
leaf_ref = lnk_ts_leaf_ref(ti_source, ts_idx, obj_ti - ti_lo);
|
|
} break;
|
|
|
|
case LNK_LeafLocType_Internal: {
|
|
U64 obj_idx = loc_idx;
|
|
|
|
LNK_PchInfo pch = input->pch_arr[obj_idx];
|
|
if (obj_ti < pch.ti_lo) {
|
|
CV_TypeIndex ti_lo = CV_MinComplexTypeIndex;
|
|
Assert(obj_ti >= ti_lo);
|
|
leaf_ref = lnk_obj_leaf_ref(obj_idx, obj_ti - ti_lo);
|
|
}
|
|
// PCH indirection
|
|
else if (obj_ti < pch.ti_hi) {
|
|
// we don't support nested precompiled types
|
|
Assert(input->pch_arr[pch.debug_p_obj_idx].debug_p_obj_idx == /* null_obj: */ 0);
|
|
Assert(input->pch_arr[pch.debug_p_obj_idx].ti_lo == input->pch_arr[pch.debug_p_obj_idx].ti_hi);
|
|
leaf_ref = lnk_obj_leaf_ref(pch.debug_p_obj_idx, obj_ti - pch.ti_lo);
|
|
} else {
|
|
leaf_ref = lnk_obj_leaf_ref(obj_idx, pch.ti_lo + (obj_ti - pch.ti_hi) - CV_MinComplexTypeIndex);
|
|
}
|
|
} break;
|
|
|
|
default: leaf_ref = lnk_leaf_ref(0, 0); break;
|
|
}
|
|
|
|
return leaf_ref;
|
|
}
|
|
|
|
internal B32
|
|
lnk_match_leaf_ref(LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef a, LNK_LeafRef b)
|
|
{
|
|
B32 are_same = 0;
|
|
|
|
U128 a_hash = lnk_hash_from_leaf_ref(hashes, a);
|
|
U128 b_hash = lnk_hash_from_leaf_ref(hashes, b);
|
|
|
|
if (u128_match(a_hash, b_hash)) {
|
|
CV_Leaf a_leaf = lnk_cv_leaf_from_leaf_ref(input, a);
|
|
CV_Leaf b_leaf = lnk_cv_leaf_from_leaf_ref(input, b);
|
|
Assert(a_leaf.kind == b_leaf.kind);
|
|
#if 0
|
|
{
|
|
Temp scratch = scratch_begin(0,0);
|
|
CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(scratch.arena, a_leaf.kind, a_leaf.data);
|
|
String8Array a_raw_data_arr = cv_get_data_around_type_indices(scratch.arena, ti_info_list, a_leaf.data);
|
|
String8Array b_raw_data_arr = cv_get_data_around_type_indices(scratch.arena, ti_info_list, b_leaf.data);
|
|
for (U64 i = 0; i < a_raw_data_arr.count; ++i) {
|
|
String8 a_chunk = a_raw_data_arr.v[i];
|
|
String8 b_chunk = b_raw_data_arr.v[i];
|
|
Assert(str8_match(a_chunk, b_chunk, 0));
|
|
}
|
|
scratch_end(scratch);
|
|
}
|
|
#endif
|
|
are_same = 1;
|
|
}
|
|
|
|
return are_same;
|
|
}
|
|
|
|
internal B32
|
|
lnk_match_leaf_ref_deep(Arena *arena, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef a, LNK_LeafRef b)
|
|
{
|
|
B32 are_equal = 0;
|
|
|
|
U128 a_hash = lnk_hash_from_leaf_ref(hashes, a);
|
|
U128 b_hash = lnk_hash_from_leaf_ref(hashes, b);
|
|
|
|
if (u128_match(a_hash, b_hash)) {
|
|
String8 a_raw_leaf = lnk_data_from_leaf_ref(input, a);
|
|
String8 b_raw_leaf = lnk_data_from_leaf_ref(input, b);
|
|
|
|
CV_LeafHeader *a_header = (CV_LeafHeader *) a_raw_leaf.str;
|
|
CV_LeafHeader *b_header = (CV_LeafHeader *) b_raw_leaf.str;
|
|
|
|
if (a_header->kind == b_header->kind && a_header->size == b_header->size) {
|
|
CV_Leaf a_leaf = cv_leaf_from_string(a_raw_leaf);
|
|
CV_Leaf b_leaf = cv_leaf_from_string(b_raw_leaf);
|
|
|
|
Temp temp = temp_begin(arena);
|
|
|
|
CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, a_leaf.kind, a_leaf.data);
|
|
String8Array a_raw_data_arr = cv_get_data_around_type_indices(temp.arena, ti_info_list, a_leaf.data);
|
|
String8Array b_raw_data_arr = cv_get_data_around_type_indices(temp.arena, ti_info_list, b_leaf.data);
|
|
|
|
are_equal = 1;
|
|
|
|
for (U64 i = 0; i < a_raw_data_arr.count; ++i) {
|
|
String8 a_chunk = a_raw_data_arr.v[i];
|
|
String8 b_chunk = b_raw_data_arr.v[i];
|
|
Assert(a_chunk.size == b_chunk.size);
|
|
are_equal = str8_match(a_chunk, b_chunk, 0);
|
|
if (!are_equal) {
|
|
goto skip_type_index_compare;
|
|
}
|
|
}
|
|
|
|
CV_TypeIndex a_ti_lo = lnk_ti_lo_from_leaf_ref(input, a);
|
|
CV_TypeIndex b_ti_lo = lnk_ti_lo_from_leaf_ref(input, b);
|
|
AssertAlways(a_ti_lo == b_ti_lo);
|
|
|
|
for (CV_TypeIndexInfo *ti_info = ti_info_list.first; ti_info != 0; ti_info = ti_info->next) {
|
|
CV_TypeIndex *a_ti_ptr = (CV_TypeIndex *) (a_leaf.data.str + ti_info->offset);
|
|
CV_TypeIndex *b_ti_ptr = (CV_TypeIndex *)(b_leaf.data.str + ti_info->offset);
|
|
|
|
if (*a_ti_ptr >= a_ti_lo && *b_ti_ptr >= b_ti_lo) {
|
|
LNK_LeafLocType a_loc_type = (a.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal) >> 31;
|
|
LNK_LeafLocType b_loc_type = (b.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal) >> 31;
|
|
|
|
U64 a_loc_idx = a.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal;
|
|
U64 b_loc_idx = b.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal;
|
|
|
|
LNK_LeafRef a_sub_leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(input, a_loc_type, ti_info->source, a_loc_idx, *a_ti_ptr);
|
|
LNK_LeafRef b_sub_leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(input, b_loc_type, ti_info->source, b_loc_idx, *b_ti_ptr);
|
|
|
|
are_equal = lnk_match_leaf_ref_deep(arena, input, hashes, a_sub_leaf_ref, b_sub_leaf_ref);
|
|
if (!are_equal) {
|
|
break;
|
|
}
|
|
}
|
|
// compare simple leaves
|
|
else {
|
|
are_equal = *a_ti_ptr == *b_ti_ptr;
|
|
if (!are_equal) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
skip_type_index_compare:;
|
|
temp_end(temp);
|
|
}
|
|
}
|
|
|
|
return are_equal;
|
|
}
|
|
|
|
internal U128
|
|
lnk_hash_cv_leaf(Arena *arena,
|
|
LNK_CodeViewInput *input,
|
|
LNK_LeafHashes *hashes,
|
|
LNK_LeafLocType loc_type,
|
|
U32 loc_idx,
|
|
Rng1U64 *ti_ranges,
|
|
CV_TypeIndex curr_ti,
|
|
CV_Leaf leaf,
|
|
CV_TypeIndexInfoList ti_info_list)
|
|
{
|
|
// init hasher
|
|
blake3_hasher hasher; blake3_hasher_init(&hasher);
|
|
|
|
// hash leaf size
|
|
blake3_hasher_update(&hasher, &leaf.data.size, sizeof leaf.data.size);
|
|
|
|
// hash leaf kind
|
|
blake3_hasher_update(&hasher, &leaf.kind, sizeof leaf.kind);
|
|
|
|
// hash bytes around indices
|
|
{
|
|
Temp temp = temp_begin(arena);
|
|
String8Array raw_data_arr = cv_get_data_around_type_indices(temp.arena, ti_info_list, leaf.data);
|
|
for (U64 i = 0; i < raw_data_arr.count; ++i) {
|
|
blake3_hasher_update(&hasher, raw_data_arr.v[i].str, raw_data_arr.v[i].size);
|
|
}
|
|
temp_end(temp);
|
|
}
|
|
|
|
// mix-in sub leaf hashes
|
|
for (CV_TypeIndexInfo *ti_n = ti_info_list.first; ti_n != 0; ti_n = ti_n->next) {
|
|
CV_TypeIndex sub_ti = *(CV_TypeIndex *) (leaf.data.str + ti_n->offset);
|
|
|
|
// is type index complex?
|
|
if (sub_ti >= ti_ranges[ti_n->source].min) {
|
|
// Mostly leaves are laid out as DAG and we can get to sub leaf hash through index lookup,
|
|
// however MASM doesn't follow DAG rule, for example:
|
|
//
|
|
// Engine\Source\Developer\Windows\LiveCoding\Private\External\LC_JumpToSelf.asm
|
|
// .debug$T (No. 4):
|
|
// LF_PROCEDURE (0x1000) [0008-0014]
|
|
// Return type: 3
|
|
// Call Convention: Near C
|
|
// Function Attribs: NULL
|
|
// Argumnet Count: 0
|
|
// Argument List Type: 1001
|
|
// LF_ARGLIST (0x1001) [0018-001C]
|
|
// Types 0
|
|
// LF_LABEL (0x1002) [0020-0024]
|
|
// $UNDEFINED: E
|
|
//
|
|
// Note: LF_ARGLIST(0x1001) > LF_PROCEDURE(0x1000)
|
|
//
|
|
// Luckily we don't have many leaves that break DAG rule and we can skip without
|
|
// much memory and perf penalty (In Ancient Game we skip 7 leaves)
|
|
if (sub_ti < curr_ti) {
|
|
LNK_LeafRef sub_leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(input, loc_type, ti_n->source, loc_idx, sub_ti);
|
|
|
|
// query sub hash
|
|
U128 sub_hash = lnk_hash_from_leaf_ref(hashes, sub_leaf_ref);
|
|
|
|
// make sure sub hash was computed (:zero_hash_array)
|
|
Assert(!u128_match(sub_hash, u128_zero()));
|
|
|
|
// mix-in sub hash
|
|
blake3_hasher_update(&hasher, &sub_hash, sizeof sub_hash);
|
|
} else {
|
|
Temp scratch = scratch_begin(0,0);
|
|
String8 leaf_kind_str = cv_string_from_leaf_kind(leaf.kind);
|
|
String8 leaf_info = push_str8f(scratch.arena, "LF_%S(type_index: 0x%x) forward refs member type index 0x%x (leaf struct offset: 0x%llx)", leaf_kind_str, curr_ti, sub_ti, ti_n->offset);
|
|
if (loc_type == LNK_LeafLocType_Internal) {
|
|
lnk_error_obj(LNK_Error_InvalidTypeIndex, input->internal_obj_arr+loc_idx, "%S", leaf_info);
|
|
} else if (loc_type == LNK_LeafLocType_External) {
|
|
lnk_error(LNK_Error_InvalidTypeIndex, "%S: %S", input->type_server_path_arr[loc_idx], leaf_info);
|
|
} else {
|
|
InvalidPath;
|
|
}
|
|
scratch_end(scratch);
|
|
}
|
|
}
|
|
// simple indices are stable across compile units
|
|
else {
|
|
blake3_hasher_update(&hasher, &sub_ti, sizeof sub_ti);
|
|
}
|
|
}
|
|
|
|
U128 hash;
|
|
blake3_hasher_finalize(&hasher, (U8 *) &hash, sizeof hash);
|
|
|
|
return hash;
|
|
}
|
|
|
|
internal void
|
|
lnk_hash_cv_leaf_deep(Arena *arena,
|
|
LNK_CodeViewInput *input,
|
|
Rng1U64 *ti_ranges,
|
|
CV_DebugT *leaves,
|
|
LNK_LeafHashes *hashes,
|
|
LNK_LeafLocType loc_type,
|
|
U32 loc_idx,
|
|
CV_TypeIndexInfoList ti_info_list,
|
|
String8 data)
|
|
{
|
|
Temp temp = temp_begin(arena);
|
|
|
|
struct stack_s {
|
|
struct stack_s *next;
|
|
CV_TypeIndexInfoList ti_info_list;
|
|
CV_TypeIndexInfo *ti_info;
|
|
CV_Leaf leaf;
|
|
String8 data;
|
|
CV_TypeIndex ti;
|
|
CV_TypeIndexSource ti_source;
|
|
};
|
|
|
|
// set up root frame
|
|
struct stack_s *root_frame = push_array_no_zero(temp.arena, struct stack_s, 1);
|
|
root_frame->next = 0;
|
|
root_frame->ti_info_list = ti_info_list;
|
|
root_frame->ti_info = ti_info_list.first;
|
|
root_frame->data = data;
|
|
root_frame->ti = 0;
|
|
root_frame->ti_source = CV_TypeIndexSource_NULL;
|
|
MemoryZeroStruct(&root_frame->leaf);
|
|
|
|
U128Array *curr_hashes = hashes->v[loc_type][loc_idx];
|
|
|
|
struct stack_s *stack = root_frame;
|
|
while (stack) {
|
|
while (stack->ti_info) {
|
|
CV_TypeIndexInfo *curr_ti_info = stack->ti_info;
|
|
|
|
// advance iterator
|
|
stack->ti_info = stack->ti_info->next;
|
|
|
|
// get type index info
|
|
CV_TypeIndex *ti_ptr = (CV_TypeIndex *) (stack->data.str + curr_ti_info->offset);
|
|
|
|
// is index complex?
|
|
if (*ti_ptr >= ti_ranges[curr_ti_info->source].min) {
|
|
// TODO: handle malformed index
|
|
AssertAlways(*ti_ptr < ti_ranges[curr_ti_info->source].max);
|
|
U64 ti_idx = (*ti_ptr - ti_ranges[curr_ti_info->source].min);
|
|
|
|
// was leaf hashed?
|
|
if (MemoryIsZeroStruct(&curr_hashes[curr_ti_info->source].v[ti_idx])) { // :zero_hash_array
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(leaves[curr_ti_info->source], ti_idx);
|
|
|
|
// find index offsets
|
|
CV_TypeIndexInfoList sub_ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data);
|
|
|
|
// do we have sub leaves?
|
|
if (sub_ti_info_list.count) {
|
|
// fill out new frame
|
|
struct stack_s *frame = push_array_no_zero(temp.arena, struct stack_s, 1);
|
|
frame->next = 0;
|
|
frame->ti_info_list = sub_ti_info_list;
|
|
frame->ti_info = sub_ti_info_list.first;
|
|
frame->leaf = leaf;
|
|
frame->data = leaf.data;
|
|
frame->ti = *ti_ptr;
|
|
frame->ti_source = curr_ti_info->source;
|
|
|
|
// recurse to sub leaf
|
|
SLLStackPush(stack, frame);
|
|
break;
|
|
} else {
|
|
curr_hashes[curr_ti_info->source].v[ti_idx] = lnk_hash_cv_leaf(temp.arena,
|
|
input,
|
|
hashes,
|
|
loc_type,
|
|
loc_idx,
|
|
ti_ranges,
|
|
CV_TypeIndex_Max,
|
|
leaf,
|
|
sub_ti_info_list);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// no more type indices, pop frame
|
|
if (!stack->ti_info) {
|
|
|
|
if (stack != root_frame) {
|
|
// sub leaves are hashed we can now hash parent leaf
|
|
Temp temp2 = temp_begin(temp.arena);
|
|
U64 leaf_idx = stack->ti - ti_ranges[stack->ti_source].min;
|
|
curr_hashes[stack->ti_source].v[leaf_idx] = lnk_hash_cv_leaf(temp2.arena,
|
|
input,
|
|
hashes,
|
|
loc_type,
|
|
loc_idx,
|
|
ti_ranges,
|
|
CV_TypeIndex_Max,
|
|
stack->leaf,
|
|
stack->ti_info_list);
|
|
temp_end(temp2);
|
|
}
|
|
|
|
SLLStackPop(stack);
|
|
}
|
|
}
|
|
|
|
temp_end(temp);
|
|
}
|
|
|
|
internal LNK_LeafBucket *
|
|
lnk_leaf_hash_table_insert_or_update(LNK_LeafHashTable *leaf_ht, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, U128 new_hash, LNK_LeafBucket *new_bucket)
|
|
{
|
|
LNK_LeafBucket *result = 0;
|
|
B32 is_inserted_or_updated = 0;
|
|
|
|
U64 best_idx = u128_mod64(new_hash, leaf_ht->cap);
|
|
U64 idx = best_idx;
|
|
|
|
do {
|
|
retry:;
|
|
LNK_LeafBucket *curr_bucket = leaf_ht->bucket_arr[idx];
|
|
|
|
if (curr_bucket == 0) {
|
|
LNK_LeafBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&leaf_ht->bucket_arr[idx], new_bucket, curr_bucket);
|
|
|
|
if (compare_bucket == curr_bucket) {
|
|
// success, bucket was inserted
|
|
is_inserted_or_updated = 1;
|
|
break;
|
|
}
|
|
|
|
// another thread took the bucket...
|
|
goto retry;
|
|
} else if (lnk_match_leaf_ref(input, hashes, curr_bucket->leaf_ref, new_bucket->leaf_ref)) {
|
|
int leaf_cmp = lnk_leaf_ref_compare(curr_bucket->leaf_ref, new_bucket->leaf_ref);
|
|
|
|
if (leaf_cmp <= 0) {
|
|
// are we inserting bucket that was already inserterd?
|
|
Assert(leaf_cmp < 0);
|
|
|
|
result = new_bucket;
|
|
|
|
is_inserted_or_updated = 1;
|
|
|
|
// don't need to update, more recent leaf is in the bucket
|
|
break;
|
|
}
|
|
|
|
LNK_LeafBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&leaf_ht->bucket_arr[idx], new_bucket, curr_bucket);
|
|
if (compare_bucket == curr_bucket) {
|
|
result = compare_bucket;
|
|
|
|
is_inserted_or_updated = 1;
|
|
break;
|
|
}
|
|
|
|
// another thread took the bucket...
|
|
goto retry;
|
|
}
|
|
|
|
// advance
|
|
idx = (idx + 1) % leaf_ht->cap;
|
|
} while (idx != best_idx);
|
|
|
|
Assert(is_inserted_or_updated);
|
|
|
|
return result;
|
|
}
|
|
|
|
internal LNK_LeafBucket *
|
|
lnk_leaf_hash_table_search(LNK_LeafHashTable *ht, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef leaf_ref)
|
|
{
|
|
LNK_LeafBucket *match = 0;
|
|
|
|
U128 hash = lnk_hash_from_leaf_ref(hashes, leaf_ref);
|
|
U64 best_bucket_idx = u128_mod64(hash, ht->cap);
|
|
U64 bucket_idx = best_bucket_idx;
|
|
|
|
do {
|
|
LNK_LeafBucket *bucket = ht->bucket_arr[bucket_idx];
|
|
|
|
if (bucket == 0) {
|
|
break;
|
|
}
|
|
|
|
if (lnk_match_leaf_ref(input, hashes, bucket->leaf_ref, leaf_ref)) {
|
|
match = bucket;
|
|
break;
|
|
}
|
|
|
|
bucket_idx = (bucket_idx + 1) % ht->cap;
|
|
} while (bucket_idx != best_bucket_idx);
|
|
|
|
return match;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_count_per_source_leaf_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_CountPerSourceLeafTask *task = raw_task;
|
|
LNK_LeafRangeList leaf_range_list = task->leaf_ranges_per_task[task_id];
|
|
|
|
for (LNK_LeafRange *leaf_range = leaf_range_list.first; leaf_range != 0; leaf_range = leaf_range->next) {
|
|
CV_DebugT debug_t = *leaf_range->debug_t;
|
|
for (U64 leaf_idx = leaf_range->range.min; leaf_idx < leaf_range->range.max; ++leaf_idx) {
|
|
CV_LeafHeader *leaf_header = cv_debug_t_get_leaf_header(debug_t, leaf_idx);
|
|
CV_TypeIndexSource leaf_source = cv_type_index_source_from_leaf_kind(leaf_header->kind);
|
|
task->count_arr_arr[leaf_source][task_id] += 1;
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal void
|
|
lnk_cv_debug_t_count_leaves_per_source(TP_Context *tp, U64 count, CV_DebugT *debug_t_arr, U64 per_source_count_arr[CV_TypeIndexSource_COUNT])
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0,0);
|
|
|
|
ProfBegin("Compute Per Task Ranges");
|
|
U64 per_task_leaf_count = 10000;
|
|
LNK_LeafRangeList *leaf_ranges_per_task = push_array(scratch.arena, LNK_LeafRangeList, tp->worker_count);
|
|
for (U64 i = 0, task_weight = 0, task_id = 0; i < count; ++i) {
|
|
CV_DebugT *debug_t = &debug_t_arr[i];
|
|
for (U64 k = 0; k < debug_t->count; k += per_task_leaf_count) {
|
|
U64 cap = per_task_leaf_count - task_weight;
|
|
|
|
LNK_LeafRange *leaf_range = push_array(scratch.arena, LNK_LeafRange, 1);
|
|
leaf_range->range = rng_1u64(k, Min(k + cap, debug_t->count));
|
|
leaf_range->debug_t = debug_t;
|
|
|
|
LNK_LeafRangeList *list = &leaf_ranges_per_task[task_id];
|
|
SLLQueuePush(list->first, list->last, leaf_range);
|
|
++list->count;
|
|
|
|
task_weight += dim_1u64(leaf_range->range);
|
|
if (task_weight >= per_task_leaf_count) {
|
|
task_id = (task_id + 1) % tp->worker_count;
|
|
task_weight = 0;
|
|
}
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
|
|
LNK_CountPerSourceLeafTask task;
|
|
task.leaf_ranges_per_task = leaf_ranges_per_task;
|
|
task.count_arr_arr = push_matrix_u64(scratch.arena, CV_TypeIndexSource_COUNT, tp->worker_count);
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_count_per_source_leaf_task, &task);
|
|
|
|
for (U64 i = 0; i < CV_TypeIndexSource_COUNT; ++i) {
|
|
per_source_count_arr[i] += sum_array_u64(tp->worker_count, task.count_arr_arr[i]);
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_hash_debug_t_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
U64 obj_idx = task_id;
|
|
LNK_LeafHasherTask *task = raw_task;
|
|
|
|
Arena *fixed_arena = task->fixed_arenas[worker_id];
|
|
CV_DebugT debug_t = task->debug_t_arr[obj_idx];
|
|
U128Array out_hashes = task->hashes->v[LNK_LeafLocType_Internal][obj_idx][CV_TypeIndexSource_TPI];
|
|
|
|
Rng1U64 ti_ranges[CV_TypeIndexSource_COUNT];
|
|
for (U64 ti_source = 0; ti_source < ArrayCount(ti_ranges); ++ti_source) {
|
|
ti_ranges[ti_source] = rng_1u64(task->input->pch_arr[obj_idx].ti_lo, task->input->pch_arr[obj_idx].ti_hi + debug_t.count);
|
|
}
|
|
|
|
for (U64 leaf_idx = 0; leaf_idx < debug_t.count; ++leaf_idx) {
|
|
Temp temp = temp_begin(fixed_arena);
|
|
|
|
// :debug_zero_hash_assert make sure we don't write same hash more than once
|
|
//Assert(MemoryIsZeroStruct(&out_hash_arr.v[leaf_idx]));
|
|
|
|
CV_TypeIndex curr_ti = lnk_type_index_from_leaf_ref(task->input, lnk_leaf_ref(obj_idx, leaf_idx));
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, leaf_idx);
|
|
CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data);
|
|
|
|
out_hashes.v[leaf_idx] = lnk_hash_cv_leaf(temp.arena,
|
|
task->input,
|
|
task->hashes,
|
|
LNK_LeafLocType_Internal,
|
|
obj_idx,
|
|
ti_ranges,
|
|
curr_ti,
|
|
leaf,
|
|
ti_info_list);
|
|
|
|
temp_end(temp);
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_hash_type_server_leaves_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_LeafHasherTask *task = raw_task;
|
|
U64 obj_idx = task_id;
|
|
|
|
LNK_CodeViewInput *input = task->input;
|
|
LNK_LeafHashes *hashes = task->hashes;
|
|
|
|
CV_SymbolListArray parsed_symbols = input->external_parsed_symbols[obj_idx];
|
|
CV_DebugS debug_s = input->external_debug_s_arr[obj_idx];
|
|
U64 ts_idx = input->external_obj_to_ts_idx_arr[obj_idx];
|
|
CV_DebugT *leaves = input->external_leaves[ts_idx];
|
|
Rng1U64 *ti_ranges = input->external_ti_ranges[ts_idx];
|
|
|
|
// hash leaves referenced in symbols
|
|
for (U64 i = 0; i < parsed_symbols.count; ++i) {
|
|
CV_SymbolList symbol_list = parsed_symbols.v[i];
|
|
for (CV_SymbolNode *symnode = symbol_list.first; symnode != 0; symnode = symnode->next) {
|
|
Temp temp = temp_begin(task->fixed_arenas[worker_id]);
|
|
CV_TypeIndexInfoList ti_info_list = cv_get_symbol_type_index_offsets(temp.arena, symnode->data.kind, symnode->data.data);
|
|
lnk_hash_cv_leaf_deep(temp.arena, task->input, ti_ranges, leaves, hashes, LNK_LeafLocType_External, ts_idx, ti_info_list, symnode->data.data);
|
|
temp_end(temp);
|
|
}
|
|
}
|
|
|
|
// hash leaves referenced in inlinees
|
|
String8List inline_data_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_InlineeLines);
|
|
for (String8Node *inline_data_node = inline_data_list.first; inline_data_node != 0; inline_data_node = inline_data_node->next) {
|
|
Temp temp = temp_begin(task->fixed_arenas[worker_id]);
|
|
CV_TypeIndexInfoList ti_info_list = cv_get_inlinee_type_index_offsets(temp.arena, inline_data_node->string);
|
|
lnk_hash_cv_leaf_deep(temp.arena, task->input, ti_ranges, leaves, hashes, LNK_LeafLocType_External, ts_idx, ti_info_list, inline_data_node->string);
|
|
temp_end(temp);
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_leaf_dedup_internal_task)
|
|
{
|
|
LNK_LeafDedupInternal *task = raw_task;
|
|
U64 obj_idx = task_id;
|
|
CV_DebugT debug_t = task->debug_t_arr[obj_idx];
|
|
|
|
ProfBeginDynamic("Leaf Dedup Task 0x%X [Leaf Count %u]", obj_idx, task->debug_t_arr[obj_idx].count);
|
|
|
|
LNK_LeafBucket *bucket = 0;
|
|
for (U64 leaf_idx = 0; leaf_idx < debug_t.count; ++leaf_idx) {
|
|
CV_LeafHeader *leaf_header = cv_debug_t_get_leaf_header(debug_t, leaf_idx);
|
|
CV_TypeIndexSource ti_source = cv_type_index_source_from_leaf_kind(leaf_header->kind);
|
|
LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[ti_source];
|
|
|
|
LNK_LeafRef leaf_ref = lnk_obj_leaf_ref(obj_idx, leaf_idx);
|
|
U128 leaf_hash = lnk_hash_from_leaf_ref(task->hashes, leaf_ref);
|
|
|
|
if (bucket == 0) {
|
|
bucket = push_array_no_zero(arena, LNK_LeafBucket, 1);
|
|
}
|
|
bucket->leaf_ref = leaf_ref;
|
|
|
|
LNK_LeafBucket *inserted_or_updated = lnk_leaf_hash_table_insert_or_update(leaf_ht, task->input, task->hashes, leaf_hash, bucket);
|
|
|
|
if (inserted_or_updated != bucket) {
|
|
bucket = 0;
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_leaf_dedup_external_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_LeafDedupExternal *task = raw_task;
|
|
U64 ts_idx = task_id;
|
|
|
|
LNK_CodeViewInput *input = task->input;
|
|
LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[task->dedup_ti_source];
|
|
U128Array hashes = task->hashes->external_hashes[ts_idx][task->dedup_ti_source];
|
|
U64 leaf_count = dim_1u64(input->external_ti_ranges[ts_idx][task->dedup_ti_source]);
|
|
|
|
LNK_LeafBucket *bucket = 0;
|
|
|
|
for (U64 leaf_idx = 0; leaf_idx < leaf_count; ++leaf_idx) {
|
|
if (!MemoryIsZeroStruct(&hashes.v[leaf_idx])) { // :zero_hash_check
|
|
LNK_LeafRef leaf_ref = lnk_ts_leaf_ref(task->dedup_ti_source, ts_idx, leaf_idx);
|
|
U128 leaf_hash = lnk_hash_from_leaf_ref(task->hashes, leaf_ref);
|
|
|
|
if (bucket == 0) {
|
|
bucket = push_array_no_zero(arena, LNK_LeafBucket, 1);
|
|
}
|
|
bucket->leaf_ref = leaf_ref;
|
|
|
|
LNK_LeafBucket *inserted_or_updated = lnk_leaf_hash_table_insert_or_update(leaf_ht, task->input, task->hashes, leaf_hash, bucket);
|
|
|
|
if (inserted_or_updated != bucket) {
|
|
bucket = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_count_present_buckets_task)
|
|
{
|
|
ProfBeginFunction();
|
|
LNK_GetPresentBucketsTask *task = raw_task;
|
|
for (U64 bucket_idx = task->range_arr[task_id].min; bucket_idx < task->range_arr[task_id].max; ++bucket_idx) {
|
|
if (task->ht->bucket_arr[bucket_idx] != 0) {
|
|
task->count_arr[task_id] += 1;
|
|
}
|
|
}
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_get_present_buckets_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_GetPresentBucketsTask *task = raw_task;
|
|
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
U64 cursor = task->offset_arr[task_id];
|
|
LNK_LeafHashTable *ht = task->ht;
|
|
|
|
for (U64 bucket_idx = range.min; bucket_idx < range.max; ++bucket_idx) {
|
|
if (ht->bucket_arr[bucket_idx]) {
|
|
task->result.v[cursor++] = ht->bucket_arr[bucket_idx];
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal LNK_LeafBucketArray
|
|
lnk_present_bucket_array_from_leaf_hash_table(TP_Context *tp, Arena *arena, LNK_LeafHashTable *ht)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
LNK_GetPresentBucketsTask task = {0};
|
|
task.ht = ht;
|
|
task.count_arr = push_array(scratch.arena, U64, tp->worker_count);
|
|
task.range_arr = tp_divide_work(scratch.arena, ht->cap, tp->worker_count);
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_count_present_buckets_task, &task);
|
|
|
|
LNK_LeafBucketArray result;
|
|
result.count = sum_array_u64(tp->worker_count, task.count_arr);
|
|
result.v = push_array_no_zero(arena, LNK_LeafBucket *, result.count);
|
|
|
|
task.result = result;
|
|
task.offset_arr = offsets_from_counts_array_u64(scratch.arena, task.count_arr, tp->worker_count);
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_get_present_buckets_task, &task);
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return result;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_leaf_ref_histo_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_LeafRadixSortTask *task = raw_task;
|
|
Rng1U64 range = task->ranges[task_id];
|
|
U32 *counts_ptr = task->counts_arr[task_id];
|
|
|
|
U32 loc_idx_bit_count_0 = task->loc_idx_bit_count_0;
|
|
U32 loc_idx_bit_count_1 = task->loc_idx_bit_count_1;
|
|
U32 loc_idx_bit_count_2 = task->loc_idx_bit_count_2;
|
|
|
|
MemoryZeroTyped(task->counts_arr[task_id], task->counts_max);
|
|
|
|
switch (task->pass_idx) {
|
|
case 0: {
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 leaf_digit0 = BitExtract(bucket->leaf_ref.enc_leaf_idx, 10, 0);
|
|
++counts_ptr[leaf_digit0];
|
|
}
|
|
} break;
|
|
case 1: {
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 leaf_digit1 = BitExtract(bucket->leaf_ref.enc_leaf_idx, 11, 10);
|
|
++counts_ptr[leaf_digit1];
|
|
}
|
|
} break;
|
|
case 2: {
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 leaf_digit2 = BitExtract(bucket->leaf_ref.enc_leaf_idx, 11, 21 - 1); // don't take into account IPI flag
|
|
++counts_ptr[leaf_digit2];
|
|
}
|
|
} break;
|
|
|
|
case 3: {
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 digit0 = BitExtract(bucket->leaf_ref.enc_loc_idx, loc_idx_bit_count_0, 0);
|
|
++counts_ptr[digit0];
|
|
}
|
|
} break;
|
|
case 4: {
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 digit1 = BitExtract(bucket->leaf_ref.enc_loc_idx, loc_idx_bit_count_1, loc_idx_bit_count_0);
|
|
++counts_ptr[digit1];
|
|
}
|
|
} break;
|
|
case 5: {
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 digit2 = BitExtract(bucket->leaf_ref.enc_loc_idx, loc_idx_bit_count_2, loc_idx_bit_count_0 + loc_idx_bit_count_1);
|
|
|
|
U64 loc_bit = !!(bucket->leaf_ref.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal);
|
|
digit2 |= loc_bit << loc_idx_bit_count_2;
|
|
|
|
++counts_ptr[digit2];
|
|
}
|
|
} break;
|
|
default: InvalidPath;
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_loc_idx_radix_sort_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_LeafRadixSortTask *task = raw_task;
|
|
Rng1U64 range = task->ranges[task_id];
|
|
U32 *counts_ptr = task->counts_arr[task_id];
|
|
U32 loc_idx_bit_count_0 = task->loc_idx_bit_count_0;
|
|
U32 loc_idx_bit_count_1 = task->loc_idx_bit_count_1;
|
|
U32 loc_idx_bit_count_2 = task->loc_idx_bit_count_2;
|
|
|
|
switch (task->pass_idx) {
|
|
//
|
|
// Sort items on leaf index
|
|
//
|
|
case 0: {
|
|
ProfBegin("Leaf Sort Low");
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 leaf_digit0 = BitExtract(bucket->leaf_ref.enc_leaf_idx, 10, 0);
|
|
task->dst[counts_ptr[leaf_digit0]++] = bucket;
|
|
}
|
|
ProfEnd();
|
|
} break;
|
|
case 1: {
|
|
ProfBegin("Leaf Sort Mid");
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 leaf_digit1 = BitExtract(bucket->leaf_ref.enc_leaf_idx, 11, 10);
|
|
task->dst[counts_ptr[leaf_digit1]++] = bucket;
|
|
}
|
|
ProfEnd();
|
|
} break;
|
|
case 2: {
|
|
ProfBegin("Leaf Sort High");
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 leaf_digit2 = BitExtract(bucket->leaf_ref.enc_leaf_idx, 11, 21 - 1); // don't take into account IPI flag
|
|
task->dst[counts_ptr[leaf_digit2]++] = bucket;
|
|
}
|
|
ProfEnd();
|
|
} break;
|
|
|
|
//
|
|
// Sort items on obj and type server index
|
|
//
|
|
case 3: {
|
|
ProfBegin("Loc Sort Low");
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 digit0 = BitExtract(bucket->leaf_ref.enc_loc_idx, loc_idx_bit_count_0, 0);
|
|
task->dst[counts_ptr[digit0]++] = bucket;
|
|
}
|
|
ProfEnd();
|
|
} break;
|
|
case 4: {
|
|
ProfBegin("Loc Sort Mid");
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 digit1 = BitExtract(bucket->leaf_ref.enc_loc_idx, loc_idx_bit_count_1, loc_idx_bit_count_0);
|
|
task->dst[counts_ptr[digit1]++] = bucket;
|
|
}
|
|
ProfEnd();
|
|
} break;
|
|
case 5: {
|
|
ProfBegin("Loc Sort High");
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->src[i];
|
|
U64 digit2 = BitExtract(bucket->leaf_ref.enc_loc_idx, loc_idx_bit_count_2, loc_idx_bit_count_0 + loc_idx_bit_count_1);
|
|
|
|
U64 loc_bit = !!(bucket->leaf_ref.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal);
|
|
digit2 |= loc_bit << loc_idx_bit_count_2;
|
|
|
|
Assert(counts_ptr[digit2] != max_U32);
|
|
task->dst[counts_ptr[digit2]++] = bucket;
|
|
}
|
|
ProfEnd();
|
|
} break;
|
|
|
|
default: InvalidPath;
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal void
|
|
lnk_leaf_bucket_array_sort(TP_Context *tp, LNK_LeafBucketArray arr, U64 obj_count, U64 type_server_count)
|
|
{
|
|
Temp scratch = scratch_begin(0,0);
|
|
|
|
#if PROFILE_TELEMETRY
|
|
String8 leaf_count_string = str8_from_count(scratch.arena, arr.count);
|
|
String8 obj_count_string = str8_from_count(scratch.arena, obj_count);
|
|
String8 type_server_count_string = str8_from_count(scratch.arena, type_server_count);
|
|
ProfBeginDynamic("Leaf Sort [Leaf Count: %.*s, Obj Count: %.*s, Type Server Count: %.*s]", str8_varg(leaf_count_string), str8_varg(obj_count_string), str8_varg(type_server_count_string));
|
|
#endif
|
|
|
|
if (arr.count > 140000) {
|
|
ProfBegin("Radix");
|
|
|
|
U32 loc_idx_max_bits = 32 - clz32(Max(obj_count, type_server_count));
|
|
|
|
LNK_LeafRadixSortTask task = {0};
|
|
task.loc_idx_bit_count_0 = Clamp(0, (S32)loc_idx_max_bits - 21, 11);
|
|
task.loc_idx_bit_count_1 = Clamp(0, (S32)loc_idx_max_bits - 10, 11);
|
|
task.loc_idx_bit_count_2 = Clamp(0, (S32)loc_idx_max_bits, 10);
|
|
task.counts_max = (1 << 11);
|
|
task.loc_idx_max = arr.count;
|
|
task.ranges = tp_divide_work(scratch.arena, arr.count, tp->worker_count);
|
|
task.dst = push_array_no_zero(scratch.arena, LNK_LeafBucket *, arr.count);
|
|
task.src = arr.v;
|
|
|
|
ProfBegin("Push Counts");
|
|
task.counts_arr = push_array_no_zero(scratch.arena, U32 *, tp->worker_count);
|
|
for (U64 i = 0; i < tp->worker_count; ++i) {
|
|
// zero-out happens in histogram step
|
|
task.counts_arr[i] = push_array_no_zero(scratch.arena, U32, task.counts_max);
|
|
}
|
|
ProfEnd();
|
|
|
|
for (task.pass_idx = 0; task.pass_idx < 6; ++task.pass_idx) {
|
|
ProfBeginDynamic("Pass: %u", task.pass_idx);
|
|
|
|
ProfBegin("Histo");
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_leaf_ref_histo_task, &task);
|
|
ProfEnd();
|
|
|
|
B32 is_range_not_empty = 0;
|
|
for (U64 task_id = 0; task_id < tp->worker_count; ++task_id) {
|
|
is_range_not_empty = task.counts_arr[task_id][0] != dim_1u64(task.ranges[task_id]);
|
|
if (is_range_not_empty) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ProfBegin("Counts -> Offsets");
|
|
{
|
|
U64 digit_cursor = 0;
|
|
for (U64 digit_idx = 0; digit_idx < task.counts_max; ++digit_idx) {
|
|
for (U64 task_id = 0; task_id < tp->worker_count; ++task_id) {
|
|
U64 count = task.counts_arr[task_id][digit_idx];
|
|
task.counts_arr[task_id][digit_idx] = digit_cursor;
|
|
digit_cursor += count;
|
|
}
|
|
}
|
|
Assert(digit_cursor == arr.count);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Sort");
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_loc_idx_radix_sort_task, &task);
|
|
Swap(LNK_LeafBucket **, task.src, task.dst);
|
|
ProfEnd();
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
if (task.src != arr.v) {
|
|
MemoryCopyTyped(arr.v, task.dst, arr.count);
|
|
}
|
|
|
|
#if 0
|
|
for (U64 i = 1; i < arr.count; ++i) {
|
|
AssertAlways(arr.v[i-1]->leaf_ref.enc_loc_idx <= arr.v[i]->leaf_ref.enc_loc_idx);
|
|
if (arr.v[i-1]->leaf_ref.enc_loc_idx == arr.v[i]->leaf_ref.enc_loc_idx) {
|
|
AssertAlways(arr.v[i-1]->leaf_ref.enc_leaf_idx <= arr.v[i]->leaf_ref.enc_leaf_idx);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ProfEnd();
|
|
} else {
|
|
ProfBegin("Radsort");
|
|
radsort(arr.v, arr.count, lnk_leaf_ref_is_before);
|
|
ProfEnd();
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_assign_type_indices_task)
|
|
{
|
|
LNK_AssignTypeIndicesTask *task = raw_task;
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
LNK_LeafBucket *bucket = task->bucket_arr.v[i];
|
|
bucket->type_index = task->min_type_index + i;
|
|
}
|
|
}
|
|
|
|
internal void
|
|
lnk_assign_type_indices(TP_Context *tp, LNK_LeafBucketArray bucket_arr, CV_TypeIndex min_type_index)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0,0);
|
|
|
|
LNK_AssignTypeIndicesTask task;
|
|
task.range_arr = tp_divide_work(scratch.arena, bucket_arr.count, tp->worker_count);
|
|
task.bucket_arr = bucket_arr;
|
|
task.min_type_index = min_type_index;
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_assign_type_indices_task, &task);
|
|
|
|
ProfEnd();
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_patch_symbols_task)
|
|
{
|
|
LNK_PatchSymbolTypesTask *task = raw_task;
|
|
Arena *fixed_arena = task->arena_arr[worker_id];
|
|
LNK_CodeViewSymbolsInput symbol_input = task->input->symbol_inputs[task_id];
|
|
|
|
LNK_LeafLocType loc_type = lnk_loc_type_from_obj_idx(task->input, symbol_input.obj_idx);
|
|
U64 loc_idx = lnk_loc_idx_from_obj_idx(task->input, symbol_input.obj_idx);
|
|
|
|
CV_TypeIndex ti_lo_arr[CV_TypeIndexSource_COUNT];
|
|
ti_lo_arr[CV_TypeIndexSource_NULL] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_NULL);
|
|
ti_lo_arr[CV_TypeIndexSource_TPI ] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_TPI);
|
|
ti_lo_arr[CV_TypeIndexSource_IPI ] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_IPI);
|
|
|
|
for (CV_SymbolNode *symnode = symbol_input.symbol_list->first; symnode != 0; symnode = symnode->next) {
|
|
Temp temp = temp_begin(fixed_arena);
|
|
|
|
// find type index offsets in symbol
|
|
CV_TypeIndexInfoList ti_list = cv_get_symbol_type_index_offsets(temp.arena, symnode->data.kind, symnode->data.data);
|
|
|
|
// overwrite type indices in symbol
|
|
for (CV_TypeIndexInfo *ti_info = ti_list.first; ti_info != 0; ti_info = ti_info->next) {
|
|
CV_TypeIndex *ti_ptr = (CV_TypeIndex *) (symnode->data.data.str + ti_info->offset);
|
|
if (*ti_ptr >= ti_lo_arr[ti_info->source]) {
|
|
LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[ti_info->source];
|
|
LNK_LeafRef leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(task->input, loc_type, ti_info->source, loc_idx, *ti_ptr);
|
|
LNK_LeafBucket *leaf_bucket = lnk_leaf_hash_table_search(leaf_ht, task->input, task->hashes, leaf_ref);
|
|
|
|
// we overwrite section memory directly
|
|
*ti_ptr = leaf_bucket->type_index;
|
|
}
|
|
}
|
|
|
|
temp_end(temp);
|
|
}
|
|
}
|
|
|
|
internal void
|
|
lnk_patch_symbols(TP_Context *tp,
|
|
LNK_CodeViewInput *input,
|
|
LNK_LeafHashes *hashes,
|
|
LNK_LeafHashTable *leaf_ht_arr)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0,0);
|
|
|
|
U64 max_ti_list_size = sizeof(CV_TypeIndexInfo) * (max_U16 / sizeof(CV_TypeIndex));
|
|
|
|
LNK_PatchSymbolTypesTask task = {0};
|
|
task.input = input;
|
|
task.hashes = hashes;
|
|
task.leaf_ht_arr = leaf_ht_arr;
|
|
task.arena_arr = alloc_fixed_size_arena_array(scratch.arena, tp->worker_count, max_ti_list_size, max_ti_list_size);
|
|
tp_for_parallel(tp, 0, input->total_symbol_input_count, lnk_patch_symbols_task, &task);
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_patch_inlines_task)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0, 0);
|
|
|
|
LNK_PatchInlinesTask *task = raw_task;
|
|
|
|
U64 loc_idx = lnk_loc_idx_from_obj_idx(task->input, task_id);
|
|
LNK_LeafLocType loc_type = lnk_loc_type_from_obj_idx(task->input, task_id);
|
|
String8List inline_data_list = cv_sub_section_from_debug_s(task->debug_s_arr[task_id], CV_C13SubSectionKind_InlineeLines);
|
|
|
|
for (String8Node *inline_data_node = inline_data_list.first; inline_data_node != 0; inline_data_node = inline_data_node->next) {
|
|
Temp temp = temp_begin(scratch.arena);
|
|
|
|
// get indices offsets
|
|
CV_TypeIndexInfoList ti_info_list = cv_get_inlinee_type_index_offsets(temp.arena, inline_data_node->string);
|
|
|
|
for (CV_TypeIndexInfo *ti_info = ti_info_list.first; ti_info != 0; ti_info = ti_info->next) {
|
|
CV_TypeIndex *ti_ptr = (CV_TypeIndex *) (inline_data_node->string.str + ti_info->offset);
|
|
CV_TypeIndex ti_lo = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, ti_info->source);
|
|
if (*ti_ptr >= ti_lo) {
|
|
LNK_LeafRef leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(task->input, loc_type, ti_info->source, loc_idx, *ti_ptr);
|
|
LNK_LeafBucket *leaf_bucket = lnk_leaf_hash_table_search(&task->leaf_ht_arr[ti_info->source], task->input, task->hashes, leaf_ref);
|
|
|
|
// patch index
|
|
*ti_ptr = leaf_bucket->type_index;
|
|
}
|
|
}
|
|
|
|
temp_end(temp);
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal void
|
|
lnk_patch_inlines(TP_Context *tp,
|
|
LNK_CodeViewInput *input,
|
|
LNK_LeafHashes *hashes,
|
|
LNK_LeafHashTable *leaf_ht_arr,
|
|
U64 obj_count,
|
|
CV_DebugS *debug_s_arr)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_PatchInlinesTask task = {0};
|
|
task.input = input;
|
|
task.hashes = hashes;
|
|
task.leaf_ht_arr = leaf_ht_arr;
|
|
task.debug_s_arr = debug_s_arr;
|
|
tp_for_parallel(tp, 0, obj_count, lnk_patch_inlines_task, &task);
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_patch_leaves_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_PatchLeavesTask *task = raw_task;
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
|
|
for (U64 bucket_idx = range.min; bucket_idx < range.max; ++bucket_idx) {
|
|
Temp temp = temp_begin(task->fixed_arena_arr[task_id]);
|
|
|
|
LNK_LeafBucket *bucket = task->bucket_arr[bucket_idx];
|
|
|
|
U64 loc_idx = bucket->leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal;
|
|
LNK_LeafLocType loc_type = lnk_loc_type_from_leaf_ref(bucket->leaf_ref);
|
|
CV_TypeIndex ti_lo = lnk_ti_lo_from_leaf_ref(task->input, bucket->leaf_ref);
|
|
String8 raw_leaf = lnk_data_from_leaf_ref(task->input, bucket->leaf_ref);
|
|
CV_Leaf leaf = cv_leaf_from_string(raw_leaf);
|
|
|
|
// get type indices offsets
|
|
CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data);
|
|
for (CV_TypeIndexInfo *ti_info = ti_info_list.first; ti_info != 0; ti_info = ti_info->next) {
|
|
CV_TypeIndex *ti_ptr = (CV_TypeIndex *) (leaf.data.str + ti_info->offset);
|
|
if (*ti_ptr >= ti_lo) {
|
|
LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[ti_info->source];
|
|
LNK_LeafRef sub_leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(task->input, loc_type, ti_info->source, loc_idx, *ti_ptr);
|
|
LNK_LeafBucket *sub_leaf_bucket = lnk_leaf_hash_table_search(leaf_ht, task->input, task->hashes, sub_leaf_ref);
|
|
|
|
// patch index
|
|
*ti_ptr = sub_leaf_bucket->type_index;
|
|
}
|
|
}
|
|
|
|
temp_end(temp);
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal void
|
|
lnk_patch_leaves(TP_Context *tp, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafHashTable *leaf_ht_arr, LNK_LeafBucketArray bucket_arr)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0,0);
|
|
|
|
LNK_PatchLeavesTask task;
|
|
task.input = input;
|
|
task.hashes = hashes;
|
|
task.leaf_ht_arr = leaf_ht_arr;
|
|
task.bucket_arr = bucket_arr.v;
|
|
task.range_arr = tp_divide_work(scratch.arena, bucket_arr.count, tp->worker_count);
|
|
task.fixed_arena_arr = alloc_fixed_size_arena_array(scratch.arena, tp->worker_count, MB(1), MB(1));
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_patch_leaves_task, &task);
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_unbucket_raw_leaves_task)
|
|
{
|
|
LNK_UnbucketRawLeavesTask *task = raw_task;
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
for (U64 i = range.min; i < range.max; ++i) {
|
|
String8 raw_leaf = lnk_data_from_leaf_ref(task->input, task->bucket_arr[i]->leaf_ref);
|
|
task->raw_leaf_arr[i] = raw_leaf.str;
|
|
}
|
|
}
|
|
|
|
internal CV_DebugT
|
|
lnk_unbucket_leaf_array(TP_Context *tp, Arena *arena, LNK_CodeViewInput *input, LNK_LeafBucketArray bucket_arr)
|
|
{
|
|
ProfBeginDynamic("Unbucket Leaves [Count %llu]", bucket_arr.count);
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
LNK_UnbucketRawLeavesTask task = {0};
|
|
task.input = input;
|
|
task.bucket_arr = bucket_arr.v;
|
|
task.raw_leaf_arr = push_array_no_zero(arena, U8 *, bucket_arr.count);
|
|
task.range_arr = tp_divide_work(scratch.arena, bucket_arr.count, tp->worker_count);
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_unbucket_raw_leaves_task, &task);
|
|
|
|
CV_DebugT debug_t = {0};
|
|
debug_t.count = bucket_arr.count;
|
|
debug_t.v = task.raw_leaf_arr;
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return debug_t;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_post_process_cv_symbols_task)
|
|
{
|
|
LNK_PostProcessCvSymbolsTask *task = raw_task;
|
|
LNK_CodeViewSymbolsInput symbol_input = task->symbol_inputs[task_id];
|
|
|
|
for (CV_SymbolNode *symnode = symbol_input.symbol_list->first; symnode != 0; symnode = symnode->next) {
|
|
CV_Symbol *symbol = &symnode->data;
|
|
|
|
if (symbol->kind == CV_SymKind_LPROC32_ID || symbol->kind == CV_SymKind_GPROC32_ID || symbol->kind == CV_SymKind_LPROC32_DPC) {
|
|
CV_SymProc32 *proc32 = (CV_SymProc32 *) symbol->data.str;
|
|
if (proc32->itype >= task->ipi_min_type_index) {
|
|
if ((proc32->itype - task->ipi_min_type_index) < task->ipi_types.count) {
|
|
U64 leaf_idx = proc32->itype - task->ipi_min_type_index;
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(task->ipi_types, leaf_idx);
|
|
|
|
if (leaf.kind == CV_LeafKind_FUNC_ID) {
|
|
if (leaf.data.size >= sizeof(CV_LeafFuncId)) {
|
|
proc32->itype = ((CV_LeafFuncId *) leaf.data.str)->itype;
|
|
} else {
|
|
Assert(!"TODO: error handle corrupt leaf");
|
|
}
|
|
} else if (leaf.kind == CV_LeafKind_MFUNC_ID) {
|
|
if (leaf.data.size >= sizeof(CV_LeafMFuncId)) {
|
|
proc32->itype = ((CV_LeafMFuncId *) leaf.data.str)->itype;
|
|
} else {
|
|
Assert(!"TODO: error handle corrupt leaf");
|
|
}
|
|
} else {
|
|
Assert(!"TODO: erorr handle unexpected leaf type");
|
|
}
|
|
} else {
|
|
Assert("TODO: error handle corrupted type index");
|
|
}
|
|
} else {
|
|
// TODO: in some cases destructors don't have a type, need a repro
|
|
}
|
|
}
|
|
|
|
// convert symbol to final type
|
|
switch (symbol->kind) {
|
|
case CV_SymKind_LPROC32_ID: symbol->kind = CV_SymKind_LPROC32; break;
|
|
case CV_SymKind_GPROC32_ID: symbol->kind = CV_SymKind_GPROC32; break;
|
|
case CV_SymKind_LPROC32_DPC_ID: symbol->kind = CV_SymKind_LPROC32_DPC; break;
|
|
case CV_SymKind_LPROCMIPS_ID: symbol->kind = CV_SymKind_LPROCMIPS; break;
|
|
case CV_SymKind_GPROCMIPS_ID: symbol->kind = CV_SymKind_GPROCMIPS; break;
|
|
case CV_SymKind_LPROCIA64_ID: symbol->kind = CV_SymKind_LPROCIA64; break;
|
|
case CV_SymKind_GPROCIA64_ID: symbol->kind = CV_SymKind_GPROCIA64; break;
|
|
case CV_SymKind_PROC_ID_END: symbol->kind = CV_SymKind_END; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal CV_DebugT *
|
|
lnk_import_types(TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input)
|
|
{
|
|
ProfBegin("Import Types");
|
|
|
|
ProfBegin("Hash Leaves");
|
|
LNK_LeafHashes *hashes = push_array(tp_temp->v[0], LNK_LeafHashes, 1);
|
|
{
|
|
Temp scratch = scratch_begin(tp_temp->v, tp_temp->count);
|
|
|
|
// push internal hash arrays
|
|
//
|
|
// TPI and IPI leaves in .debug$T are stored in one array (we don't move them
|
|
// to respective arrays before this point to save on memory move)
|
|
ProfBegin("Push Internal Hash Arrays");
|
|
hashes->internal_hashes = push_array_no_zero(tp_temp->v[0], U128Array *, input->internal_count);
|
|
for (U64 obj_idx = 0; obj_idx < input->internal_count; ++obj_idx) {
|
|
CV_DebugT debug_t = input->merged_debug_t_p_arr[obj_idx];
|
|
|
|
U128Array arr = {0};
|
|
arr.count = debug_t.count;
|
|
arr.v = push_array_no_zero(tp_temp->v[0], U128, debug_t.count);
|
|
// :debug_zero_hash_assert
|
|
#if BUILD_DEBUG
|
|
MemoryZeroTyped(arr.v, arr.count);
|
|
#endif
|
|
|
|
hashes->internal_hashes[obj_idx] = push_array(tp_temp->v[0], U128Array, CV_TypeIndexSource_COUNT);
|
|
for (U64 ti_source = 0; ti_source < CV_TypeIndexSource_COUNT; ++ti_source) {
|
|
hashes->internal_hashes[obj_idx][ti_source] = arr;
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
// push external hash arrays
|
|
ProfBegin("Push External Hash Arrays");
|
|
hashes->external_hashes = push_array_no_zero(tp_temp->v[0], U128Array *, input->type_server_count);
|
|
for (U64 ts_idx = 0; ts_idx < input->type_server_count; ++ts_idx) {
|
|
hashes->external_hashes[ts_idx] = push_array_no_zero(tp_temp->v[0], U128Array, CV_TypeIndexSource_COUNT);
|
|
for (U64 ti_source = 0; ti_source < CV_TypeIndexSource_COUNT; ++ti_source) {
|
|
U64 leaf_count = dim_1u64(input->external_ti_ranges[ts_idx][ti_source]);
|
|
hashes->external_hashes[ts_idx][ti_source].count = leaf_count;
|
|
hashes->external_hashes[ts_idx][ti_source].v = push_array(tp_temp->v[0], U128, leaf_count); // :zero_hash_check
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
LNK_LeafHasherTask task = {0};
|
|
task.input = input;
|
|
task.hashes = hashes;
|
|
task.fixed_arenas = alloc_fixed_size_arena_array(scratch.arena, tp->worker_count, MB(1), MB(1));
|
|
|
|
// hash .debug$P first so we can mix in hashes for precompiled sub leaves when hashing leaves in .debug$T
|
|
ProfBeginDynamic("Hash .debug$P [Count: %llu]", input->internal_count);
|
|
task.debug_t_arr = input->internal_debug_p_arr;
|
|
tp_for_parallel(tp, 0, input->internal_count, lnk_hash_debug_t_task, &task);
|
|
ProfEnd();
|
|
|
|
#if PROFILE_TELEMETRY
|
|
String8 count_string = str8_from_count(scratch.arena, input->internal_count);
|
|
ProfBegin("Hash .debug$T [Count: %.*s]", str8_varg(count_string));
|
|
#endif
|
|
task.debug_t_arr = input->internal_debug_t_arr;
|
|
tp_for_parallel(tp, 0, input->internal_count, lnk_hash_debug_t_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Hash Type Server Leaves [Count: %.*s]", str8_varg(count_string));
|
|
tp_for_parallel(tp, 0, input->external_count, lnk_hash_type_server_leaves_task, &task);
|
|
ProfEnd();
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Leaf Hash Table Init");
|
|
LNK_LeafHashTable leaf_ht_arr[CV_TypeIndexSource_COUNT] = { 0 };
|
|
U64 internal_per_source_count[CV_TypeIndexSource_COUNT] = { 0 };
|
|
U64 external_per_source_count[CV_TypeIndexSource_COUNT] = { 0 };
|
|
{
|
|
// count internal leaves
|
|
lnk_cv_debug_t_count_leaves_per_source(tp, input->internal_count, input->internal_debug_p_arr, internal_per_source_count);
|
|
lnk_cv_debug_t_count_leaves_per_source(tp, input->internal_count, input->internal_debug_t_arr, internal_per_source_count);
|
|
|
|
// count external leaves
|
|
for (U64 ts_idx = 0; ts_idx < input->type_server_count; ++ts_idx) {
|
|
for (U64 ti_source = 0; ti_source < CV_TypeIndexSource_COUNT; ++ti_source) {
|
|
external_per_source_count[ti_source] += dim_1u64(input->external_ti_ranges[ts_idx][ti_source]);
|
|
}
|
|
}
|
|
|
|
// push buckets per source
|
|
for (U64 ti_source = 0; ti_source < CV_TypeIndexSource_COUNT; ++ti_source) {
|
|
U64 bucket_cap = 0;
|
|
bucket_cap += internal_per_source_count[ti_source];
|
|
bucket_cap += external_per_source_count[ti_source];
|
|
bucket_cap = (U64) ((F64) bucket_cap * 1.3);
|
|
|
|
#if PROFILE_TELEMETRY
|
|
tmMessage(0, TMMF_ICON_NOTE, "%.*s Bucket Count: %llu", str8_varg(cv_string_from_type_index_source(ti_source)), bucket_cap);
|
|
#endif
|
|
|
|
leaf_ht_arr[ti_source].cap = bucket_cap;
|
|
leaf_ht_arr[ti_source].bucket_arr = push_array(tp_temp->v[0], LNK_LeafBucket *, bucket_cap);
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
#if PROFILE_TELEMETRY
|
|
String8 obj_count_string = str8_from_count(tp_temp->v[0], input->internal_count);
|
|
String8 tpi_count_string = str8_from_count(tp_temp->v[0], internal_per_source_count[CV_TypeIndexSource_TPI]);
|
|
String8 ipi_count_string = str8_from_count(tp_temp->v[0], internal_per_source_count[CV_TypeIndexSource_IPI]);
|
|
ProfBeginDynamic("Internal Leaf Dedup [Obj Count: %.*s, TPI: %.*s, IPI: %.*s]",
|
|
str8_varg(obj_count_string),
|
|
str8_varg(tpi_count_string),
|
|
str8_varg(ipi_count_string));
|
|
#endif
|
|
{
|
|
|
|
LNK_LeafDedupInternal task;
|
|
task.input = input;
|
|
task.hashes = hashes;
|
|
task.leaf_ht_arr = leaf_ht_arr;
|
|
|
|
ProfBegin("Dedup .debug$P");
|
|
task.debug_t_arr = input->internal_debug_p_arr;
|
|
tp_for_parallel(tp, tp_temp, input->internal_count, lnk_leaf_dedup_internal_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Dedup .debug$T");
|
|
task.debug_t_arr = input->internal_debug_t_arr;
|
|
tp_for_parallel(tp, tp_temp, input->internal_count, lnk_leaf_dedup_internal_task, &task);
|
|
ProfEnd();
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBeginDynamic("External Leaf Import [Type Server Count: %llu, Dependent Obj Count: %llu]", input->type_server_count, input->external_count);
|
|
{
|
|
LNK_LeafDedupExternal task = {0};
|
|
task.input = input;
|
|
task.hashes = hashes;
|
|
task.leaf_ht_arr = leaf_ht_arr;
|
|
|
|
ProfBeginDynamic("Dedup TPI [Leaf Count %llu]", external_per_source_count[CV_TypeIndexSource_TPI]);
|
|
task.dedup_ti_source = CV_TypeIndexSource_TPI;
|
|
tp_for_parallel(tp, tp_temp, input->type_server_count, lnk_leaf_dedup_external_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBeginDynamic("Dedup IPI [Leaf Count %llu]", external_per_source_count[CV_TypeIndexSource_IPI]);
|
|
task.dedup_ti_source = CV_TypeIndexSource_IPI;
|
|
tp_for_parallel(tp, tp_temp, input->type_server_count, lnk_leaf_dedup_external_task, &task);
|
|
ProfEnd();
|
|
}
|
|
ProfEnd();
|
|
|
|
// extract present buckets from the hash tables
|
|
LNK_LeafBucketArray tpi_arr = lnk_present_bucket_array_from_leaf_hash_table(tp, tp_temp->v[0], &leaf_ht_arr[CV_TypeIndexSource_TPI]);
|
|
LNK_LeafBucketArray ipi_arr = lnk_present_bucket_array_from_leaf_hash_table(tp, tp_temp->v[0], &leaf_ht_arr[CV_TypeIndexSource_IPI]);
|
|
|
|
// sort output leaves based on { location index, leaf index } to guarantee determinism
|
|
lnk_leaf_bucket_array_sort(tp, ipi_arr, input->internal_count, input->type_server_count);
|
|
lnk_leaf_bucket_array_sort(tp, tpi_arr, input->internal_count, input->type_server_count);
|
|
|
|
// assign type indices to each bucket
|
|
lnk_assign_type_indices(tp, tpi_arr, CV_MinComplexTypeIndex);
|
|
lnk_assign_type_indices(tp, ipi_arr, CV_MinComplexTypeIndex);
|
|
|
|
// patch indices in symbols, inline sites, and leaves
|
|
lnk_patch_symbols(tp, input, hashes, leaf_ht_arr);
|
|
lnk_patch_inlines(tp, input, hashes, leaf_ht_arr, input->count, input->debug_s_arr);
|
|
lnk_patch_leaves(tp, input, hashes, leaf_ht_arr, tpi_arr);
|
|
lnk_patch_leaves(tp, input, hashes, leaf_ht_arr, ipi_arr);
|
|
|
|
CV_DebugT tpi_types = lnk_unbucket_leaf_array(tp, tp_temp->v[0], input, tpi_arr);
|
|
CV_DebugT ipi_types = lnk_unbucket_leaf_array(tp, tp_temp->v[0], input, ipi_arr);
|
|
|
|
ProfBegin("Post Process CV Symbols");
|
|
{
|
|
LNK_PostProcessCvSymbolsTask task = {0};
|
|
task.ipi_min_type_index = CV_MinComplexTypeIndex;
|
|
task.ipi_types = ipi_types;
|
|
task.symbol_inputs = input->symbol_inputs;
|
|
task.parsed_symbols = input->parsed_symbols;
|
|
tp_for_parallel(tp, 0, input->total_symbol_input_count, lnk_post_process_cv_symbols_task, &task);
|
|
}
|
|
ProfEnd();
|
|
|
|
CV_DebugT *types = push_array(tp_temp->v[0], CV_DebugT, CV_TypeIndexSource_COUNT);
|
|
types[CV_TypeIndexSource_TPI] = tpi_types;
|
|
types[CV_TypeIndexSource_IPI] = ipi_types;
|
|
|
|
ProfEnd();
|
|
return types;
|
|
}
|
|
|
|
internal U64
|
|
lnk_format_u128(U8 *buf, U64 buf_max, U64 length, U128 v)
|
|
{
|
|
U64 size = 0;
|
|
if (length > 0 && buf_max > 0) {
|
|
if (length <= 8) {
|
|
U64 mask = length == 8 ? max_U64 : (1ull << (length*8)) - 1;
|
|
size = raddbg_snprintf((char*)buf, buf_max - 1, "%llX", (long long)(v.u64[0] & mask));
|
|
} else {
|
|
U64 mask1 = length == 16 ? max_U64 : (1ull << ((length-8)*8)) - 1;
|
|
size = raddbg_snprintf((char*)buf, buf_max, "%llX%llX", (long long)(v.u64[1] & mask1), (long long)v.u64[0]);
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_replace_type_names_with_hashes_lenient_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_TypeNameReplacer *task = raw_task;
|
|
Rng1U64 range = task->ranges[task_id];
|
|
CV_DebugT debug_t = task->debug_t;
|
|
U64 hash_length = task->hash_length;
|
|
|
|
B32 make_map = task->make_map;
|
|
Arena *map_arena = 0;
|
|
String8List *map = 0;
|
|
if (make_map) {
|
|
map_arena = task->map_arena->v[task_id];
|
|
map = &task->maps[task_id];
|
|
}
|
|
|
|
U64 hash_max_chars = hash_length*2;
|
|
U8 temp[128];
|
|
|
|
for (U64 leaf_idx = range.min; leaf_idx < range.max; ++leaf_idx) {
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, leaf_idx);
|
|
if (leaf.kind == CV_LeafKind_STRUCTURE || leaf.kind == CV_LeafKind_CLASS) {
|
|
CV_UDTInfo udt_info = cv_get_udt_info(leaf.kind, leaf.data);
|
|
|
|
if ((udt_info.props & CV_TypeProp_HasUniqueName) &&
|
|
udt_info.unique_name.size > hash_max_chars &&
|
|
udt_info.name.size > hash_max_chars) {
|
|
// hash unique name
|
|
U128 name_hash;
|
|
blake3_hasher hasher; blake3_hasher_init(&hasher);
|
|
blake3_hasher_update(&hasher, udt_info.unique_name.str, udt_info.unique_name.size);
|
|
blake3_hasher_finalize(&hasher, (U8*)&name_hash, sizeof(name_hash));
|
|
|
|
// emit hash -> unique name map
|
|
if (make_map) {
|
|
lnk_format_u128(temp, sizeof(temp), hash_length, name_hash);
|
|
str8_list_pushf(map_arena, map, "%s %S\n", temp, str8_varg(udt_info.unique_name));
|
|
}
|
|
|
|
// parse leaf size
|
|
CV_NumericParsed dummy;
|
|
U64 numeric_size = cv_read_numeric(leaf.data, sizeof(CV_LeafStruct), &dummy);
|
|
|
|
String8 lambda_prefix = str8_lit("<lambda_");
|
|
U64 colon_pos = str8_find_needle_reverse(udt_info.name, 0, lambda_prefix, 0);
|
|
B32 is_lambda = colon_pos != 0;
|
|
|
|
if (is_lambda) {
|
|
U64 size = lnk_format_u128(temp, sizeof(temp), hash_length, name_hash);
|
|
Assert(size < udt_info.name.size);
|
|
Assert(size < udt_info.unique_name.size);
|
|
MemoryCopy(udt_info.name.str, temp, size+1);
|
|
MemoryCopy(udt_info.name.str+size+1, temp, size+1);
|
|
udt_info.name.size = size;
|
|
udt_info.unique_name.size = size;
|
|
|
|
// update leaf header
|
|
CV_LeafHeader *header = cv_debug_t_get_leaf_header(debug_t, leaf_idx);
|
|
header->size = sizeof(CV_LeafKind) +
|
|
sizeof(CV_LeafStruct) +
|
|
numeric_size +
|
|
udt_info.name.size + 1 +
|
|
udt_info.unique_name.size + 1;
|
|
} else {
|
|
// replace uniuqe type name with hash
|
|
udt_info.unique_name.str = udt_info.name.str + udt_info.name.size + 1;
|
|
udt_info.unique_name.size = lnk_format_u128(udt_info.unique_name.str, udt_info.unique_name.size, hash_length, name_hash);
|
|
|
|
// update leaf header
|
|
CV_LeafHeader *header = cv_debug_t_get_leaf_header(debug_t, leaf_idx);
|
|
header->size = sizeof(CV_LeafKind) +
|
|
sizeof(CV_LeafStruct) +
|
|
numeric_size +
|
|
udt_info.name.size + 1 +
|
|
udt_info.unique_name.size + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_replace_type_names_with_hashes_full_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_TypeNameReplacer *task = raw_task;
|
|
Rng1U64 range = task->ranges[task_id];
|
|
CV_DebugT debug_t = task->debug_t;
|
|
U64 hash_length = task->hash_length;
|
|
|
|
B32 make_map = task->make_map;
|
|
Arena *map_arena = 0;
|
|
String8List *map = 0;
|
|
if (make_map) {
|
|
map_arena = task->map_arena->v[task_id];
|
|
map = &task->maps[task_id];
|
|
}
|
|
|
|
U64 hash_max_chars = hash_length*2;
|
|
U8 temp[128];
|
|
|
|
for (U64 leaf_idx = range.min; leaf_idx < range.max; ++leaf_idx) {
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, leaf_idx);
|
|
if (leaf.kind == CV_LeafKind_STRUCTURE || leaf.kind == CV_LeafKind_CLASS) {
|
|
CV_UDTInfo udt_info = cv_get_udt_info(leaf.kind, leaf.data);
|
|
|
|
if (udt_info.name.size > hash_max_chars) {
|
|
// pick name to hash
|
|
String8 name;
|
|
if (udt_info.props & CV_TypeProp_HasUniqueName) {
|
|
name = udt_info.unique_name;
|
|
} else {
|
|
name = udt_info.name;
|
|
}
|
|
|
|
// hash name
|
|
U128 name_hash;
|
|
blake3_hasher hasher; blake3_hasher_init(&hasher);
|
|
blake3_hasher_update(&hasher, udt_info.name.str, udt_info.name.size);
|
|
blake3_hasher_finalize(&hasher, (U8*)&name_hash, sizeof(name_hash));
|
|
|
|
// emit hash -> name map
|
|
if (make_map) {
|
|
lnk_format_u128(temp, sizeof(temp), hash_length, name_hash);
|
|
str8_list_pushf(map_arena, map, "%s %.*s\n", temp, str8_varg(name));
|
|
}
|
|
|
|
// replace name with hash
|
|
udt_info.name.size = lnk_format_u128(udt_info.name.str, udt_info.name.size, hash_length, name_hash);
|
|
|
|
// parse struct size
|
|
CV_NumericParsed dummy;
|
|
U64 numeric_size = cv_read_numeric(leaf.data, sizeof(CV_LeafStruct), &dummy);
|
|
|
|
// update header
|
|
CV_LeafHeader *header = cv_debug_t_get_leaf_header(debug_t, leaf_idx);
|
|
header->size = sizeof(CV_LeafKind) + sizeof(CV_LeafStruct) + numeric_size + udt_info.name.size + 1;
|
|
|
|
// discard unique name
|
|
CV_LeafStruct *lf = (CV_LeafStruct *)(header + 1);
|
|
lf->props &= ~CV_TypeProp_HasUniqueName;
|
|
}
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal void
|
|
lnk_replace_type_names_with_hashes(TP_Context *tp, TP_Arena *arena, CV_DebugT debug_t, LNK_TypeNameHashMode mode, U64 hash_length, String8 map_name)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(arena->v, arena->count);
|
|
|
|
// init task context
|
|
LNK_TypeNameReplacer task = {0};
|
|
task.debug_t = debug_t;
|
|
task.ranges = tp_divide_work(scratch.arena, debug_t.count, tp->worker_count);
|
|
task.hash_length = Clamp(1, hash_length, 16);
|
|
|
|
if (map_name.size > 0) {
|
|
task.make_map = 1;
|
|
task.map_arena = tp_arena_alloc(tp);
|
|
task.maps = push_array(scratch.arena, String8List, tp->worker_count);
|
|
}
|
|
|
|
// pick task function
|
|
TP_TaskFunc *func = 0;
|
|
switch (mode) {
|
|
case LNK_TypeNameHashMode_Null:
|
|
case LNK_TypeNameHashMode_None:
|
|
break;
|
|
|
|
case LNK_TypeNameHashMode_Lenient: func = lnk_replace_type_names_with_hashes_lenient_task; break;
|
|
case LNK_TypeNameHashMode_Full: func = lnk_replace_type_names_with_hashes_full_task; break;
|
|
}
|
|
|
|
// run task
|
|
tp_for_parallel(tp, arena, tp->worker_count, func, &task);
|
|
|
|
// optionally write out map file
|
|
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, str8_zero(), map);
|
|
tp_arena_release(&task.map_arena);
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_filter_out_gsi_symbols_task)
|
|
{
|
|
U64 obj_idx = task_id;
|
|
LNK_ProcessSymDataTaskData *task = raw_task;
|
|
CV_SymbolList *gsi_list = &task->gsi_list_arr[obj_idx];
|
|
CV_SymbolListArray parsed_symbols = task->parsed_symbols[obj_idx];
|
|
|
|
CV_SymbolList global_list = {0};
|
|
CV_SymbolList typedef_list = {0};
|
|
for (U64 i = 0; i < parsed_symbols.count; ++i) {
|
|
CV_SymbolList *list = &parsed_symbols.v[i];
|
|
U64 depth = 0;
|
|
for (CV_SymbolNode *curr = list->first, *next; curr != 0; curr = next) {
|
|
next = curr->next;
|
|
|
|
if (cv_is_global_symbol(curr->data.kind)) {
|
|
cv_symbol_list_remove_node(list, curr);
|
|
cv_symbol_list_push_node(&global_list, curr);
|
|
} else if (cv_is_typedef(curr->data.kind)) {
|
|
if (depth == 0) {
|
|
cv_symbol_list_remove_node(list, curr);
|
|
cv_symbol_list_push_node(&typedef_list, curr);
|
|
}
|
|
}
|
|
// Undocumented symbol that appears only in objs.
|
|
// MSVC removes these symbols from output.
|
|
//
|
|
// LLD-link replaces symbol with S_SKIP:
|
|
// https://github.com/llvm/llvm-project/blob/main/lld/COFF/PDB.cpp#L575
|
|
else if (curr->data.kind == 0x1176) {
|
|
cv_symbol_list_remove_node(list, curr);
|
|
}
|
|
|
|
if (cv_is_scope_symbol(curr->data.kind)) {
|
|
++depth;
|
|
} else if (cv_is_end_symbol(curr->data.kind)) {
|
|
Assert(depth > 0);
|
|
--depth;
|
|
}
|
|
}
|
|
}
|
|
|
|
// collect GSI symbols
|
|
Assert(gsi_list->count == 0);
|
|
cv_symbol_list_concat_in_place(gsi_list, &global_list);
|
|
cv_symbol_list_concat_in_place(gsi_list, &typedef_list);
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_make_proc_refs_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
U64 obj_idx = task_id;
|
|
LNK_ProcessSymDataTaskData *task = raw_task;
|
|
PDB_DbiModule *mod = task->mod_arr[obj_idx];
|
|
CV_SymbolList *gsi_list = &task->gsi_list_arr[obj_idx];
|
|
CV_SymbolListArray parsed_symbols = task->parsed_symbols[obj_idx];
|
|
|
|
for (U64 i = 0; i < parsed_symbols.count; ++i) {
|
|
CV_SymbolList list = parsed_symbols.v[i];
|
|
CV_SymbolList proc_refs = cv_make_proc_refs(arena, mod->imod, list);
|
|
cv_symbol_list_concat_in_place(gsi_list, &proc_refs);
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_process_sym_data_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
U64 obj_idx = task_id;
|
|
LNK_ProcessSymDataTaskData *task = raw_task;
|
|
CV_SymbolListArray parsed_symbols = task->parsed_symbols[obj_idx];
|
|
|
|
static CV_Signature MODULE_SYMBOL_SIGNATURE = CV_Signature_C13;
|
|
|
|
ProfBegin("Compute Buffer Size");
|
|
U64 buffer_size = sizeof(MODULE_SYMBOL_SIGNATURE);
|
|
for (U64 i = 0; i < parsed_symbols.count; ++i) {
|
|
CV_SymbolList list = parsed_symbols.v[i];
|
|
U64 data_size = cv_patch_symbol_tree_offsets(list, buffer_size, PDB_SYMBOL_ALIGN);
|
|
buffer_size += data_size;
|
|
}
|
|
ProfEnd();
|
|
|
|
// alloc buffer
|
|
U8 *buffer = push_array_no_zero(arena, U8, buffer_size);
|
|
U64 buffer_cursor = 0;
|
|
|
|
// MS Symbol and Type Information p.4:
|
|
// "The first four bytes of the $$SYMBOLS segment is used as a signature to specify the version of
|
|
// the Symbol and Type OMF contained in the $$SYMBOLS segment."
|
|
CV_Signature *sig_ptr = (CV_Signature *) (buffer + buffer_cursor);
|
|
*sig_ptr = MODULE_SYMBOL_SIGNATURE;
|
|
buffer_cursor += sizeof(*sig_ptr);
|
|
|
|
ProfBegin("Serialize Symbols");
|
|
for (U64 i = 0; i < parsed_symbols.count; ++i) {
|
|
CV_SymbolList list = parsed_symbols.v[i];
|
|
for (CV_SymbolNode *symbol_n = list.first; symbol_n != 0; symbol_n = symbol_n->next) {
|
|
symbol_n->data.offset = buffer_cursor;
|
|
buffer_cursor += cv_serialize_symbol_to_buffer(buffer, buffer_cursor, buffer_size, &symbol_n->data, PDB_SYMBOL_ALIGN);
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
// output
|
|
Assert(task->symbol_data_arr[obj_idx].total_size == 0);
|
|
str8_list_push(arena, &task->symbol_data_arr[obj_idx], str8(buffer, buffer_size));
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal LNK_ProcessedCodeViewC11Data
|
|
lnk_process_c11_data(TP_Context *tp, TP_Arena *arena, U64 obj_count, CV_DebugS *debug_s_arr, U64 string_data_base_offset, CV_StringHashTable string_ht, MSF_Context *msf, PDB_DbiModule **mod_arr)
|
|
{
|
|
// TODO: handle c11 data
|
|
String8List *data_list_arr = push_array(arena->v[0], String8List, obj_count);
|
|
LNK_ProcessedCodeViewC11Data result;
|
|
result.data_list_arr = data_list_arr;
|
|
return result;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_process_c13_data_task)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena,1);
|
|
|
|
U64 obj_idx = task_id;
|
|
LNK_ProcessC13DataTask *task = raw_task;
|
|
CV_DebugS debug_s = task->debug_s_arr[obj_idx];
|
|
|
|
// parse checksum data
|
|
String8List checksum_data = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms);
|
|
CV_ChecksumList checksum_list = cv_c13_parse_checksum_data_list(scratch.arena, checksum_data);
|
|
|
|
// get strings sub-section
|
|
String8 string_data = cv_string_table_from_debug_s(debug_s);
|
|
|
|
// collect source file names from checksum headers
|
|
String8List source_file_names_list = cv_c13_collect_source_file_names(arena, checksum_list, string_data);
|
|
|
|
// relocate checksum data
|
|
cv_c13_patch_string_offsets_in_checksum_list(checksum_list, string_data, task->string_data_base_offset, task->string_ht);
|
|
|
|
// get module sub-sections
|
|
PDB_DbiModule *mod = task->dbi_mod_arr[obj_idx];
|
|
String8 mod_c13_data = dbi_module_read_c13_data(scratch.arena, task->msf, mod);
|
|
CV_DebugS mod_debug_s = cv_parse_debug_s_c13(scratch.arena, mod_c13_data);
|
|
|
|
// relocate line and frame data
|
|
String8List *mod_checksum_data = cv_sub_section_ptr_from_debug_s(&mod_debug_s, CV_C13SubSectionKind_FileChksms);
|
|
U64 checksum_base = mod_checksum_data->total_size;
|
|
B32 is_checksum_patch_needed = checksum_base > 0;
|
|
if (is_checksum_patch_needed) {
|
|
String8List line_data = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_Lines);
|
|
String8List frame_data = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FrameData);
|
|
cv_c13_patch_checksum_offsets_in_line_data_list(line_data, checksum_base);
|
|
cv_c13_patch_checksum_offsets_in_frame_data_list(frame_data, checksum_base);
|
|
}
|
|
|
|
// push obj c13 data to module
|
|
cv_debug_s_concat_in_place(&mod_debug_s, &debug_s);
|
|
|
|
// serialize c13 data
|
|
B32 include_sig = 0;
|
|
String8List c13_data = cv_data_c13_from_debug_s(arena, &mod_debug_s, include_sig);
|
|
|
|
// store for later pass
|
|
task->c13_data_arr[obj_idx] = c13_data;
|
|
task->source_file_names_list_arr[obj_idx] = source_file_names_list;
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal LNK_ProcessedCodeViewC13Data
|
|
lnk_process_c13_data(TP_Context *tp, TP_Arena *arena, U64 obj_count, CV_DebugS *debug_s_arr, U64 string_data_base_offset, CV_StringHashTable string_ht, MSF_Context *msf, PDB_DbiModule **mod_arr)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_ProcessC13DataTask task = {0};
|
|
task.debug_s_arr = debug_s_arr;
|
|
task.msf = msf;
|
|
task.dbi_mod_arr = mod_arr;
|
|
task.c13_data_arr = push_array_no_zero(arena->v[0], String8List, obj_count);
|
|
task.source_file_names_list_arr = push_array_no_zero(arena->v[0], String8List, obj_count);
|
|
task.string_data_base_offset = string_data_base_offset;
|
|
task.string_ht = string_ht;
|
|
tp_for_parallel(tp, arena, obj_count, lnk_process_c13_data_task, &task);
|
|
|
|
// fill out result
|
|
LNK_ProcessedCodeViewC13Data result = {0};
|
|
result.data_list_arr = task.c13_data_arr;
|
|
result.source_file_names_list_arr = task.source_file_names_list_arr;
|
|
|
|
ProfEnd();
|
|
return result;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_write_module_data_task)
|
|
{
|
|
U64 obj_idx = task_id;
|
|
LNK_WriteModuleDataTask *task = raw_task;
|
|
|
|
PDB_DbiModule *mod = task->mod_arr[obj_idx];
|
|
String8List sym_data = task->symbol_data_arr[obj_idx];
|
|
String8List c11_data = task->c11_data_list_arr[obj_idx];
|
|
String8List c13_data = task->c13_data_list_arr[obj_idx];
|
|
String8List globrefs = task->globrefs_arr[obj_idx];
|
|
|
|
U32 sym_data_size32 = safe_cast_u32(sym_data.total_size);
|
|
U32 c11_data_size32 = safe_cast_u32(c11_data.total_size);
|
|
U32 c13_data_size32 = safe_cast_u32(c13_data.total_size);
|
|
U32 globrefs_size32 = safe_cast_u32(globrefs.total_size);
|
|
|
|
// layout module data
|
|
String8List module_data = {0};
|
|
str8_list_concat_in_place(&module_data, &sym_data);
|
|
str8_list_concat_in_place(&module_data, &c11_data);
|
|
str8_list_concat_in_place(&module_data, &c13_data);
|
|
str8_list_concat_in_place(&module_data, &globrefs);
|
|
|
|
// make stream has enough memory so it doens't trigger memory allocations in MSF
|
|
// during multi-thread write
|
|
MSF_UInt stream_pos = msf_stream_get_pos(task->msf, mod->sn);
|
|
if (stream_pos != 0) {
|
|
Assert(!"stream must be at start position");
|
|
}
|
|
MSF_UInt stream_cap = msf_stream_get_cap(task->msf, mod->sn);
|
|
if (stream_cap < module_data.total_size) {
|
|
Assert(!"not enough bytes in destination stream to copy module data");
|
|
}
|
|
|
|
// write data
|
|
B32 is_write_ok = msf_stream_write_list(task->msf, mod->sn, module_data);
|
|
|
|
// update module data sizes
|
|
if (is_write_ok) {
|
|
mod->sym_data_size = sym_data_size32;
|
|
mod->c11_data_size = c11_data_size32;
|
|
mod->c13_data_size = c13_data_size32;
|
|
mod->globrefs_size = globrefs_size32;
|
|
} else {
|
|
// TODO: error handle
|
|
}
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_cv_symbol_ptr_array_hasher)
|
|
{
|
|
LNK_CvSymbolPtrArrayHasher *task = raw_task;
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
for (U64 symbol_idx = range.min; symbol_idx < range.max; ++symbol_idx) {
|
|
task->hash_arr[symbol_idx] = XXH3_64bits(task->arr[symbol_idx]->data.data.str, task->arr[symbol_idx]->data.data.size);
|
|
}
|
|
}
|
|
|
|
internal U64 *
|
|
lnk_hash_cv_symbol_ptr_arr(TP_Context *tp, Arena *arena, CV_SymbolPtrArray arr)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
LNK_CvSymbolPtrArrayHasher task = {0};
|
|
task.hash_arr = push_array_no_zero(arena, U64, arr.count);
|
|
task.arr = arr.v;
|
|
task.range_arr = tp_divide_work(scratch.arena, arr.count, tp->worker_count);
|
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_cv_symbol_ptr_array_hasher, &task);
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return task.hash_arr;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_push_dbi_sec_contrib_task)
|
|
{
|
|
// TODO: use chunked lists for SC
|
|
// TODO: put back unused sc nodes
|
|
// TODO: compute CRC for relocations
|
|
|
|
U64 obj_idx = task_id;
|
|
LNK_PushDbiSecContribTaskData *task = raw_task;
|
|
PDB_DbiModule *mod = task->mod_arr[obj_idx];
|
|
LNK_Obj *obj = &task->obj_arr[obj_idx];
|
|
|
|
COFF_SectionHeader *obj_section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str;
|
|
PDB_DbiSectionContribNode *sc_arr = push_array_no_zero(arena, PDB_DbiSectionContribNode, obj->header.section_count_no_null);
|
|
U64 sc_count = 0;
|
|
|
|
for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) {
|
|
COFF_SectionHeader *obj_sect_header = &obj_section_table[sect_idx];
|
|
|
|
if (obj_sect_header->flags & COFF_SectionFlag_LnkRemove) {
|
|
continue;
|
|
}
|
|
if (lnk_is_coff_section_debug(obj, sect_idx)) {
|
|
continue;
|
|
}
|
|
|
|
U64 sect_number;
|
|
String8 sect_data;
|
|
U32 sect_off;
|
|
U32 data_crc;
|
|
if (obj_sect_header->flags & COFF_SectionFlag_CntUninitializedData) {
|
|
if (obj_sect_header->vsize == 0) {
|
|
continue;
|
|
}
|
|
sect_number = rng_1u64_array_bsearch(task->image_section_virt_ranges, obj_sect_header->voff);
|
|
Assert(sect_number < task->image_section_virt_ranges.count);
|
|
sect_data = str8_zero();
|
|
sect_off = obj_sect_header->voff - task->image_section_virt_ranges.v[sect_number].min;
|
|
data_crc = 0;
|
|
} else {
|
|
if (obj_sect_header->fsize == 0) {
|
|
continue;
|
|
}
|
|
sect_number = rng_1u64_array_bsearch(task->image_section_file_ranges, obj_sect_header->foff);
|
|
Assert(sect_number < task->image_section_file_ranges.count);
|
|
sect_data = str8_substr(task->image_data, rng_1u64(obj_sect_header->foff, obj_sect_header->foff + obj_sect_header->fsize));
|
|
sect_off = obj_sect_header->foff - task->image_section_file_ranges.v[sect_number].min;
|
|
data_crc = update_crc32(0, sect_data.str, sect_data.size);
|
|
}
|
|
|
|
// fill out SC
|
|
PDB_DbiSectionContribNode *sc = sc_arr + sc_count++;
|
|
sc->data.base.sec = (U16)sect_number;
|
|
sc->data.base.pad0 = 0;
|
|
sc->data.base.sec_off = sect_off;
|
|
sc->data.base.size = obj_sect_header->vsize;
|
|
sc->data.base.flags = obj_sect_header->flags;
|
|
sc->data.base.mod = mod->imod;
|
|
sc->data.base.pad1 = 0;
|
|
sc->data.data_crc = 0;
|
|
sc->data.reloc_crc = 0;
|
|
|
|
dbi_sec_contrib_list_push_node(&task->sc_list[obj_idx], sc);
|
|
}
|
|
|
|
// Mod1::fUpdateSecContrib
|
|
if (sc_count > 0) {
|
|
for (U64 sc_idx = 0; sc_idx < sc_count; ++sc_idx) {
|
|
if (sc_arr[sc_idx].data.base.flags & COFF_SectionFlag_CntCode) {
|
|
mod->first_sc = sc_arr[sc_idx].data;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_build_pdb_public_symbols_defined_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_BuildPublicSymbolsTask *task = raw_task;
|
|
CV_SymbolList *pub_list = &task->pub_list_arr[task_id];
|
|
LNK_SymbolHashTrieChunkList chunk_list = task->chunk_lists[task_id];
|
|
|
|
for (LNK_SymbolHashTrieChunk *chunk = chunk_list.first; chunk != 0; chunk = chunk->next) {
|
|
CV_SymbolNode *nodes = push_array_no_zero(arena, CV_SymbolNode, chunk->count);
|
|
|
|
for (U64 i = 0, node_idx = 0; i < chunk->count; ++i) {
|
|
LNK_Symbol *symbol = chunk->v[i].symbol;
|
|
|
|
COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
|
|
COFF_SymbolValueInterpType interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class);
|
|
if (interp == COFF_SymbolValueInterp_Regular) {
|
|
CV_Pub32Flags flags = 0;
|
|
if (COFF_SymbolType_IsFunc(parsed_symbol.type)) {
|
|
flags |= CV_Pub32Flag_Function;
|
|
}
|
|
|
|
ISectOff sc = lnk_sc_from_symbol(symbol);
|
|
U16 symbol_isect16 = safe_cast_u16(sc.isect);
|
|
U32 symbol_off32 = safe_cast_u32(sc.off);
|
|
|
|
nodes[node_idx].data = cv_make_pub32(arena, flags, symbol_off32, symbol_isect16, symbol->name);
|
|
cv_symbol_list_push_node(pub_list, &nodes[node_idx]);
|
|
|
|
node_idx += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_gsi_hash_cv_list_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_BuildPublicSymbolsTask *task = raw_task;
|
|
Rng1U64 range = task->symbol_ranges[task_id];
|
|
|
|
for (U64 symbol_idx = range.min; symbol_idx < range.max; ++symbol_idx) {
|
|
CV_Symbol *symbol = &task->symbols.v[symbol_idx]->data;
|
|
String8 name = cv_name_from_symbol(symbol->kind, symbol->data);
|
|
task->hashes[symbol_idx] = gsi_hash(task->gsi, name);
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal void
|
|
lnk_build_pdb_public_symbols(TP_Context *tp,
|
|
TP_Arena *arena,
|
|
LNK_SymbolTable *symtab,
|
|
PDB_PsiContext *psi)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(arena->v, arena->count);
|
|
|
|
ProfBegin("Defined");
|
|
LNK_BuildPublicSymbolsTask task = {0};
|
|
task.pub_list_arr = push_array(scratch.arena, CV_SymbolList, tp->worker_count);
|
|
task.chunk_lists = symtab->chunk_lists[LNK_SymbolScope_Defined];
|
|
tp_for_parallel(tp, arena, tp->worker_count, lnk_build_pdb_public_symbols_defined_task, &task);
|
|
ProfEnd();
|
|
|
|
CV_SymbolPtrArray symbols = cv_symbol_ptr_array_from_list(scratch.arena, tp, tp->worker_count, task.pub_list_arr);
|
|
|
|
ProfBegin("GSI Push");
|
|
gsi_push_many_arr(tp, psi->gsi, symbols.count, symbols.v);
|
|
ProfEnd();
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal String8List
|
|
lnk_build_pdb(TP_Context *tp,
|
|
TP_Arena *tp_arena,
|
|
String8 image_data,
|
|
LNK_Config *config,
|
|
LNK_SymbolTable *symtab,
|
|
U64 obj_count,
|
|
LNK_Obj *obj_arr,
|
|
CV_DebugS *debug_s_arr,
|
|
U64 total_symbol_input_count,
|
|
LNK_CodeViewSymbolsInput *symbol_inputs,
|
|
CV_SymbolListArray *parsed_symbols,
|
|
CV_DebugT types[CV_TypeIndexSource_COUNT])
|
|
{
|
|
ProfBegin("PDB");
|
|
Temp scratch = scratch_begin(tp_arena->v, tp_arena->count);
|
|
|
|
PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data);
|
|
COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range);
|
|
U64 image_section_table_count = pe.section_count+1;
|
|
|
|
ProfBegin("Setup PDB Context");
|
|
PDB_Context *pdb = pdb_alloc(config->pdb_page_size, config->machine, config->time_stamp, config->age, config->guid);
|
|
ProfEnd();
|
|
|
|
// move patched type data
|
|
//
|
|
// leaf data is stored in g_file_arena which has linker's life-time
|
|
// and this way we skip redundant leaf copy to the type server to make things faster
|
|
pdb_type_server_push_parallel(tp, pdb->type_servers[CV_TypeIndexSource_IPI], types[CV_TypeIndexSource_IPI]);
|
|
pdb_type_server_push_parallel(tp, pdb->type_servers[CV_TypeIndexSource_TPI], types[CV_TypeIndexSource_TPI]);
|
|
|
|
ProfBegin("Collect Symbols for GSI");
|
|
CV_SymbolList *gsi_list_arr = push_array(scratch.arena, CV_SymbolList, obj_count);
|
|
{
|
|
LNK_ProcessSymDataTaskData task = {0};
|
|
task.gsi_list_arr = gsi_list_arr;
|
|
task.parsed_symbols = parsed_symbols;
|
|
tp_for_parallel(tp, 0, obj_count, lnk_filter_out_gsi_symbols_task, &task);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Reserve DBI Modules");
|
|
PDB_DbiModule **mod_arr = push_array(tp_arena->v[0], PDB_DbiModule *, obj_count);
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
LNK_Obj *obj = obj_arr + obj_idx;
|
|
mod_arr[obj_idx] = dbi_push_module(pdb->dbi, obj->path, obj->lib_path);
|
|
|
|
// we don't support symbol append
|
|
Assert(mod_arr[obj_idx]->sn == MSF_INVALID_STREAM_NUMBER);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Build String Table");
|
|
CV_StringHashTable string_ht = cv_dedup_string_tables(tp_arena, tp, obj_count, debug_s_arr);
|
|
cv_string_hash_table_assign_buffer_offsets(tp, string_ht);
|
|
U64 string_data_base_offset = pdb->info->strtab.size;
|
|
pdb_strtab_add_cv_string_hash_table(&pdb->info->strtab, string_ht);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Build DBI Modules");
|
|
{
|
|
TP_Temp temp = tp_temp_begin(tp_arena);
|
|
{
|
|
ProfBegin("Reloc Module Data");
|
|
|
|
ProfBegin("Serialize Symbols");
|
|
String8List *serialized_symbol_data = push_array(scratch.arena, String8List, obj_count);
|
|
{
|
|
LNK_ProcessSymDataTaskData task = {0};
|
|
task.symbol_inputs = symbol_inputs;
|
|
task.parsed_symbols = parsed_symbols;
|
|
task.mod_arr = mod_arr;
|
|
task.symbol_data_arr = serialized_symbol_data;
|
|
tp_for_parallel(tp, tp_arena, obj_count, lnk_process_sym_data_task, &task);
|
|
}
|
|
ProfEnd();
|
|
|
|
LNK_ProcessedCodeViewC11Data processed_c11 = lnk_process_c11_data(tp, tp_arena, obj_count, debug_s_arr, string_data_base_offset, string_ht, pdb->msf, mod_arr);
|
|
LNK_ProcessedCodeViewC13Data processed_c13 = lnk_process_c13_data(tp, tp_arena, obj_count, debug_s_arr, string_data_base_offset, string_ht, pdb->msf, mod_arr);
|
|
|
|
ProfEnd();
|
|
|
|
// TODO: actually collect offsets and pass them here
|
|
ProfBegin("Build Empty Global Reference Array");
|
|
String8List *globrefs_arr = push_array(tp_arena->v[0], String8List, obj_count);
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
String8List *globrefs = &globrefs_arr[obj_idx];
|
|
str8_serial_begin(tp_arena->v[0], globrefs);
|
|
Assert(globrefs->total_size == 0);
|
|
str8_serial_push_u32(tp_arena->v[0], globrefs, globrefs->total_size);
|
|
}
|
|
ProfEnd();
|
|
|
|
// reserve memory for module streams
|
|
ProfBegin("Reserve Modules Memory");
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
// compute number of bytes needed for module data
|
|
U64 mod_size = 0;
|
|
mod_size += serialized_symbol_data[obj_idx].total_size;
|
|
mod_size += processed_c11.data_list_arr[obj_idx].total_size;
|
|
mod_size += processed_c13.data_list_arr[obj_idx].total_size;
|
|
mod_size += globrefs_arr[obj_idx].total_size;
|
|
|
|
U32 mod_size32 = safe_cast_u32(mod_size);
|
|
|
|
// allocate stream for module
|
|
PDB_DbiModule *mod = mod_arr[obj_idx];
|
|
mod->sn = msf_stream_alloc_ex(pdb->msf, mod_size32);
|
|
}
|
|
ProfEnd();
|
|
|
|
// copy data to module streams
|
|
ProfBegin("Write Modules Data");
|
|
LNK_WriteModuleDataTask write_module_data_task_data;
|
|
write_module_data_task_data.msf = pdb->msf;
|
|
write_module_data_task_data.mod_arr = mod_arr;
|
|
write_module_data_task_data.symbol_data_arr = serialized_symbol_data;
|
|
write_module_data_task_data.c11_data_list_arr = processed_c11.data_list_arr;
|
|
write_module_data_task_data.c13_data_list_arr = processed_c13.data_list_arr;
|
|
write_module_data_task_data.globrefs_arr = globrefs_arr;
|
|
tp_for_parallel(tp, 0, obj_count, lnk_write_module_data_task, &write_module_data_task_data);
|
|
ProfEnd();
|
|
|
|
// push source files per module info
|
|
ProfBegin("Build Source Files List");
|
|
for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) {
|
|
PDB_DbiModule *mod = mod_arr[obj_idx];
|
|
String8List source_file_list_scratch = processed_c13.source_file_names_list_arr[obj_idx];
|
|
String8List source_file_list = str8_list_copy(pdb->dbi->arena, &source_file_list_scratch);
|
|
str8_list_concat_in_place(&mod->source_file_list, &source_file_list);
|
|
}
|
|
ProfEnd();
|
|
}
|
|
tp_temp_end(temp);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Make Proc Refs");
|
|
{
|
|
LNK_ProcessSymDataTaskData task = {0};
|
|
task.mod_arr = mod_arr;
|
|
task.gsi_list_arr = gsi_list_arr;
|
|
task.parsed_symbols = parsed_symbols;
|
|
tp_for_parallel(tp, tp_arena, obj_count, lnk_make_proc_refs_task, &task);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Push Global Symbols");
|
|
{
|
|
CV_SymbolPtrArray global_symbols = cv_symbol_ptr_array_from_list(scratch.arena, tp, obj_count, gsi_list_arr);
|
|
cv_dedup_symbol_ptr_array(tp, &global_symbols);
|
|
gsi_push_many_arr(tp, pdb->gsi, global_symbols.count, global_symbols.v);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Build DBI Section Headers");
|
|
{
|
|
for (U64 sect_idx = 1; sect_idx < image_section_table_count; sect_idx += 1) {
|
|
dbi_push_section(pdb->dbi, image_section_table[sect_idx]);
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Build Section Contrib Map");
|
|
{
|
|
|
|
Rng1U64Array image_section_file_ranges = {0};
|
|
image_section_file_ranges.count = 0;
|
|
image_section_file_ranges.v = push_array(scratch.arena, Rng1U64, image_section_table_count);
|
|
Rng1U64Array image_section_virt_ranges = {0};
|
|
image_section_virt_ranges.count = image_section_table_count;
|
|
image_section_virt_ranges.v = push_array(scratch.arena, Rng1U64, image_section_table_count);
|
|
for (U64 i = 0; i < image_section_table_count; i += 1) {
|
|
COFF_SectionHeader *sect_header = image_section_table[i];
|
|
if (~sect_header->flags & COFF_SectionFlag_CntUninitializedData) {
|
|
image_section_file_ranges.v[image_section_file_ranges.count++] = rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize);
|
|
}
|
|
image_section_virt_ranges.v[i] = rng_1u64(sect_header->voff, sect_header->voff + sect_header->vsize);
|
|
}
|
|
|
|
LNK_PushDbiSecContribTaskData task = {0};
|
|
task.obj_arr = obj_arr;
|
|
task.mod_arr = mod_arr;
|
|
task.sc_list = push_array(scratch.arena, PDB_DbiSectionContribList, obj_count);
|
|
task.image_data = image_data;
|
|
task.image_section_file_ranges = image_section_file_ranges;
|
|
task.image_section_virt_ranges = image_section_virt_ranges;
|
|
tp_for_parallel(tp, tp_arena, obj_count, lnk_push_dbi_sec_contrib_task, &task);
|
|
|
|
dbi_sec_list_concat_arr(&pdb->dbi->sec_contrib_list, 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);
|
|
|
|
for (U64 i = 0; i < natvis_file_data_arr.count; ++i) {
|
|
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;
|
|
}
|
|
|
|
// 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(pdb->info, 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();
|
|
|
|
lnk_build_pdb_public_symbols(tp, tp_arena, symtab, pdb->psi);
|
|
|
|
pdb_build(tp, tp_arena, pdb, string_ht);
|
|
|
|
MSF_Error msf_err = msf_build(pdb->msf);
|
|
if (msf_err != MSF_Error_OK) {
|
|
lnk_error(LNK_Error_UnableToSerializeMsf, "unable to serialize MSF: %s", msf_error_to_string(msf_err));
|
|
}
|
|
|
|
ProfBegin("Get Page Nodes");
|
|
String8List page_data_list = msf_get_page_data_nodes(tp_arena->v[0], pdb->msf);
|
|
ProfEnd();
|
|
|
|
|
|
// NOTE: linker is about to exit so we can skip memory release
|
|
// and let windows free memory since it does this faster
|
|
#if 0
|
|
ProfBegin("Context Release");
|
|
pdb_release(&pdb);
|
|
ProfEnd();
|
|
#endif
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return page_data_list;
|
|
}
|
|
|
|
internal U64
|
|
lnk_udt_name_hash_table_hash(String8 string)
|
|
{
|
|
return XXH3_64bits(string.str, string.size);
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_build_udt_name_hash_table_task)
|
|
{
|
|
LNK_BuildUDTNameHashTableTask *task = raw_task;
|
|
|
|
LNK_UDTNameBucket *new_bucket = 0;
|
|
|
|
for (U64 leaf_idx = task->ranges[task_id].min; leaf_idx < task->ranges[task_id].max; ++leaf_idx) {
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(task->debug_t, leaf_idx);
|
|
if (cv_is_udt(leaf.kind)) {
|
|
CV_UDTInfo udt_info = cv_get_udt_info(leaf.kind, leaf.data);
|
|
if (~udt_info.props & CV_TypeProp_FwdRef) {
|
|
if (!cv_is_udt_name_anon(udt_info.name)) {
|
|
String8 name = cv_name_from_udt_info(udt_info);
|
|
U64 hash = lnk_udt_name_hash_table_hash(name);
|
|
U64 best_idx = hash % task->buckets_cap;
|
|
U64 bucket_idx = best_idx;
|
|
|
|
if (new_bucket == 0) {
|
|
new_bucket = push_array(arena, LNK_UDTNameBucket, 1);
|
|
}
|
|
new_bucket->name = name;
|
|
new_bucket->leaf_idx = leaf_idx;
|
|
|
|
B32 is_inserted_or_updated = 0;
|
|
do {
|
|
retry:;
|
|
LNK_UDTNameBucket *curr_bucket = task->buckets[bucket_idx];
|
|
|
|
if (curr_bucket == 0) {
|
|
LNK_UDTNameBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&task->buckets[bucket_idx], new_bucket, curr_bucket);
|
|
|
|
if (compare_bucket == curr_bucket) {
|
|
// success, bucket was inserted
|
|
is_inserted_or_updated = 1;
|
|
break;
|
|
}
|
|
|
|
// another thread took the bucket...
|
|
goto retry;
|
|
} else if (str8_match(curr_bucket->name, name, 0)) {
|
|
// there is more than one UDT with identical name, pick most recent and ignore others
|
|
|
|
if (leaf_idx < curr_bucket->leaf_idx) {
|
|
LNK_UDTNameBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&task->buckets[bucket_idx], new_bucket, curr_bucket);
|
|
if (compare_bucket == curr_bucket) {
|
|
is_inserted_or_updated = 1;
|
|
break;
|
|
}
|
|
} else {
|
|
// don't need to update, more recent leaf is in the bucket
|
|
break;
|
|
}
|
|
|
|
// another thread took the bucket...
|
|
goto retry;
|
|
}
|
|
|
|
// advance
|
|
bucket_idx = (bucket_idx + 1) % task->buckets_cap;
|
|
} while (bucket_idx != best_idx);
|
|
|
|
if (is_inserted_or_updated) {
|
|
new_bucket = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
internal LNK_UDTNameBucket **
|
|
lnk_udt_name_hash_table_from_debug_t(TP_Context *tp,
|
|
TP_Arena *arena,
|
|
CV_DebugT debug_t,
|
|
U64 *buckets_cap_out)
|
|
{
|
|
Temp scratch = scratch_begin(&arena->v[0], 1);
|
|
LNK_BuildUDTNameHashTableTask task = {0};
|
|
task.debug_t = debug_t;
|
|
task.buckets_cap = (U64)((F64)debug_t.count * 1.3);
|
|
task.buckets = push_array(arena->v[0], LNK_UDTNameBucket *, task.buckets_cap);
|
|
task.ranges = tp_divide_work(scratch.arena, debug_t.count, tp->worker_count);
|
|
tp_for_parallel(tp, arena, tp->worker_count, lnk_build_udt_name_hash_table_task, &task);
|
|
*buckets_cap_out = task.buckets_cap;
|
|
scratch_end(scratch);
|
|
return task.buckets;
|
|
}
|
|
|
|
internal LNK_UDTNameBucket *
|
|
lnk_udt_name_hash_table_lookup(LNK_UDTNameBucket **buckets, U64 cap, String8 name)
|
|
{
|
|
U64 hash = lnk_udt_name_hash_table_hash(name);
|
|
U64 best_idx = hash % cap;
|
|
U64 bucket_idx = best_idx;
|
|
do {
|
|
if (buckets[bucket_idx] == 0) {
|
|
break;
|
|
}
|
|
if (str8_match(buckets[bucket_idx]->name, name, 0)) {
|
|
return buckets[bucket_idx];
|
|
}
|
|
bucket_idx = (bucket_idx + 1) % cap;
|
|
} while (bucket_idx != best_idx);
|
|
return 0;
|
|
}
|
|
|
|
internal CV_TypeIndex
|
|
lnk_udt_name_hash_table_lookup_itype(LNK_UDTNameBucket **buckets, U64 cap, String8 name)
|
|
{
|
|
LNK_UDTNameBucket *bucket = lnk_udt_name_hash_table_lookup(buckets, cap, name);
|
|
if (bucket != 0) {
|
|
return CV_MinComplexTypeIndex + bucket->leaf_idx;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
internal RDIB_Type *
|
|
lnk_push_converted_codeview_type(Arena *arena, RDIB_TypeChunkList *list, RDIB_Type **itype_map, CV_TypeIndex itype)
|
|
{
|
|
RDIB_Type *type = rdib_type_chunk_list_push(arena, list, 8196);
|
|
type->final_idx = 0;
|
|
type->itype = itype;
|
|
|
|
Assert(itype_map[itype] == 0);
|
|
itype_map[itype] = type;
|
|
|
|
return type;
|
|
}
|
|
|
|
internal void
|
|
lnk_push_basic_itypes(Arena *arena, RDIB_DataModel data_model, RDIB_Type **itype_map, RDIB_TypeChunkList *rdib_types_list)
|
|
{
|
|
RDI_TypeKind short_type = rdib_short_type_from_data_model(data_model);
|
|
RDI_TypeKind ushort_type = rdib_unsigned_short_type_from_data_model(data_model);
|
|
RDI_TypeKind int_type = rdib_int_type_from_data_model(data_model);
|
|
RDI_TypeKind uint_type = rdib_unsigned_int_type_from_data_model(data_model);
|
|
RDI_TypeKind long_type = rdib_long_type_from_data_model(data_model);
|
|
RDI_TypeKind ulong_type = rdib_unsigned_long_type_from_data_model(data_model);
|
|
RDI_TypeKind long_long_type = rdib_long_long_type_from_data_model(data_model);
|
|
RDI_TypeKind ulong_long_type = rdib_unsigned_long_long_type_from_data_model(data_model);
|
|
RDI_TypeKind ptr_type = rdib_pointer_size_t_type_from_data_model(data_model);
|
|
|
|
struct {
|
|
char * name;
|
|
RDI_TypeKind kind_rdi;
|
|
CV_LeafKind kind_cv;
|
|
B32 make_pointer_near;
|
|
B32 make_pointer_32;
|
|
B32 make_pointer_64;
|
|
} table[] = {
|
|
{ "void" , RDI_TypeKind_Void , CV_BasicType_VOID , 1, 1, 1 },
|
|
{ "HRESULT" , RDI_TypeKind_Handle , CV_BasicType_HRESULT , 0, 1, 1 },
|
|
{ "signed char" , RDI_TypeKind_Char8 , CV_BasicType_CHAR , 1, 1, 1 }, // TODO: we need Signed Char8 in RDI
|
|
{ "short" , short_type , CV_BasicType_SHORT , 1, 1, 1 },
|
|
{ "long" , long_type , CV_BasicType_LONG , 1, 1, 1 },
|
|
{ "long long" , long_long_type , CV_BasicType_QUAD , 1, 1, 1 },
|
|
{ "__int128" , RDI_TypeKind_S128 , CV_BasicType_OCT , 1, 1, 1 }, // GCC/Clang type
|
|
{ "unsigned char" , RDI_TypeKind_UChar8 , CV_BasicType_UCHAR , 1, 1, 1 },
|
|
{ "unsigned short" , ushort_type , CV_BasicType_USHORT , 1, 1, 1 },
|
|
{ "unsigned long" , ulong_type , CV_BasicType_ULONG , 1, 1, 1 },
|
|
{ "unsigned long long" , ulong_long_type , CV_BasicType_UQUAD , 1, 1, 1 },
|
|
{ "__uint128" , RDI_TypeKind_U128 , CV_BasicType_UOCT , 1, 1, 1 }, // GCC/Clang type
|
|
{ "bool" , RDI_TypeKind_S8 , CV_BasicType_BOOL8 , 1, 1, 1 }, // TODO: we need a actual boolean type in RDI so we can format value as true/false.
|
|
{ "__bool16" , RDI_TypeKind_S16 , CV_BasicType_BOOL16 , 1, 1, 1 }, // not real C type
|
|
{ "__bool32" , RDI_TypeKind_S32 , CV_BasicType_BOOL32 , 1, 1, 1 }, // not real C type
|
|
{ "float" , RDI_TypeKind_F32 , CV_BasicType_FLOAT32 , 1, 1, 1 },
|
|
{ "double" , RDI_TypeKind_F64 , CV_BasicType_FLOAT64 , 1, 1, 1 },
|
|
{ "long double" , RDI_TypeKind_F80 , CV_BasicType_FLOAT80 , 1, 1, 1 },
|
|
{ "__float128" , RDI_TypeKind_F128 , CV_BasicType_FLOAT128 , 1, 1, 1 }, // GCC/Clang type
|
|
{ "__float48" , RDI_TypeKind_F48 , CV_BasicType_FLOAT48 , 1, 1, 1 }, // not real C type
|
|
{ "__float32pp" , RDI_TypeKind_F32PP , CV_BasicType_FLOAT32PP , 1, 1, 1 }, // not real C type
|
|
{ "_Complex float" , RDI_TypeKind_ComplexF32 , CV_BasicType_COMPLEX32 , 0, 0, 0 },
|
|
{ "_Complex double" , RDI_TypeKind_ComplexF64 , CV_BasicType_COMPLEX64 , 0, 0, 0 },
|
|
{ "_Complex long double" , RDI_TypeKind_ComplexF80 , CV_BasicType_COMPLEX80 , 0, 0, 0 },
|
|
{ "_Complex __float128" , RDI_TypeKind_ComplexF128, CV_BasicType_COMPLEX128 , 0, 0, 0 },
|
|
{ "__int8" , RDI_TypeKind_S8 , CV_BasicType_INT8 , 1, 1, 1 },
|
|
{ "__uint8" , RDI_TypeKind_U8 , CV_BasicType_UINT8 , 1, 1, 1 },
|
|
{ "__int16" , RDI_TypeKind_S16 , CV_BasicType_INT16 , 1, 1, 1 },
|
|
{ "__uint16" , RDI_TypeKind_U16 , CV_BasicType_UINT16 , 1, 1, 1 },
|
|
{ "int" , int_type , CV_BasicType_INT32 , 1, 1, 1 },
|
|
{ "unsigned int" , uint_type , CV_BasicType_UINT32 , 1, 1, 1 },
|
|
{ "__int64" , RDI_TypeKind_S64 , CV_BasicType_INT64 , 1, 1, 1 },
|
|
{ "__uint64" , RDI_TypeKind_U64 , CV_BasicType_UINT64 , 1, 1, 1 },
|
|
{ "__int128" , RDI_TypeKind_S128 , CV_BasicType_INT128 , 1, 1, 1 },
|
|
{ "__uint128" , RDI_TypeKind_U128 , CV_BasicType_UINT128 , 1, 1, 1 },
|
|
{ "char" , RDI_TypeKind_Char8 , CV_BasicType_RCHAR , 1, 1, 1 }, // always ASCII
|
|
{ "wchar_t" , RDI_TypeKind_UChar16 , CV_BasicType_WCHAR , 1, 1, 1 }, // on windows always UTF-16
|
|
{ "char8_t" , RDI_TypeKind_Char8 , CV_BasicType_CHAR8 , 1, 1, 1 }, // always UTF-8
|
|
{ "char16_t" , RDI_TypeKind_Char16 , CV_BasicType_CHAR16 , 1, 1, 1 }, // always UTF-16
|
|
{ "char32_t" , RDI_TypeKind_Char32 , CV_BasicType_CHAR32 , 1, 1, 1 }, // always UTF-32
|
|
{ "__pointer" , ptr_type , CV_BasicType_PTR , 0, 0, 0 }
|
|
};
|
|
|
|
for (U64 i = 0; i < ArrayCount(table); ++i) {
|
|
U64 builtin_size;
|
|
if (table[i].kind_rdi == RDI_TypeKind_Void || table[i].kind_rdi == RDI_TypeKind_Handle) {
|
|
builtin_size = rdi_size_from_basic_type_kind(ptr_type);
|
|
} else {
|
|
builtin_size = rdi_size_from_basic_type_kind(table[i].kind_rdi);
|
|
}
|
|
|
|
RDIB_Type *builtin = lnk_push_converted_codeview_type(arena, rdib_types_list, itype_map, table[i].kind_cv);
|
|
builtin->kind = table[i].kind_rdi;
|
|
builtin->builtin.name = str8_cstring(table[i].name);
|
|
builtin->builtin.size = builtin_size;
|
|
|
|
RDIB_Type **wrapper = push_array(arena, RDIB_Type *, 1);
|
|
*wrapper = builtin;
|
|
|
|
if (table[i].make_pointer_near) {
|
|
RDIB_Type *ptr_near = lnk_push_converted_codeview_type(arena, rdib_types_list, itype_map, table[i].kind_cv | 0x100);
|
|
ptr_near->kind = RDI_TypeKind_Ptr;
|
|
ptr_near->ptr.size = rdi_size_from_basic_type_kind(ptr_type);
|
|
ptr_near->ptr.type_ref = wrapper;
|
|
}
|
|
if (table[i].make_pointer_32) {
|
|
RDIB_Type *ptr_32 = lnk_push_converted_codeview_type(arena, rdib_types_list, itype_map, table[i].kind_cv | 0x400);
|
|
ptr_32->kind = RDI_TypeKind_Ptr;
|
|
ptr_32->ptr.size = 4;
|
|
ptr_32->ptr.type_ref = wrapper;
|
|
}
|
|
if (table[i].make_pointer_64) {
|
|
RDIB_Type *ptr_64 = lnk_push_converted_codeview_type(arena, rdib_types_list, itype_map, table[i].kind_cv | 0x600);
|
|
ptr_64->kind = RDI_TypeKind_Ptr;
|
|
ptr_64->ptr.size = 8;
|
|
ptr_64->ptr.type_ref = wrapper;
|
|
}
|
|
|
|
#if 0
|
|
RDIB_Type *ptr_far = lnk_push_converted_codeview_type(arena, rdib_types_list, itype_map, table[i].kind_cv | 0x200);
|
|
RDIB_Type *ptr_huge = lnk_push_converted_codeview_type(arena, rdib_types_list, itype_map, table[i].kind_cv | 0x300);
|
|
RDIB_Type *ptr_16_32 = lnk_push_converted_codeview_type(arena, rdib_types_list, itype_map, table[i].kind_cv | 0x500);
|
|
|
|
ptr_far->kind = RDI_TypeKind_Ptr;
|
|
ptr_far->ptr.size = rdi_size_from_basic_type_kind(ptr_type);
|
|
ptr_far->ptr.type_ref = wrapper;
|
|
|
|
ptr_huge->kind = RDI_TypeKind_Ptr;
|
|
ptr_huge->ptr.size = 4;
|
|
ptr_huge->ptr.type_ref = wrapper;
|
|
|
|
|
|
ptr_16_32->kind = RDI_TypeKind_Ptr;
|
|
ptr_16_32->ptr.size = 6;
|
|
ptr_16_32->ptr.type_ref = wrapper;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
internal RDIB_TypeRef
|
|
lnk_rdib_type_from_itype(LNK_ConvertTypesToRDI *task, CV_TypeIndex itype)
|
|
{
|
|
RDIB_TypeRef result = &task->tpi_itype_map[0];
|
|
Rng1U64 tpi_range = task->itype_ranges[CV_TypeIndexSource_TPI];
|
|
|
|
if (itype < tpi_range.min) {
|
|
// check for supported CodeView pointer formats:
|
|
AssertAlways(BitExtract(itype, 8, 8) == /* near */ 0x1 ||
|
|
BitExtract(itype, 8, 8) == /* 32 bit */ 0x4 ||
|
|
BitExtract(itype, 8, 8) == /* 64 bit */ 0x6 ||
|
|
BitExtract(itype, 8, 8) == /* regular */ 0x0);
|
|
}
|
|
|
|
if (itype < tpi_range.max) {
|
|
CV_TypeIndex final_itype = itype;
|
|
|
|
// try to resovle forward reference (defn might be missing)
|
|
if (itype >= tpi_range.min) {
|
|
U64 leaf_idx = itype - tpi_range.min;
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(task->types[CV_TypeIndexSource_TPI], leaf_idx);
|
|
if (cv_is_udt(leaf.kind)) {
|
|
CV_UDTInfo udt_info = cv_get_udt_info(leaf.kind, leaf.data);
|
|
if (udt_info.props & CV_TypeProp_FwdRef) {
|
|
String8 name = cv_name_from_udt_info(udt_info);
|
|
CV_TypeIndex resolved_itype = lnk_udt_name_hash_table_lookup_itype(task->udt_name_buckets, task->udt_name_bucket_cap, name);
|
|
if (resolved_itype != 0) {
|
|
final_itype = resolved_itype;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result = &task->tpi_itype_map[final_itype];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal RDI_MemberKind
|
|
lnk_rdib_method_kind_from_cv_prop(CV_MethodProp prop)
|
|
{
|
|
switch (prop) {
|
|
case CV_MethodProp_Vanilla: return RDI_MemberKind_Method;
|
|
case CV_MethodProp_Virtual: return RDI_MemberKind_VirtualMethod;
|
|
case CV_MethodProp_Static: return RDI_MemberKind_StaticMethod;
|
|
case CV_MethodProp_Friend: NotImplemented;
|
|
case CV_MethodProp_Intro: return RDI_MemberKind_VirtualMethod;
|
|
case CV_MethodProp_PureVirtual: return RDI_MemberKind_VirtualMethod;
|
|
case CV_MethodProp_PureIntro: return RDI_MemberKind_VirtualMethod;
|
|
}
|
|
return RDI_MemberKind_NULL;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_convert_types_to_rdi_task)
|
|
{
|
|
ProfBeginFunction();
|
|
LNK_ConvertTypesToRDI *task = raw_task;
|
|
|
|
// upfront push output type array
|
|
U64 leaf_count = dim_1u64(task->ranges[task_id]);
|
|
rdib_type_chunk_list_reserve(arena, &task->rdib_types_lists[task_id], leaf_count);
|
|
|
|
for(U64 leaf_idx = task->ranges[task_id].min; leaf_idx < task->ranges[task_id].max; ++leaf_idx) {
|
|
U64 itype = task->itype_ranges[CV_TypeIndexSource_TPI].min + leaf_idx;
|
|
CV_Leaf src = cv_debug_t_get_leaf(task->types[CV_TypeIndexSource_TPI], leaf_idx);
|
|
|
|
switch (src.kind) {
|
|
case CV_LeafKind_MODIFIER: {
|
|
CV_LeafModifier *modifier = (CV_LeafModifier *) src.data.str;
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = RDI_TypeKind_Modifier;
|
|
dst->modifier.flags = rdi_type_modifier_flags_from_cv_modifier_flags(modifier->flags);
|
|
dst->modifier.type_ref = lnk_rdib_type_from_itype(task, modifier->itype);
|
|
} break;
|
|
case CV_LeafKind_POINTER: {
|
|
CV_LeafPointer *ptr = (CV_LeafPointer *) src.data.str;
|
|
CV_PointerKind ptr_kind = CV_PointerAttribs_Extract_Kind(ptr->attribs);
|
|
CV_PointerMode ptr_mode = CV_PointerAttribs_Extract_Mode(ptr->attribs);
|
|
U32 ptr_size = CV_PointerAttribs_Extract_Size(ptr->attribs);
|
|
(void)ptr_kind;
|
|
|
|
// parse ahead type chain and squash modifiers
|
|
RDI_TypeModifierFlags modifier_flags = rdi_type_modifier_flags_from_cv_pointer_attribs(ptr->attribs);
|
|
CV_TypeIndex next_itype;
|
|
for (next_itype = ptr->itype; task->itype_ranges[CV_TypeIndexSource_TPI].min <= next_itype && next_itype < task->itype_ranges[CV_TypeIndexSource_TPI].max;) {
|
|
U64 next_leaf_idx = next_itype - task->itype_ranges[CV_TypeIndexSource_TPI].min;
|
|
CV_Leaf next_leaf = cv_debug_t_get_leaf(task->types[CV_TypeIndexSource_TPI], next_leaf_idx);
|
|
if (next_leaf.kind != CV_LeafKind_MODIFIER) {
|
|
break;
|
|
}
|
|
|
|
// parse LF_MODIFIER
|
|
CV_LeafModifier *sym_modifier = (CV_LeafModifier *) next_leaf.data.str;
|
|
RDI_TypeModifierFlags flags = rdi_type_modifier_flags_from_cv_modifier_flags(sym_modifier->flags);
|
|
|
|
// accumulate modifier flags
|
|
modifier_flags |= flags;
|
|
|
|
// advance
|
|
next_itype = sym_modifier->itype;
|
|
}
|
|
|
|
if (modifier_flags == 0) {
|
|
// No modifer just generate pointer type.
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = rdi_type_kind_from_pointer(ptr->attribs, ptr_mode);
|
|
dst->ptr.size = ptr_size;
|
|
dst->ptr.type_ref = lnk_rdib_type_from_itype(task, ptr->itype);
|
|
} else {
|
|
// CodeView embeds modifier in pointer struct, we don't have an equivalent
|
|
// so generate a modifier type in pointer slot and link with pointer type.
|
|
|
|
RDIB_Type *ptr_type = rdib_type_chunk_list_push(arena, &task->rdib_types_lists[task_id], task->type_cap);
|
|
ptr_type->kind = rdi_type_kind_from_pointer(ptr->attribs, ptr_mode);
|
|
ptr_type->ptr.type_ref = lnk_rdib_type_from_itype(task, next_itype);
|
|
RDIB_Type **indirect_ptr_type = push_array(arena, RDIB_Type *, 1);
|
|
*indirect_ptr_type = ptr_type;
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = RDI_TypeKind_Modifier;
|
|
dst->modifier.flags = modifier_flags;
|
|
dst->modifier.type_ref = indirect_ptr_type;
|
|
}
|
|
} break;
|
|
case CV_LeafKind_PROCEDURE: {
|
|
CV_LeafProcedure *proc = (CV_LeafProcedure *) src.data.str;
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = RDI_TypeKind_Function;
|
|
dst->func.return_type = lnk_rdib_type_from_itype(task, proc->ret_itype);
|
|
dst->func.params_type = lnk_rdib_type_from_itype(task, proc->arg_itype);
|
|
} break;
|
|
case CV_LeafKind_MFUNCTION: {
|
|
CV_LeafMFunction *mfunc = (CV_LeafMFunction *) src.data.str;
|
|
B32 is_static_method = mfunc->this_itype == 0;
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_lists[task_id], task->tpi_itype_map, itype);
|
|
|
|
if (is_static_method) {
|
|
dst->kind = RDI_TypeKindExt_StaticMethod;
|
|
dst->static_method.class_type = lnk_rdib_type_from_itype(task, mfunc->class_itype);
|
|
dst->static_method.return_type = lnk_rdib_type_from_itype(task, mfunc->ret_itype);
|
|
dst->static_method.params_type = lnk_rdib_type_from_itype(task, mfunc->arg_itype);
|
|
} else {
|
|
dst->kind = RDI_TypeKind_Method;
|
|
dst->method.class_type = lnk_rdib_type_from_itype(task, mfunc->class_itype);
|
|
dst->method.this_type = lnk_rdib_type_from_itype(task, mfunc->this_itype);
|
|
dst->method.return_type = lnk_rdib_type_from_itype(task, mfunc->ret_itype);
|
|
dst->method.params_type = lnk_rdib_type_from_itype(task, mfunc->arg_itype);
|
|
}
|
|
} break;
|
|
case CV_LeafKind_BITFIELD: {
|
|
CV_LeafBitField *bitfield = (CV_LeafBitField *) src.data.str;
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = RDI_TypeKind_Bitfield;
|
|
dst->bitfield.off = bitfield->pos;
|
|
dst->bitfield.count = bitfield->len;
|
|
dst->bitfield.value_type = lnk_rdib_type_from_itype(task, bitfield->itype);
|
|
} break;
|
|
case CV_LeafKind_ARRAY: {
|
|
CV_LeafArray *array = (CV_LeafArray *) src.data.str;
|
|
CV_NumericParsed size = cv_numeric_from_data_range(src.data.str + sizeof(CV_LeafArray), src.data.str + src.data.size);
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = RDI_TypeKind_Array;
|
|
dst->array.entry_type = lnk_rdib_type_from_itype(task, array->entry_itype);
|
|
dst->array.size = cv_u64_from_numeric(&size);
|
|
} break;
|
|
case CV_LeafKind_CLASS:
|
|
case CV_LeafKind_STRUCTURE: {
|
|
CV_LeafStruct *udt = (CV_LeafStruct *) src.data.str;
|
|
CV_NumericParsed size = cv_numeric_from_data_range(src.data.str + sizeof(CV_LeafStruct), src.data.str + src.data.size);
|
|
|
|
String8 name;
|
|
String8 link_name;
|
|
if (udt->props & CV_TypeProp_HasUniqueName) {
|
|
name = str8_cstring_capped(src.data.str + sizeof(CV_LeafStruct) + size.encoded_size, src.data.str + src.data.size);
|
|
link_name = str8_cstring_capped_reverse(name.str + name.size + 1, src.data.str + src.data.size);
|
|
} else {
|
|
name = str8_cstring_capped_reverse(src.data.str + sizeof(CV_LeafStruct) + size.encoded_size, src.data.str + src.data.size);
|
|
link_name = name;
|
|
}
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_struct_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->udt.name = name;
|
|
dst->udt.link_name = link_name;
|
|
dst->udt.members = lnk_rdib_type_from_itype(task, udt->field_itype);
|
|
dst->udt.struct_type.size = cv_u64_from_numeric(&size);
|
|
dst->udt.struct_type.derived = lnk_rdib_type_from_itype(task, udt->derived_itype);
|
|
dst->udt.struct_type.vtshape = lnk_rdib_type_from_itype(task, udt->vshape_itype);
|
|
|
|
if (udt->props & CV_TypeProp_FwdRef) {
|
|
dst->kind = src.kind == CV_LeafKind_CLASS ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_IncompleteStruct;
|
|
} else {
|
|
dst->kind = src.kind == CV_LeafKind_CLASS ? RDI_TypeKind_Class : RDI_TypeKind_Struct;
|
|
}
|
|
} break;
|
|
case CV_LeafKind_CLASS2:
|
|
case CV_LeafKind_STRUCT2: {
|
|
CV_LeafStruct2 *udt = (CV_LeafStruct2 *) src.data.str;
|
|
CV_NumericParsed size = cv_numeric_from_data_range(src.data.str + sizeof(CV_LeafStruct2), src.data.str + src.data.size);
|
|
|
|
String8 name;
|
|
String8 link_name;
|
|
if (udt->props & CV_TypeProp_HasUniqueName) {
|
|
name = str8_cstring_capped(src.data.str + sizeof(CV_LeafStruct2) + size.encoded_size, src.data.str + src.data.size);
|
|
link_name = str8_cstring_capped_reverse(name.str + name.size + 1, src.data.str + src.data.size);
|
|
} else {
|
|
name = str8_cstring_capped_reverse(src.data.str + sizeof(CV_LeafStruct2) + size.encoded_size, src.data.str + src.data.size);
|
|
link_name = name;
|
|
}
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_struct_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->udt.name = name;
|
|
dst->udt.link_name = link_name;
|
|
dst->udt.members = lnk_rdib_type_from_itype(task, udt->field_itype);
|
|
dst->udt.struct_type.size = cv_u64_from_numeric(&size);
|
|
dst->udt.struct_type.derived = lnk_rdib_type_from_itype(task, udt->derived_itype);
|
|
dst->udt.struct_type.vtshape = lnk_rdib_type_from_itype(task, udt->vshape_itype);
|
|
|
|
if (udt->props & CV_TypeProp_FwdRef) {
|
|
dst->kind = src.kind == CV_LeafKind_CLASS2 ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_IncompleteStruct;
|
|
} else {
|
|
dst->kind = src.kind == CV_LeafKind_CLASS2 ? RDI_TypeKind_Class : RDI_TypeKind_Struct;
|
|
}
|
|
} break;
|
|
case CV_LeafKind_UNION: {
|
|
CV_LeafUnion *udt = (CV_LeafUnion *) src.data.str;
|
|
CV_NumericParsed size = cv_numeric_from_data_range(src.data.str + sizeof(CV_LeafUnion), src.data.str + src.data.size);
|
|
|
|
String8 name;
|
|
String8 link_name;
|
|
if (udt->props & CV_TypeProp_HasUniqueName) {
|
|
name = str8_cstring_capped(src.data.str + sizeof(CV_LeafUnion) + size.encoded_size, src.data.str + src.data.size);
|
|
link_name = str8_cstring_capped_reverse(name.str + name.size + 1, src.data.str + src.data.size);
|
|
} else {
|
|
name = str8_cstring_capped_reverse(src.data.str + sizeof(CV_LeafUnion) + size.encoded_size, src.data.str + src.data.size);
|
|
link_name = name;
|
|
}
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_union_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->udt.name = name;
|
|
dst->udt.link_name = link_name;
|
|
dst->udt.members = lnk_rdib_type_from_itype(task, udt->field_itype);
|
|
dst->udt.union_type.size = cv_u64_from_numeric(&size);
|
|
|
|
if (udt->props & CV_TypeProp_FwdRef) {
|
|
dst->kind = RDI_TypeKind_IncompleteUnion;
|
|
} else {
|
|
dst->kind = RDI_TypeKind_Union;
|
|
}
|
|
} break;
|
|
case CV_LeafKind_ENUM: {
|
|
CV_LeafEnum *udt = (CV_LeafEnum *) src.data.str;
|
|
|
|
String8 name;
|
|
String8 link_name;
|
|
if (udt->props & CV_TypeProp_HasUniqueName) {
|
|
name = str8_cstring_capped(src.data.str + sizeof(*udt), src.data.str + src.data.size);
|
|
link_name = str8_cstring_capped_reverse(name.str + name.size + 1, src.data.str + src.data.size);
|
|
} else {
|
|
name = str8_cstring_capped_reverse(src.data.str + sizeof(*udt), src.data.str + src.data.size);
|
|
link_name = name;
|
|
}
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_enum_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = (RDI_TypeKindExt)RDI_TypeKind_Enum;
|
|
dst->udt.name = name;
|
|
dst->udt.link_name = link_name;
|
|
dst->udt.members = lnk_rdib_type_from_itype(task, udt->field_itype);
|
|
dst->udt.enum_type.base_type = lnk_rdib_type_from_itype(task, udt->base_itype);
|
|
|
|
if (udt->props & CV_TypeProp_FwdRef) {
|
|
dst->kind = RDI_TypeKind_IncompleteEnum;
|
|
} else {
|
|
dst->kind = (RDI_TypeKindExt)RDI_TypeKind_Enum;
|
|
}
|
|
} break;
|
|
case CV_LeafKind_ARGLIST: {
|
|
CV_LeafArgList *arglist = (CV_LeafArgList *) src.data.str;
|
|
CV_TypeIndex *itypes = (CV_TypeIndex *) (arglist + 1);
|
|
|
|
if (arglist->count * sizeof(CV_TypeIndex) + sizeof(CV_LeafArgList) > src.data.size) {
|
|
AssertAlways("error: ill-formed LF_ARGLIST");
|
|
break;
|
|
}
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_params_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = RDI_TypeKindExt_Params; // there is no Params kind in RDI
|
|
dst->params.count = arglist->count;
|
|
dst->params.types = push_array(arena, RDIB_TypeRef, arglist->count);
|
|
for (U64 param_idx = 0; param_idx < arglist->count; ++param_idx) {
|
|
// strange way to encode variadic params, when outside LF_ARGLIST LF_NOTYPE actually means null...
|
|
if (itypes[param_idx] == CV_LeafKind_NOTYPE) {
|
|
dst->params.types[param_idx] = task->variadic_type_ref;
|
|
} else {
|
|
dst->params.types[param_idx] = lnk_rdib_type_from_itype(task, itypes[param_idx]);
|
|
}
|
|
}
|
|
} break;
|
|
case CV_LeafKind_FIELDLIST: {
|
|
RDIB_UDTMemberChunkList *rdib_member_list;
|
|
RDIB_TypeChunkList *rdib_member_types;
|
|
B32 is_enum = sizeof(CV_LeafKind) <= src.data.size && (*(CV_LeafKind *)src.data.str == CV_LeafKind_ENUMERATE);
|
|
if (is_enum) {
|
|
rdib_member_list = &task->rdib_enum_members_lists[worker_id];
|
|
rdib_member_types = &task->rdib_types_enum_members_lists[worker_id];
|
|
} else {
|
|
rdib_member_list = &task->rdib_udt_members_lists[worker_id];
|
|
rdib_member_types = &task->rdib_types_udt_members_lists[worker_id];
|
|
}
|
|
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, rdib_member_types, task->tpi_itype_map, itype);
|
|
dst->kind = RDI_TypeKindExt_Members;
|
|
|
|
for (U64 cursor = 0; cursor + sizeof(CV_LeafKind) <= src.data.size; ) {
|
|
CV_LeafKind field_kind = *(CV_LeafKind *) (src.data.str + cursor);
|
|
cursor += sizeof(field_kind);
|
|
|
|
// do we have bytes to read?
|
|
U64 header_size = cv_header_struct_size_from_leaf_kind(field_kind);
|
|
if (cursor + header_size > src.data.size) {
|
|
break;
|
|
}
|
|
|
|
switch (field_kind) {
|
|
case CV_LeafKind_INDEX: {
|
|
CV_LeafIndex *index = (CV_LeafIndex *) (src.data.str + cursor);
|
|
cursor += sizeof(*index);
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB member list pointer
|
|
member->kind = RDI_MemberKindExt_MemberListPointer;
|
|
member->member_list_pointer = lnk_rdib_type_from_itype(task, index->itype);
|
|
} break;
|
|
case CV_LeafKind_MEMBER: {
|
|
// prase CodeView struct/class/union data member
|
|
CV_LeafMember *leaf_member = (CV_LeafMember *) (src.data.str + cursor);
|
|
CV_NumericParsed offset = cv_numeric_from_data_range((U8 *)(leaf_member + 1), src.data.str + src.data.size);
|
|
String8 name = str8_cstring_capped(src.data.str + cursor + sizeof(CV_LeafMember) + offset.encoded_size, src.data.str + src.data.size);
|
|
cursor += sizeof(CV_LeafMember);
|
|
cursor += offset.encoded_size;
|
|
cursor += name.size + 1;
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB data member
|
|
member->kind = RDI_MemberKind_DataField;
|
|
member->data_field.name = name;
|
|
member->data_field.type_ref = lnk_rdib_type_from_itype(task, leaf_member->itype);
|
|
member->data_field.offset = cv_u64_from_numeric(&offset);
|
|
} break;
|
|
case CV_LeafKind_STMEMBER: {
|
|
// parse CodeView static member
|
|
CV_LeafStMember *st_member = (CV_LeafStMember *) (src.data.str + cursor);
|
|
String8 name = str8_cstring_capped(st_member + 1, src.data.str + src.data.size);
|
|
cursor += sizeof(CV_LeafStMember);
|
|
cursor += name.size + 1;
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB static member
|
|
member->kind = RDI_MemberKind_StaticData;
|
|
member->static_data.name = name;
|
|
member->static_data.type_ref = lnk_rdib_type_from_itype(task, st_member->itype);
|
|
} break;
|
|
case CV_LeafKind_METHOD: {
|
|
// parse CodeView over-loaded method
|
|
CV_LeafMethod *method = (CV_LeafMethod *) (src.data.str + cursor);
|
|
String8 name = str8_cstring_capped(method + 1, src.data.str + src.data.size);
|
|
cursor += sizeof(CV_LeafMethod);
|
|
cursor += name.size + 1;
|
|
|
|
if (contains_1u64(task->itype_ranges[CV_TypeIndexSource_TPI], method->list_itype)) {
|
|
U64 method_list_leaf_idx = method->list_itype - task->itype_ranges[CV_TypeIndexSource_TPI].min;
|
|
CV_Leaf method_list_leaf = cv_debug_t_get_leaf(task->types[CV_TypeIndexSource_TPI], method_list_leaf_idx);
|
|
if (method_list_leaf.kind == CV_LeafKind_METHODLIST) {
|
|
for (U64 cursor = 0; cursor + sizeof(CV_LeafMethodListMember) <= method_list_leaf.data.size; ) {
|
|
// parse CodeView method overload info
|
|
CV_LeafMethodListMember *list_member = (CV_LeafMethodListMember *) (method_list_leaf.data.str + cursor);
|
|
CV_MethodProp prop = CV_FieldAttribs_Extract_MethodProp(list_member->attribs);
|
|
cursor += sizeof(CV_LeafMethodListMember);
|
|
U32 vftable_offset = 0;
|
|
if (prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro) {
|
|
str8_deserial_read_struct(src.data, cursor, &vftable_offset);
|
|
cursor += sizeof(vftable_offset);
|
|
}
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB method
|
|
member->kind = RDI_MemberKind_Method;
|
|
member->method.kind = lnk_rdib_method_kind_from_cv_prop(prop);
|
|
member->method.name = name;
|
|
member->method.type_ref = lnk_rdib_type_from_itype(task, list_member->itype);
|
|
member->method.vftable_offset = vftable_offset;
|
|
}
|
|
} else {
|
|
Assert(!"error: expected LF_METHODLIST");
|
|
}
|
|
}
|
|
} break;
|
|
case CV_LeafKind_ONEMETHOD: {
|
|
// parse CodeView method
|
|
CV_LeafOneMethod *one_method = (CV_LeafOneMethod *) (src.data.str + cursor);
|
|
CV_MethodProp prop = CV_FieldAttribs_Extract_MethodProp(one_method->attribs);
|
|
cursor += sizeof(CV_LeafOneMethod);
|
|
U32 vftable_offset = 0;
|
|
if (prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro) {
|
|
str8_deserial_read_struct(src.data, cursor, &vftable_offset);
|
|
cursor += sizeof(vftable_offset);
|
|
}
|
|
String8 name = str8_cstring_capped(src.data.str + cursor, src.data.str + src.data.size);
|
|
cursor += name.size + 1;
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB member
|
|
member->kind = RDI_MemberKind_Method;
|
|
member->method.kind = lnk_rdib_method_kind_from_cv_prop(prop);
|
|
member->method.name = name;
|
|
member->method.type_ref = lnk_rdib_type_from_itype(task, one_method->itype);
|
|
member->method.vftable_offset = vftable_offset;
|
|
} break;
|
|
case CV_LeafKind_NESTTYPE: {
|
|
// parse CodeView nested type
|
|
CV_LeafNestType *nest_type = (CV_LeafNestType *) (src.data.str + cursor);
|
|
String8 name = str8_cstring_capped(nest_type + 1, src.data.str + src.data.size);
|
|
cursor += sizeof(CV_LeafNestType);
|
|
cursor += name.size + 1;
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB nested type member
|
|
member->kind = RDI_MemberKind_NestedType;
|
|
member->nested_type.name = name;
|
|
member->nested_type.type_ref = lnk_rdib_type_from_itype(task, nest_type->itype);
|
|
} break;
|
|
case CV_LeafKind_NESTTYPEEX: {
|
|
// parse CodeView nested type extended
|
|
CV_LeafNestTypeEx *nest_type_ex = (CV_LeafNestTypeEx *) (src.data.str + cursor);
|
|
String8 name = str8_cstring_capped(nest_type_ex + 1, src.data.str + src.data.size);
|
|
cursor += sizeof(CV_LeafNestTypeEx);
|
|
cursor += name.size + 1;
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB nested type member
|
|
member->kind = RDI_MemberKind_NestedType;
|
|
member->nested_type.name = name;
|
|
member->nested_type.type_ref = lnk_rdib_type_from_itype(task, nest_type_ex->itype);
|
|
} break;
|
|
case CV_LeafKind_BCLASS: {
|
|
// parse CodeView base class member
|
|
CV_LeafBClass *bclass = (CV_LeafBClass *) (src.data.str + cursor);
|
|
CV_NumericParsed offset = cv_numeric_from_data_range((U8 *)(bclass + 1), src.data.str + src.data.size);
|
|
cursor += sizeof(CV_LeafBClass);
|
|
cursor += offset.encoded_size;
|
|
|
|
U64 offset64 = cv_u64_from_numeric(&offset);
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB base class member
|
|
member->kind = RDI_MemberKind_Base;
|
|
member->base_class.type_ref = lnk_rdib_type_from_itype(task, bclass->itype);
|
|
member->base_class.offset = offset64;
|
|
} break;
|
|
case CV_LeafKind_VBCLASS:
|
|
case CV_LeafKind_IVBCLASS: {
|
|
// parse CodeView virtual base class
|
|
CV_LeafVBClass *vbclass = (CV_LeafVBClass *) (src.data.str + cursor);
|
|
CV_NumericParsed vbptr_off = cv_numeric_from_data_range(src.data.str + cursor + sizeof(*vbclass), src.data.str + src.data.size);
|
|
CV_NumericParsed vtable_off = cv_numeric_from_data_range(src.data.str + cursor + sizeof(*vbclass) + vbptr_off.encoded_size, src.data.str + src.data.size);
|
|
cursor += sizeof(CV_LeafVBClass);
|
|
cursor += vbptr_off.encoded_size;
|
|
cursor += vtable_off.encoded_size;
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB virtual base class member
|
|
member->kind = RDI_MemberKind_VirtualBase;
|
|
member->virtual_base_class.type_ref = lnk_rdib_type_from_itype(task, vbclass->itype);
|
|
member->virtual_base_class.vbptr_off = cv_u64_from_numeric(&vbptr_off);
|
|
member->virtual_base_class.vtable_off = cv_u64_from_numeric(&vtable_off);
|
|
} break;
|
|
case CV_LeafKind_VFUNCTAB: {
|
|
// parse CodeView virtual function table
|
|
CV_LeafVFuncTab *vfunc_tab = (CV_LeafVFuncTab *) (src.data.str + cursor);
|
|
cursor += sizeof(*vfunc_tab);
|
|
|
|
// TODO: we don't have an equivalent in RDI
|
|
} break;
|
|
case CV_LeafKind_ENUMERATE: {
|
|
// parse CodeView enum member
|
|
CV_LeafEnumerate *enumerate = (CV_LeafEnumerate *) (src.data.str + cursor);
|
|
CV_NumericParsed value = cv_numeric_from_data_range((U8 *) (enumerate + 1), src.data.str + src.data.size);
|
|
String8 name = str8_cstring_capped(src.data.str + cursor + sizeof(CV_LeafEnumerate) + value.encoded_size, src.data.str + src.data.size);
|
|
cursor += sizeof(CV_LeafEnumerate);
|
|
cursor += value.encoded_size;
|
|
cursor += name.size + 1;
|
|
|
|
// push new node
|
|
RDIB_UDTMember *member = rdib_udt_member_chunk_list_push(arena, rdib_member_list, task->udt_cap);
|
|
rdib_udt_member_list_push_node(&dst->members.list, member);
|
|
|
|
// fill out RDIB enum member
|
|
member->kind = RDI_MemberKind_NULL;
|
|
member->enumerate.name = name;
|
|
member->enumerate.value = cv_u64_from_numeric(&value);
|
|
} break;
|
|
default: InvalidPath;
|
|
}
|
|
|
|
cursor = AlignPow2(cursor, 4);
|
|
}
|
|
} break;
|
|
case CV_LeafKind_METHODLIST: {
|
|
// see CV_LeafKind_METHOD
|
|
} break;
|
|
case CV_LeafKind_LABEL: {
|
|
// ???
|
|
} break;
|
|
case CV_LeafKind_VTSHAPE: {
|
|
RDIB_Type *dst = lnk_push_converted_codeview_type(arena, &task->rdib_types_lists[task_id], task->tpi_itype_map, itype);
|
|
dst->kind = RDI_TypeKindExt_VirtualTable;
|
|
// ???
|
|
} break;
|
|
case CV_LeafKind_VFTABLE: {
|
|
// ???
|
|
} break;
|
|
default: InvalidPath; break;
|
|
}
|
|
|
|
#undef push_converted_type
|
|
}
|
|
ProfEnd();
|
|
}
|
|
|
|
internal U64
|
|
lnk_src_file_hash_cv(String8 normal_full_path, CV_C13ChecksumKind checksum_kind, String8 checksum)
|
|
{
|
|
XXH3_state_t state;
|
|
XXH3_INITSTATE(&state);
|
|
XXH3_64bits_reset(&state);
|
|
XXH3_64bits_update(&state, normal_full_path.str, normal_full_path.size);
|
|
XXH3_64bits_update(&state, &checksum_kind, sizeof(checksum_kind));
|
|
XXH3_64bits_update(&state, checksum.str, checksum.size);
|
|
XXH64_hash_t result = XXH3_64bits_digest(&state);
|
|
return result;
|
|
}
|
|
|
|
internal String8
|
|
lnk_normalize_src_file_path(Arena *arena, String8 file_path)
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
String8 result = file_path;
|
|
result = lower_from_str8(scratch.arena, result);
|
|
result = path_convert_slashes(scratch.arena, result, PathStyle_UnixAbsolute);
|
|
result = push_str8_copy(arena, result);
|
|
scratch_end(scratch);
|
|
return result;
|
|
}
|
|
|
|
internal LNK_SourceFileBucket *
|
|
lnk_src_file_hash_table_lookup_slot(LNK_SourceFileBucket **buckets,
|
|
U64 cap,
|
|
U64 hash,
|
|
String8 normal_path,
|
|
CV_C13ChecksumKind checksum_kind,
|
|
String8 checksum)
|
|
{
|
|
U64 best_idx = hash % cap;
|
|
U64 bucket_idx = best_idx;
|
|
|
|
RDIB_SourceFile temp = {0};
|
|
temp.normal_full_path = normal_path;
|
|
temp.checksum_kind = checksum_kind;
|
|
temp.checksum = checksum;
|
|
|
|
do {
|
|
if (buckets[bucket_idx] == 0) {
|
|
break;
|
|
}
|
|
if (rdib_source_file_match(buckets[bucket_idx]->src_file, &temp, operating_system_from_context())) {
|
|
return buckets[bucket_idx];
|
|
}
|
|
bucket_idx = (bucket_idx + 1) % cap;
|
|
} while (bucket_idx != best_idx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
internal LNK_SourceFileBucket *
|
|
lnk_src_file_insert_or_update(LNK_SourceFileBucket **buckets, U64 cap, U64 hash, LNK_SourceFileBucket *new_bucket)
|
|
{
|
|
LNK_SourceFileBucket *result = 0;
|
|
|
|
U64 best_idx = hash % cap;
|
|
U64 idx = best_idx;
|
|
do {
|
|
retry:;
|
|
LNK_SourceFileBucket *curr_bucket = buckets[idx];
|
|
|
|
if (curr_bucket == 0) {
|
|
LNK_SourceFileBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&buckets[idx], new_bucket, curr_bucket);
|
|
|
|
if (compare_bucket == curr_bucket) {
|
|
// success, bucket was inserted
|
|
result = curr_bucket;
|
|
break;
|
|
}
|
|
|
|
// another thread took the bucket...
|
|
goto retry;
|
|
} else if (rdib_source_file_match(curr_bucket->src_file, new_bucket->src_file, operating_system_from_context())) {
|
|
// do we need to update value in the bucket?
|
|
int cmp = u64_compar(&curr_bucket->obj_idx, &new_bucket->obj_idx);
|
|
if (cmp <= 0) {
|
|
// are we inserting bucket that was already inserterd?
|
|
Assert(cmp < 0);
|
|
|
|
// don't need to update, more recent value is in the bucket
|
|
break;
|
|
}
|
|
|
|
LNK_SourceFileBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&buckets[idx], new_bucket, curr_bucket);
|
|
|
|
if (compare_bucket == curr_bucket) {
|
|
// success, bucket was inserted
|
|
result = compare_bucket;
|
|
break;
|
|
}
|
|
|
|
// another thread took the bucket...
|
|
goto retry;
|
|
}
|
|
|
|
// advance
|
|
idx = (idx + 1);
|
|
idx = idx == cap ? 0 : idx;
|
|
} while (idx != best_idx);
|
|
|
|
return result;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_count_source_files_task)
|
|
{
|
|
U64 unit_idx = task_id;
|
|
LNK_ConvertSourceFilesToRDITask *task = raw_task;
|
|
CV_DebugS debug_s = task->debug_s_arr[unit_idx];
|
|
String8List raw_chksms_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms);
|
|
|
|
U64 count = 0;
|
|
|
|
for (String8Node *raw_chksms_n = raw_chksms_list.first; raw_chksms_n != 0; raw_chksms_n = raw_chksms_n->next) {
|
|
for(U64 cursor = 0; cursor + sizeof(CV_C13Checksum) <= raw_chksms_n->string.size; ) {
|
|
// parse header
|
|
CV_C13Checksum *header = (CV_C13Checksum *) (raw_chksms_n->string.str + cursor);
|
|
|
|
// update count
|
|
++count;
|
|
|
|
// advance cursor
|
|
cursor += sizeof(*header);
|
|
cursor += header->len;
|
|
cursor = AlignPow2(cursor, CV_FileCheckSumsAlign);
|
|
}
|
|
}
|
|
|
|
// update total count
|
|
ins_atomic_u64_add_eval(&task->total_src_file_count, count);
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_insert_src_files_task)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
U64 obj_idx = task_id;
|
|
LNK_ConvertSourceFilesToRDITask *task = raw_task;
|
|
CV_DebugS debug_s = task->debug_s_arr[obj_idx];
|
|
String8List raw_chksms_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms);
|
|
String8List raw_strtab_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_StringTable);
|
|
|
|
if (raw_strtab_list.node_count > 1) {
|
|
lnk_error_obj(LNK_Warning_IllData, &task->obj_arr[obj_idx], "Multiple string table sub-sections, picking first one.");
|
|
}
|
|
if (raw_chksms_list.node_count > 1) {
|
|
lnk_error_obj(LNK_Warning_IllData, &task->obj_arr[obj_idx], "Multiple file checksum sub-sections, picking first one.");
|
|
}
|
|
|
|
String8 string_table = cv_string_table_from_debug_s(debug_s);
|
|
LNK_SourceFileBucket *curr_bucket = 0;
|
|
|
|
for (String8Node *raw_chksms_n = raw_chksms_list.first; raw_chksms_n != 0; raw_chksms_n = raw_chksms_n->next) {
|
|
for (U64 cursor = 0; cursor + sizeof(CV_C13Checksum) <= raw_chksms_n->string.size; ) {
|
|
// parse header
|
|
CV_C13Checksum *header = (CV_C13Checksum *) (raw_chksms_n->string.str + cursor);
|
|
|
|
// grab checksum
|
|
String8 checksum = str8_substr(raw_chksms_n->string, rng_1u64(cursor + sizeof(CV_C13Checksum),
|
|
cursor + sizeof(CV_C13Checksum) + header->len));
|
|
|
|
// grab file path
|
|
Assert(header->name_off < string_table.size);
|
|
String8 file_path = str8_cstring_capped(string_table.str + header->name_off, string_table.str + string_table.size);
|
|
|
|
// normalize file path
|
|
String8 normal_path = lnk_normalize_src_file_path(arena, file_path);
|
|
|
|
// push new bucket
|
|
if (curr_bucket == 0) {
|
|
curr_bucket = push_array(arena, LNK_SourceFileBucket, 1);
|
|
curr_bucket->src_file = push_array(arena, RDIB_SourceFile, 1);
|
|
}
|
|
|
|
// fill out obj idx so we can decide which source file to keep in the hash table
|
|
curr_bucket->obj_idx = obj_idx;
|
|
|
|
// fill out part with source file info
|
|
curr_bucket->src_file->file_path = file_path;
|
|
curr_bucket->src_file->normal_full_path = normal_path;
|
|
curr_bucket->src_file->checksum_kind = rdi_checksum_from_cv_c13(header->kind);
|
|
curr_bucket->src_file->checksum = checksum;
|
|
curr_bucket->src_file->line_table_frags = 0;
|
|
|
|
// insert bucket
|
|
U64 normal_path_hash = lnk_src_file_hash_cv(normal_path, header->kind, checksum);
|
|
LNK_SourceFileBucket *insert_result = lnk_src_file_insert_or_update(task->src_file_buckets, task->src_file_buckets_cap, normal_path_hash, curr_bucket);
|
|
|
|
if (curr_bucket == insert_result) {
|
|
// bucket was inserted into empty slot, reset current bucket
|
|
curr_bucket = 0;
|
|
} else if (curr_bucket != insert_result) {
|
|
// reuse evicted bucket
|
|
curr_bucket = insert_result;
|
|
}
|
|
|
|
// advance cursor
|
|
cursor += sizeof(*header);
|
|
cursor += header->len;
|
|
cursor = AlignPow2(cursor, CV_FileCheckSumsAlign);
|
|
}
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal RDIB_Type *
|
|
lnk_find_container_type(String8 name, Rng1U64 tpi_itype_range, LNK_UDTNameBucket **udt_name_buckets, U64 udt_name_buckets_cap, RDIB_Type **tpi_itype_map)
|
|
{
|
|
CV_TypeIndex container_itype = 0;
|
|
|
|
String8 delim = str8_lit("::");
|
|
U64 delim_pos = str8_find_needle_reverse(name, 0, delim, 0);
|
|
if (delim_pos > 0) {
|
|
U64 container_name_size = delim_pos - delim.size;
|
|
String8 container_name = str8_prefix(name, container_name_size);
|
|
container_itype = lnk_udt_name_hash_table_lookup_itype(udt_name_buckets, udt_name_buckets_cap, container_name);
|
|
}
|
|
|
|
RDIB_Type *container = 0;
|
|
if (container_itype > 0) {
|
|
Assert(container_itype < tpi_itype_range.max);
|
|
container = tpi_itype_map[container_itype];
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
internal RDIB_Type *
|
|
lnk_type_from_itype(CV_TypeIndex itype, Rng1U64 tpi_itype_range, RDIB_Type **tpi_itype_map, LNK_Obj *obj, CV_SymKind symbol_kind, U64 symbol_offset)
|
|
{
|
|
RDIB_Type *type = 0;
|
|
if (itype < tpi_itype_range.max) {
|
|
type = tpi_itype_map[itype];
|
|
} else {
|
|
lnk_error_obj(LNK_Error_CvIllSymbolData, obj, "Out of bounds type index 0x%x in S_%S @ 0x%llx.",
|
|
itype, cv_string_from_sym_kind(symbol_kind), symbol_offset);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
internal U64
|
|
lnk_voff_from_sect_off(U64 sect_idx, U64 sect_off, COFF_SectionHeaderArray image_sects, LNK_Obj *obj, CV_SymKind symbol_kind, U64 symbol_offset)
|
|
{
|
|
U64 voff = 0;
|
|
if (sect_idx < image_sects.count) {
|
|
voff = image_sects.v[sect_idx].voff + sect_off;
|
|
} else {
|
|
lnk_error_obj(LNK_Error_CvIllSymbolData, obj, "Out of bounds section index 0x%x in S_%S @ 0x%llx.",
|
|
sect_idx, cv_string_from_sym_kind(symbol_kind), symbol_offset);
|
|
}
|
|
return voff;
|
|
}
|
|
|
|
internal Rng1U64
|
|
lnk_virt_range_from_sect_off_size(U64 sect_idx, U64 sect_off, U64 size, COFF_SectionHeaderArray image_sects, LNK_Obj *obj, CV_SymKind symbol_kind, U64 symbol_offset)
|
|
{
|
|
Rng1U64 virt_range = {0};
|
|
if (sect_idx < image_sects.count) {
|
|
U64 voff = image_sects.v[sect_idx].voff + sect_off;
|
|
virt_range = rng_1u64(voff, voff + size);
|
|
} else {
|
|
lnk_error_obj(LNK_Error_CvIllSymbolData, obj, "Out of bounds section index 0x%x in S_%S @ 0x%llx.",
|
|
sect_idx, cv_string_from_sym_kind(symbol_kind), symbol_offset);
|
|
}
|
|
return virt_range;
|
|
}
|
|
|
|
internal void
|
|
lnk_error_on_invalid_defrange_symbol(LNK_Obj *obj, CV_Symbol symbol)
|
|
{
|
|
lnk_error_obj(LNK_Error_CvIllSymbolData, obj, "Unable to parse symbol stream, unexpected S_%S without preceding S_LOCAL @ 0x%llx.",
|
|
cv_string_from_sym_kind(symbol.kind), symbol.offset);
|
|
}
|
|
|
|
internal void
|
|
lnk_error_on_missing_cv_frameproc(LNK_Obj *obj, CV_Symbol symbol)
|
|
{
|
|
lnk_error_obj(LNK_Error_CvIllSymbolData, obj, "Missing S_FRAMEPROC, unable to parse S_%S @ 0x%llx.",
|
|
cv_string_from_sym_kind(symbol.kind), symbol.offset);
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_find_obj_compiler_info_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_ConvertUnitToRDITask *task = raw_task;
|
|
CV_SymbolListArray parsed_symbols = task->parsed_symbols[task_id];
|
|
LNK_CodeViewCompilerInfo *comp_info = &task->comp_info_arr[task_id];
|
|
|
|
comp_info->arch = (CV_Arch)~0u;
|
|
comp_info->language = (CV_Language)~0u;
|
|
comp_info->compiler_name = str8_zero();
|
|
|
|
// infer unit compiler data from S_COMPILE* which always follows S_OBJ
|
|
for (U64 symbol_list_idx = 0; symbol_list_idx < parsed_symbols.count; ++symbol_list_idx) {
|
|
CV_SymbolList symbol_list = parsed_symbols.v[symbol_list_idx];
|
|
for (CV_SymbolNode *symbol_n = symbol_list.first; symbol_n != 0; symbol_n = symbol_n->next) {
|
|
CV_Symbol symbol = symbol_n->data;
|
|
if (symbol.kind == CV_SymKind_COMPILE) {
|
|
AssertAlways(sizeof(CV_SymCompile) <= symbol.data.size);
|
|
CV_SymCompile *compile = (CV_SymCompile *)symbol.data.str;
|
|
comp_info->arch = compile->machine;
|
|
comp_info->language = CV_CompileFlags_Extract_Language(compile->flags);
|
|
comp_info->compiler_name = str8_cstring_capped(compile + 1, symbol.data.str + symbol.data.size);
|
|
goto exit;
|
|
} else if (symbol.kind == CV_SymKind_COMPILE2) {
|
|
AssertAlways(sizeof(CV_SymCompile2) <= symbol.data.size);
|
|
CV_SymCompile2 *compile2 = (CV_SymCompile2 *)symbol.data.str;
|
|
comp_info->arch = compile2->machine;
|
|
comp_info->language = CV_Compile2Flags_Extract_Language(compile2->flags);
|
|
comp_info->compiler_name = str8_cstring_capped(compile2 + 1, symbol.data.str + symbol.data.size);
|
|
goto exit;
|
|
} else if (symbol.kind == CV_SymKind_COMPILE3) {
|
|
AssertAlways(sizeof(CV_SymCompile3) <= symbol.data.size);
|
|
CV_SymCompile3 *compile3 = (CV_SymCompile3 *)symbol.data.str;
|
|
comp_info->arch = compile3->machine;
|
|
comp_info->language = CV_Compile3Flags_Extract_Language(compile3->flags);
|
|
comp_info->compiler_name = str8_cstring_capped(compile3 + 1, symbol.data.str + symbol.data.size);
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
exit:;
|
|
|
|
LNK_Obj *obj = &task->obj_arr[task_id];
|
|
|
|
// fill out unit info
|
|
U64 unit_chunk_idx = task_id / task->unit_chunk_cap;
|
|
U64 local_unit_idx = task_id - unit_chunk_idx * task->unit_chunk_cap;
|
|
|
|
RDIB_Unit *dst = &task->units[unit_chunk_idx].v[local_unit_idx];
|
|
dst->arch = rdi_arch_from_cv_arch(comp_info->arch);
|
|
dst->unit_name = str8_skip_last_slash(obj->path);
|
|
dst->compiler_name = comp_info->compiler_name;
|
|
dst->source_file = str8_zero();
|
|
dst->object_file = push_str8_copy(arena, obj->path);
|
|
dst->archive_file = push_str8_copy(arena, obj->lib_path);
|
|
dst->build_path = str8_zero();
|
|
dst->language = rdi_language_from_cv_language(comp_info->language);
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_convert_line_tables_to_rdi_task)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
U64 unit_idx = task_id;
|
|
LNK_ConvertUnitToRDITask *task = raw_task;
|
|
LNK_Obj *obj = &task->obj_arr[unit_idx];
|
|
CV_DebugS debug_s = task->debug_s_arr[unit_idx];
|
|
|
|
U64 unit_chunk_idx = unit_idx / task->unit_chunk_cap;
|
|
U64 local_unit_idx = unit_idx - unit_chunk_idx * task->unit_chunk_cap;
|
|
RDIB_Unit *dst = &task->units[unit_chunk_idx].v[local_unit_idx];
|
|
|
|
// find sub sections
|
|
String8 raw_string_table = cv_string_table_from_debug_s(debug_s);
|
|
String8 raw_file_chksms = cv_file_chksms_from_debug_s(debug_s);
|
|
String8List raw_lines_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_Lines);
|
|
|
|
// emit line table fragments for each source file from C13 line info
|
|
dst->line_table = rdib_line_table_chunk_list_push(arena, &task->line_tables[worker_id], task->line_table_cap);
|
|
|
|
for (String8Node *raw_lines_node = raw_lines_list.first; raw_lines_node != 0; raw_lines_node = raw_lines_node->next) {
|
|
String8 raw_lines = raw_lines_node->string;
|
|
CV_C13LinesHeaderList parsed_list = cv_c13_lines_from_sub_sections(scratch.arena, raw_lines, rng_1u64(0, raw_lines.size));
|
|
for (CV_C13LinesHeaderNode *lines_node = parsed_list.first; lines_node != 0; lines_node = lines_node->next) {
|
|
CV_C13LinesHeader parsed_lines = lines_node->v;
|
|
|
|
// parse checksum header
|
|
if (parsed_lines.file_off + sizeof(CV_C13Checksum) > raw_file_chksms.size) {
|
|
lnk_error_obj(LNK_Warning_IllData, obj, "Out of bounds $$FILE_CHECKSUM offset (0x%llx) in line table header.", parsed_lines.file_off);
|
|
continue;
|
|
}
|
|
CV_C13Checksum *checksum_header = (CV_C13Checksum *) (raw_file_chksms.str + parsed_lines.file_off);
|
|
if (parsed_lines.file_off + sizeof(CV_C13Checksum) + checksum_header->len > raw_file_chksms.size) {
|
|
lnk_error_obj(LNK_Warning_IllData, obj, "Not enough bytes to read file checksum @ 0x%llx.", parsed_lines.file_off);
|
|
continue;
|
|
}
|
|
String8 file_path = str8_cstring_capped(raw_string_table.str + checksum_header->name_off, raw_string_table.str + raw_string_table.size);
|
|
String8 checksum_bytes = str8((U8 *) (checksum_header + 1), checksum_header->len);
|
|
|
|
// read out lines
|
|
if (0 == parsed_lines.sec_idx || parsed_lines.sec_idx > task->image_sects.count) {
|
|
lnk_error_obj(LNK_Warning_IllData, obj, "Out of bounds section index (%u) in $$LINES; skip line info for \"%S\".", parsed_lines.sec_idx, file_path);
|
|
continue;
|
|
}
|
|
COFF_SectionHeader *sect = &task->image_sects.v[parsed_lines.sec_idx];
|
|
CV_LineArray lines = cv_c13_line_array_from_data(arena, raw_lines, sect->voff, parsed_lines);
|
|
|
|
// find source file for this line table
|
|
String8 normal_path = lnk_normalize_src_file_path(scratch.arena, file_path);
|
|
U64 src_file_hash = lnk_src_file_hash_cv(normal_path, checksum_header->kind, checksum_bytes);
|
|
LNK_SourceFileBucket *src_file_bucket = lnk_src_file_hash_table_lookup_slot(task->src_file_buckets, task->src_file_buckets_cap, src_file_hash, normal_path, checksum_header->kind, checksum_bytes);
|
|
if (src_file_bucket == 0) {
|
|
lnk_error_obj(LNK_Error_UnexpectedCodePath, obj, "Unable to find source file in the hash table: \"%S\".", file_path);
|
|
continue;
|
|
}
|
|
RDIB_SourceFile *src_file = src_file_bucket->src_file;
|
|
|
|
// fill out line table fragment and atomically insert
|
|
RDIB_LineTableFragment *frag = rdib_line_table_push(arena, dst->line_table);
|
|
frag->src_file = src_file;
|
|
frag->voffs = lines.voffs;
|
|
frag->line_nums = lines.line_nums;
|
|
frag->col_nums = lines.col_nums;
|
|
frag->line_count = lines.line_count;
|
|
frag->col_count = lines.col_count;
|
|
|
|
// build list of line table fragments per file
|
|
frag->next_src_file = ins_atomic_ptr_eval_assign(&src_file->line_table_frags, frag);
|
|
}
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_build_inlinee_lines_accels_task)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
LNK_ConvertUnitToRDITask *task = raw_task;
|
|
CV_DebugS debug_s = task->debug_s_arr[task_id];
|
|
|
|
String8List raw_inlinee_lines = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_InlineeLines);
|
|
CV_C13InlineeLinesParsedList inlinee_lines = cv_c13_inlinee_lines_from_sub_sections(arena, raw_inlinee_lines);
|
|
CV_InlineeLinesAccel *inlinee_lines_accel = cv_c13_make_inlinee_lines_accel(arena, inlinee_lines);
|
|
|
|
task->inlinee_lines_accel_arr[task_id] = inlinee_lines_accel;
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_convert_symbols_to_rdi_task)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
LNK_ConvertUnitToRDITask *task = raw_task;
|
|
LNK_CodeViewSymbolsInput symbols_input = task->symbol_inputs[task_id];
|
|
LNK_Obj *obj = &task->obj_arr[symbols_input.obj_idx];
|
|
LNK_CodeViewCompilerInfo comp_info = task->comp_info_arr[symbols_input.obj_idx];
|
|
CV_InlineeLinesAccel *inlinee_lines_accel = task->inlinee_lines_accel_arr[symbols_input.obj_idx];
|
|
|
|
RDI_Arch arch_rdi = rdi_arch_from_cv_arch(comp_info.arch);
|
|
|
|
struct ScopeFrame {
|
|
struct ScopeFrame *prev;
|
|
RDIB_Scope *scope;
|
|
RDIB_Procedure *proc;
|
|
CV_ProcFlags proc_flags;
|
|
CV_SymFrameproc *frameproc;
|
|
U64 param_count;
|
|
U64 regrel32_idx;
|
|
RDIB_Variable *defrange_target;
|
|
};
|
|
#define push_scope_frame() do { \
|
|
struct ScopeFrame *frame; \
|
|
if (free_scope_stack != 0) { \
|
|
frame = free_scope_stack; \
|
|
SLLStackPop_N(free_scope_stack, prev); \
|
|
} else { \
|
|
frame = push_array(scratch.arena, struct ScopeFrame, 1); \
|
|
} \
|
|
SLLStackPush_N(scope_stack, frame, prev); \
|
|
} while (0)
|
|
|
|
struct ScopeFrame *scope_stack = 0;
|
|
struct ScopeFrame *free_scope_stack = 0;
|
|
|
|
// root frame
|
|
push_scope_frame();
|
|
|
|
for (CV_SymbolNode *symbol_n = symbols_input.symbol_list->first; symbol_n != 0; symbol_n = symbol_n->next) {
|
|
CV_Symbol symbol = symbol_n->data;
|
|
|
|
switch (symbol.kind) {
|
|
case CV_SymKind_COMPILE:
|
|
case CV_SymKind_COMPILE2:
|
|
case CV_SymKind_COMPILE3: {
|
|
// handled above
|
|
} break;
|
|
case CV_SymKind_INLINESITE_END:
|
|
case CV_SymKind_END: {
|
|
if (scope_stack != 0) {
|
|
// move top frame to free stack
|
|
struct ScopeFrame *free_frame = scope_stack;
|
|
SLLStackPop_N(scope_stack, prev);
|
|
SLLStackPush_N(free_scope_stack, free_frame, prev);
|
|
} else {
|
|
lnk_error_obj(LNK_Error_CvIllSymbolData, obj, "Encountered unbalanced blocks. Unable to finish symbol parse.");
|
|
goto exit;
|
|
}
|
|
} break;
|
|
case CV_SymKind_BLOCK32: {
|
|
CV_SymBlock32 *block32 = (CV_SymBlock32 *) symbol.data.str;
|
|
Rng1U64 virt_range = lnk_virt_range_from_sect_off_size(block32->sec, block32->off, block32->len, task->image_sects, obj, symbol.kind, symbol.offset);
|
|
|
|
// push new scope node
|
|
RDIB_Scope *scope = rdib_scope_chunk_list_push(arena, &task->scopes[worker_id], task->symbol_chunk_cap);
|
|
|
|
// fill out scope
|
|
scope->container_proc = scope_stack->proc;
|
|
scope->parent = scope_stack->scope;
|
|
SLLQueuePush_N(scope_stack->scope->first_child, scope_stack->scope->last_child, scope, next_sibling);
|
|
rng1u64_list_push(arena, &scope->ranges, virt_range);
|
|
|
|
#if 0
|
|
if (scope->parent) {
|
|
Assert(virt_range.min >= scope->parent->ranges.first->v.min);
|
|
Assert(virt_range.max <= scope->parent->ranges.first->v.max);
|
|
}
|
|
#endif
|
|
|
|
// push new scope stack frame
|
|
push_scope_frame();
|
|
scope_stack->scope = scope;
|
|
scope_stack->proc = scope->container_proc;
|
|
scope_stack->proc_flags = scope_stack->proc_flags;
|
|
scope_stack->frameproc = scope_stack->prev->frameproc;
|
|
} break;
|
|
case CV_SymKind_GDATA32:
|
|
case CV_SymKind_LDATA32: {
|
|
CV_SymData32 *data32 = (CV_SymData32 *) symbol.data.str;
|
|
String8 name = str8_cstring_capped(data32 + 1, symbol.data.str + symbol.data.size);
|
|
RDIB_Type *type = lnk_type_from_itype(data32->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
RDIB_Type *container_type = lnk_find_container_type(name, task->tpi_itype_range, task->udt_name_buckets, task->udt_name_buckets_cap, task->tpi_itype_map);
|
|
U64 data_voff = lnk_voff_from_sect_off(data32->sec, data32->off, task->image_sects, obj, symbol.kind, symbol.offset);
|
|
|
|
B32 is_comp_gen = symbol.kind == CV_SymKind_LDATA32 && name.size == 0 && type == 0;
|
|
if (!is_comp_gen) {
|
|
|
|
// get link name through virtual offset look up
|
|
String8 link_name = {0};
|
|
if (symbol.kind == CV_SymKind_GDATA32) {
|
|
KeyValuePair *pair = hash_table_search_u64(task->extern_symbol_voff_ht, data_voff);
|
|
if (pair != 0) {
|
|
LNK_Symbol *link_symbol = pair->value_raw;
|
|
link_name = link_symbol->name;
|
|
}
|
|
}
|
|
|
|
// make module relative location
|
|
RDIB_LocationList locations = {0};
|
|
{
|
|
RDIB_EvalBytecode bytecode = {0};
|
|
rdib_bytecode_push_op(arena, &bytecode, RDI_EvalOp_ModuleOff, data_voff);
|
|
|
|
U64 data_size = rdib_size_from_type(type);
|
|
if (data_size == 0) {
|
|
data_size = max_U64;
|
|
}
|
|
|
|
Rng1U64List ranges = {0};
|
|
rng1u64_list_push(arena, &ranges, rng_1u64(data_voff, data_voff + data_size));
|
|
|
|
RDIB_Location location = rdib_make_location_addr_byte_stream(ranges, bytecode);
|
|
rdib_location_list_push(arena, &locations, location);
|
|
}
|
|
|
|
RDIB_VariableChunkList *var_chunk_list = symbol.kind == CV_SymKind_GDATA32 ?
|
|
&task->extern_gvars[worker_id] : &task->static_gvars[worker_id];
|
|
|
|
// push new node
|
|
RDIB_Variable *gvar = rdib_variable_chunk_list_push(arena, var_chunk_list, task->symbol_chunk_cap);
|
|
gvar->link_flags = symbol.kind == CV_SymKind_GDATA32 ? RDI_LinkFlag_External : 0;
|
|
gvar->name = name;
|
|
gvar->link_name = link_name;
|
|
gvar->type = type;
|
|
gvar->container_type = container_type;
|
|
gvar->container_proc = scope_stack->proc;
|
|
gvar->locations = locations;
|
|
}
|
|
} break;
|
|
case CV_SymKind_LTHREAD32:
|
|
case CV_SymKind_GTHREAD32: {
|
|
CV_SymThread32 *thread32 = (CV_SymThread32 *) symbol.data.str;
|
|
String8 name = str8_cstring_capped(thread32 + 1, symbol.data.str + symbol.data.size);
|
|
RDIB_Type *type = lnk_type_from_itype(thread32->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
RDIB_Type *container_type = lnk_find_container_type(name, task->tpi_itype_range, task->udt_name_buckets, task->udt_name_buckets_cap, task->tpi_itype_map);
|
|
|
|
// make TLS offset location
|
|
RDIB_LocationList locations = {0};
|
|
{
|
|
RDIB_EvalBytecode bytecode = {0};
|
|
rdib_bytecode_push_op(arena, &bytecode, RDI_EvalOp_TLSOff, thread32->tls_off);
|
|
|
|
Rng1U64List ranges = {0};
|
|
rng1u64_list_push(arena, &ranges, rng_1u64(0, max_U64));
|
|
|
|
RDIB_Location location = rdib_make_location_addr_byte_stream(ranges, bytecode);
|
|
rdib_location_list_push(arena, &locations, location);
|
|
}
|
|
|
|
// push new node
|
|
RDIB_VariableChunkList *tvar_list = symbol.kind == CV_SymKind_GTHREAD32 ? &task->extern_tvars[worker_id] : &task->static_tvars[worker_id];
|
|
RDIB_Variable *tvar = rdib_variable_chunk_list_push(arena, tvar_list, task->symbol_chunk_cap);
|
|
|
|
// fill out thread variable
|
|
tvar->link_flags = symbol.kind == CV_SymKind_GTHREAD32 ? RDI_LinkFlag_External : 0;
|
|
tvar->name = name;
|
|
tvar->link_name = str8(0,0);
|
|
tvar->type = type;
|
|
tvar->container_type = container_type;
|
|
tvar->container_proc = scope_stack->proc;
|
|
tvar->locations = locations;
|
|
} break;
|
|
case CV_SymKind_LPROC32_ID:
|
|
case CV_SymKind_GPROC32_ID: {
|
|
AssertAlways(!"linker converts *_ID symbols in post-process step, if we ever get to this case then we have a bug in post-process step");
|
|
} break;
|
|
case CV_SymKind_LPROC32:
|
|
case CV_SymKind_GPROC32: {
|
|
CV_SymProc32 *proc32 = (CV_SymProc32 *) symbol.data.str;
|
|
String8 name = str8_cstring_capped(proc32 + 1, symbol.data.str + symbol.data.size);
|
|
RDIB_Type *type = lnk_type_from_itype(proc32->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
Rng1U64 virt_range = lnk_virt_range_from_sect_off_size(proc32->sec, proc32->off, proc32->len, task->image_sects, obj, symbol.kind, symbol.offset);
|
|
|
|
// infer container type for method
|
|
RDIB_Type *container_type = 0;
|
|
if (type != 0) {
|
|
if (type->kind == RDI_TypeKind_Method) {
|
|
container_type = (RDIB_Type *) type->method.class_type;
|
|
} else if (type->kind == RDI_TypeKindExt_StaticMethod) {
|
|
container_type = (RDIB_Type *) type->static_method.class_type;
|
|
}
|
|
}
|
|
|
|
// get link name through virtual offset look up
|
|
String8 link_name = str8(0,0);
|
|
if (symbol.kind == CV_SymKind_GPROC32) {
|
|
KeyValuePair *pair = hash_table_search_u64(task->extern_symbol_voff_ht, virt_range.min);
|
|
if (pair != 0) {
|
|
LNK_Symbol *link_symbol = pair->value_raw;
|
|
link_name = link_symbol->name;
|
|
}
|
|
}
|
|
|
|
// scan ahead for context S_FRAMEPROC (must be defined in scope of PROC symbol)
|
|
CV_SymFrameproc *frameproc = 0;
|
|
{
|
|
U64 depth = 1;
|
|
for (CV_SymbolNode *lookahead = symbol_n->next; lookahead != 0; lookahead = lookahead->next) {
|
|
if (lookahead->data.kind == CV_SymKind_FRAMEPROC) {
|
|
frameproc = (CV_SymFrameproc *) lookahead->data.data.str;
|
|
break;
|
|
}
|
|
if (cv_is_scope_symbol(lookahead->data.kind)) {
|
|
++depth;
|
|
} else if (cv_is_end_symbol(lookahead->data.kind)) {
|
|
--depth;
|
|
if (depth == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// push new procedure node
|
|
RDIB_ProcedureChunkList *proc_list = symbol.kind == CV_SymKind_GPROC32 ? &task->extern_procs[worker_id] : &task->static_procs[worker_id];
|
|
RDIB_Procedure *proc = rdib_procedure_chunk_list_push(arena, proc_list, task->symbol_chunk_cap);
|
|
|
|
// push new scope node
|
|
RDIB_Scope *root_scope = rdib_scope_chunk_list_push(arena, &task->scopes[worker_id], task->symbol_chunk_cap);
|
|
root_scope->container_proc = proc;
|
|
root_scope->parent = scope_stack->scope;
|
|
if (scope_stack->scope != 0) {
|
|
SLLQueuePush_N(scope_stack->scope->first_child, scope_stack->scope->last_child, root_scope, next_sibling);
|
|
}
|
|
rng1u64_list_push(arena, &root_scope->ranges, virt_range);
|
|
|
|
// fill out procedure
|
|
proc->link_flags = symbol.kind == CV_SymKind_GPROC32 ? RDI_LinkFlag_External : 0;
|
|
proc->name = name;
|
|
proc->link_name = link_name;
|
|
proc->type = type;
|
|
proc->container_type = container_type;
|
|
proc->container_proc = scope_stack->proc;
|
|
proc->scope = root_scope;
|
|
|
|
// push scope frame
|
|
push_scope_frame();
|
|
scope_stack->scope = root_scope;
|
|
scope_stack->proc = proc;
|
|
scope_stack->proc_flags = proc32->flags;
|
|
scope_stack->frameproc = frameproc;
|
|
|
|
// set number of params for procedure on scope so we can figure out which S_REGREL32 is param
|
|
{
|
|
B32 is_proc_scope = (scope_stack->proc->scope == scope_stack->scope);
|
|
if (is_proc_scope) {
|
|
RDIB_Type *params = 0;
|
|
if (scope_stack->proc != 0) {
|
|
RDIB_Type *proc_type = scope_stack->proc->type;
|
|
if (proc_type != 0) {
|
|
if (proc_type->kind == RDI_TypeKind_NULL) {
|
|
// compiler generates procedures with no type for __try/__except, lambdas, and etc.
|
|
} else if (proc_type->kind == RDI_TypeKind_Function) {
|
|
params = (RDIB_Type *)proc_type->func.params_type;
|
|
} else if (proc_type->kind == RDI_TypeKind_Method) {
|
|
params = (RDIB_Type *)proc_type->method.params_type;
|
|
} else if (proc_type->kind == RDI_TypeKindExt_StaticMethod) {
|
|
params = (RDIB_Type *)proc_type->static_method.params_type;
|
|
} else {
|
|
InvalidPath;
|
|
}
|
|
}
|
|
}
|
|
if (params != 0) {
|
|
AssertAlways(params->kind == RDI_TypeKindExt_Params);
|
|
scope_stack->param_count = params->params.count;
|
|
scope_stack->regrel32_idx = 0;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case CV_SymKind_THUNK32: {
|
|
CV_SymThunk32 *thunk32 = (CV_SymThunk32 *) symbol.data.str;
|
|
String8 name = str8_cstring_capped(thunk32 + 1, symbol.data.str + symbol.data.size);
|
|
Rng1U64 virt_range = lnk_virt_range_from_sect_off_size(thunk32->sec, thunk32->off, thunk32->len, task->image_sects, obj, symbol.kind, symbol.offset);
|
|
|
|
// scan ahead for context S_FRAMEPROC (must be defined in scope of PROC symbol)
|
|
CV_SymFrameproc *frameproc = 0;
|
|
{
|
|
U64 depth = 1;
|
|
for (CV_SymbolNode *lookahead = symbol_n->next; lookahead != 0; lookahead = lookahead->next) {
|
|
if (lookahead->data.kind == CV_SymKind_FRAMEPROC) {
|
|
frameproc = (CV_SymFrameproc *) lookahead->data.data.str;
|
|
break;
|
|
}
|
|
if (cv_is_scope_symbol(lookahead->data.kind)) {
|
|
++depth;
|
|
} else if (cv_is_end_symbol(lookahead->data.kind)) {
|
|
--depth;
|
|
if (depth == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// push new procedure node
|
|
RDIB_ProcedureChunkList *proc_list = &task->static_procs[worker_id];
|
|
RDIB_Procedure *thunk = rdib_procedure_chunk_list_push(arena, proc_list, task->symbol_chunk_cap);
|
|
|
|
// push new scope node
|
|
RDIB_Scope *root_scope = rdib_scope_chunk_list_push(arena, &task->scopes[worker_id], task->symbol_chunk_cap);
|
|
root_scope->container_proc = thunk;
|
|
root_scope->parent = scope_stack->scope;
|
|
if (scope_stack->scope != 0) {
|
|
SLLQueuePush_N(scope_stack->scope->first_child, scope_stack->scope->last_child, root_scope, next_sibling);
|
|
}
|
|
rng1u64_list_push(arena, &root_scope->ranges, virt_range);
|
|
|
|
// fill out procedure
|
|
thunk->name = name;
|
|
thunk->type = 0;
|
|
thunk->scope = root_scope;
|
|
|
|
// push scope frame
|
|
push_scope_frame();
|
|
scope_stack->scope = root_scope;
|
|
scope_stack->proc = thunk;
|
|
scope_stack->proc_flags = 0;
|
|
scope_stack->frameproc = frameproc;
|
|
} break;
|
|
case CV_SymKind_REGREL32: {
|
|
if (~scope_stack->proc_flags & CV_ProcFlag_OptDbgInfo) {
|
|
CV_SymRegrel32 *regrel32 = (CV_SymRegrel32 *) symbol.data.str;
|
|
String8 name = str8_cstring_capped(regrel32 + 1, symbol.data.str + symbol.data.size);
|
|
RDIB_Type *type = lnk_type_from_itype(regrel32->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
|
|
RDI_LocalKind local_kind = RDI_LocalKind_Variable;
|
|
B32 is_ref = 0;
|
|
if (scope_stack->regrel32_idx < scope_stack->param_count) {
|
|
local_kind = RDI_LocalKind_Parameter;
|
|
if (type != 0) {
|
|
U64 byte_size = rdib_size_from_type(type);
|
|
switch (comp_info.arch) {
|
|
case CV_Arch_8086: is_ref = byte_size > 4 || !IsPow2OrZero(byte_size); break;
|
|
case CV_Arch_X64: is_ref = byte_size > 8 || !IsPow2OrZero(byte_size); break;
|
|
default: NotImplemented;
|
|
}
|
|
}
|
|
}
|
|
|
|
// push node
|
|
RDIB_Variable *local = rdib_variable_chunk_list_push(arena, &task->locals[worker_id], task->symbol_chunk_cap);
|
|
SLLQueuePush(scope_stack->scope->local_first, scope_stack->scope->local_last, local);
|
|
++scope_stack->scope->local_count;
|
|
|
|
// fill out local
|
|
local->link_flags = 0;
|
|
local->name = name;
|
|
local->kind = local_kind;
|
|
local->type = type;
|
|
|
|
// encode location
|
|
RDI_RegCode reg_code = rdi_reg_code_from_cv(comp_info.arch, regrel32->reg);
|
|
U32 value_size = 8;
|
|
U32 value_pos = 0;
|
|
rdib_push_location_addr_reg_off(arena, &local->locations, arch_rdi, reg_code, value_size, value_pos, (S64)regrel32->reg_off, is_ref, scope_stack->scope->ranges);
|
|
|
|
// advance reg rel index
|
|
++scope_stack->regrel32_idx;
|
|
}
|
|
} break;
|
|
case CV_SymKind_LOCAL: {
|
|
CV_SymLocal *sym_local = (CV_SymLocal *) symbol.data.str;
|
|
String8 name = str8_cstring_capped(sym_local + 1, symbol.data.str + symbol.data.size);
|
|
RDIB_Type *type = lnk_type_from_itype(sym_local->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
|
|
// reset defrange target
|
|
scope_stack->defrange_target = 0;
|
|
|
|
if (sym_local->flags & CV_LocalFlag_Global) {
|
|
// TODO: apply global modifications
|
|
} else if (sym_local->flags & CV_LocalFlag_Static) {
|
|
// TODO: apply local modifications
|
|
}
|
|
|
|
// push New node
|
|
RDIB_Variable *local = rdib_variable_chunk_list_push(arena, &task->locals[worker_id], task->symbol_chunk_cap);
|
|
SLLQueuePush(scope_stack->scope->local_first, scope_stack->scope->local_last, local);
|
|
++scope_stack->scope->local_count;
|
|
|
|
// fill out local
|
|
local->link_flags = 0;
|
|
local->kind = sym_local->flags & CV_LocalFlag_Param ? RDI_LocalKind_Parameter : RDI_LocalKind_Variable;
|
|
local->name = name;
|
|
local->type = type;
|
|
|
|
scope_stack->defrange_target = local;
|
|
} break;
|
|
case CV_SymKind_FILESTATIC: {
|
|
CV_SymFileStatic *file_static = (CV_SymFileStatic *) symbol.data.str;
|
|
String8 name = str8_cstring_capped(file_static + 1, symbol.data.str + symbol.data.size);
|
|
RDIB_Type *type = lnk_type_from_itype(file_static->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
|
|
// push New node
|
|
RDIB_Variable *local = rdib_variable_chunk_list_push(arena, &task->locals[worker_id], task->symbol_chunk_cap);
|
|
SLLQueuePush(scope_stack->scope->local_first, scope_stack->scope->local_last, local);
|
|
++scope_stack->scope->local_count;
|
|
|
|
// fill out local
|
|
local->link_flags = 0;
|
|
local->kind = RDI_LocalKind_Variable;
|
|
local->name = name;
|
|
local->type = type;
|
|
|
|
// set target for following defrange modifications
|
|
scope_stack->defrange_target = local;
|
|
} break;
|
|
case CV_SymKind_DEFRANGE_REGISTER: {
|
|
if (scope_stack->defrange_target == 0) {
|
|
lnk_error_on_invalid_defrange_symbol(obj, symbol);
|
|
break;
|
|
}
|
|
|
|
CV_SymDefrangeRegister *defrange_reg = (CV_SymDefrangeRegister *) symbol.data.str;
|
|
RDI_RegCode reg_code = rdi_reg_code_from_cv(comp_info.arch, defrange_reg->reg);
|
|
CV_LvarAddrGap *gaps = (CV_LvarAddrGap *) (defrange_reg + 1);
|
|
U64 gap_count = (symbol.data.size - sizeof(*defrange_reg)) / sizeof(*gaps);
|
|
|
|
Rng1U64 defrange = lnk_virt_range_from_sect_off_size(defrange_reg->range.sec, defrange_reg->range.off, defrange_reg->range.len, task->image_sects, obj, symbol.kind, symbol.offset);
|
|
Rng1U64List ranges = cv_make_defined_range_list_from_gaps(arena, defrange, gaps, gap_count);
|
|
RDIB_Location location = rdib_make_location_val_reg(ranges, reg_code);
|
|
|
|
rdib_location_list_push(arena, &scope_stack->defrange_target->locations, location);
|
|
} break;
|
|
case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL: {
|
|
if (scope_stack->defrange_target == 0) {
|
|
lnk_error_on_invalid_defrange_symbol(obj, symbol);
|
|
break;
|
|
}
|
|
if (scope_stack->frameproc == 0) {
|
|
lnk_error_on_missing_cv_frameproc(obj, symbol);
|
|
break;
|
|
}
|
|
|
|
CV_SymDefrangeFramepointerRel *defrange_fprel = (CV_SymDefrangeFramepointerRel *)symbol.data.str;
|
|
CV_LvarAddrGap *gaps = (CV_LvarAddrGap *) (defrange_fprel + 1);
|
|
U64 gap_count = (symbol.data.size - sizeof(*defrange_fprel)) / sizeof(gaps[0]);
|
|
|
|
B32 is_local_param = scope_stack->defrange_target->kind == RDI_LocalKind_Parameter;
|
|
CV_EncodedFramePtrReg encoded_fp_reg = cv_pick_fp_encoding(scope_stack->frameproc, is_local_param);
|
|
CV_Reg fp_reg = cv_decode_fp_reg(comp_info.arch, encoded_fp_reg);
|
|
RDI_RegCode fp_reg_rdi = rdi_reg_code_from_cv(comp_info.arch, fp_reg);
|
|
Rng1U64 defrange = lnk_virt_range_from_sect_off_size(defrange_fprel->range.sec, defrange_fprel->range.off, defrange_fprel->range.len, task->image_sects, obj, symbol.kind, symbol.offset);
|
|
Rng1U64List ranges = cv_make_defined_range_list_from_gaps(arena, defrange, gaps, gap_count);
|
|
U32 value_pos = 0;
|
|
U32 value_size = rdi_addr_size_from_arch(arch_rdi);
|
|
|
|
rdib_push_location_addr_reg_off(arena, &scope_stack->defrange_target->locations, arch_rdi, fp_reg_rdi, value_size, value_pos, (S64)defrange_fprel->off, 0, ranges);
|
|
} break;
|
|
case CV_SymKind_DEFRANGE_SUBFIELD_REGISTER: {
|
|
if (scope_stack->defrange_target == 0) {
|
|
lnk_error_on_invalid_defrange_symbol(obj, symbol);
|
|
break;
|
|
}
|
|
|
|
CV_SymDefrangeSubfieldRegister *defrange_subfield_register = (CV_SymDefrangeSubfieldRegister *) symbol.data.str;
|
|
CV_LvarAddrGap *gaps = (CV_LvarAddrGap *) (defrange_subfield_register + 1);
|
|
U64 gap_count = (symbol.data.size - sizeof(*defrange_subfield_register)) / sizeof(gaps[0]);
|
|
RDI_RegCode reg_rdi = rdi_reg_code_from_cv(comp_info.arch, defrange_subfield_register->reg);
|
|
U32 value_pos = CV_DefrangeSubfieldRegister_Extract_ParentOffset(defrange_subfield_register->field_offset);
|
|
U32 value_size = cv_size_from_reg(comp_info.arch, defrange_subfield_register->reg) - value_pos;
|
|
Rng1U64 defrange = lnk_virt_range_from_sect_off_size(defrange_subfield_register->range.sec, defrange_subfield_register->range.off, defrange_subfield_register->range.len, task->image_sects, obj, symbol.kind, symbol.offset);
|
|
Rng1U64List ranges = cv_make_defined_range_list_from_gaps(arena, defrange, gaps, gap_count);
|
|
|
|
rdib_push_location_addr_reg_off(arena, &scope_stack->defrange_target->locations, arch_rdi, reg_rdi, value_size, value_pos, 0, 0, ranges);
|
|
} break;
|
|
case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: {
|
|
if (scope_stack->defrange_target == 0) {
|
|
lnk_error_on_invalid_defrange_symbol(obj, symbol);
|
|
break;
|
|
}
|
|
if (scope_stack->frameproc == 0) {
|
|
lnk_error_on_missing_cv_frameproc(obj, symbol);
|
|
break;
|
|
}
|
|
|
|
CV_SymDefrangeFramepointerRelFullScope *defrange_fprelfs = (CV_SymDefrangeFramepointerRelFullScope *) symbol.data.str;
|
|
B32 is_local_param = scope_stack->defrange_target->kind == RDI_LocalKind_Parameter;
|
|
CV_EncodedFramePtrReg encoded_fp_reg = cv_pick_fp_encoding(scope_stack->frameproc, is_local_param);
|
|
CV_Reg fp_reg = cv_decode_fp_reg(comp_info.arch, encoded_fp_reg);
|
|
RDI_RegCode fp_reg_rdi = rdi_reg_code_from_cv(comp_info.arch, fp_reg);
|
|
U32 value_size = cv_size_from_reg(comp_info.arch, fp_reg);
|
|
U32 value_pos = 0;
|
|
Rng1U64List ranges = scope_stack->scope->ranges; // variable is available everywhere in the scope
|
|
|
|
rdib_push_location_addr_reg_off(arena, &scope_stack->defrange_target->locations, arch_rdi, fp_reg_rdi, value_size, value_pos, (S64)defrange_fprelfs->off, 0, ranges);
|
|
} break;
|
|
case CV_SymKind_DEFRANGE_REGISTER_REL: {
|
|
if (scope_stack->defrange_target == 0) {
|
|
lnk_error_on_invalid_defrange_symbol(obj, symbol);
|
|
break;
|
|
}
|
|
|
|
CV_SymDefrangeRegisterRel *defrange_register_rel = (CV_SymDefrangeRegisterRel *) symbol.data.str;
|
|
CV_LvarAddrGap *gaps = (CV_LvarAddrGap *) (defrange_register_rel + 1);
|
|
U64 gap_count = (symbol.data.size - sizeof(*defrange_register_rel)) / sizeof(gaps[0]);
|
|
RDI_RegCode reg_rdi = rdi_reg_code_from_cv(comp_info.arch, defrange_register_rel->reg);
|
|
U64 value_size = cv_size_from_reg(comp_info.arch, defrange_register_rel->reg);
|
|
U64 value_pos = 0;
|
|
Rng1U64 defrange = lnk_virt_range_from_sect_off_size(defrange_register_rel->range.sec, defrange_register_rel->range.off, defrange_register_rel->range.len, task->image_sects, obj, symbol.kind, symbol.offset);
|
|
Rng1U64List ranges = cv_make_defined_range_list_from_gaps(arena, defrange, gaps, gap_count);
|
|
|
|
rdib_push_location_addr_reg_off(arena, &scope_stack->defrange_target->locations, arch_rdi, reg_rdi, value_size, value_pos, (S64)defrange_register_rel->reg_off, 0, ranges);
|
|
} break;
|
|
case CV_SymKind_INLINESITE: {
|
|
CV_SymInlineSite *sym_inline_site = (CV_SymInlineSite *) symbol.data.str;
|
|
String8 binary_annots = str8_skip(symbol.data, sizeof(*sym_inline_site));
|
|
|
|
U64 parent_voff = 0;
|
|
if (scope_stack != 0) {
|
|
RDIB_Scope *proc_scope = scope_stack->proc->scope;
|
|
Assert(proc_scope->ranges.count == 1);
|
|
Rng1U64 scope_vrange = proc_scope->ranges.first->v;
|
|
parent_voff = scope_vrange.min;
|
|
} else {
|
|
Assert(!"S_INLINESITE doesn't have a parent procedure symbol");
|
|
}
|
|
|
|
// parse binary annots
|
|
CV_C13InlineeLinesParsed *inlinee_parsed = cv_c13_inlinee_lines_accel_find(inlinee_lines_accel, sym_inline_site->inlinee);
|
|
CV_InlineBinaryAnnotsParsed binary_annots_parsed = cv_c13_parse_inline_binary_annots(arena, parent_voff, inlinee_parsed, binary_annots);
|
|
|
|
String8 name = str8_zero();
|
|
RDIB_Type *type = 0;
|
|
RDIB_Type *owner = 0;
|
|
if (task->ipi_itype_range.min <= sym_inline_site->inlinee && sym_inline_site->inlinee < task->ipi_itype_range.max) {
|
|
U64 leaf_idx = sym_inline_site->inlinee - task->tpi_itype_range.min;
|
|
CV_Leaf leaf = cv_debug_t_get_leaf(task->ipi, leaf_idx);
|
|
if (leaf.kind == CV_LeafKind_MFUNC_ID) {
|
|
if (sizeof(CV_LeafMFuncId) <= leaf.data.size) {
|
|
CV_LeafMFuncId *mfunc_id = (CV_LeafMFuncId *) leaf.data.str;
|
|
name = str8_cstring_capped_reverse(mfunc_id + 1, leaf.data.str + leaf.data.size);
|
|
type = lnk_type_from_itype(mfunc_id->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
owner = lnk_type_from_itype(mfunc_id->owner_itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
} else {
|
|
Assert(!"invalid leaf size");
|
|
}
|
|
} else if (leaf.kind == CV_LeafKind_FUNC_ID) {
|
|
if (sizeof(CV_LeafFuncId) <= leaf.data.size) {
|
|
CV_LeafFuncId *func_id = (CV_LeafFuncId *) leaf.data.str;
|
|
name = str8_cstring_capped_reverse(func_id + 1, leaf.data.str + leaf.data.size);
|
|
type = lnk_type_from_itype(func_id->itype, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
owner = lnk_type_from_itype(func_id->scope_string_id, task->tpi_itype_range, task->tpi_itype_map, obj, symbol.kind, symbol.offset);
|
|
} else {
|
|
Assert(!"invalid leaf size");
|
|
}
|
|
} else {
|
|
Assert(!"inlinee must pointer to LF_FUNC_ID or LF_MFUNC_ID");
|
|
}
|
|
} else {
|
|
Assert(!"out of bounds inlinee");
|
|
}
|
|
|
|
// fill out inline site
|
|
RDIB_InlineSite *inline_site = rdib_inline_site_chunk_list_push(arena, &task->inline_sites[worker_id], task->inline_site_cap);
|
|
inline_site->name = name;
|
|
inline_site->type = type;
|
|
inline_site->owner = owner;
|
|
|
|
inline_site->convert_ref.ud0 = binary_annots_parsed.lines;
|
|
inline_site->convert_ref.ud1 = binary_annots_parsed.lines_count;
|
|
inline_site->convert_ref.ud2 = symbols_input.obj_idx;
|
|
|
|
// fill out scope
|
|
RDIB_Scope *scope = rdib_scope_chunk_list_push(arena, &task->scopes[worker_id], task->symbol_chunk_cap);
|
|
scope->container_proc = scope_stack->proc;
|
|
scope->parent = scope_stack->scope;
|
|
scope->inline_site = inline_site;
|
|
scope->ranges = binary_annots_parsed.code_ranges;
|
|
|
|
// push new scope stack frame
|
|
push_scope_frame();
|
|
scope_stack->scope = scope;
|
|
scope_stack->proc = scope->container_proc;
|
|
scope_stack->proc_flags = scope_stack->proc_flags;
|
|
scope_stack->frameproc = scope_stack->prev->frameproc;
|
|
} break;
|
|
default: break;
|
|
}
|
|
}
|
|
exit:;
|
|
|
|
#undef push_scope_frame
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_convert_inline_site_line_tables_task)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
LNK_ConvertUnitToRDITask *task = raw_task;
|
|
RDIB_InlineSiteChunk *chunk = task->inline_site_chunks[task_id];
|
|
|
|
RDIB_LineTableFragmentChunkList frag_chunk_list = {0};
|
|
|
|
for (U64 i = 0; i < chunk->count; ++i) {
|
|
RDIB_InlineSite *inline_site = &chunk->v[i];
|
|
|
|
CV_LineArray *lines_arr = inline_site->convert_ref.ud0;
|
|
U64 lines_count = inline_site->convert_ref.ud1;
|
|
U64 obj_idx = inline_site->convert_ref.ud2;
|
|
|
|
CV_DebugS debug_s = task->debug_s_arr[obj_idx];
|
|
String8 raw_string_table = cv_string_table_from_debug_s(debug_s);
|
|
String8 raw_file_chksms = cv_file_chksms_from_debug_s(debug_s);
|
|
|
|
if (lines_count > 0) {
|
|
inline_site->line_table = rdib_line_table_chunk_list_push(arena, &task->line_tables[worker_id], task->line_table_cap);
|
|
} else {
|
|
inline_site->line_table = task->null_line_table;
|
|
}
|
|
|
|
// emit line tables for each file (yes, it is possbile to split inline site among two or more files via #include)
|
|
for (U64 file_idx = 0; file_idx < lines_count; ++file_idx) {
|
|
CV_LineArray lines = lines_arr[file_idx];
|
|
|
|
// prase checksum header
|
|
CV_C13Checksum *checksum_header = (CV_C13Checksum *) (raw_file_chksms.str + lines.file_off);
|
|
if (lines.file_off + sizeof(CV_C13Checksum) + checksum_header->len > raw_file_chksms.size) {
|
|
LNK_Obj *obj = task->obj_arr + obj_idx;
|
|
lnk_error_obj(LNK_Warning_IllData, obj, "Not enough bytes to read file checksum @ 0x%llx.", lines.file_off);
|
|
continue;
|
|
}
|
|
String8 file_path = str8_cstring_capped(raw_string_table.str + checksum_header->name_off, raw_string_table.str + raw_string_table.size);
|
|
String8 checksum_bytes = str8((U8 *) (checksum_header + 1), checksum_header->len);
|
|
|
|
// find source file for this line table
|
|
String8 normal_path = lnk_normalize_src_file_path(scratch.arena, file_path);
|
|
U64 src_file_hash = lnk_src_file_hash_cv(normal_path, checksum_header->kind, checksum_bytes);
|
|
LNK_SourceFileBucket *src_file_bucket = lnk_src_file_hash_table_lookup_slot(task->src_file_buckets, task->src_file_buckets_cap, src_file_hash, normal_path, checksum_header->kind, checksum_bytes);
|
|
if (src_file_bucket == 0) {
|
|
LNK_Obj *obj = task->obj_arr + obj_idx;
|
|
lnk_error_obj(LNK_Error_UnexpectedCodePath, obj, "Unable to find source file in the hash table: \"%S\".", file_path);
|
|
continue;
|
|
}
|
|
RDIB_SourceFile *src_file = src_file_bucket->src_file;
|
|
|
|
// fill out line table fragment
|
|
RDIB_LineTableFragment *frag = rdib_line_table_fragment_chunk_list_push(arena, &frag_chunk_list, chunk->count);
|
|
frag->src_file = src_file;
|
|
frag->voffs = lines.voffs;
|
|
frag->line_nums = lines.line_nums;
|
|
frag->col_nums = lines.col_nums;
|
|
frag->line_count = lines.line_count;
|
|
frag->col_count = lines.col_count;
|
|
|
|
// build list of fragments per line table
|
|
rdib_line_table_push_fragment_node(inline_site->line_table, frag);
|
|
|
|
// build list of line table fragments per file
|
|
frag->next_src_file = ins_atomic_ptr_eval_assign(&src_file->line_table_frags, frag);
|
|
}
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_collect_obj_virtual_ranges_task)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
LNK_ConvertUnitToRDITask *task = raw_task;
|
|
|
|
U64 unit_idx = task_id;
|
|
LNK_Obj *obj = &task->obj_arr[unit_idx];
|
|
|
|
U64 unit_chunk_idx = unit_idx / task->unit_chunk_cap;
|
|
U64 local_unit_idx = unit_idx - unit_chunk_idx * task->unit_chunk_cap;
|
|
|
|
RDIB_Unit *dst = &task->units[unit_chunk_idx].v[local_unit_idx];
|
|
dst->virt_range_count = 0;
|
|
dst->virt_ranges = push_array_no_zero(arena, Rng1U64, obj->header.section_count_no_null);
|
|
|
|
COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str;
|
|
|
|
for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) {
|
|
COFF_SectionHeader *sect_header = §ion_table[sect_idx];
|
|
|
|
if (sect_header->flags & COFF_SectionFlag_LnkRemove) {
|
|
continue;
|
|
}
|
|
if (sect_header->vsize == 0) {
|
|
continue;
|
|
}
|
|
|
|
dst->virt_ranges[dst->virt_range_count] = rng_1u64(sect_header->voff, sect_header->voff + sect_header->vsize);
|
|
++dst->virt_range_count;
|
|
}
|
|
|
|
// free unused memory
|
|
arena_pop(arena, sizeof(dst->virt_ranges[0]) * (obj->header.section_count_no_null - dst->virt_range_count));
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
internal String8List
|
|
lnk_build_rad_debug_info(TP_Context *tp,
|
|
TP_Arena *tp_arena,
|
|
OperatingSystem os,
|
|
RDI_Arch arch,
|
|
String8 image_name,
|
|
String8 image_data,
|
|
U64 obj_count,
|
|
LNK_Obj *obj_arr,
|
|
CV_DebugS *debug_s_arr,
|
|
U64 total_symbol_input_count,
|
|
LNK_CodeViewSymbolsInput *symbol_inputs,
|
|
CV_SymbolListArray *parsed_symbols,
|
|
CV_DebugT types[CV_TypeIndexSource_COUNT])
|
|
{
|
|
ProfBegin("RDI");
|
|
Temp scratch = scratch_begin(tp_arena->v,tp_arena->count);
|
|
|
|
RDIB_Input input = rdib_init_input(scratch.arena);
|
|
|
|
COFF_SectionHeaderArray image_sects;
|
|
String8 image_strtab;
|
|
{
|
|
PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data);
|
|
image_sects.count = pe.section_count;
|
|
image_sects.v = (COFF_SectionHeader *)str8_substr(image_data, pe.section_table_range).str;
|
|
image_strtab = str8_substr(image_data, pe.string_table_range);
|
|
}
|
|
|
|
ProfBegin("Top Level Info");
|
|
{
|
|
U64 image_vsize = 0;
|
|
for (U64 sect_idx = 0; sect_idx < image_sects.count; sect_idx++) {
|
|
COFF_SectionHeader *sect = &image_sects.v[sect_idx];
|
|
image_vsize = Max(image_vsize, sect->voff + sect->vsize);
|
|
}
|
|
|
|
input.top_level_info.arch = arch;
|
|
input.top_level_info.exe_name = image_name;
|
|
input.top_level_info.exe_hash = rdi_hash(image_data.str, image_data.size);
|
|
input.top_level_info.voff_max = image_vsize;
|
|
input.top_level_info.producer_string = push_str8f(scratch.arena, "%s [Debug Info: CodeView]", BUILD_VERSION_STRING_LITERAL);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Sections");
|
|
{
|
|
input.sect_count = image_sects.count;
|
|
input.sections = push_array(scratch.arena, RDIB_BinarySection, image_sects.count);
|
|
for (U64 sect_idx = 0; sect_idx < image_sects.count; ++sect_idx) {
|
|
COFF_SectionHeader *src = &image_sects.v[sect_idx];
|
|
RDIB_BinarySection *dst = &input.sections[sect_idx];
|
|
String8 sect_name = coff_name_from_section_header(image_strtab, src);
|
|
|
|
dst->name = push_str8_copy(scratch.arena, sect_name);
|
|
dst->flags = rdi_binary_section_flags_from_coff_section_flags(src->flags);
|
|
dst->voff_first = src->voff;
|
|
dst->voff_opl = src->voff + src->vsize;
|
|
dst->foff_first = src->foff;
|
|
dst->foff_opl = src->foff + src->fsize;
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
// assing low and high type indices per source
|
|
Rng1U64 itype_ranges[CV_TypeIndexSource_COUNT];
|
|
for (U64 i = 0; i < ArrayCount(itype_ranges); ++i) {
|
|
itype_ranges[i] = rng_1u64(CV_MinComplexTypeIndex, CV_MinComplexTypeIndex + types[i].count);
|
|
}
|
|
|
|
ProfBegin("Convert Types");
|
|
U64 udt_name_buckets_cap;
|
|
LNK_UDTNameBucket **udt_name_buckets;
|
|
RDIB_Type **tpi_itype_map;
|
|
{
|
|
ProfBegin("Push TPI itype -> RDIB Type map");
|
|
tpi_itype_map = push_array(scratch.arena, RDIB_Type *, itype_ranges[CV_TypeIndexSource_TPI].max);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Push Built-in Types");
|
|
RDIB_DataModel data_model = rdib_infer_data_model(os, arch);
|
|
lnk_push_basic_itypes(scratch.arena, data_model, tpi_itype_map, &input.types);
|
|
ProfEnd();
|
|
|
|
Assert(tpi_itype_map[0] == 0);
|
|
tpi_itype_map[0] = input.null_type;
|
|
|
|
ProfBegin("Build UDT Name Hash Table");
|
|
// TODO: fix memory life-time
|
|
udt_name_buckets_cap = 0;
|
|
udt_name_buckets = lnk_udt_name_hash_table_from_debug_t(tp, tp_arena, types[CV_TypeIndexSource_TPI], &udt_name_buckets_cap);
|
|
ProfEnd();
|
|
|
|
|
|
ProfBegin("Convert CodeView types to RDIB Types");
|
|
LNK_ConvertTypesToRDI task = {0};
|
|
task.types = types;
|
|
task.type_cap = input.type_cap;
|
|
task.udt_cap = input.udt_cap;
|
|
task.variadic_type_ref = rdib_make_type_ref(scratch.arena, input.variadic_type);
|
|
task.itype_ranges = itype_ranges;
|
|
task.tpi_itype_map = tpi_itype_map;
|
|
task.udt_name_bucket_cap = udt_name_buckets_cap;
|
|
task.udt_name_buckets = udt_name_buckets;
|
|
task.rdib_types_lists = push_array(scratch.arena, RDIB_TypeChunkList, tp->worker_count);
|
|
task.rdib_types_struct_lists = push_array(scratch.arena, RDIB_TypeChunkList, tp->worker_count);
|
|
task.rdib_types_union_lists = push_array(scratch.arena, RDIB_TypeChunkList, tp->worker_count);
|
|
task.rdib_types_enum_lists = push_array(scratch.arena, RDIB_TypeChunkList, tp->worker_count);
|
|
task.rdib_types_udt_members_lists = push_array(scratch.arena, RDIB_TypeChunkList, tp->worker_count);
|
|
task.rdib_types_enum_members_lists = push_array(scratch.arena, RDIB_TypeChunkList, tp->worker_count);
|
|
task.rdib_types_params_lists = push_array(scratch.arena, RDIB_TypeChunkList, tp->worker_count);
|
|
task.rdib_udt_members_lists = push_array(scratch.arena, RDIB_UDTMemberChunkList, tp->worker_count);
|
|
task.rdib_enum_members_lists = push_array(scratch.arena, RDIB_UDTMemberChunkList, tp->worker_count);
|
|
task.ranges = tp_divide_work(scratch.arena, types[CV_TypeIndexSource_TPI].count, tp->worker_count);
|
|
tp_for_parallel(tp, tp_arena, tp->worker_count, lnk_convert_types_to_rdi_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Concat converted types");
|
|
rdib_type_chunk_list_concat_in_place_many (&input.types, task.rdib_types_lists, tp->worker_count);
|
|
rdib_type_chunk_list_concat_in_place_many (&input.struct_list, task.rdib_types_struct_lists, tp->worker_count);
|
|
rdib_type_chunk_list_concat_in_place_many (&input.union_list, task.rdib_types_union_lists, tp->worker_count);
|
|
rdib_type_chunk_list_concat_in_place_many (&input.enum_list, task.rdib_types_enum_lists, tp->worker_count);
|
|
rdib_type_chunk_list_concat_in_place_many (&input.param_types, task.rdib_types_params_lists, tp->worker_count);
|
|
rdib_type_chunk_list_concat_in_place_many (&input.member_types, task.rdib_types_udt_members_lists, tp->worker_count);
|
|
rdib_type_chunk_list_concat_in_place_many (&input.enum_types, task.rdib_types_enum_members_lists, tp->worker_count);
|
|
rdib_udt_member_chunk_list_concat_in_place_many(&input.udt_members, task.rdib_udt_members_lists, tp->worker_count);
|
|
rdib_udt_member_chunk_list_concat_in_place_many(&input.enum_members, task.rdib_enum_members_lists, tp->worker_count);
|
|
ProfEnd();
|
|
|
|
// types are converted and we can remove indirection and release 'itype_map'
|
|
ProfBegin("Deref Type Refs");
|
|
rdib_deref_type_refs(tp, &input.types);
|
|
rdib_deref_type_refs(tp, &input.struct_list);
|
|
rdib_deref_type_refs(tp, &input.union_list);
|
|
rdib_deref_type_refs(tp, &input.enum_list);
|
|
rdib_deref_type_refs(tp, &input.param_types);
|
|
rdib_deref_type_refs(tp, &input.member_types);
|
|
rdib_deref_type_refs(tp, &input.enum_types);
|
|
ProfEnd();
|
|
}
|
|
ProfEnd();
|
|
|
|
// Loop over source files in objs and build a hash table
|
|
// for path -> source file maps. During symbol conversion
|
|
// we use the hash table to lookup source files and append
|
|
// inline site line tables.
|
|
U64 src_file_buckets_cap;
|
|
LNK_SourceFileBucket **src_file_buckets;
|
|
{
|
|
ProfBegin("Build Source File Hash Table");
|
|
|
|
LNK_ConvertSourceFilesToRDITask task = {0};
|
|
task.obj_arr = obj_arr;
|
|
task.debug_s_arr = debug_s_arr;
|
|
|
|
ProfBegin("Count Source Files");
|
|
tp_for_parallel(tp, 0, obj_count, lnk_count_source_files_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBeginDynamic("Insert Source Files [Count %llu]", task.total_src_file_count);
|
|
task.src_file_buckets_cap = (U64)(task.total_src_file_count * 1.3);
|
|
task.src_file_buckets = push_array(tp_arena->v[0], LNK_SourceFileBucket*, task.src_file_buckets_cap);
|
|
tp_for_parallel(tp, tp_arena, obj_count, lnk_insert_src_files_task, &task);
|
|
ProfEnd();
|
|
|
|
src_file_buckets_cap = task.src_file_buckets_cap;
|
|
src_file_buckets = task.src_file_buckets;
|
|
|
|
ProfEnd();
|
|
}
|
|
|
|
// Copy source files to a contiguous array and update source file pointers
|
|
// in buckets so we can do lookup and compute source file index in output array
|
|
// with a pointer subtraction.
|
|
ProfBegin("Source Files");
|
|
for (U64 bucket_idx = 0; bucket_idx < src_file_buckets_cap; ++bucket_idx) {
|
|
LNK_SourceFileBucket *bucket = src_file_buckets[bucket_idx];
|
|
if (bucket != 0) {
|
|
RDIB_SourceFile *new_src_file = rdib_source_file_chunk_list_push(scratch.arena, &input.src_files, input.src_file_chunk_cap);
|
|
|
|
// restore chunk pointer after copy
|
|
RDIB_SourceFileChunk *new_src_file_chunk = new_src_file->chunk;
|
|
*new_src_file = *bucket->src_file;
|
|
new_src_file->chunk = new_src_file_chunk;
|
|
|
|
bucket->src_file = new_src_file;
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Units");
|
|
{
|
|
LNK_ConvertUnitToRDITask task = {0};
|
|
task.image_sects = image_sects;
|
|
task.obj_arr = obj_arr;
|
|
task.debug_s_arr = debug_s_arr;
|
|
task.ipi = types[CV_TypeIndexSource_IPI];
|
|
task.symbol_inputs = symbol_inputs;
|
|
task.parsed_symbols = parsed_symbols;
|
|
task.ipi_itype_range = itype_ranges[CV_TypeIndexSource_IPI];
|
|
task.tpi_itype_range = itype_ranges[CV_TypeIndexSource_TPI];
|
|
task.tpi_itype_map = tpi_itype_map;
|
|
task.src_file_buckets_cap = src_file_buckets_cap;
|
|
task.src_file_buckets = src_file_buckets;
|
|
task.udt_name_buckets = udt_name_buckets;
|
|
task.udt_name_buckets_cap = udt_name_buckets_cap;
|
|
task.src_file_chunk_cap = input.src_file_chunk_cap;
|
|
task.line_table_cap = input.line_table_cap;
|
|
task.symbol_chunk_cap = input.symbol_chunk_cap;
|
|
task.unit_chunk_cap = input.unit_chunk_cap;
|
|
task.inline_site_cap = input.inline_site_cap;
|
|
task.null_line_table = input.null_line_table;
|
|
task.extern_symbol_voff_ht = hash_table_init(scratch.arena, 256);
|
|
task.units = rdib_unit_chunk_list_reserve_ex(scratch.arena, &input.units, input.unit_chunk_cap, obj_count);
|
|
task.scopes = push_array(scratch.arena, RDIB_ScopeChunkList, tp->worker_count);
|
|
task.locals = push_array(scratch.arena, RDIB_VariableChunkList, tp->worker_count);
|
|
task.extern_gvars = push_array(scratch.arena, RDIB_VariableChunkList, tp->worker_count);
|
|
task.static_gvars = push_array(scratch.arena, RDIB_VariableChunkList, tp->worker_count);
|
|
task.extern_tvars = push_array(scratch.arena, RDIB_VariableChunkList, tp->worker_count);
|
|
task.static_tvars = push_array(scratch.arena, RDIB_VariableChunkList, tp->worker_count);
|
|
task.extern_procs = push_array(scratch.arena, RDIB_ProcedureChunkList, tp->worker_count);
|
|
task.static_procs = push_array(scratch.arena, RDIB_ProcedureChunkList, tp->worker_count);
|
|
task.inline_sites = push_array(scratch.arena, RDIB_InlineSiteChunkList, tp->worker_count);
|
|
task.line_tables = push_array(scratch.arena, RDIB_LineTableChunkList, tp->worker_count);
|
|
|
|
ProfBegin("Gather Compiler Info");
|
|
task.comp_info_arr = push_array(scratch.arena, LNK_CodeViewCompilerInfo, obj_count);
|
|
tp_for_parallel(tp, tp_arena, obj_count, lnk_find_obj_compiler_info_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Convert Line Tables");
|
|
tp_for_parallel(tp, tp_arena, obj_count, lnk_convert_line_tables_to_rdi_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Build Inlinee Lines Accels");
|
|
task.inlinee_lines_accel_arr = push_array(scratch.arena, CV_InlineeLinesAccel *, obj_count);
|
|
tp_for_parallel(tp, tp_arena, obj_count, lnk_build_inlinee_lines_accels_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Convert Symbols");
|
|
tp_for_parallel(tp, tp_arena, total_symbol_input_count, lnk_convert_symbols_to_rdi_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Convert Inline Sites Line Tables");
|
|
rdib_inline_site_chunk_list_concat_in_place_many(&input.inline_sites, task.inline_sites, tp->worker_count);
|
|
task.inline_site_chunks = rdib_array_from_inline_site_chunk_list(scratch.arena, input.inline_sites);
|
|
tp_for_parallel(tp, tp_arena, input.inline_sites.count, lnk_convert_inline_site_line_tables_task, &task);
|
|
ProfEnd();
|
|
|
|
ProfBegin("Collect Units Virtual Ranges");
|
|
tp_for_parallel(tp, tp_arena, obj_count, lnk_collect_obj_virtual_ranges_task, &task);
|
|
ProfEnd();
|
|
|
|
rdib_line_table_chunk_list_concat_in_place_many(&input.line_tables, task.line_tables, tp->worker_count);
|
|
rdib_scope_chunk_list_concat_in_place_many(&input.scopes, task.scopes, tp->worker_count);
|
|
rdib_variable_chunk_list_concat_in_place_many(&input.locals, task.locals, tp->worker_count);
|
|
rdib_variable_chunk_list_concat_in_place_many(&input.extern_gvars, task.extern_gvars, tp->worker_count);
|
|
rdib_variable_chunk_list_concat_in_place_many(&input.static_gvars, task.static_gvars, tp->worker_count);
|
|
rdib_variable_chunk_list_concat_in_place_many(&input.extern_tvars, task.extern_tvars, tp->worker_count);
|
|
rdib_variable_chunk_list_concat_in_place_many(&input.static_tvars, task.static_tvars, tp->worker_count);
|
|
rdib_procedure_chunk_list_concat_in_place_many(&input.extern_procs, task.extern_procs, tp->worker_count);
|
|
rdib_procedure_chunk_list_concat_in_place_many(&input.static_procs, task.static_procs, tp->worker_count);
|
|
}
|
|
ProfEnd();
|
|
|
|
String8List rdi_data = rdib_finish(tp, tp_arena, &input);
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return rdi_data;
|
|
}
|
|
|