mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 17:28:07 +00:00
add support for LLVM ghash sections
This commit is contained in:
+6
-3
@@ -34,6 +34,7 @@
|
||||
#include "msf/msf_parse.h"
|
||||
#include "pdb/pdb.h"
|
||||
#include "msvc_crt/msvc_crt.h"
|
||||
#include "llvm/llvm.h"
|
||||
|
||||
#include "base/base_inc.c"
|
||||
#include "os/os_inc.c"
|
||||
@@ -52,6 +53,7 @@
|
||||
#include "msf/msf_parse.c"
|
||||
#include "pdb/pdb.c"
|
||||
#include "msvc_crt/msvc_crt.c"
|
||||
#include "llvm/llvm.c"
|
||||
|
||||
// --- RDI ---------------------------------------------------------------------
|
||||
|
||||
@@ -120,7 +122,7 @@ lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line)
|
||||
{
|
||||
LNK_CmdLine cmd_line = {0};
|
||||
|
||||
// setup default flags
|
||||
// default flags
|
||||
lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Align, "%u", KB(4));
|
||||
lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Debug, "none");
|
||||
lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_FileAlign, "%u", 512);
|
||||
@@ -136,6 +138,7 @@ lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line)
|
||||
if (!lnk_cmd_line_has_switch(user_cmd_line, LNK_CmdSwitch_Brepro)) {
|
||||
lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_TimeStamp, "%u", os_get_process_start_time_unix());
|
||||
}
|
||||
lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_TypeHashAlg, "BLAKE3");
|
||||
lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_Age, "%u", 1);
|
||||
lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, "");
|
||||
lnk_cmd_line_push_option_if_not_presentf(arena, &cmd_line, LNK_CmdSwitch_Rad_DoMerge, "");
|
||||
@@ -5246,7 +5249,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config)
|
||||
//
|
||||
// CodeView
|
||||
//
|
||||
LNK_CodeViewInput cv = lnk_make_code_view_input(tp, arena, config->io_flags, config->lib_dir_list, config->alt_pch_dirs, debug_info_objs_count, debug_info_objs);
|
||||
LNK_CodeViewInput cv = lnk_make_code_view_input(tp, arena, config, debug_info_objs_count, debug_info_objs);
|
||||
LNK_MergedTypes cv_types = lnk_merge_types(tp, arena, &cv);
|
||||
|
||||
//
|
||||
@@ -5354,7 +5357,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config)
|
||||
}
|
||||
|
||||
LNK_CodeViewInput stripped_cv = {0};
|
||||
stripped_cv.io_flags = config->io_flags;
|
||||
stripped_cv.config = config;
|
||||
stripped_cv.is_stripped = 1;
|
||||
stripped_cv.obj_arr = cv.obj_arr;
|
||||
stripped_cv.obj_count = cv.obj_count;
|
||||
|
||||
@@ -92,6 +92,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
|
||||
{ LNK_CmdSwitch_Rad_Ignore, 0, "RAD_IGNORE", ":#", "Ignore the specified RAD linker warning." },
|
||||
{ LNK_CmdSwitch_Rad_WriteTempFiles, 0, "RAD_WRITE_TEMP_FILES", "[:NO]", "When speicifed linker writes image and debug info to temporary files and renames after link is done." },
|
||||
{ LNK_CmdSwitch_Rad_TimeStamp, 0, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." },
|
||||
{ LNK_CmdSwitch_Rad_TypeHashAlg, 0, "RAD_TPYE_HASH_ALG", ":{BLAKE3}", "Sets hashing algorithm for type merging." },
|
||||
{ LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, 0, "RAD_UNRESOLVED_SYMBOL_LIMIT", ":#", "Limits number of unresolved symbol errors linker reports." },
|
||||
{ LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, 0, "RAD_UNRESOLVED_SYMBOL_REF_LIMIT", ":#", "Limit number of unresolved symbol references linker reports." },
|
||||
{ LNK_CmdSwitch_Rad_Version, 0, "RAD_VERSION", "", "Print version and exit." },
|
||||
@@ -2065,6 +2066,17 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
|
||||
lnk_cmd_switch_parse_u32(obj, cmd_switch, value_strings, &config->time_stamp, 0);
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Rad_TypeHashAlg: {
|
||||
String8 alg = {0};
|
||||
if (lnk_cmd_switch_parse_string(obj, cmd_switch, value_strings, &alg)) {
|
||||
if (str8_match(alg, str8_lit("BLAKE3"), StringMatchFlag_CaseInsensitive)) {
|
||||
config->type_hash_alg = LLVM_GHashAlg_BLAKE3;
|
||||
} else {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unknown hash alg: %S", alg);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Rad_UnresolvedSymbolLimit: {
|
||||
lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->unresolved_symbol_limit, 0);
|
||||
} break;
|
||||
|
||||
@@ -119,6 +119,7 @@ typedef enum
|
||||
LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers,
|
||||
LNK_CmdSwitch_Rad_Ignore,
|
||||
LNK_CmdSwitch_Rad_TimeStamp,
|
||||
LNK_CmdSwitch_Rad_TypeHashAlg,
|
||||
LNK_CmdSwitch_Rad_UnresolvedSymbolLimit,
|
||||
LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit,
|
||||
LNK_CmdSwitch_Rad_Version,
|
||||
@@ -360,6 +361,7 @@ typedef struct LNK_Config
|
||||
U64 unresolved_symbol_ref_limit;
|
||||
LNK_SwitchState map_lines_for_unresolved_symbols;
|
||||
String8List alt_pch_dirs;
|
||||
LLVM_GHashAlg type_hash_alg;
|
||||
} LNK_Config;
|
||||
|
||||
// --- MSVC Error Codes --------------------------------------------------------
|
||||
|
||||
+119
-34
@@ -57,6 +57,76 @@ THREAD_POOL_TASK_FUNC(lnk_parse_debug_s_task)
|
||||
}
|
||||
}
|
||||
|
||||
internal
|
||||
THREAD_POOL_TASK_FUNC(lnk_parse_debug_h_task)
|
||||
{
|
||||
U64 obj_idx = task_id;
|
||||
LNK_CodeViewInput *task = raw_task;
|
||||
|
||||
String8List sect_list = task->debug_h_list_arr[obj_idx];
|
||||
CV_DebugH *debug_h = &task->debug_h_arr [obj_idx];
|
||||
|
||||
if (sect_list.node_count > 0) {
|
||||
if (sect_list.node_count > 1) {
|
||||
lnk_error_obj(LNK_Warning_GHash, task->obj_arr[obj_idx],
|
||||
"obj contains multiple .debug$H sections; using first one");
|
||||
}
|
||||
|
||||
// select first .debug$H
|
||||
String8 raw_debug_h = sect_list.first->string;
|
||||
LLVM_GHash ghash = {0};
|
||||
U64 ghash_read_size = str8_deserial_read_struct(raw_debug_h, 0, &ghash);
|
||||
|
||||
// was header read completely?
|
||||
if (ghash_read_size != sizeof(ghash)) {
|
||||
lnk_error_obj(LNK_Warning_GHash, task->obj_arr[obj_idx],
|
||||
".debug$H section is too small to contain the header");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// validate magic
|
||||
if (ghash.magic != LLVM_GHash_Magic) {
|
||||
lnk_error_obj(LNK_Warning_GHash, task->obj_arr[obj_idx],
|
||||
".debug$H contains invalid magic: got 0x%x, expected 0x%x", ghash.magic, LLVM_GHash_Magic);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// validate version
|
||||
if (ghash.version != LLVM_GHash_CurrentVersion) {
|
||||
lnk_error_obj(LNK_Warning_GHash, task->obj_arr[obj_idx],
|
||||
"mismatched .debug$H version: got %u, expected %u",
|
||||
ghash.version, LLVM_GHash_CurrentVersion);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// validate hashing algorithm
|
||||
if (ghash.hash_alg != task->config->type_hash_alg) {
|
||||
lnk_error_obj(LNK_Warning_GHash, task->obj_arr[obj_idx],
|
||||
"mismatched .debug$H hash algorithm: got %S, expected %S",
|
||||
llvm_string_from_ghash_alg(ghash.hash_alg),
|
||||
llvm_string_from_ghash_alg(task->config->type_hash_alg));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// input.debug_h_arr must be 1:1 with input.debug_t_arr
|
||||
U64 hash_size = llvm_hash_size_from_alg(ghash.hash_alg);
|
||||
U64 hash_count = (raw_debug_h.size - sizeof(ghash)) / hash_size;
|
||||
if (hash_count != task->debug_t_arr[obj_idx].count) {
|
||||
lnk_error_obj(LNK_Warning_GHash, task->obj_arr[obj_idx],
|
||||
"mismatched .debug$H hash count and type count: got %llu hashes for %llu types",
|
||||
hash_count, task->debug_t_arr[obj_idx].count);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// load hashes
|
||||
String8 hashes = str8_substr(raw_debug_h, r1u64(sizeof(ghash), raw_debug_h.size));
|
||||
debug_h->count = hash_count;
|
||||
debug_h->v = (U64 *)hashes.str;
|
||||
|
||||
exit:;
|
||||
}
|
||||
}
|
||||
|
||||
internal
|
||||
THREAD_POOL_TASK_FUNC(lnk_strip_debug_t_sig_task)
|
||||
{
|
||||
@@ -106,7 +176,7 @@ THREAD_POOL_TASK_FUNC(lnk_read_type_servers_task)
|
||||
LNK_TypeServer *ts = &task->ts_arr.v[ts_idx];
|
||||
|
||||
// read PDB from disk
|
||||
String8 msf_data = lnk_read_data_from_file_path(scratch.arena, task->io_flags, ts->ts_path);
|
||||
String8 msf_data = lnk_read_data_from_file_path(scratch.arena, task->config->io_flags, ts->ts_path);
|
||||
|
||||
// check magic
|
||||
if (!msf_check_magic_70(msf_data) && msf_check_magic_20(msf_data)) { goto exit; }
|
||||
@@ -198,48 +268,49 @@ THREAD_POOL_TASK_FUNC(lnk_read_type_servers_task)
|
||||
}
|
||||
|
||||
internal LNK_CodeViewInput
|
||||
lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, String8List alt_pch_dirs, U64 obj_count, LNK_Obj **obj_arr)
|
||||
lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, U64 obj_count, LNK_Obj **obj_arr)
|
||||
{
|
||||
ProfBegin("Extract CodeView");
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
|
||||
LNK_CodeViewInput input = { .io_flags = io_flags, .obj_count = obj_count, .count = obj_count, .obj_arr = obj_arr, .ts_obj_range = r1u64(0,0) };
|
||||
LNK_CodeViewInput input = { .config = config, .obj_count = obj_count, .count = obj_count, .obj_arr = obj_arr, .ts_obj_range = r1u64(0,0) };
|
||||
|
||||
ProfBegin("Collect CodeView");
|
||||
input.debug_s_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$S"), 0);
|
||||
input.debug_p_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$P"), 0);
|
||||
input.debug_t_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$T"), 0);
|
||||
input.debug_h_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$H"), 0);
|
||||
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;
|
||||
U64 total_debug_s_size = 0, total_debug_t_size = 0, total_debug_p_size = 0, total_debug_h_size = 0;
|
||||
for EachIndex(obj_idx, obj_count) {
|
||||
for EachNode(n, String8Node, input.debug_s_list_arr[obj_idx].first) { total_debug_s_size += n->string.size; }
|
||||
for EachNode(n, String8Node, input.debug_t_list_arr[obj_idx].first) { total_debug_t_size += n->string.size; }
|
||||
for EachNode(n, String8Node, input.debug_p_list_arr[obj_idx].first) { total_debug_p_size += n->string.size; }
|
||||
for EachNode(n, String8Node, input.debug_h_list_arr[obj_idx].first) { total_debug_h_size += n->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);
|
||||
ProfNoteV("Total .debug$H Input Size: %M", total_debug_H_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);
|
||||
lnk_log(LNK_Log_Debug, "[Total .debug$H Input Size %M]", total_debug_h_size);
|
||||
}
|
||||
}
|
||||
|
||||
ProfBegin("Parse CodeView");
|
||||
CV_DebugT *debug_p_arr;
|
||||
{
|
||||
input.debug_s_arr = push_array(tp_arena->v[0], CV_DebugS, obj_count);
|
||||
input.debug_s_arr = push_array(tp_arena->v[0], CV_DebugS, input.obj_count);
|
||||
tp_for_parallel_prof(tp, tp_arena, obj_count, lnk_parse_debug_s_task, &input, "Parse .debug$S");
|
||||
|
||||
input.debug_h_arr = push_array(tp_arena->v[0], CV_DebugH, obj_count); // TODO: collect & parse .debug$H
|
||||
|
||||
LNK_ParseCvTypes parse_types = { .input = &input };
|
||||
|
||||
debug_p_arr = push_array(tp_arena->v[0], CV_DebugT, obj_count);
|
||||
parse_types.raw_types = str8_array_from_list_arr(scratch.arena, input.debug_p_list_arr, obj_count);
|
||||
parse_types.out_types = debug_p_arr;
|
||||
@@ -251,6 +322,9 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla
|
||||
parse_types.out_types = input.debug_t_arr;
|
||||
tp_for_parallel_prof(tp, 0, obj_count, lnk_strip_debug_t_sig_task, &parse_types, "Strip .debug$T");
|
||||
tp_for_parallel_prof(tp, tp_arena, obj_count, lnk_parse_debug_t_task, &parse_types, "Parse .debug$T");
|
||||
|
||||
input.debug_h_arr = push_array(tp_arena->v[0], CV_DebugH, input.obj_count);
|
||||
tp_for_parallel_prof(tp, tp_arena, obj_count, lnk_parse_debug_h_task, &input, "Parse .debug$H");
|
||||
}
|
||||
ProfEnd();
|
||||
|
||||
@@ -297,7 +371,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla
|
||||
CV_DebugT *debug_t = &input.debug_t_arr[obj_idx];
|
||||
CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, 0);
|
||||
CV_TypeServerInfo ts_info = cv_type_server_info_from_leaf(leaf);
|
||||
String8 ts_path = lnk_find_first_file(scratch.arena, lib_dir_list, ts_info.name);
|
||||
String8 ts_path = lnk_find_first_file(scratch.arena, config->lib_dir_list, ts_info.name);
|
||||
|
||||
if (ts_path.size == 0) {
|
||||
lnk_discard_cv_debug_info(&input, obj_idx);
|
||||
@@ -424,7 +498,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla
|
||||
if ( ! hash_table_search_path_u64(debug_p_ht, obj_path, &debug_p_obj_idx)) {
|
||||
// try alternative directory for the PCH
|
||||
String8 obj_name = str8_skip_last_slash(obj_path);
|
||||
for EachNode(alt_dir_n, String8Node, alt_pch_dirs.first) {
|
||||
for EachNode(alt_dir_n, String8Node, config->alt_pch_dirs.first) {
|
||||
String8 alt_obj_path = str8f(scratch.arena, "%S/%S", alt_dir_n->string, obj_name);
|
||||
if (hash_table_search_path_u64(debug_p_ht, alt_obj_path, &debug_p_obj_idx)) { break; }
|
||||
}
|
||||
@@ -1325,36 +1399,47 @@ lnk_merge_types(TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input)
|
||||
ProfBegin("Produce Hashes");
|
||||
{
|
||||
ProfBegin("Alloc Hashes");
|
||||
U32Array indices[] = {
|
||||
input->debug_p_indices,
|
||||
input->int_obj_indices,
|
||||
input->type_server_indices,
|
||||
struct HashTarget {
|
||||
TP_TaskFunc *hasher_task;
|
||||
U32Array indices;
|
||||
U32Array hash_indices;
|
||||
} hash_targets[] = {
|
||||
{ lnk_hash_debug_t_task, input->debug_p_indices }, // hash .debug$P first so we can mix in hashes for precompiled sub leaves when hashing leaves in .debug$T
|
||||
{ lnk_hash_debug_t_task, input->int_obj_indices },
|
||||
{ lnk_hash_debug_t_deep_task, input->type_server_indices },
|
||||
};
|
||||
for EachElement(i, indices) {
|
||||
for EachIndex(k, indices[i].count) {
|
||||
U64 obj_idx = indices[i].v[k];
|
||||
CV_DebugT *debug_t = &input->debug_t_arr[obj_idx];
|
||||
|
||||
for EachElement(i, hash_targets) {
|
||||
// reserve array for obj indices that need hashing
|
||||
U32Array *h = &hash_targets[i].hash_indices;
|
||||
h->count = 0;
|
||||
h->v = push_array(scratch.arena, U32, hash_targets[i].indices.count);
|
||||
|
||||
for EachIndex(k, hash_targets[i].indices.count) {
|
||||
U32 obj_idx = hash_targets[i].indices.v[k];
|
||||
CV_DebugH *debug_h = &input->debug_h_arr[obj_idx];
|
||||
debug_h->count = debug_t->count;
|
||||
debug_h->v = push_array(scratch.arena, U64, debug_h->count);
|
||||
|
||||
if (debug_h->count == 0) {
|
||||
// alloc hashes
|
||||
CV_DebugT *debug_t = &input->debug_t_arr[obj_idx];
|
||||
debug_h->count = debug_t->count;
|
||||
debug_h->v = push_array(scratch.arena, U64, debug_h->count);
|
||||
|
||||
// schedule obj types to be hashed
|
||||
h->v[h->count++] = obj_idx;
|
||||
} else {
|
||||
// hash was loaded from .debug$H
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfEnd();
|
||||
|
||||
// hash .debug$P first so we can mix in hashes for precompiled sub leaves when hashing leaves in .debug$T
|
||||
ProfBegin("Hash");
|
||||
task.indices = input->debug_p_indices;
|
||||
ProfScope(".debug$P [Count: %llu]", input->debug_p_indices.count)
|
||||
tp_for_parallel(tp, 0, task.indices.count, lnk_hash_debug_t_task, &task);
|
||||
|
||||
task.indices = input->int_obj_indices;
|
||||
ProfScope(".debug$T [Count: %.*s]", str8_varg(str8_from_count(scratch.arena, task.indices.count)))
|
||||
tp_for_parallel(tp, 0, task.indices.count, lnk_hash_debug_t_task, &task);
|
||||
|
||||
task.indices = input->type_server_indices;
|
||||
ProfScope("Type Servers [Count: %.*s]", str8_varg(str8_from_count(scratch.arena, task.indices.count)))
|
||||
tp_for_parallel(tp, 0, task.indices.count, lnk_hash_debug_t_deep_task, &task);
|
||||
ProfEnd();
|
||||
for EachElement(i, hash_targets) {
|
||||
task.indices = hash_targets[i].hash_indices;
|
||||
ProfBegin("Hash [Count: %.*s]", str8_varg(str8_from_count(scratch.arena, task.indices.count)));
|
||||
tp_for_parallel(tp, 0, hash_targets[i].indices.count, hash_targets[i].hasher_task, &task);
|
||||
ProfEnd();
|
||||
}
|
||||
|
||||
#if BUILD_DEBUG
|
||||
for EachIndex(i, input->count) {
|
||||
|
||||
@@ -25,7 +25,7 @@ typedef struct LNK_SymbolInput
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LNK_IO_Flags io_flags;
|
||||
LNK_Config *config;
|
||||
U64 obj_count;
|
||||
B32 is_stripped;
|
||||
|
||||
@@ -39,6 +39,7 @@ typedef struct
|
||||
String8List *debug_s_list_arr;
|
||||
String8List *debug_p_list_arr;
|
||||
String8List *debug_t_list_arr;
|
||||
String8List *debug_h_list_arr;
|
||||
|
||||
U32Array int_obj_indices;
|
||||
U32Array ext_obj_indices;
|
||||
@@ -278,7 +279,7 @@ typedef struct
|
||||
////////////////////////////////
|
||||
// CodeView
|
||||
|
||||
internal LNK_CodeViewInput lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, String8List alt_pch_dirs, U64 objs_count, LNK_Obj **objs);
|
||||
internal LNK_CodeViewInput lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, U64 objs_count, LNK_Obj **objs);
|
||||
|
||||
internal int lnk_leaf_ref_compare (LNK_LeafRef a, LNK_LeafRef b);
|
||||
internal int lnk_leaf_ref_is_before (void *raw_a, void *raw_b);
|
||||
|
||||
@@ -108,6 +108,7 @@ typedef enum
|
||||
LNK_Warning_NoLargeAddressAwarenessForDll,
|
||||
LNK_Warning_TryingToExportEntryPoint,
|
||||
LNK_Warning_InferAsanFail,
|
||||
LNK_Warning_GHash,
|
||||
LNK_Warning_Last,
|
||||
|
||||
LNK_Error_Count
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal String8
|
||||
llvm_string_from_ghash_alg(LLVM_GHashAlg v)
|
||||
{
|
||||
switch (v) {
|
||||
case LLVM_GHashAlg_SHA1: return str8_lit("SHA1");
|
||||
case LLVM_GHashAlg_SHA1_8: return str8_lit("SHA1_8");
|
||||
case LLVM_GHashAlg_BLAKE3: return str8_lit("BALK3");
|
||||
}
|
||||
return str8_zero();
|
||||
}
|
||||
|
||||
internal U64
|
||||
llvm_hash_size_from_alg(LLVM_GHashAlg v)
|
||||
{
|
||||
switch (v) {
|
||||
case LLVM_GHashAlg_SHA1: return 20;
|
||||
case LLVM_GHashAlg_SHA1_8: return 8;
|
||||
case LLVM_GHashAlg_BLAKE3: return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef LLVM_H
|
||||
#define LLVM_H
|
||||
|
||||
#define LLVM_GHash_Magic 0x133c9c5
|
||||
#define LLVM_GHash_CurrentVersion 0
|
||||
|
||||
typedef U16 LLVM_GHashAlg;
|
||||
typedef enum LLVM_GHashAlgEnum
|
||||
{
|
||||
LLVM_GHashAlg_SHA1 = 0,
|
||||
LLVM_GHashAlg_SHA1_8 = 1,
|
||||
LLVM_GHashAlg_BLAKE3 = 2,
|
||||
} LLVM_GHashAlgEnum;
|
||||
|
||||
typedef struct LLVM_GHash
|
||||
{
|
||||
U32 magic;
|
||||
LLVM_GHashAlg hash_alg;
|
||||
U16 version;
|
||||
// * hashes[]
|
||||
} LLVM_GHash;
|
||||
|
||||
internal String8 llvm_string_from_ghash_alg(LLVM_GHashAlg v);
|
||||
|
||||
#endif // LLVM_H
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "pdb/pdb_parse.h"
|
||||
#include "pdb/pdb_stringize.h"
|
||||
#include "dwarf/dwarf_inc.h"
|
||||
#include "llvm/llvm.h"
|
||||
#include "rdi_from_coff/rdi_from_coff.h"
|
||||
#include "rdi_from_elf/rdi_from_elf.h"
|
||||
#include "rdi_from_pdb/rdi_from_pdb.h"
|
||||
@@ -126,6 +127,7 @@
|
||||
#include "pdb/pdb_parse.c"
|
||||
#include "pdb/pdb_stringize.c"
|
||||
#include "dwarf/dwarf_inc.c"
|
||||
#include "llvm/llvm.c"
|
||||
#include "rdi_from_coff/rdi_from_coff.c"
|
||||
#include "rdi_from_elf/rdi_from_elf.c"
|
||||
#include "rdi_from_pdb/rdi_from_pdb.c"
|
||||
|
||||
@@ -5006,6 +5006,257 @@ TEST(pdbstripped)
|
||||
T_Ok(ipi.size == sizeof(PDB_TpiHeader));
|
||||
}
|
||||
|
||||
TEST(ghash_check_corrupt)
|
||||
{
|
||||
String8List t = {0}; str8_serial_begin(arena, &t);
|
||||
str8_serial_push_u32(arena, &t, CV_Signature_C13);
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_STRUCTURE, str8_struct(&(CV_LeafStruct){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_UNION, str8_struct(&(CV_LeafUnion){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_ENUM, str8_struct(&(CV_LeafEnum){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
String8 debug_t = str8_serial_end(arena, &t);
|
||||
|
||||
String8List h = {0}; str8_serial_begin(arena, &h);
|
||||
String8 debug_h = str8_serial_end(arena, &h);
|
||||
|
||||
COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$T"), PE_DEBUG_SECTION_FLAGS, debug_t);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$H"), PE_DEBUG_SECTION_FLAGS, debug_h);
|
||||
String8 debug_obj = coff_obj_writer_serialize(arena, cow);
|
||||
coff_obj_writer_release(&cow);
|
||||
|
||||
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
String8 output = {0};
|
||||
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full entry.obj debug.obj"), max_U64, arena, &output);
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
B32 is_warning_found = 0;
|
||||
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
|
||||
String8 expected_line = str8f(arena, "Warning(%03u): %S: .debug$H section is too small to contain the header", LNK_Warning_GHash, debug_obj_path, LLVM_GHash_Magic);
|
||||
for (String8 i = output; i.size > 0 && !is_warning_found; ) {
|
||||
String8 line = t_chop_line(&i);
|
||||
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
|
||||
}
|
||||
T_Ok(is_warning_found);
|
||||
}
|
||||
|
||||
TEST(ghash_check_magic)
|
||||
{
|
||||
String8List t = {0}; str8_serial_begin(arena, &t);
|
||||
str8_serial_push_u32(arena, &t, CV_Signature_C13);
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_STRUCTURE, str8_struct(&(CV_LeafStruct){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_UNION, str8_struct(&(CV_LeafUnion){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_ENUM, str8_struct(&(CV_LeafEnum){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
String8 debug_t = str8_serial_end(arena, &t);
|
||||
|
||||
#if 0
|
||||
U64 *hashes = push_array(arena, U64, t.node_count);
|
||||
U64 i = 0;
|
||||
for EachNode(n, String8Node, t.first->next) {
|
||||
blake3(&hashes[i], sizeof(hashes[i]), n->string.str, n->string.size);
|
||||
i += 1;
|
||||
}
|
||||
#endif
|
||||
String8List h = {0}; str8_serial_begin(arena, &h);
|
||||
str8_serial_push_struct(arena, &h, (&(LLVM_GHash){ .magic = 123, .hash_alg = LLVM_GHashAlg_BLAKE3, .version = LLVM_GHash_CurrentVersion }));
|
||||
str8_serial_push_u64(arena, &h, 0x6ebacae08af4fda5ull);
|
||||
str8_serial_push_u64(arena, &h, 0xc385876694f9769aull);
|
||||
str8_serial_push_u64(arena, &h, 0x7ea8a529a89b2288ull);
|
||||
String8 debug_h = str8_serial_end(arena, &h);
|
||||
|
||||
COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$T"), PE_DEBUG_SECTION_FLAGS, debug_t);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$H"), PE_DEBUG_SECTION_FLAGS, debug_h);
|
||||
String8 debug_obj = coff_obj_writer_serialize(arena, cow);
|
||||
coff_obj_writer_release(&cow);
|
||||
|
||||
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
String8 output = {0};
|
||||
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full entry.obj debug.obj"), max_U64, arena, &output);
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
B32 is_warning_found = 0;
|
||||
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
|
||||
String8 expected_line = str8f(arena, "Warning(%03u): %S: .debug$H contains invalid magic: got 0x7b, expected 0x%x", LNK_Warning_GHash, debug_obj_path, LLVM_GHash_Magic);
|
||||
for (String8 i = output; i.size > 0 && !is_warning_found; ) {
|
||||
String8 line = t_chop_line(&i);
|
||||
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
|
||||
}
|
||||
T_Ok(is_warning_found);
|
||||
}
|
||||
|
||||
TEST(ghash_check_version)
|
||||
{
|
||||
String8List t = {0}; str8_serial_begin(arena, &t);
|
||||
str8_serial_push_u32(arena, &t, CV_Signature_C13);
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_STRUCTURE, str8_struct(&(CV_LeafStruct){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_UNION, str8_struct(&(CV_LeafUnion){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_ENUM, str8_struct(&(CV_LeafEnum){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
String8 debug_t = str8_serial_end(arena, &t);
|
||||
|
||||
#if 0
|
||||
U64 *hashes = push_array(arena, U64, t.node_count);
|
||||
U64 i = 0;
|
||||
for EachNode(n, String8Node, t.first->next) {
|
||||
blake3(&hashes[i], sizeof(hashes[i]), n->string.str, n->string.size);
|
||||
i += 1;
|
||||
}
|
||||
#endif
|
||||
String8List h = {0}; str8_serial_begin(arena, &h);
|
||||
str8_serial_push_struct(arena, &h, (&(LLVM_GHash){ .magic = LLVM_GHash_Magic, .hash_alg = LLVM_GHashAlg_BLAKE3, .version = 0xbeef }));
|
||||
str8_serial_push_u64(arena, &h, 0x6ebacae08af4fda5ull);
|
||||
str8_serial_push_u64(arena, &h, 0xc385876694f9769aull);
|
||||
str8_serial_push_u64(arena, &h, 0x7ea8a529a89b2288ull);
|
||||
String8 debug_h = str8_serial_end(arena, &h);
|
||||
|
||||
COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$T"), PE_DEBUG_SECTION_FLAGS, debug_t);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$H"), PE_DEBUG_SECTION_FLAGS, debug_h);
|
||||
String8 debug_obj = coff_obj_writer_serialize(arena, cow);
|
||||
coff_obj_writer_release(&cow);
|
||||
|
||||
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
String8 output = {0};
|
||||
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full entry.obj debug.obj"), max_U64, arena, &output);
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
B32 is_warning_found = 0;
|
||||
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
|
||||
String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H version: got %u, expected %u", LNK_Warning_GHash, debug_obj_path, 0xbeef, LLVM_GHash_CurrentVersion);
|
||||
for (String8 i = output; i.size > 0 && !is_warning_found; ) {
|
||||
String8 line = t_chop_line(&i);
|
||||
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
|
||||
}
|
||||
T_Ok(is_warning_found);
|
||||
}
|
||||
|
||||
TEST(ghash_check_hash_alg)
|
||||
{
|
||||
String8List t = {0}; str8_serial_begin(arena, &t);
|
||||
str8_serial_push_u32(arena, &t, CV_Signature_C13);
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_STRUCTURE, str8_struct(&(CV_LeafStruct){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_UNION, str8_struct(&(CV_LeafUnion){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_ENUM, str8_struct(&(CV_LeafEnum){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
String8 debug_t = str8_serial_end(arena, &t);
|
||||
|
||||
String8List h = {0}; str8_serial_begin(arena, &h);
|
||||
str8_serial_push_struct(arena, &h, (&(LLVM_GHash){ .magic = LLVM_GHash_Magic, .hash_alg = LLVM_GHashAlg_SHA1_8, .version = LLVM_GHash_CurrentVersion }));
|
||||
str8_serial_push_u64(arena, &h, 0x6ebacae08af4fda5ull);
|
||||
str8_serial_push_u64(arena, &h, 0xc385876694f9769aull);
|
||||
str8_serial_push_u64(arena, &h, 0x7ea8a529a89b2288ull);
|
||||
String8 debug_h = str8_serial_end(arena, &h);
|
||||
|
||||
COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$T"), PE_DEBUG_SECTION_FLAGS, debug_t);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$H"), PE_DEBUG_SECTION_FLAGS, debug_h);
|
||||
String8 debug_obj = coff_obj_writer_serialize(arena, cow);
|
||||
coff_obj_writer_release(&cow);
|
||||
|
||||
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
String8 output = {0};
|
||||
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full entry.obj debug.obj"), max_U64, arena, &output);
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
B32 is_warning_found = 0;
|
||||
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
|
||||
String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H hash algorithm: got SHA1_8, expected BALK3", LNK_Warning_GHash, debug_obj_path);
|
||||
for (String8 i = output; i.size > 0 && !is_warning_found; ) {
|
||||
String8 line = t_chop_line(&i);
|
||||
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
|
||||
}
|
||||
T_Ok(is_warning_found);
|
||||
}
|
||||
|
||||
TEST(ghash_match_debug_t)
|
||||
{
|
||||
String8List t = {0}; str8_serial_begin(arena, &t);
|
||||
str8_serial_push_u32(arena, &t, CV_Signature_C13);
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_STRUCTURE, str8_struct(&(CV_LeafStruct){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_UNION, str8_struct(&(CV_LeafUnion){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
//str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_ENUM, str8_struct(&(CV_LeafEnum){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
String8 debug_t = str8_serial_end(arena, &t);
|
||||
|
||||
String8List h = {0}; str8_serial_begin(arena, &h);
|
||||
str8_serial_push_struct(arena, &h, (&(LLVM_GHash){ .magic = LLVM_GHash_Magic, .hash_alg = LLVM_GHashAlg_BLAKE3, .version = LLVM_GHash_CurrentVersion }));
|
||||
str8_serial_push_u64(arena, &h, 0x6ebacae08af4fda5ull);
|
||||
str8_serial_push_u64(arena, &h, 0xc385876694f9769aull);
|
||||
str8_serial_push_u64(arena, &h, 0x7ea8a529a89b2288ull);
|
||||
String8 debug_h = str8_serial_end(arena, &h);
|
||||
|
||||
COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$T"), PE_DEBUG_SECTION_FLAGS, debug_t);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$H"), PE_DEBUG_SECTION_FLAGS, debug_h);
|
||||
String8 debug_obj = coff_obj_writer_serialize(arena, cow);
|
||||
coff_obj_writer_release(&cow);
|
||||
|
||||
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
String8 output = {0};
|
||||
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full entry.obj debug.obj"), max_U64, arena, &output);
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
B32 is_warning_found = 0;
|
||||
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
|
||||
String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H hash count and type count: got 3 hashes for 2 types", LNK_Warning_GHash, debug_obj_path);
|
||||
for (String8 i = output; i.size > 0 && !is_warning_found; ) {
|
||||
String8 line = t_chop_line(&i);
|
||||
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
|
||||
}
|
||||
T_Ok(is_warning_found);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TEST(ghash_basic)
|
||||
{
|
||||
String8 a_obj;
|
||||
{
|
||||
String8List t = {0}; str8_serial_begin(arena, &t);
|
||||
str8_serial_push_u32(arena, &t, CV_Signature_C13);
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_STRUCTURE, str8_struct(&(CV_LeafStruct){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_UNION, str8_struct(&(CV_LeafUnion){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
str8_serial_push_string(arena, &t, cv_make_leaf(arena, CV_LeafKind_ENUM, str8_struct(&(CV_LeafEnum){ .props = CV_TypeProp_FwdRef }), CV_LeafAlign));
|
||||
String8 debug_t = str8_serial_end(arena, &t);
|
||||
|
||||
String8List h = {0}; str8_serial_begin(arena, &h);
|
||||
str8_serial_push_struct(arena, &h, (&(LLVM_GHash){ .magic = LLVM_GHash_Magic, .hash_alg = LLVM_GHashAlg_BLAKE3, .version = LLVM_GHash_CurrentVersion }));
|
||||
str8_serial_push_u64(arena, &h, 1);
|
||||
str8_serial_push_u64(arena, &h, 2);
|
||||
str8_serial_push_u64(arena, &h, 3);
|
||||
String8 debug_h = str8_serial_end(arena, &h);
|
||||
|
||||
COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$T"), PE_DEBUG_SECTION_FLAGS, debug_t);
|
||||
coff_obj_writer_push_section(cow, str8_lit(".debug$H"), PE_DEBUG_SECTION_FLAGS, debug_h);
|
||||
a_obj = coff_obj_writer_serialize(arena, cow);
|
||||
coff_obj_writer_release(&cow);
|
||||
}
|
||||
|
||||
T_Ok(t_write_file(str8_lit("a.obj"), a_obj));
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
String8 output = {0};
|
||||
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full entry.obj a.obj"), max_U64, arena, &output);
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
B32 is_warning_found = 0;
|
||||
String8 a_obj_path = t_make_file_path(arena, str8_lit("a.obj"));
|
||||
String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H hash count and type count: got 3 hashes for 2 types", LNK_Warning_GHash, a_obj_path);
|
||||
for (String8 i = output; i.size > 0 && !is_warning_found; ) {
|
||||
String8 line = t_chop_line(&i);
|
||||
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
|
||||
}
|
||||
T_Ok(is_warning_found);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(patch_cv_symbol_tree)
|
||||
{
|
||||
String8List raw_symbols = {0};
|
||||
|
||||
Reference in New Issue
Block a user