Files
raddebugger/src/linker/lnk_lib.c
T
2025-09-05 15:19:30 -07:00

246 lines
8.1 KiB
C

// Copyright (c) 2025 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal LNK_LibNode *
lnk_lib_list_pop_node_atomic(LNK_LibList *list)
{
for (;;) {
LNK_LibNode *expected = list->first;
LNK_LibNode *current = ins_atomic_ptr_eval_cond_assign(&list->first, expected->next, expected);
if (expected == current) {
ins_atomic_u64_dec_eval(&list->count);
return expected;
}
}
}
internal void
lnk_lib_list_push_node_atomic(LNK_LibList *list, LNK_LibNode *node)
{
for (;;) {
LNK_LibNode *expected = list->first;
LNK_LibNode *current = ins_atomic_ptr_eval_cond_assign(&list->first, node, expected);
if (current == expected) {
node->next = expected;
ins_atomic_u64_inc_eval(&list->count);
return;
}
}
}
internal void
lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node)
{
SLLStackPush(list->first, node);
list->count += 1;
}
internal LNK_LibList
lnk_lib_list_reserve(Arena *arena, U64 count)
{
LNK_LibList result = {0};
LNK_LibNode *nodes = push_array(arena, LNK_LibNode, count);
for (U64 i = 0; i < count; i += 1) { lnk_lib_list_push_node(&result, &nodes[i]); }
return result;
}
internal LNK_LibNodeArray
lnk_array_from_lib_list(Arena *arena, LNK_LibList list)
{
LNK_LibNodeArray result = {0};
result.v = push_array(arena, LNK_LibNode, list.count);
for (LNK_LibNode *n = list.first; n != 0; n = n->next) { result.v[result.count++] = *n; }
return result;
}
internal B32
lnk_lib_from_data(Arena *arena, String8 data, String8 path, LNK_Lib *lib_out)
{
ProfBeginFunction();
// is data archive?
COFF_ArchiveType type = coff_archive_type_from_data(data);
if (type == COFF_Archive_Null) {
return 0;
}
COFF_ArchiveParse parse = coff_archive_parse_from_data(data);
if (parse.error.size) {
return 0;
}
U64 symbol_count = 0;
String8 string_table = {0};
U16 *symbol_indices = 0;
U32 *member_offsets = 0;
// try to init library from optional second member
if (parse.second_member.member_count) {
COFF_ArchiveSecondMember second_member = parse.second_member;
Assert(second_member.symbol_count == second_member.symbol_index_count);
Assert(second_member.member_count == second_member.member_offset_count);
symbol_count = second_member.symbol_count;
string_table = second_member.string_table;
member_offsets = second_member.member_offsets;
symbol_indices = second_member.symbol_indices;
}
// first member is deprecated however tools emit it for compatibility reasons
// and lld-link with /DLL emits only first member
else if (parse.first_member.symbol_count) {
Temp scratch = scratch_begin(&arena, 1);
COFF_ArchiveFirstMember first_member = parse.first_member;
Assert(first_member.symbol_count == first_member.member_offset_count);
symbol_count = first_member.symbol_count;
string_table = first_member.string_table;
// convert big endian offsets
for (U32 offset_idx = 0; offset_idx < symbol_count; offset_idx += 1) {
first_member.member_offsets[offset_idx] = from_be_u32(first_member.member_offsets[offset_idx]);
}
// compress member offsets to match those from the second header
{
HashTable *member_off_ht = hash_table_init(scratch.arena, (U64)((F64)first_member.symbol_count * 1.3));
for EachIndex(symbol_idx, symbol_count) {
if (!hash_table_search_u32_u32(member_off_ht, first_member.member_offsets[symbol_idx], 0)) {
hash_table_push_u32_u32(scratch.arena, member_off_ht, first_member.member_offsets[symbol_idx], member_off_ht->count);
}
}
symbol_indices = push_array(arena, U16, first_member.symbol_count);
for EachIndex(symbol_idx, first_member.symbol_count) {
U32 member_off = first_member.member_offsets[symbol_idx];
U32 member_off_idx = 0;
if (!hash_table_search_u32_u32(member_off_ht, member_off, &member_off_idx)) {
InvalidPath;
}
symbol_indices[symbol_idx] = member_off_idx+1;
}
member_offsets = push_array_no_zero(arena, U32, member_off_ht->count);
for EachIndex(bucket_idx, member_off_ht->cap) {
BucketList *bucket = &member_off_ht->buckets[bucket_idx];
for (BucketNode *n = bucket->first; n != 0; n = n->next) {
U32 member_off = n->v.key_u32;
U32 member_off_idx = n->v.value_u32;
member_offsets[member_off_idx] = member_off;
}
}
}
scratch_end(scratch);
}
// parse string table
String8Array symbol_names;
{
Temp scratch = scratch_begin(&arena, 1);
String8List symbol_name_list = str8_split_by_string_chars(scratch.arena, string_table, str8_lit("\0"), StringSplitFlag_KeepEmpties);
Assert(symbol_name_list.node_count >= symbol_count);
symbol_names = str8_array_from_list(arena, &symbol_name_list);
scratch_end(scratch);
}
symbol_count = Min(symbol_count, symbol_names.count);
// init lib
lib_out->path = push_str8_copy(arena, path);
lib_out->data = data;
lib_out->type = type;
lib_out->symbol_count = symbol_count;
lib_out->member_offsets = member_offsets;
lib_out->symbol_indices = symbol_indices;
lib_out->symbol_names = symbol_names;
lib_out->long_names = parse.long_names;
ProfEnd();
return 1;
}
internal
THREAD_POOL_TASK_FUNC(lnk_lib_initer)
{
LNK_LibIniter *task = raw_task;
LNK_LibNode *lib_node = lnk_lib_list_pop_node_atomic(&task->free_libs);
lib_node->data.input_idx = task_id;
B32 is_valid_lib = lnk_lib_from_data(arena, task->data_arr[task_id], task->path_arr[task_id], &lib_node->data);
if (is_valid_lib) {
lnk_lib_list_push_node_atomic(&task->valid_libs, lib_node);
} else {
lnk_lib_list_push_node_atomic(&task->invalid_libs, lib_node);
}
}
internal int
lnk_lib_node_is_before(void *a, void *b)
{
return ((LNK_LibNode*)a)->data.input_idx < ((LNK_LibNode*)b)->data.input_idx;
}
internal LNK_LibNodeArray
lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, String8Array data_arr, String8Array path_arr)
{
Temp scratch = scratch_begin(arena->v, arena->count);
Assert(data_arr.count == path_arr.count);
U64 lib_count = data_arr.count;
// parse libs in parallel
LNK_LibIniter task = {0};
task.free_libs = lnk_lib_list_reserve(scratch.arena, lib_count);
task.data_arr = data_arr.v;
task.path_arr = path_arr.v;
tp_for_parallel(tp, arena, lib_count, lnk_lib_initer, &task);
// report invalid libs
LNK_LibNodeArray invalid_libs = lnk_array_from_lib_list(scratch.arena, task.invalid_libs);
radsort(invalid_libs.v, invalid_libs.count, lnk_lib_node_is_before);
for (U64 i = 0; i < task.invalid_libs.count; i += 1) {
U64 input_idx = invalid_libs.v[i].data.input_idx;
lnk_error(LNK_Error_InvalidLib, "%S: failed to parse library", path_arr.v[input_idx]);
}
// push parsed libs
LNK_LibNodeArray result = lnk_array_from_lib_list(arena->v[0], task.valid_libs);
radsort(result.v, result.count, lnk_lib_node_is_before);
for (U64 i = result.count; i > 0; i -= 1) {
result.v[i-1].data.input_idx = list->count;
lnk_lib_list_push_node(list, &result.v[i-1]);
}
scratch_end(scratch);
return result;
}
internal
THREAD_POOL_TASK_FUNC(lnk_push_lib_symbols_task)
{
LNK_SymbolPusher *task = raw_task;
LNK_SymbolTable *symtab = task->symtab;
LNK_Lib *lib = &task->u.libs.v[task_id].data;
for EachIndex(symbol_idx, lib->symbol_count) {
U32 member_offset_number = lib->symbol_indices[symbol_idx];
if (member_offset_number > 0) {
LNK_Symbol *symbol = lnk_make_lib_symbol(arena, lib->symbol_names.v[symbol_idx], lib, lib->member_offsets[member_offset_number-1]);
lnk_symbol_table_push_(symtab, arena, worker_id, LNK_SymbolScope_Lib, symbol);
}
}
}
internal void
lnk_input_lib_symbols(TP_Context *tp, LNK_SymbolTable *symtab, LNK_LibNodeArray libs)
{
ProfBeginFunction();
LNK_SymbolPusher task = {0};
task.symtab = symtab;
task.u.libs = libs;
tp_for_parallel(tp, symtab->arena, libs.count, lnk_push_lib_symbols_task, &task);
ProfEnd();
}