mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-25 05:04:58 -07:00
4a2e1dd8d0
Initial implementation of the symbol table allowed us to push symbols from one thread at a time and required a wonky lock-step and spread out logic for replacing symbols. And because it was a externally chained hash table we had to guesstimate optimal capacity for small and big targets. We erred on the side of larger capacity to prevent degradation and rehashing, however this meant small targets had to spent unreasonable amount of time memory allocing slots. With hash trie we can dynamically scale up to 4 ^ 32 items and atomically replace symbols when needed. Neat!
944 lines
34 KiB
C
944 lines
34 KiB
C
// Copyright (c) 2024 Epic Games Tools
|
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
|
|
////////////////////////////////
|
|
|
|
internal void
|
|
lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...)
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
String8 text = push_str8fv(scratch.arena, fmt, args);
|
|
|
|
if (obj->lib_path.size) {
|
|
lnk_error(code, "%S(%S): %S", obj->lib_path, obj->path, text);
|
|
} else {
|
|
lnk_error(code, "%S: %S", obj->path, text);
|
|
}
|
|
|
|
va_end(args);
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
internal void
|
|
lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node)
|
|
{
|
|
SLLQueuePush(list->first, list->last, node);
|
|
++list->count;
|
|
}
|
|
|
|
internal LNK_InputObj *
|
|
lnk_input_obj_list_push(Arena *arena, LNK_InputObjList *list)
|
|
{
|
|
LNK_InputObj *node = push_array(arena, LNK_InputObj, 1);
|
|
lnk_input_obj_list_push_node(list, node);
|
|
return node;
|
|
}
|
|
|
|
internal LNK_InputObj **
|
|
lnk_array_from_input_obj_list(Arena *arena, LNK_InputObjList list)
|
|
{
|
|
LNK_InputObj **result = push_array_no_zero(arena, LNK_InputObj *, list.count);
|
|
U64 i = 0;
|
|
for (LNK_InputObj *n = list.first; n != 0; n = n->next, ++i) {
|
|
Assert(i < list.count);
|
|
result[i] = n;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal void
|
|
lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat)
|
|
{
|
|
SLLConcatInPlace(list, to_concat);
|
|
}
|
|
|
|
internal int
|
|
lnk_input_obj_compar(const void *raw_a, const void *raw_b)
|
|
{
|
|
const LNK_InputObj **a = (const LNK_InputObj **) raw_a;
|
|
const LNK_InputObj **b = (const LNK_InputObj **) raw_b;
|
|
int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path);
|
|
return cmp;
|
|
}
|
|
|
|
internal int
|
|
lnk_input_obj_compar_is_before(void *raw_a, void *raw_b)
|
|
{
|
|
LNK_InputObj **a = raw_a;
|
|
LNK_InputObj **b = raw_b;
|
|
int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path);
|
|
int is_before = cmp < 0;
|
|
return is_before;
|
|
}
|
|
|
|
internal LNK_InputObjList
|
|
lnk_list_from_input_obj_arr(LNK_InputObj **arr, U64 count)
|
|
{
|
|
LNK_InputObjList list = {0};
|
|
for (U64 i = 0; i < count; ++i) {
|
|
SLLQueuePush(list.first, list.last, arr[i]);
|
|
++list.count;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
internal LNK_InputObjList
|
|
lnk_input_obj_list_from_string_list(Arena *arena, String8List list)
|
|
{
|
|
LNK_InputObjList input_list = {0};
|
|
for (String8Node *path = list.first; path != 0; path = path->next) {
|
|
LNK_InputObj *input = lnk_input_obj_list_push(arena, &input_list);
|
|
input->is_thin = 1;
|
|
input->dedup_id = path->string;
|
|
input->path = path->string;
|
|
}
|
|
return input_list;
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
internal LNK_Obj **
|
|
lnk_obj_arr_from_list(Arena *arena, LNK_ObjList list)
|
|
{
|
|
LNK_Obj **arr = push_array_no_zero(arena, LNK_Obj *, list.count);
|
|
U64 idx = 0;
|
|
for (LNK_ObjNode *node = list.first; node != 0; node = node->next, ++idx) {
|
|
arr[idx] = &node->data;
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
internal LNK_ObjNodeArray
|
|
lnk_obj_list_reserve(Arena *arena, LNK_ObjList *list, U64 count)
|
|
{
|
|
LNK_ObjNodeArray arr = {0};
|
|
if (count) {
|
|
arr.count = count;
|
|
arr.v = push_array(arena, LNK_ObjNode, count);
|
|
for (LNK_ObjNode *ptr = arr.v, *opl = arr.v + arr.count; ptr < opl; ++ptr) {
|
|
SLLQueuePush(list->first, list->last, ptr);
|
|
}
|
|
list->count += count;
|
|
} else {
|
|
MemoryZeroStruct(&arr);
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
|
|
internal LNK_ChunkList
|
|
lnk_obj_search_chunks(Arena *arena, LNK_Obj *obj, String8 name, String8 postfix, B32 collect_discarded)
|
|
{
|
|
LNK_ChunkList list = {0};
|
|
for (U64 sect_idx = 0; sect_idx < obj->chunk_count; ++sect_idx) {
|
|
String8 obj_sect_name = obj->sect_name_arr[sect_idx];
|
|
String8 obj_sect_sort = obj->sect_sort_arr[sect_idx];
|
|
|
|
B32 is_match = str8_match(obj_sect_name, name, 0) &&
|
|
str8_match(obj_sect_sort, postfix, 0);
|
|
|
|
if (is_match) {
|
|
LNK_ChunkPtr chunk = &obj->chunk_arr[sect_idx];
|
|
|
|
if (!collect_discarded && lnk_chunk_is_discarded(chunk)) {
|
|
continue;
|
|
}
|
|
|
|
LNK_ChunkNode *node = push_array_no_zero(arena, LNK_ChunkNode, 1);
|
|
node->next = 0;
|
|
node->data = chunk;
|
|
|
|
SLLQueuePush(list.first, list.last, node);
|
|
++list.count;
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_collect_obj_chunks_task)
|
|
{
|
|
U64 obj_idx = task_id;
|
|
LNK_CollectObjChunksTaskData *task = raw_task;
|
|
LNK_Obj *obj = task->obj_arr[obj_idx];
|
|
LNK_ChunkList *list_ptr = &task->list_arr[obj_idx];
|
|
*list_ptr = lnk_obj_search_chunks(arena, obj, task->name, task->postfix, task->collect_discarded);
|
|
}
|
|
|
|
internal LNK_ChunkList *
|
|
lnk_collect_obj_chunks(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8 name, String8 postfix, B32 collect_discarded)
|
|
{
|
|
LNK_CollectObjChunksTaskData task_data = {0};
|
|
task_data.obj_arr = obj_arr;
|
|
task_data.name = name;
|
|
task_data.postfix = postfix;
|
|
task_data.list_arr = push_array_no_zero(arena->v[0], LNK_ChunkList, obj_count);
|
|
task_data.collect_discarded = collect_discarded;
|
|
tp_for_parallel(tp, arena, obj_count, lnk_collect_obj_chunks_task, &task_data);
|
|
return task_data.list_arr;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_symbol_collector)
|
|
{
|
|
LNK_SymbolCollector *task = raw_task;
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
LNK_SymbolList *list = &task->out_arr[task_id];
|
|
for (U64 obj_idx = range.min; obj_idx < range.max; ++obj_idx) {
|
|
LNK_Obj *obj = &task->in_arr.v[obj_idx].data;
|
|
for (LNK_SymbolNode *node = obj->symbol_list.first; node != 0; node = node->next) {
|
|
if (node->data->type == task->type) {
|
|
lnk_symbol_list_push(arena, list, node->data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal LNK_SymbolList
|
|
lnk_run_symbol_collector(TP_Context *tp, TP_Arena *arena, LNK_ObjNodeArray arr, LNK_SymbolType symbol_type)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0,0);
|
|
|
|
LNK_SymbolCollector task_data;
|
|
task_data.type = symbol_type;
|
|
task_data.range_arr = tp_divide_work(scratch.arena, arr.count, tp->worker_count);
|
|
task_data.in_arr = arr;
|
|
task_data.out_arr = push_array(scratch.arena, LNK_SymbolList, tp->worker_count);
|
|
|
|
tp_for_parallel(tp, arena, tp->worker_count, lnk_symbol_collector, &task_data);
|
|
|
|
LNK_SymbolList list = {0};
|
|
for (U64 ithread = 0; ithread < tp->worker_count; ++ithread) {
|
|
lnk_symbol_list_concat_in_place(&list, &task_data.out_arr[ithread]);
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return list;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_default_lib_collector)
|
|
{
|
|
LNK_DefaultLibCollector *task = raw_task;
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
String8List *result = &task->out_arr[task_id];
|
|
for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) {
|
|
LNK_Obj *obj = &task->in_arr.v[obj_idx].data;
|
|
String8List list = lnk_parse_default_lib_directive(arena, &obj->directive_info.v[LNK_Directive_DefaultLib]);
|
|
str8_list_concat_in_place(result, &list);
|
|
}
|
|
}
|
|
|
|
internal LNK_InputLibList
|
|
lnk_collect_default_lib_obj_arr(TP_Context *tp, TP_Arena *arena, LNK_ObjNodeArray arr)
|
|
{
|
|
Temp scratch = scratch_begin(0,0);
|
|
|
|
LNK_DefaultLibCollector task_data;
|
|
task_data.range_arr = tp_divide_work(scratch.arena, arr.count, tp->worker_count);
|
|
task_data.in_arr = arr;
|
|
task_data.out_arr = push_array(scratch.arena, LNK_InputLibList, tp->worker_count);
|
|
tp_for_parallel(tp, arena, tp->worker_count, lnk_default_lib_collector, &task_data);
|
|
|
|
String8List result = str8_list_arr_concat(task_data.out_arr, tp->worker_count);
|
|
|
|
scratch_end(scratch);
|
|
return result;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_manifest_dependency_collector)
|
|
{
|
|
LNK_ManifestDependencyCollector *task = raw_task;
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
String8List *list = &task->out_arr[task_id];
|
|
|
|
LNK_ObjNode *obj_ptr = &task->in_arr[range.min];
|
|
LNK_ObjNode *obj_opl = &task->in_arr[range.max];
|
|
|
|
for (; obj_ptr < obj_opl; obj_ptr += 1) {
|
|
LNK_Obj *obj = &obj_ptr->data;
|
|
LNK_DirectiveList *dirs = &obj->directive_info.v[LNK_Directive_ManifestDependency];
|
|
for (LNK_Directive *dir = dirs->first; dir != 0; dir = dir->next) {
|
|
String8List dep = str8_list_copy(arena, &dir->value_list);
|
|
str8_list_concat_in_place(list, &dep);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal String8List
|
|
lnk_collect_manifest_dependency_list(TP_Context *tp, TP_Arena *arena, LNK_ObjNodeArray obj_node_arr)
|
|
{
|
|
Temp scratch = scratch_begin(arena->v, arena->count);
|
|
|
|
LNK_ManifestDependencyCollector task_data = {0};
|
|
task_data.in_arr = obj_node_arr.v;
|
|
task_data.out_arr = push_array(scratch.arena, String8List, tp->worker_count);
|
|
task_data.range_arr = tp_divide_work(scratch.arena, obj_node_arr.count, tp->worker_count);
|
|
tp_for_parallel(tp, arena, tp->worker_count, lnk_manifest_dependency_collector, &task_data);
|
|
|
|
String8List result = str8_list_arr_concat(task_data.out_arr, tp->worker_count);
|
|
|
|
scratch_end(scratch);
|
|
return result;
|
|
}
|
|
|
|
internal void
|
|
lnk_sect_defn_list_push_node(LNK_SectDefnList *list, LNK_SectDefn *node)
|
|
{
|
|
SLLQueuePush(list->first, list->last, node);
|
|
++list->count;
|
|
}
|
|
|
|
internal LNK_SectDefn *
|
|
lnk_sect_defn_list_push(Arena *arena, LNK_SectDefnList *list, LNK_Obj *obj, String8 name, U64 idx, COFF_SectionFlags flags)
|
|
{
|
|
LNK_SectDefn *node = push_array_no_zero(arena, LNK_SectDefn, 1);
|
|
node->next = 0;
|
|
node->obj = obj;
|
|
node->name = name;
|
|
node->idx = idx;
|
|
node->flags = flags;
|
|
lnk_sect_defn_list_push_node(list, node);
|
|
return node;
|
|
}
|
|
|
|
internal void
|
|
lnk_sect_defn_list_concat_in_place(LNK_SectDefnList *list, LNK_SectDefnList *to_concat)
|
|
{
|
|
SLLConcatInPlace(list, to_concat);
|
|
}
|
|
|
|
internal void
|
|
lnk_sect_defn_list_concat_in_place_arr(LNK_SectDefnList *list, LNK_SectDefnList *to_concat_arr, U64 count)
|
|
{
|
|
SLLConcatInPlaceArray(list, to_concat_arr, count);
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_obj_initer)
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
LNK_ObjIniter *task = raw_task;
|
|
LNK_InputObj *input = task->inputs[task_id];
|
|
U64 obj_idx = task->obj_id_base + task_id;
|
|
LNK_ObjNode *obj_node = task->obj_node_arr + task_id;
|
|
LNK_Obj *obj = &obj_node->data;
|
|
|
|
//Assert(coff_data.size > 0);
|
|
|
|
// cache path, we need it for error reports and debug stuff
|
|
String8 cached_path = push_str8_copy(arena, input->path);
|
|
String8 cached_lib_path = push_str8_copy(arena, input->lib_path);
|
|
|
|
// parse coff obj
|
|
COFF_HeaderInfo coff_info = coff_header_info_from_data(input->data);
|
|
COFF_SectionHeader *coff_sect_arr = (COFF_SectionHeader *)(input->data.str + coff_info.section_array_off);
|
|
COFF_Symbol32Array coff_symbols = coff_symbol_array_from_data(scratch.arena, input->data, coff_info.symbol_off, coff_info.symbol_count, coff_info.symbol_size);
|
|
|
|
// handle machines we dont support
|
|
if (coff_info.machine != COFF_MachineType_UNKNOWN &&
|
|
coff_info.machine != COFF_MachineType_X64) {
|
|
lnk_error(LNK_Error_UnsupportedMachine, "%S: %S machine is supported", input->path, coff_string_from_machine_type(coff_info.machine));
|
|
}
|
|
|
|
U64 chunk_count = 0;
|
|
chunk_count += coff_info.section_count_no_null;
|
|
chunk_count += 1; // :common_block
|
|
|
|
String8 *sect_name_arr = push_array_no_zero(arena, String8, chunk_count);
|
|
String8 *sect_sort_arr = push_array_no_zero(arena, String8, chunk_count);
|
|
LNK_Chunk *chunk_arr = push_array_no_zero(arena, LNK_Chunk, chunk_count);
|
|
|
|
// init section name and postfix array
|
|
for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) {
|
|
COFF_SectionHeader *coff_sect = &coff_sect_arr[sect_idx];
|
|
|
|
// read name
|
|
String8 sect_name = coff_section_header_get_name(coff_sect, input->data, coff_info.string_table_off);
|
|
|
|
// parse section name
|
|
String8 name, postfix;
|
|
coff_parse_section_name(sect_name, &name, &postfix);
|
|
|
|
// fill out
|
|
sect_name_arr[sect_idx] = name;
|
|
sect_sort_arr[sect_idx] = postfix;
|
|
}
|
|
|
|
// :common_block
|
|
U64 common_block_idx = chunk_count - 1;
|
|
sect_name_arr[common_block_idx] = str8_lit(".bss");
|
|
sect_sort_arr[common_block_idx] = str8_lit("~");
|
|
|
|
for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) {
|
|
COFF_SectionHeader *coff_sect = &coff_sect_arr[sect_idx];
|
|
|
|
String8 data;
|
|
if (coff_sect->flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA) {
|
|
data = str8(0, coff_sect->fsize);
|
|
} else {
|
|
data = str8(input->data.str + coff_sect->foff, coff_sect->fsize);
|
|
}
|
|
|
|
LNK_Chunk *chunk = &chunk_arr[sect_idx];
|
|
chunk->ref = lnk_chunk_ref(0,0); // :chunk_ref_assign
|
|
chunk->align = coff_align_size_from_section_flags(coff_sect->flags);
|
|
chunk->is_discarded = !!(coff_sect->flags & COFF_SectionFlag_LNK_REMOVE);
|
|
chunk->sort_chunk = 1;
|
|
chunk->type = LNK_Chunk_Leaf;
|
|
chunk->sort_idx = sect_sort_arr[sect_idx];
|
|
chunk->input_idx = LNK_MakeChunkInputIdx(obj_idx, sect_idx);
|
|
chunk->flags = coff_sect->flags;
|
|
chunk->associate = 0;
|
|
chunk->u.leaf = data;
|
|
lnk_chunk_set_debugf(arena, chunk, "%S: name: %S, isect: 0x%llX", path, sect_name_arr[sect_idx], sect_idx);
|
|
}
|
|
|
|
// :common_block
|
|
LNK_Chunk *master_common_block = &chunk_arr[common_block_idx];
|
|
master_common_block->ref = lnk_chunk_ref(0,0); // :chunk_ref_assign
|
|
master_common_block->align = 1;
|
|
master_common_block->is_discarded = 0;
|
|
master_common_block->sort_chunk = 0;
|
|
master_common_block->type = LNK_Chunk_List;
|
|
master_common_block->sort_idx = sect_sort_arr[common_block_idx];
|
|
master_common_block->input_idx = LNK_MakeChunkInputIdx(obj_idx, common_block_idx);
|
|
master_common_block->flags = LNK_BSS_SECTION_FLAGS;
|
|
master_common_block->associate = 0;
|
|
master_common_block->u.list = push_array(arena, LNK_ChunkList, 1);
|
|
lnk_chunk_set_debugf(arena, master_common_block, "%S: master common block", path);
|
|
|
|
// convert from coff
|
|
LNK_SymbolArray symbol_arr = lnk_symbol_array_from_coff(arena, input->data, obj, cached_path, coff_info.string_table_off, coff_info.section_count_no_null, coff_sect_arr, coff_symbols, chunk_arr, master_common_block);
|
|
LNK_SymbolList symbol_list = lnk_symbol_list_from_array(arena, symbol_arr);
|
|
LNK_RelocList *reloc_list_arr = lnk_reloc_list_array_from_coff(arena, coff_info.machine, input->data, coff_info.section_count_no_null, coff_sect_arr, chunk_arr, symbol_arr);
|
|
|
|
// fill out obj
|
|
obj->data = input->data;
|
|
obj->path = cached_path;
|
|
obj->lib_path = cached_lib_path;
|
|
obj->input_idx = obj_idx;
|
|
obj->machine = coff_info.machine;
|
|
obj->chunk_count = chunk_count;
|
|
obj->sect_count = coff_info.section_count_no_null;
|
|
obj->sect_name_arr = sect_name_arr;
|
|
obj->sect_sort_arr = sect_sort_arr;
|
|
obj->chunk_arr = chunk_arr;
|
|
obj->symbol_list = symbol_list;
|
|
obj->sect_reloc_list_arr = reloc_list_arr;
|
|
obj->directive_info = lnk_init_directives(arena, cached_path, coff_info.section_count_no_null, sect_name_arr, chunk_arr);
|
|
|
|
// parse exports
|
|
LNK_ExportParseList export_parse = {0};
|
|
for (LNK_Directive *dir = obj->directive_info.v[LNK_Directive_Export].first; dir != 0; dir = dir->next) {
|
|
lnk_parse_export_direcive(arena, &obj->export_parse, dir->value_list, obj);
|
|
}
|
|
|
|
// push /export symbols
|
|
for (LNK_ExportParse *exp = export_parse.first; exp != 0; exp = exp->next) {
|
|
LNK_Symbol *symbol = lnk_make_undefined_symbol(arena, exp->name, LNK_SymbolScopeFlag_Main);
|
|
lnk_symbol_list_push(arena, &obj->symbol_list, symbol);
|
|
}
|
|
|
|
// push /include symbols
|
|
for (LNK_Directive *dir = obj->directive_info.v[LNK_Directive_Include].first; dir != 0; dir = dir->next) {
|
|
str8_list_concat_in_place(&obj->include_symbol_list, &dir->value_list);
|
|
}
|
|
|
|
// parse /alternatename
|
|
for (LNK_Directive *dir = obj->directive_info.v[LNK_Directive_AlternateName].first; dir != 0; dir = dir->next) {
|
|
String8 *invalid_string = lnk_parse_alt_name_directive_list(arena, dir->value_list, &obj->alt_name_list);
|
|
if (invalid_string != 0) {
|
|
lnk_error_obj(LNK_Error_Cmdl, obj, "invalid syntax \"%S\", expected format \"FROM=TO\"", *invalid_string);
|
|
}
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_obj_new_sect_scanner)
|
|
{
|
|
LNK_ObjNewSectScanner *task = raw_task;
|
|
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
HashTable *ht = hash_table_init(arena, 128);
|
|
|
|
for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) {
|
|
LNK_Obj *obj = &task->obj_node_arr[obj_idx].data;
|
|
|
|
for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) {
|
|
String8 sect_name = obj->sect_name_arr[chunk_idx];
|
|
COFF_SectionFlags sect_flags = obj->chunk_arr[chunk_idx].flags & ~COFF_SectionFlags_LNK_FLAGS;
|
|
|
|
KeyValuePair *is_present = hash_table_search_string(ht, sect_name);
|
|
if (is_present) {
|
|
if (lnk_is_error_code_active(LNK_Warning_SectionFlagsConflict)) {
|
|
LNK_SectDefn *defn = is_present->value_raw;
|
|
if (defn->flags != sect_flags) {
|
|
lnk_sect_defn_list_push(arena, &task->defn_arr[task_id], obj, sect_name, chunk_idx, sect_flags);
|
|
}
|
|
}
|
|
} else {
|
|
LNK_SectDefn *defn = lnk_sect_defn_list_push(arena, &task->defn_arr[task_id], obj, sect_name, chunk_idx, sect_flags);
|
|
hash_table_push_string_raw(arena, ht, sect_name, defn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LNK_CHUNK_VISITOR_SIG(lnk_chunk_get_count_cb)
|
|
{
|
|
U64 *counter = (U64 *)ud;
|
|
*counter += 1;
|
|
return 0;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_chunk_counter)
|
|
{
|
|
U64 obj_idx = task_id;
|
|
LNK_ChunkCounter *task = raw_task;
|
|
LNK_Obj *obj = &task->obj_arr[obj_idx].data;
|
|
for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) {
|
|
String8 name = obj->sect_name_arr[chunk_idx];
|
|
LNK_Chunk *chunk = &obj->chunk_arr[chunk_idx];
|
|
LNK_Section *sect = lnk_section_table_search(task->st, name);
|
|
|
|
U64 count = 0;
|
|
lnk_visit_chunks(0, chunk, lnk_chunk_get_count_cb, &count);
|
|
|
|
task->chunk_count_arr_arr[sect->id][obj_idx] += count;
|
|
}
|
|
}
|
|
|
|
internal
|
|
LNK_CHUNK_VISITOR_SIG(lnk_chunk_ref_assign)
|
|
{
|
|
LNK_ChunkRefAssign *ctx = ud;
|
|
|
|
// alloc chunk id
|
|
U64 chunk_id = ctx->chunk_id_arr_arr[sect_id][ctx->obj_idx];
|
|
ctx->chunk_id_arr_arr[sect_id][ctx->obj_idx] += 1;
|
|
|
|
// set chunk ref
|
|
chunk->ref = lnk_chunk_ref(sect_id, chunk_id);
|
|
|
|
// keep visiting chunks
|
|
return 0;
|
|
}
|
|
|
|
internal
|
|
THREAD_POOL_TASK_FUNC(lnk_chunk_ref_assigner)
|
|
{
|
|
LNK_ChunkRefAssigner *task = raw_task;
|
|
Rng1U64 range = task->range_arr[task_id];
|
|
|
|
for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) {
|
|
LNK_Obj *obj = &task->obj_arr[obj_idx].data;
|
|
|
|
for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) {
|
|
String8 name = obj->sect_name_arr[chunk_idx];
|
|
String8 sort = obj->sect_sort_arr[chunk_idx];
|
|
LNK_Chunk *chunk = &obj->chunk_arr[chunk_idx];
|
|
|
|
// :find_chunk_section
|
|
LNK_Section *sect = lnk_section_table_search(task->st, name);
|
|
Assert(sect);
|
|
|
|
// :chunk_ref_assign
|
|
LNK_ChunkRefAssign ctx;
|
|
ctx.cman = sect->cman;
|
|
ctx.chunk_id_arr_arr = task->chunk_id_arr_arr;
|
|
ctx.obj_idx = obj_idx;
|
|
lnk_visit_chunks(sect->id, chunk, lnk_chunk_ref_assign, &ctx);
|
|
|
|
// push to section chunk list
|
|
LNK_ChunkList **chunk_list_arr_arr = sort.size ? task->chunk_list_arr_arr : task->nosort_chunk_list_arr_arr;
|
|
lnk_chunk_list_push(arena, &chunk_list_arr_arr[sect->id][task_id], chunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal LNK_ObjNodeArray
|
|
lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_ObjList *obj_list, LNK_SectionTable *st, U64 input_count, LNK_InputObj **inputs)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(arena->v, arena->count);
|
|
|
|
U64 obj_id_base = obj_list->count;
|
|
LNK_ObjNodeArray obj_arr = lnk_obj_list_reserve(arena->v[0], obj_list, input_count);
|
|
|
|
ProfBegin("Obj Initer");
|
|
{
|
|
LNK_ObjIniter task = {0};
|
|
task.inputs = inputs;
|
|
task.obj_id_base = obj_id_base;
|
|
task.obj_node_arr = obj_arr.v;
|
|
tp_for_parallel(tp, arena, input_count, lnk_obj_initer, &task);
|
|
}
|
|
ProfEnd();
|
|
|
|
if (st) {
|
|
ProfBegin("Section Table Update");
|
|
{
|
|
TP_Temp temp = tp_temp_begin(arena);
|
|
|
|
LNK_ObjNewSectScanner task;
|
|
task.range_arr = tp_divide_work(arena->v[0], obj_arr.count, tp->worker_count);
|
|
task.obj_node_arr = obj_arr.v;
|
|
task.defn_arr = push_array(arena->v[0], LNK_SectDefnList, tp->worker_count);
|
|
task.conf_arr = push_array(arena->v[0], LNK_SectDefnList, tp->worker_count);
|
|
tp_for_parallel(tp, arena, tp->worker_count, lnk_obj_new_sect_scanner, &task);
|
|
|
|
|
|
LNK_SectDefnList defn_list = {0};
|
|
LNK_SectDefnList conf_list = {0};
|
|
lnk_sect_defn_list_concat_in_place_arr(&defn_list, task.defn_arr, tp->worker_count);
|
|
lnk_sect_defn_list_concat_in_place_arr(&conf_list, task.conf_arr, tp->worker_count);
|
|
|
|
|
|
HashTable *ht = hash_table_init(arena->v[0], 128);
|
|
for (LNK_SectionNode *sect_node = st->list.first; sect_node != 0; sect_node = sect_node->next) {
|
|
LNK_Section *sect = §_node->data;
|
|
hash_table_push_string_u64(arena->v[0], ht, sect->name, sect->flags);
|
|
}
|
|
|
|
|
|
LNK_SectDefnList new_list = {0};
|
|
for (LNK_SectDefn *curr = defn_list.first, *next; curr != 0; curr = next) {
|
|
next = curr->next;
|
|
curr->next = 0;
|
|
|
|
KeyValuePair *is_present = hash_table_search_string(ht, curr->name);
|
|
if (is_present) {
|
|
if (lnk_is_error_code_active(LNK_Warning_SectionFlagsConflict)) {
|
|
COFF_SectionFlags flags = is_present->value_u64;
|
|
if (flags != curr->flags) {
|
|
lnk_sect_defn_list_push_node(&conf_list, curr);
|
|
} else {
|
|
// section is present or is in new_list
|
|
}
|
|
}
|
|
} else {
|
|
lnk_sect_defn_list_push_node(&new_list, curr);
|
|
hash_table_push_string_u64(arena->v[0], ht, curr->name, curr->flags);
|
|
}
|
|
}
|
|
|
|
|
|
for (LNK_SectDefn *defn = conf_list.first; defn != 0; defn = defn->next) {
|
|
KeyValuePair *is_present = hash_table_search_string(ht, defn->name);
|
|
if (!is_present) {
|
|
InvalidPath;
|
|
}
|
|
U64 sect_number = (defn->idx + 1);
|
|
COFF_SectionFlags expected_flags = is_present->value_u64;
|
|
String8 expected_flags_str = coff_string_from_section_flags(scratch.arena, expected_flags);
|
|
String8 current_flags_str = coff_string_from_section_flags(scratch.arena, defn->flags);
|
|
lnk_error_obj(LNK_Warning_SectionFlagsConflict, defn->obj, "detected section flags conflict in %S(No. %X); expected {%S} but got {%S}", defn->name, sect_number, expected_flags_str, current_flags_str);
|
|
}
|
|
|
|
|
|
// push new sections for :find_chunk_section
|
|
for (LNK_SectDefn *curr = new_list.first; curr != 0; curr = curr->next) {
|
|
lnk_section_table_push(st, curr->name, curr->flags & ~COFF_SectionFlags_LNK_FLAGS);
|
|
}
|
|
|
|
tp_temp_end(temp);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Count Chunks Per Section");
|
|
U64 **chunk_id_arr_arr;
|
|
{
|
|
U64 **chunk_count_arr_arr = push_array_no_zero(scratch.arena, U64 *, st->id_max);
|
|
for (U64 sect_idx = 0; sect_idx < st->id_max; sect_idx += 1) {
|
|
chunk_count_arr_arr[sect_idx] = push_array(scratch.arena, U64, obj_arr.count);
|
|
}
|
|
|
|
LNK_ChunkCounter task;
|
|
task.st = st;
|
|
task.obj_arr = obj_arr.v;
|
|
task.chunk_count_arr_arr = chunk_count_arr_arr;
|
|
tp_for_parallel(tp, 0, obj_arr.count, lnk_chunk_counter, &task);
|
|
|
|
chunk_id_arr_arr = chunk_count_arr_arr;
|
|
for (U64 sect_idx = 1; sect_idx < st->id_max; sect_idx += 1) {
|
|
LNK_Section *sect = lnk_section_table_search_id(st, sect_idx);
|
|
if (!sect) continue;
|
|
for (U64 obj_idx = 0; obj_idx < obj_arr.count; obj_idx += 1) {
|
|
U64 chunk_id_base = sect->cman->total_chunk_count;
|
|
sect->cman->total_chunk_count += chunk_count_arr_arr[sect_idx][obj_idx];
|
|
chunk_id_arr_arr[sect_idx][obj_idx] = chunk_id_base;
|
|
}
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Assign Chunk Refs");
|
|
{
|
|
LNK_ChunkRefAssigner task;
|
|
task.st = st;
|
|
task.range_arr = tp_divide_work(scratch.arena, obj_arr.count, tp->worker_count);
|
|
task.chunk_id_arr_arr = chunk_id_arr_arr;
|
|
task.obj_arr = obj_arr.v;
|
|
task.nosort_chunk_list_arr_arr = lnk_make_chunk_list_arr_arr(scratch.arena, st->id_max, tp->worker_count);
|
|
task.chunk_list_arr_arr = lnk_make_chunk_list_arr_arr(scratch.arena, st->id_max, tp->worker_count);
|
|
tp_for_parallel(tp, arena, tp->worker_count, lnk_chunk_ref_assigner, &task);
|
|
|
|
// merge chunks
|
|
for (LNK_SectionNode *sect_node = st->list.first; sect_node != 0; sect_node = sect_node->next) {
|
|
LNK_Section *sect = §_node->data;
|
|
lnk_chunk_list_concat_in_place_arr(sect->nosort_chunk->u.list, task.nosort_chunk_list_arr_arr[sect->id], tp->worker_count);
|
|
lnk_chunk_list_concat_in_place_arr(sect->root->u.list, task.chunk_list_arr_arr[sect->id], tp->worker_count);
|
|
}
|
|
}
|
|
ProfEnd();
|
|
}
|
|
|
|
ProfEnd();
|
|
scratch_end(scratch);
|
|
return obj_arr;
|
|
}
|
|
|
|
internal LNK_SymbolArray
|
|
lnk_symbol_array_from_coff(Arena *arena,
|
|
String8 coff_data,
|
|
LNK_Obj *obj,
|
|
String8 obj_path,
|
|
U64 string_table_off,
|
|
U64 sect_count,
|
|
COFF_SectionHeader *coff_sect_arr,
|
|
COFF_Symbol32Array coff_symbols,
|
|
LNK_Chunk *chunk_arr,
|
|
LNK_Chunk *master_common_block)
|
|
{
|
|
LNK_SymbolList weak_symbol_list = {0};
|
|
|
|
LNK_SymbolArray symbol_array = {0};
|
|
symbol_array.count = coff_symbols.count;
|
|
symbol_array.v = push_array(arena, LNK_Symbol, symbol_array.count);
|
|
|
|
for (U64 symbol_idx = 0; symbol_idx < coff_symbols.count; symbol_idx += 1) {
|
|
COFF_Symbol32 *coff_symbol = &coff_symbols.v[symbol_idx];
|
|
LNK_Symbol *symbol = &symbol_array.v[symbol_idx];
|
|
|
|
symbol->obj = obj;
|
|
|
|
String8 name = coff_read_symbol_name(coff_data, string_table_off, &coff_symbol->name);
|
|
|
|
// TODO: we convert 16-bit symbols and copy them to arena; symbols with short names
|
|
// are stored in the symbol itself and becuase converted symbols are pushed to scratch
|
|
// that memory is discarded after obj is processed
|
|
name = push_str8_copy(arena, name);
|
|
|
|
COFF_SymbolValueInterpType interp = coff_interp_symbol(coff_symbol);
|
|
switch (interp) {
|
|
case COFF_SymbolValueInterp_REGULAR: {
|
|
if (coff_symbol->section_number == 0 || coff_symbol->section_number > sect_count) {
|
|
lnk_error(LNK_Error_IllData, "%S: out ouf bounds section index in symbol \"%S (%u)\"", obj_path, name, coff_symbol->section_number);
|
|
break;
|
|
}
|
|
|
|
LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Static;
|
|
if (coff_symbol->storage_class == COFF_SymStorageClass_EXTERNAL) {
|
|
visibility = LNK_DefinedSymbolVisibility_Extern;
|
|
}
|
|
|
|
LNK_DefinedSymbolFlags flags = 0;
|
|
if (coff_symbol->type.u.lsb == COFF_SymType_NULL && coff_symbol->type.u.msb == COFF_SymDType_FUNC) {
|
|
flags |= LNK_DefinedSymbolFlag_IsFunc;
|
|
}
|
|
|
|
COFF_ComdatSelectType selection = COFF_ComdatSelectType_ANY;
|
|
U64 check_sum = 0;
|
|
{
|
|
B32 is_comdat = !!(coff_sect_arr[coff_symbol->section_number - 1].flags & COFF_SectionFlag_LNK_COMDAT);
|
|
B32 has_static_def = is_comdat &&
|
|
coff_symbol->value == 0 &&
|
|
coff_symbol->type.u.lsb == COFF_SymType_NULL &&
|
|
coff_symbol->storage_class == COFF_SymStorageClass_STATIC &&
|
|
coff_symbol->aux_symbol_count == 1;
|
|
if (has_static_def) {
|
|
COFF_SymbolSecDef *secdef = (COFF_SymbolSecDef *)(coff_symbol + 1);
|
|
selection = secdef->selection;
|
|
check_sum = secdef->check_sum;
|
|
|
|
if (secdef->selection == COFF_ComdatSelectType_ASSOCIATIVE) {
|
|
LNK_Chunk *head_chunk = &chunk_arr[secdef->number - 1];
|
|
LNK_Chunk *associate_chunk = &chunk_arr[coff_symbol->section_number - 1];
|
|
lnk_chunk_associate(arena, head_chunk, associate_chunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
LNK_Chunk *chunk = &chunk_arr[coff_symbol->section_number - 1];
|
|
U64 offset = coff_symbol->value;
|
|
lnk_init_defined_symbol_chunk(symbol, name, visibility, flags, chunk, offset, selection, check_sum);
|
|
} break;
|
|
case COFF_SymbolValueInterp_UNDEFINED: {
|
|
lnk_init_undefined_symbol(symbol, name, LNK_SymbolScopeFlag_Main);
|
|
} break;
|
|
case COFF_SymbolValueInterp_COMMON: {
|
|
// :common_block
|
|
LNK_Chunk *chunk = push_array_no_zero(arena, LNK_Chunk, 1);
|
|
chunk->ref = lnk_chunk_ref(0,0); // :chunk_ref_assign
|
|
chunk->align = 1;
|
|
chunk->is_discarded = 0;
|
|
chunk->sort_chunk = 1;
|
|
chunk->type = LNK_Chunk_Leaf;
|
|
chunk->sort_idx = str8(0,0);
|
|
chunk->input_idx = LNK_MakeChunkInputIdx(0, lnk_chunk_list_get_node_count(master_common_block));
|
|
chunk->flags = LNK_BSS_SECTION_FLAGS;
|
|
chunk->associate = 0;
|
|
chunk->u.leaf = str8(0, coff_symbol->value);
|
|
lnk_chunk_set_debugf(arena, chunk, "common block %S", name);
|
|
lnk_chunk_list_push(arena, master_common_block->u.list, chunk);
|
|
|
|
LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Extern;
|
|
|
|
LNK_DefinedSymbolFlags flags = 0;
|
|
if (coff_symbol->type.u.lsb == COFF_SymType_NULL && coff_symbol->type.u.msb == COFF_SymDType_FUNC) {
|
|
flags |= LNK_DefinedSymbolFlag_IsFunc;
|
|
}
|
|
|
|
lnk_init_defined_symbol_chunk(symbol, name, visibility, flags, chunk, 0, COFF_ComdatSelectType_LARGEST, 0);
|
|
} break;
|
|
case COFF_SymbolValueInterp_WEAK: {
|
|
if (coff_symbol->aux_symbol_count == 0 || symbol_idx + 1 >= coff_symbols.count) {
|
|
lnk_error(LNK_Error_IllData, "%S: Weak symbol \"%S (%u)\" must at least one aux symbol", obj_path, name, symbol_idx);
|
|
break;
|
|
}
|
|
|
|
COFF_SymbolWeakExt *weak_ext = (COFF_SymbolWeakExt*)(coff_symbol + 1);
|
|
if (weak_ext->tag_index >= symbol_array.count) {
|
|
lnk_error(LNK_Error_IllData, "%S: Weak symbol \"%S (%u)\" points to out of bounds symbol", obj_path, name, symbol_idx);
|
|
break;
|
|
}
|
|
#if 0
|
|
if (symbol_array.v[weak_ext->tag_index] == NULL) {
|
|
lnk_error(LNK_ERROR_ILL_DATA, "%S: Weak symbol \"%S (%u)\" tags auxiliary symbol %u",
|
|
obj_path, name, symbol_idx, weak_ext->tag_index);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
lnk_init_weak_symbol(symbol, name, weak_ext->characteristics, &symbol_array.v[weak_ext->tag_index]);
|
|
|
|
lnk_symbol_list_push(arena, &weak_symbol_list, symbol);
|
|
} break;
|
|
case COFF_SymbolValueInterp_ABS: {
|
|
// Never code or data, synthetic symbol. COFF spec says bits in value are used
|
|
// as flags in symbol @feat.00, other symbols like @comp.id and @vol.md are undocumented.
|
|
// LLVM uses undocumented mask 0x4800 on @feat.00 to tell if object was compiled with /guard:cf.
|
|
|
|
LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Static;
|
|
if (coff_symbol->storage_class == COFF_SymStorageClass_EXTERNAL) {
|
|
visibility = LNK_DefinedSymbolVisibility_Extern;
|
|
}
|
|
|
|
lnk_init_defined_symbol_va(symbol, name, visibility, 0, coff_symbol->value);
|
|
} break;
|
|
case COFF_SymbolValueInterp_DEBUG: {
|
|
// ignore
|
|
} break;
|
|
}
|
|
|
|
// skip aux symbols
|
|
symbol_idx += coff_symbol->aux_symbol_count;
|
|
}
|
|
|
|
return symbol_array;
|
|
}
|
|
|
|
internal LNK_RelocList *
|
|
lnk_reloc_list_array_from_coff(Arena *arena, COFF_MachineType machine, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, LNK_Chunk *chunk_arr, LNK_SymbolArray symbol_array)
|
|
{
|
|
LNK_RelocList *reloc_list_arr = push_array_no_zero(arena, LNK_RelocList, sect_count);
|
|
for (U64 sect_idx = 0; sect_idx < sect_count; sect_idx += 1) {
|
|
COFF_SectionHeader *coff_header = &coff_sect_arr[sect_idx];
|
|
COFF_RelocInfo coff_reloc_info = coff_reloc_info_from_section_header(coff_data, coff_header);
|
|
COFF_Reloc *coff_reloc_v = (COFF_Reloc *)(coff_data.str + coff_reloc_info.array_off);
|
|
LNK_Chunk *sect_chunk = &chunk_arr[sect_idx];
|
|
reloc_list_arr[sect_idx] = lnk_reloc_list_from_coff_reloc_array(arena, machine, sect_chunk, symbol_array, coff_reloc_v, coff_reloc_info.count);
|
|
}
|
|
return reloc_list_arr;
|
|
}
|
|
|
|
internal LNK_DirectiveInfo
|
|
lnk_init_directives(Arena *arena, String8 obj_path, U64 chunk_count, String8 *sect_name_arr, LNK_Chunk *chunk_arr)
|
|
{
|
|
LNK_DirectiveInfo directive_info = {0};
|
|
for (U64 chunk_idx = 0; chunk_idx < chunk_count; chunk_idx += 1) {
|
|
String8 sect_name = sect_name_arr[chunk_idx];
|
|
LNK_Chunk *sect_chunk = &chunk_arr[chunk_idx];
|
|
Assert(sect_chunk->type == LNK_Chunk_Leaf);
|
|
|
|
if (!str8_match(sect_name, str8_lit(".drectve"), 0)) {
|
|
continue;
|
|
}
|
|
if (sect_chunk->u.leaf.size < 3) {
|
|
lnk_error(LNK_Warning_IllData, "%S: can't parse %S", obj_path, sect_name);
|
|
continue;
|
|
}
|
|
if (~sect_chunk->flags & COFF_SectionFlag_LNK_INFO) {
|
|
lnk_error(LNK_Warning_IllData, "%S: %S missing COFF_SectionFlag_LNK_INFO.", obj_path, sect_name);
|
|
}
|
|
|
|
// TODO: warn if section has relocations
|
|
|
|
lnk_parse_directives(arena, &directive_info, sect_chunk->u.leaf, obj_path);
|
|
int bad_vs = 0; (void)bad_vs;
|
|
}
|
|
return directive_info;
|
|
}
|
|
|
|
internal COFF_FeatFlags
|
|
lnk_obj_get_features(LNK_Obj *obj)
|
|
{
|
|
COFF_FeatFlags result = 0;
|
|
LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@feat.00"), 0);
|
|
if (sym) {
|
|
Assert(LNK_Symbol_IsDefined(sym->type));
|
|
Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA);
|
|
result = sym->u.defined.u.va;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal U32
|
|
lnk_obj_get_comp_id(LNK_Obj *obj)
|
|
{
|
|
U32 result = 0;
|
|
LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@comp.id"), 0);
|
|
if (sym) {
|
|
Assert(LNK_Symbol_IsDefined(sym->type));
|
|
Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA);
|
|
result = sym->u.defined.u.va;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal U32
|
|
lnk_obj_get_vol_md(LNK_Obj *obj)
|
|
{
|
|
U32 result = 0;
|
|
LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@vol.md"), 0);
|
|
if (sym) {
|
|
Assert(LNK_Symbol_IsDefined(sym->type));
|
|
Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA);
|
|
result = sym->u.defined.u.va;
|
|
}
|
|
return result;
|
|
}
|
|
|