diff --git a/src/linker/lnk.c b/src/linker/lnk.c index bddb1755..1a611829 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -103,6 +103,7 @@ #include "lnk_log.h" #include "lnk_timer.h" +#include "lnk_hasher.h" #include "lnk_io.h" #include "lnk_cmd_line.h" #include "lnk_config.h" @@ -116,6 +117,7 @@ #include "lnk_log.c" #include "lnk_timer.c" +#include "lnk_hasher.c" #include "lnk_io.c" #include "lnk_cmd_line.c" #include "lnk_config.c" @@ -150,7 +152,6 @@ lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line) "/RAD_BOOT_MODE:LINKER", //"/RAD_BUILD_EXP", "/RAD_BUILD_IMPLIB", - "/RAD_TPYE_HASH_ALG:BLAKE3", "/RAD_AGE:1", "/RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "/RAD_DO_MERGE", @@ -173,6 +174,13 @@ lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line) (char*)str8f(scratch.arena, "/RAD_MT_PATH:%s", LNK_MANIFEST_MERGE_TOOL_NAME).str, (char*)str8f(scratch.arena, "/RAD_DATA_DIR_COUNT:%u", PE_DataDirectoryIndex_COUNT).str, + // Set BLAKE3 as the default to match the LLVM default. + // + // When hash kinds conflict, radlink discards any .debug$H sections + // whose hash kind does not match the selected default. + "/RAD_DEBUG_TYPE_HASH:BLAKE3", + + // Use LLVM significant addresses hints for the /OPT:ICF. "/LLVM_ADDRSIG", }; @@ -6696,7 +6704,7 @@ lnk_run_type_server(TP_Context *tp, TP_Arena *arena, LNK_Config *config) include_objs = u64_array_from_list(scratch.arena, &include_obj_list); } - LNK_RRT rrt = {0}; + LNK_RRT rrt = { .debug_types_hash = config->debug_types_hash }; ProfScope("Pack Type Data & Data Ranges") { LNK_RRTTypeDataSerializer task = { &cv_types, &rrt.type_data_raw, rrt.type_data_ranges }; diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index d783784c..eb08ca3b 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -102,7 +102,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_Rad_ImageAltPath, 0, "RAD_IMAGEALTPATH", ":FILENAME", "Alternative name for the image" }, { 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_DebugTypeHash, 0, "RAD_DEBUG_TYPE_HASH", ":{BLAKE3|XXHASH}", "Sets hashing algorithm for debug 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." }, @@ -2325,11 +2325,13 @@ 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: { + case LNK_CmdSwitch_Rad_DebugTypeHash: { 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; + config->debug_types_hash = LNK_HashKind_BLAKE3; + } else if (str8_match(alg, str8_lit("XXHASH"), StringMatchFlag_CaseInsensitive)) { + config->debug_types_hash = LNK_HashKind_XXHash; } else { lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unknown hash alg: %S", alg); } @@ -2954,4 +2956,3 @@ lnk_config_init(LNK_CmdLine cmd_line) ProfEnd(); return config; } - diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index d469a413..9fd5057f 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -136,7 +136,7 @@ typedef enum LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, LNK_CmdSwitch_Rad_SortImports, LNK_CmdSwitch_Rad_TimeStamp, - LNK_CmdSwitch_Rad_TypeHashAlg, + LNK_CmdSwitch_Rad_DebugTypeHash, LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, LNK_CmdSwitch_Rad_Version, @@ -305,7 +305,6 @@ typedef enum LNK_TypeNameHashMode_Full, } LNK_TypeNameHashMode; - typedef struct LNK_Config { Arena *arena; @@ -411,7 +410,7 @@ typedef struct LNK_Config U64 unresolved_symbol_limit; U64 unresolved_symbol_ref_limit; LNK_SwitchState map_lines_for_unresolved_symbols; - LLVM_GHashAlg type_hash_alg; + LNK_HashKind debug_types_hash; String8 type_server_name; LNK_SwitchState type_server; LNK_SwitchState sort_imports; @@ -616,4 +615,3 @@ internal void lnk_apply_cmd_option_to_config(LNK_Config *config, String8 name, S internal void lnk_config_pushf(LNK_Config *config, char *fmt, ...); internal LNK_Config * lnk_config_init(LNK_CmdLine cmd_line); - diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index ef1e0dfc..65e42380 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -107,11 +107,11 @@ THREAD_POOL_TASK_FUNC(lnk_parse_debug_h_task) } // validate hashing algorithm - if (ghash.hash_alg != task->config->type_hash_alg) { + if (lnk_hash_kind_from_llvm(ghash.hash_alg) != task->config->debug_types_hash) { lnk_error_obj(LNK_Warning_GHash, task->obj_arr[obj_idx], - "mismatched .debug$H hash algorithm: got %S, expected %S", + "mismatched .debug$H hash algorithm: got %S, expected %S; types will be rehashed", llvm_string_from_ghash_alg(ghash.hash_alg), - llvm_string_from_ghash_alg(task->config->type_hash_alg)); + lnk_string_hash_kind(task->config->debug_types_hash)); goto exit; } @@ -320,42 +320,45 @@ lnk_string_list_from_rrt(Arena *arena, LNK_RRT *rrt) str8_list_push(arena, &rrt_data, g_rrt_magic); // (2) version - str8_list_push(arena, &rrt_data, str8_struct(&g_rrt_version)); + str8_list_push(arena, &rrt_data, str8_struct(push_u64(arena, g_rrt_version))); - // (3) type data ranges + // (3) debug types hash + str8_list_push(arena, &rrt_data, str8_struct(&rrt->debug_types_hash)); + + // (4) type data ranges str8_list_push(arena, &rrt_data, str8_array_fixed(rrt->type_data_ranges)); - // (4) type data + // (5) type data str8_list_push(arena, &rrt_data, rrt->type_data_raw); - // (5) type index ranges + // (6) type index ranges str8_list_push(arena, &rrt_data, str8_array_fixed(rrt->ti_ranges)); - // (6) type hashes size + // (7) type hashes size U64 total_hash_count = 0; for EachIndex(i, CV_TypeIndexSource_COUNT) { total_hash_count += dim_1u64(rrt->ti_ranges[i]); } U64 type_hashes_size = sizeof(**rrt->type_hashes_unpacked) * total_hash_count; str8_list_push(arena, &rrt_data, str8_struct(push_u64(arena, type_hashes_size))); - // (7) type hashes + // (8) type hashes for EachIndex(i, CV_TypeIndexSource_COUNT) { U64 type_count = dim_1u64(rrt->ti_ranges[i]); str8_list_push(arena, &rrt_data, str8_array(rrt->type_hashes_unpacked[i], type_count)); } - // (8) object count + // (9) object count str8_list_push(arena, &rrt_data, str8_struct(&rrt->obj_count)); - // (9) per object type index ranges + // (10) per object type index ranges str8_list_push(arena, &rrt_data, str8_array(rrt->obj_ti_ranges, rrt->obj_count)); - // (10) per object time stamps + // (11) per object time stamps str8_list_push(arena, &rrt_data, str8_array(rrt->obj_time_stamps, rrt->obj_count)); - // (11) per object leaf counts + // (12) per object leaf counts str8_list_push(arena, &rrt_data, str8_array(rrt->obj_leaf_counts, rrt->obj_count)); - // (12) per object file reverse lookup table for type indices + // (13) per object file reverse lookup table for type indices for EachIndex(obj_idx, rrt->obj_count) { CV_TypeIndex *obj_ti_map = rrt->obj_ti_maps[obj_idx]; U64 obj_ti_count = rrt->obj_leaf_counts[obj_idx]; @@ -363,16 +366,16 @@ lnk_string_list_from_rrt(Arena *arena, LNK_RRT *rrt) str8_list_push(arena, &rrt_data, str8_array(obj_ti_map, obj_ti_count)); } - // (13) object file paths size + // (14) object file paths size str8_list_push(arena, &rrt_data, str8_struct(push_u64(arena, obj_paths.size))); - // (14) object file paths block + // (15) object file paths block str8_list_push(arena, &rrt_data, obj_paths); - // (15) PCH type index ranges + // (16) PCH type index ranges str8_list_push(arena, &rrt_data, str8_array(rrt->obj_pch_ti_ranges, rrt->obj_count)); - // (16) PCH object indices + // (17) PCH object indices str8_list_push(arena, &rrt_data, str8_array(rrt->obj_pch_indices, rrt->obj_count)); ProfEnd(); @@ -404,11 +407,27 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o // match version if (version != g_rrt_version) { - lnk_error(LNK_Error_IllData, "ERROR: %S: RRT version mismatch, got %llu, expected %llu", path, version, g_rrt_version); + lnk_error(LNK_Error_IllData, "ERROR: %S: RRT version mismatch, got %llu, expected 2 or %llu", path, version, g_rrt_version); goto exit; } - // (3) type data ranges + // (3) debug types hash + LNK_HashKind debug_types_hash = LNK_HashKind_BLAKE3; + if (version == g_rrt_version) { + U64 debug_types_hash_size = str8_deserial_read_struct(rrt_data, cursor, &debug_types_hash); + if (debug_types_hash_size != sizeof(debug_types_hash)) { + lnk_error(LNK_Error_IllData, "ERROR: %S: RRT file does not contain enough bytes to read the debug types hash", path); + goto exit; + } + cursor += debug_types_hash_size; + + if (debug_types_hash != LNK_HashKind_BLAKE3 && debug_types_hash != LNK_HashKind_XXHash) { + lnk_error(LNK_Error_IllData, "ERROR: %S: RRT file has invalid debug types hash %u", path, debug_types_hash); + goto exit; + } + } + + // (4) type data ranges Rng1U64 type_data_ranges[CV_TypeIndexSource_COUNT] = {0}; U64 type_data_ranges_size = str8_deserial_read_array(rrt_data, cursor, type_data_ranges, ArrayCount(type_data_ranges)); if (type_data_ranges_size != sizeof(type_data_ranges)) { @@ -421,7 +440,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o U64 total_type_data_size = 0; for EachElement(i, type_data_ranges) total_type_data_size += dim_1u64(type_data_ranges[i]); - // (4) type data + // (5) type data String8 type_data_raw = {0}; U64 type_data_raw_size = str8_deserial_read_block(rrt_data, cursor, total_type_data_size, &type_data_raw); if (type_data_raw_size != total_type_data_size) { @@ -430,7 +449,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } cursor += type_data_raw_size; - // (5) type index ranges + // (6) type index ranges Rng1U64 ti_ranges[CV_TypeIndexSource_COUNT] = {0}; U64 ti_ranges_size = str8_deserial_read_array(rrt_data, cursor, ti_ranges, ArrayCount(ti_ranges)); if (ti_ranges_size != sizeof(ti_ranges)) { @@ -439,7 +458,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } cursor += ti_ranges_size; - // (6) type hashes size + // (7) type hashes size U64 type_hashes_size = 0; U64 type_hashes_size_size = str8_deserial_read_struct(rrt_data, cursor, &type_hashes_size); if (type_hashes_size_size != sizeof(type_hashes_size)) { @@ -459,7 +478,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } } - // (7) type hashes + // (8) type hashes String8 type_hashes = {0}; U64 type_hashes_read_size = str8_deserial_read_block(rrt_data, cursor, type_hashes_size, &type_hashes); if (type_hashes_read_size != type_hashes_size) { @@ -479,7 +498,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } } - // (8) object count + // (9) object count U64 obj_count = 0; U64 obj_count_size = str8_deserial_read_struct(rrt_data, cursor, &obj_count); if (obj_count_size == 0) { @@ -488,7 +507,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } cursor += obj_count_size; - // (9) per object type index ranges + // (10) per object type index ranges Rng1U64 *obj_ti_ranges = str8_deserial_get_raw_ptr(rrt_data, cursor, sizeof(*obj_ti_ranges) * obj_count); if (obj_ti_ranges == 0) { lnk_error(LNK_Error_IllData, "ERROR: %S: RRT file is missing the object type index ranges", path); @@ -496,7 +515,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } cursor += sizeof(*obj_ti_ranges) * obj_count; - // (10) last observed time stamp of the object files + // (11) last observed time stamp of the object files U64 *obj_time_stamps = str8_deserial_get_raw_ptr(rrt_data, cursor, sizeof(*obj_time_stamps) * obj_count); if (obj_time_stamps == 0) { lnk_error(LNK_Error_IllData, "ERROR: %S: RRT file is missing the object timestamps", path); @@ -504,7 +523,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } cursor += sizeof(*obj_time_stamps) * obj_count; - // (11) per object leaf counts + // (12) per object leaf counts U64 *obj_leaf_counts = str8_deserial_get_raw_ptr(rrt_data, cursor, sizeof(*obj_leaf_counts) * obj_count); if (obj_leaf_counts == 0) { lnk_error(LNK_Error_IllData, "ERROR: %S: RRT file is missing the object leaf counts", path); @@ -512,7 +531,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } cursor += sizeof(*obj_leaf_counts) * obj_count; - // (12) per object file reverse lookup table for type indices + // (13) per object file reverse lookup table for type indices CV_TypeIndex **obj_ti_maps = push_array(arena, CV_TypeIndex *, obj_count); for EachIndex(obj_idx, obj_count) { U64 obj_ti_count = obj_leaf_counts[obj_idx]; @@ -524,7 +543,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o cursor += obj_ti_count * sizeof(*obj_ti_maps[obj_idx]); } - // (13) object file paths size + // (14) object file paths size U64 obj_file_paths_size = 0; U64 obj_file_paths_size_size = str8_deserial_read_struct(rrt_data, cursor, &obj_file_paths_size); if (obj_file_paths_size_size == 0) { @@ -533,7 +552,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } cursor += obj_file_paths_size_size; - // (14) object file paths block + // (15) object file paths block String8 obj_file_paths_block = {0}; U64 obj_file_paths_block_size = str8_deserial_read_block(rrt_data, cursor, obj_file_paths_size, &obj_file_paths_block); if (obj_file_paths_block_size != obj_file_paths_size) { @@ -542,7 +561,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o } cursor += obj_file_paths_block_size; - // (15) PCH type index ranges + // (16) PCH type index ranges Rng1U64 *obj_pch_ti_ranges = str8_deserial_get_raw_ptr(rrt_data, cursor, obj_count * sizeof(*obj_pch_ti_ranges)); if (obj_pch_ti_ranges == 0) { lnk_error(LNK_Error_IllData, "ERROR: %S: RRT file is too small to read object PCH type index ranges"); @@ -584,6 +603,7 @@ lnk_rrt_from_string(Arena *arena, String8 rrt_data, String8 path, LNK_RRT *rrt_o // fill out result if (rrt_out) { rrt_out->path = path; + rrt_out->debug_types_hash = debug_types_hash; rrt_out->type_data_raw = type_data_raw; rrt_out->type_hashes = type_hashes; MemoryCopyArray(rrt_out->type_data_ranges, type_data_ranges); @@ -937,7 +957,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, // wire RRT hashes to type servers .debug$H for EachIndex(ts_idx, ts_arr.count) { LNK_TypeServer *ts = &ts_arr.v[ts_idx]; - if (ts->rrt) { + if (ts->rrt && ts->rrt->debug_types_hash == config->debug_types_hash) { U64 ts_obj_idx = input.ts_obj_range.min + ts_idx; CV_DebugT *debug_t = &input.debug_t_arr[ts_obj_idx]; CV_DebugH *debug_h = &input.debug_h_arr[ts_obj_idx]; @@ -1248,7 +1268,8 @@ lnk_hash_cv_leaf(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInf CV_TypeIndex curr_ti = cv_ti_from_leaf_idx(debug_t, curr_ti_source, leaf_ref.leaf_idx); // init hasher - blake3_hasher hasher; blake3_hasher_init(&hasher); + LNK_Hasher hasher; + lnk_hasher_init(&hasher, input->config->debug_types_hash); // hash bytes around indices { @@ -1256,14 +1277,14 @@ lnk_hash_cv_leaf(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInf for EachNode(ti_info, CV_TypeIndexInfo, ti_info_list.first) { U8 *bytes = leaf.data.str + last_ti_off; U64 size = ti_info->offset - last_ti_off; - blake3_hasher_update(&hasher, bytes, size); + lnk_hasher_update(&hasher, bytes, size); last_ti_off = ti_info->offset + sizeof(CV_TypeIndex); } Assert(leaf.data.size >= last_ti_off); U8 *bytes = leaf.data.str + last_ti_off; U64 size = leaf.data.size - last_ti_off; - blake3_hasher_update(&hasher, bytes, size); + lnk_hasher_update(&hasher, bytes, size); } // mix-in sub leaf hashes @@ -1273,7 +1294,7 @@ lnk_hash_cv_leaf(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInf // simple indices are stable across compile units if (sub_ti < debug_t->ti_ranges[sub_ti_n->source].min) { - blake3_hasher_update(&hasher, &sub_ti, sizeof(sub_ti)); + lnk_hasher_update_struct(&hasher, &sub_ti); continue; } @@ -1285,7 +1306,7 @@ lnk_hash_cv_leaf(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInf memory_write16(leaf_header + OffsetOf(CV_LeafHeader, size), sizeof(CV_LeafKind)); // reset hasher - blake3_hasher_init(&hasher); + lnk_hasher_init(&hasher, input->config->debug_types_hash); // log error Temp scratch = scratch_begin(0,0); @@ -1307,7 +1328,7 @@ lnk_hash_cv_leaf(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInf memory_write16(leaf_header + OffsetOf(CV_LeafHeader, size), sizeof(CV_LeafKind)); // reset hasher - blake3_hasher_init(&hasher); + lnk_hasher_init(&hasher, input->config->debug_types_hash); // log error Temp scratch = scratch_begin(0,0); @@ -1324,20 +1345,21 @@ lnk_hash_cv_leaf(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInf U64 sub_hash = input->debug_h_arr[sub_ref.obj_idx].v[sub_ref.leaf_idx]; // mix-in sub-type hash - blake3_hasher_update(&hasher, &sub_hash, sizeof(sub_hash)); + lnk_hasher_update_struct(&hasher, &sub_hash); } // hash leaf header - CV_LeafHeader *leaf_header = cv_debug_t_get_leaf_header(debug_t, leaf_ref.leaf_idx); - blake3_hasher_update(&hasher, leaf_header, sizeof(*leaf_header)); + CV_LeafHeader *leaf_header_ptr = cv_debug_t_get_leaf_header(debug_t, leaf_ref.leaf_idx); + lnk_hasher_update_struct(&hasher, leaf_header_ptr); - U64 hash; - blake3_hasher_finalize(&hasher, (U8 *) &hash, sizeof(hash)); + // finalize the type hash + U64 hash = lnk_hasher_digest(&hasher); Assert(hash != 0); Assert(input->debug_h_arr[leaf_ref.obj_idx].v[leaf_ref.leaf_idx] == 0 || input->debug_h_arr[leaf_ref.obj_idx].v[leaf_ref.leaf_idx] == 1); input->debug_h_arr[leaf_ref.obj_idx].v[leaf_ref.leaf_idx] = hash; + return hash; } diff --git a/src/linker/lnk_debug_info.h b/src/linker/lnk_debug_info.h index 455738ca..28ecf301 100644 --- a/src/linker/lnk_debug_info.h +++ b/src/linker/lnk_debug_info.h @@ -7,11 +7,12 @@ // RRT global read_only String8 g_rrt_magic = str8_lit_comp("RAD-TYPE-SERVER\0"); -global read_only U64 g_rrt_version = 2; +global read_only U64 g_rrt_version = 3; typedef struct LNK_RRT { String8 path; + LNK_HashKind debug_types_hash; String8 type_data_raw; union { diff --git a/src/linker/lnk_hasher.c b/src/linker/lnk_hasher.c new file mode 100644 index 00000000..d11467b1 --- /dev/null +++ b/src/linker/lnk_hasher.c @@ -0,0 +1,60 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal void +lnk_hasher_init(LNK_Hasher *hasher, LNK_HashKind kind) +{ + hasher->kind = kind; + switch (kind) { + case LNK_HashKind_BLAKE3: blake3_hasher_init(&hasher->u.blake3); break; + case LNK_HashKind_XXHash: XXH3_64bits_reset(&hasher->u.xxhash); break; + default: InvalidPath; break; + } +} + +internal void +lnk_hasher_update(LNK_Hasher *hasher, void *data, U64 size) +{ + switch (hasher->kind) { + case LNK_HashKind_BLAKE3: blake3_hasher_update(&hasher->u.blake3, data, size); break; + case LNK_HashKind_XXHash: XXH3_64bits_update(&hasher->u.xxhash, data, size); break; + default: InvalidPath; break; + } +} + +internal U64 +lnk_hasher_digest(LNK_Hasher *hasher) +{ + U64 hash = 0; + switch (hasher->kind) { + case LNK_HashKind_BLAKE3: blake3_hasher_finalize(&hasher->u.blake3, (U8 *)&hash, sizeof(hash)); break; + case LNK_HashKind_XXHash: hash = XXH3_64bits_digest(&hasher->u.xxhash); break; + default: InvalidPath; break; + } + return hash; +} + +internal String8 +lnk_string_hash_kind(LNK_HashKind hash_kind) +{ +#define X(NAME) case LNK_HashKind_##NAME: return str8_lit(Stringify(NAME)); + switch (hash_kind) { + LNK_HashKind_XList + } +#undef X + return str8_zero(); +} + +internal LNK_HashKind +lnk_hash_kind_from_llvm(LLVM_GHashAlgEnum v) +{ + switch (v) { + case LLVM_GHashAlg_SHA1: + case LLVM_GHashAlg_SHA1_8: + return LNK_HashKind_Null; + case LLVM_GHashAlg_BLAKE3: + return LNK_HashKind_BLAKE3; + } + return LNK_HashKind_Null; +} + diff --git a/src/linker/lnk_hasher.h b/src/linker/lnk_hasher.h new file mode 100644 index 00000000..a07aa321 --- /dev/null +++ b/src/linker/lnk_hasher.h @@ -0,0 +1,37 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#pragma once + +#define LNK_HashKind_XList \ + X(Null) \ + X(BLAKE3) \ + X(XXHash) + +typedef enum +{ +#define X(NAME) LNK_HashKind_##NAME, + LNK_HashKind_XList +#undef X +} LNK_HashKind; + +typedef struct LNK_Hasher +{ + LNK_HashKind kind; + union + { + blake3_hasher blake3; + XXH3_state_t xxhash; + } u; +} LNK_Hasher; + +internal void lnk_hasher_init (LNK_Hasher *hasher, LNK_HashKind kind); +internal void lnk_hasher_update(LNK_Hasher *hasher, void *data, U64 size); +#define lnk_hasher_update_struct(hasher, ptr) lnk_hasher_update(hasher, ptr, sizeof(*ptr)) +internal U64 lnk_hasher_digest(LNK_Hasher *hasher); + +internal String8 lnk_string_hash_kind(LNK_HashKind hash_kind); + +// LLVM extension +internal LNK_HashKind lnk_hash_kind_from_llvm(LLVM_GHashAlgEnum v); + diff --git a/src/linker/tests/linker_tests.c b/src/linker/tests/linker_tests.c index cabae47c..66c25f58 100644 --- a/src/linker/tests/linker_tests.c +++ b/src/linker/tests/linker_tests.c @@ -7423,7 +7423,7 @@ TEST(ghash_check_hash_alg) B32 is_warning_found = 0; String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj")); - String8 expected_line = str8f(arena, "Warning(*): *: mismatched .debug$H hash algorithm: got SHA1_8, expected BLAKE3"); + String8 expected_line = str8f(arena, "Warning(*): *: mismatched .debug$H hash algorithm: got SHA1_8, expected *"); for (String8 i = g_errors; i.size > 0 && !is_warning_found; ) { String8 line = t_chop_line(&i); is_warning_found = str8_match_wildcard(line, expected_line, StringMatchFlag_CaseInsensitive);