mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-22 19:54:59 -07:00
1199 lines
47 KiB
C
1199 lines
47 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, ...)
|
|
{
|
|
va_list args; va_start(args, fmt);
|
|
lnk_error_with_loc_fv(code, obj->path, obj->lib_path, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
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;
|
|
for (LNK_Directive *dir = obj->directive_info.v[LNK_CmdSwitch_DefaultLib].first; dir != 0; dir = dir->next) {
|
|
str8_list_concat_in_place(result, &dir->value_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_CmdSwitch_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)
|
|
{
|
|
LNK_ObjIniter *task = raw_task;
|
|
LNK_InputObj *input = task->inputs[task_id];
|
|
LNK_Obj *obj = &task->obj_node_arr[task_id].data;
|
|
U64 obj_idx = task->obj_id_base + task_id;
|
|
|
|
//
|
|
// parse obj header
|
|
//
|
|
COFF_FileHeaderInfo coff_info = coff_file_header_info_from_data(input->data);
|
|
String8 raw_coff_section_table = str8_substr(input->data, coff_info.section_table_range);
|
|
String8 raw_coff_symbol_table = str8_substr(input->data, coff_info.symbol_table_range);
|
|
String8 raw_coff_string_table = str8_substr(input->data, coff_info.string_table_range);
|
|
|
|
//
|
|
// error check: section table / symbol table / string table
|
|
//
|
|
if (raw_coff_section_table.size != dim_1u64(coff_info.section_table_range)) {
|
|
lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read section header table");
|
|
}
|
|
if (raw_coff_symbol_table.size != dim_1u64(coff_info.symbol_table_range)) {
|
|
lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read symbol table");
|
|
}
|
|
if (raw_coff_string_table.size != dim_1u64(coff_info.string_table_range)) {
|
|
lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "corrupted file, unable to read string table");
|
|
}
|
|
|
|
U64 chunk_count = coff_info.section_count_no_null + /* :common_block */ 1;
|
|
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(arena, LNK_Chunk, chunk_count);
|
|
LNK_ChunkPtr *chunk_ptr_arr = push_array_no_zero(arena, LNK_ChunkPtr, chunk_count);
|
|
|
|
for (U64 chunk_idx = 0; chunk_idx < chunk_count; chunk_idx += 1) {
|
|
chunk_ptr_arr[chunk_idx] = &chunk_arr[chunk_idx];
|
|
}
|
|
|
|
//
|
|
// setup :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("~");
|
|
|
|
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);
|
|
master_common_block->obj = obj;
|
|
lnk_chunk_set_debugf(arena, master_common_block, "obj[%llx] master common block", obj_idx);
|
|
|
|
//
|
|
// parse section table
|
|
//
|
|
COFF_SectionHeader *coff_section_table = (COFF_SectionHeader *)raw_coff_section_table.str;
|
|
for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) {
|
|
COFF_SectionHeader *coff_sect_header = &coff_section_table[sect_idx];
|
|
|
|
// read name
|
|
String8 sect_name = coff_name_from_section_header(raw_coff_string_table, coff_sect_header);
|
|
|
|
// parse name
|
|
coff_parse_section_name(sect_name, §_name_arr[sect_idx], §_sort_arr[sect_idx]);
|
|
|
|
// find contents
|
|
String8 sect_data;
|
|
if (coff_sect_header->flags & COFF_SectionFlag_CntUninitializedData) {
|
|
sect_data = str8(0, coff_sect_header->fsize);
|
|
} else {
|
|
if (coff_sect_header->fsize > 0) {
|
|
Rng1U64 sect_range = rng_1u64(coff_sect_header->foff, coff_sect_header->foff + coff_sect_header->fsize);
|
|
sect_data = str8_substr(input->data, sect_range);
|
|
|
|
if (contains_1u64(coff_info.header_range, coff_sect_header->foff) ||
|
|
(coff_sect_header->fsize > 0 && contains_1u64(coff_info.header_range, sect_range.max-1))) {
|
|
lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into file header)", sect_name, sect_idx+1);
|
|
}
|
|
if (contains_1u64(coff_info.section_table_range, coff_sect_header->foff) ||
|
|
(coff_sect_header->fsize > 0 && contains_1u64(coff_info.section_table_range, sect_range.max-1))) {
|
|
lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into section header table)", sect_name, sect_idx+1);
|
|
}
|
|
if (contains_1u64(coff_info.symbol_table_range, coff_sect_header->foff) ||
|
|
(coff_sect_header->fsize > 0 && contains_1u64(coff_info.symbol_table_range, sect_range.max-1))) {
|
|
lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into symbol table)", sect_name, sect_idx+1);
|
|
}
|
|
if (dim_1u64(sect_range) != coff_sect_header->fsize) {
|
|
lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "header (%S No. %#llx) defines out of bounds section data", sect_name, sect_idx+1);
|
|
}
|
|
} else {
|
|
sect_data = str8_zero();
|
|
}
|
|
}
|
|
|
|
// fill out chunk
|
|
LNK_Chunk *chunk = &chunk_arr[sect_idx];
|
|
chunk->align = coff_align_size_from_section_flags(coff_sect_header->flags);
|
|
chunk->is_discarded = !!(coff_sect_header->flags & COFF_SectionFlag_LnkRemove);
|
|
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_header->flags;
|
|
chunk->u.leaf = sect_data;
|
|
chunk->obj = obj;
|
|
lnk_chunk_set_debugf(arena, chunk, "obj[%llx] sect[%llx]", obj_idx, sect_idx);
|
|
}
|
|
|
|
//
|
|
// :function_pad_min
|
|
//
|
|
U64 function_pad_min;
|
|
if (task->function_pad_min) {
|
|
function_pad_min = *task->function_pad_min;
|
|
} else {
|
|
function_pad_min = lnk_get_default_function_pad_min(coff_info.machine);
|
|
}
|
|
|
|
//
|
|
// convert from COFF
|
|
//
|
|
void *coff_symbol_table = raw_coff_symbol_table.str;
|
|
LNK_SymbolArray symbol_arr = lnk_symbol_array_from_coff(arena, obj, input->path, input->lib_path, coff_info.is_big_obj, function_pad_min, coff_info.section_count_no_null, coff_section_table, coff_info.symbol_count, coff_symbol_table, raw_coff_string_table, chunk_ptr_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_section_table, chunk_ptr_arr, symbol_arr);
|
|
|
|
//
|
|
// parse directives
|
|
//
|
|
LNK_DirectiveInfo directive_info = lnk_directive_info_from_sections(arena, input->path, input->lib_path, coff_info.section_count_no_null, reloc_list_arr, sect_name_arr, chunk_arr);
|
|
|
|
// parse exports
|
|
LNK_ExportParseList export_parse = {0};
|
|
for (LNK_Directive *dir = directive_info.v[LNK_CmdSwitch_Export].first; dir != 0; dir = dir->next) {
|
|
lnk_parse_export_directive(arena, &export_parse, dir->value_list, input->path, input->lib_path);
|
|
}
|
|
|
|
// 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, &symbol_list, symbol);
|
|
}
|
|
|
|
// push /include symbols
|
|
String8List include_symbol_list = {0};
|
|
for (LNK_Directive *dir = directive_info.v[LNK_CmdSwitch_Include].first; dir != 0; dir = dir->next) {
|
|
str8_list_concat_in_place(&include_symbol_list, &dir->value_list);
|
|
}
|
|
|
|
// parse /alternatename
|
|
LNK_AltNameList alt_name_list = {0};
|
|
for (LNK_Directive *dir = directive_info.v[LNK_CmdSwitch_AlternateName].first; dir != 0; dir = dir->next) {
|
|
String8 *invalid_string = lnk_parse_alt_name_directive_list(arena, dir->value_list, &alt_name_list);
|
|
if (invalid_string != 0) {
|
|
lnk_error_with_loc(LNK_Error_Cmdl, input->path, input->lib_path, "invalid syntax \"%S\", expected format \"FROM=TO\"", *invalid_string);
|
|
}
|
|
}
|
|
|
|
|
|
// fill out obj
|
|
obj->data = input->data;
|
|
obj->path = push_str8_copy(arena, input->path);
|
|
obj->lib_path = push_str8_copy(arena, input->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_ptr_arr;
|
|
obj->symbol_list = symbol_list;
|
|
obj->sect_reloc_list_arr = reloc_list_arr;
|
|
obj->directive_info = directive_info;
|
|
obj->export_parse = export_parse;
|
|
obj->include_symbol_list = include_symbol_list;
|
|
obj->alt_name_list = alt_name_list;
|
|
}
|
|
|
|
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_LnkFlags;
|
|
|
|
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->sectab, name);
|
|
|
|
U64 count = 0;
|
|
lnk_visit_chunks(0, chunk, lnk_chunk_get_count_cb, &count);
|
|
|
|
task->chunk_counts[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;
|
|
*ctx->chunk_id += 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->sectab, name);
|
|
|
|
// :chunk_ref_assign
|
|
LNK_ChunkRefAssign ctx = {0};
|
|
ctx.cman = sect->cman;
|
|
ctx.chunk_id = &task->chunk_ids[sect->id][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 *sectab,
|
|
U64 *function_pad_min,
|
|
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;
|
|
task.function_pad_min = function_pad_min;
|
|
tp_for_parallel(tp, arena, input_count, lnk_obj_initer, &task);
|
|
}
|
|
ProfEnd();
|
|
|
|
if (sectab) {
|
|
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 = sectab->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(sectab, curr->name, curr->flags & ~COFF_SectionFlags_LnkFlags);
|
|
}
|
|
|
|
tp_temp_end(temp);
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Count Chunks Per Section");
|
|
U64 **chunk_ids;
|
|
{
|
|
U64 **chunk_counts = push_array_no_zero(scratch.arena, U64 *, sectab->id_max);
|
|
for (U64 sect_idx = 0; sect_idx < sectab->id_max; sect_idx += 1) {
|
|
chunk_counts[sect_idx] = push_array(scratch.arena, U64, obj_arr.count);
|
|
}
|
|
|
|
LNK_ChunkCounter task = {0};
|
|
task.sectab = sectab;
|
|
task.obj_arr = obj_arr.v;
|
|
task.chunk_counts = chunk_counts;
|
|
tp_for_parallel(tp, 0, obj_arr.count, lnk_chunk_counter, &task);
|
|
|
|
chunk_ids = chunk_counts;
|
|
for (U64 sect_idx = 1; sect_idx < sectab->id_max; sect_idx += 1) {
|
|
LNK_Section *sect = lnk_section_table_search_id(sectab, 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_counts[sect_idx][obj_idx];
|
|
chunk_ids[sect_idx][obj_idx] = chunk_id_base;
|
|
}
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Assign Chunk Refs");
|
|
{
|
|
LNK_ChunkRefAssigner task;
|
|
task.sectab = sectab;
|
|
task.range_arr = tp_divide_work(scratch.arena, obj_arr.count, tp->worker_count);
|
|
task.chunk_ids = chunk_ids;
|
|
task.obj_arr = obj_arr.v;
|
|
task.nosort_chunk_list_arr_arr = lnk_make_chunk_list_arr_arr(scratch.arena, sectab->id_max, tp->worker_count);
|
|
task.chunk_list_arr_arr = lnk_make_chunk_list_arr_arr(scratch.arena, sectab->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 = sectab->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,
|
|
LNK_Obj *obj,
|
|
String8 obj_path,
|
|
String8 lib_path,
|
|
B32 is_big_obj,
|
|
U64 function_pad_min,
|
|
U64 sect_count,
|
|
COFF_SectionHeader *section_table,
|
|
U64 symbol_count,
|
|
void *symbol_table,
|
|
String8 string_table,
|
|
LNK_ChunkPtr *chunk_table,
|
|
LNK_Chunk *master_common_block)
|
|
{
|
|
if (function_pad_min) {
|
|
COFF_ParsedSymbol symbol;
|
|
for (U64 symbol_idx = 0; symbol_idx < symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) {
|
|
// read symbol
|
|
if (is_big_obj) {
|
|
symbol = coff_parse_symbol32(string_table, &((COFF_Symbol32 *)symbol_table)[symbol_idx]);
|
|
} else {
|
|
symbol = coff_parse_symbol16(string_table, &((COFF_Symbol16 *)symbol_table)[symbol_idx]);
|
|
}
|
|
|
|
// is this a function symbol?
|
|
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
|
|
if (interp == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) {
|
|
if (symbol.section_number == 0 || symbol.section_number > sect_count) {
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "out ouf bounds section index in symbol \"%S (%u)\"", symbol.name, symbol.section_number);
|
|
}
|
|
if (symbol.value > section_table[symbol.section_number-1].fsize) {
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "out of bounds section offset in symbol \"%S (%u)\"", symbol.name, symbol.value);
|
|
}
|
|
|
|
LNK_Chunk *chunk = chunk_table[symbol.section_number-1];
|
|
if (symbol.value > 0) {
|
|
// convert leaf to list
|
|
//
|
|
// there is no way to know up front how many splits we have,
|
|
// so lazily convert chunks when see two or more functions
|
|
// in a section
|
|
if (chunk->type == LNK_Chunk_Leaf) {
|
|
// make a list chunk
|
|
LNK_Chunk *chunk_list = push_array(arena, LNK_Chunk, 1);
|
|
chunk_list->type = LNK_Chunk_List;
|
|
chunk_list->align = chunk->align;
|
|
chunk_list->is_discarded = chunk->is_discarded;
|
|
chunk_list->sort_idx = chunk->sort_idx;
|
|
chunk_list->input_idx = chunk->input_idx;
|
|
chunk_list->flags = chunk->flags;
|
|
chunk_list->u.list = push_array(arena, LNK_ChunkList, 1);
|
|
chunk_list->obj = obj;
|
|
lnk_chunk_set_debugf(arena, chunk_list, "function chunk list for %S", symbol.name);
|
|
|
|
// update properties on first chunk
|
|
chunk->min_size = function_pad_min;
|
|
chunk->sort_chunk = 0;
|
|
chunk->sort_idx = str8_zero();
|
|
chunk->input_idx = 0;
|
|
|
|
// push leaf to list
|
|
lnk_chunk_list_push(arena, chunk_list->u.list, chunk);
|
|
|
|
// set list as target chunk
|
|
chunk = chunk_list;
|
|
|
|
// set list chunk to be head of this section
|
|
chunk_table[symbol.section_number-1] = chunk_list;
|
|
}
|
|
|
|
// find chunk that is near symbol
|
|
U64 offset_cursor = 0;
|
|
LNK_ChunkNode *current = chunk->u.list->last;
|
|
for (LNK_ChunkNode *c = chunk->u.list->first; c != 0; c = c->next) {
|
|
Assert(c->data->type == LNK_Chunk_Leaf);
|
|
if (offset_cursor + c->data->u.leaf.size >= symbol.value) {
|
|
current = c;
|
|
break;
|
|
}
|
|
offset_cursor += c->data->u.leaf.size;
|
|
}
|
|
Assert(current->data->type == LNK_Chunk_Leaf);
|
|
|
|
if (offset_cursor < symbol.value) {
|
|
// bifurcate chunk at symbol offset
|
|
U64 split_pos = symbol.value - offset_cursor;
|
|
Rng1U64 left_data_range = rng_1u64(0, split_pos);
|
|
Rng1U64 right_data_range = rng_1u64(left_data_range.max, current->data->u.leaf.size);
|
|
String8 left_data = str8_substr(current->data->u.leaf, left_data_range);
|
|
String8 right_data = str8_substr(current->data->u.leaf, right_data_range);
|
|
|
|
// create new chunk
|
|
LNK_Chunk *split_chunk = push_array(arena, LNK_Chunk, 1);
|
|
split_chunk->type = LNK_Chunk_Leaf;
|
|
split_chunk->align = current->data->align;
|
|
split_chunk->min_size = function_pad_min;
|
|
split_chunk->is_discarded = current->data->is_discarded;
|
|
split_chunk->flags = current->data->flags;
|
|
split_chunk->u.leaf = right_data;
|
|
split_chunk->obj = obj;
|
|
lnk_chunk_set_debugf(arena, split_chunk, "chunk split on function %S sect %x split pos %#llx", symbol.name, symbol.section_number, split_pos);
|
|
|
|
LNK_ChunkNode *split_node = push_array(arena, LNK_ChunkNode, 1);
|
|
split_node->data = split_chunk;
|
|
|
|
// update split chunk data
|
|
current->data->u.leaf = left_data;
|
|
|
|
// insert split chunk after current chunk
|
|
if (split_node->next == 0) {
|
|
chunk->u.list->last = split_node;
|
|
}
|
|
split_node->next = current->next;
|
|
current->next = split_node;
|
|
chunk->u.list->count += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LNK_SymbolArray symbol_array = {0};
|
|
symbol_array.count = symbol_count;
|
|
symbol_array.v = push_array(arena, LNK_Symbol, symbol_array.count);
|
|
|
|
COFF_ParsedSymbol parsed_symbol;
|
|
for (U64 symbol_idx = 0; symbol_idx < symbol_count; symbol_idx += (1 + parsed_symbol.aux_symbol_count)) {
|
|
void *aux_symbols;
|
|
if (is_big_obj) {
|
|
COFF_Symbol32 *ptr = &((COFF_Symbol32 *)symbol_table)[symbol_idx];
|
|
parsed_symbol = coff_parse_symbol32(string_table, ptr);
|
|
aux_symbols = parsed_symbol.aux_symbol_count ? ptr+1 : 0;
|
|
} else {
|
|
COFF_Symbol16 *ptr = (COFF_Symbol16 *)symbol_table + symbol_idx;
|
|
parsed_symbol = coff_parse_symbol16(string_table, ptr);
|
|
aux_symbols = parsed_symbol.aux_symbol_count ? ptr+1 : 0;
|
|
}
|
|
|
|
if (symbol_idx + parsed_symbol.aux_symbol_count + 1 > symbol_count) {
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "symbol %S (No. %llx) has out of bounds aux symbol count %llu", parsed_symbol.name, symbol_idx, parsed_symbol.aux_symbol_count);
|
|
}
|
|
|
|
COFF_SymbolValueInterpType interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class);
|
|
switch (interp) {
|
|
case COFF_SymbolValueInterp_Regular: {
|
|
if (parsed_symbol.section_number == 0 || parsed_symbol.section_number > sect_count) {
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "symbol %S (No. %llx) has out ouf bounds section index %x", parsed_symbol.name, symbol_idx, parsed_symbol.section_number);
|
|
}
|
|
if (parsed_symbol.value > section_table[parsed_symbol.section_number-1].fsize) {
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "symbol %S (No. %llx) has out of bounds section offset %x into section %x", parsed_symbol.name, symbol_idx, parsed_symbol.value, parsed_symbol.section_number);
|
|
}
|
|
|
|
|
|
LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Static;
|
|
if (parsed_symbol.storage_class == COFF_SymStorageClass_External) {
|
|
visibility = LNK_DefinedSymbolVisibility_Extern;
|
|
}
|
|
LNK_DefinedSymbolFlags flags = 0;
|
|
if (COFF_SymbolType_IsFunc(parsed_symbol.type)) {
|
|
flags |= LNK_DefinedSymbolFlag_IsFunc;
|
|
}
|
|
|
|
|
|
LNK_Chunk *chunk = chunk_table[parsed_symbol.section_number-1];
|
|
U64 offset = parsed_symbol.value;
|
|
COFF_ComdatSelectType selection = COFF_ComdatSelect_Any;
|
|
U64 check_sum = 0;
|
|
|
|
|
|
B32 is_comdat = (section_table[parsed_symbol.section_number-1].flags & COFF_SectionFlag_LnkCOMDAT) &&
|
|
parsed_symbol.value == 0 &&
|
|
parsed_symbol.aux_symbol_count > 0 &&
|
|
parsed_symbol.type.u.lsb == COFF_SymType_Null &&
|
|
parsed_symbol.storage_class == COFF_SymStorageClass_Static;
|
|
if (is_comdat) {
|
|
COFF_SymbolSecDef *secdef = aux_symbols;
|
|
|
|
selection = secdef->selection;
|
|
check_sum = secdef->check_sum;
|
|
|
|
// create association link between chunks
|
|
if (secdef->selection == COFF_ComdatSelect_Associative) {
|
|
U32 secdef_number = secdef->number_lo;
|
|
|
|
// promote secdef number to 32 bits
|
|
if (is_big_obj) {
|
|
secdef_number |= (U32)secdef->number_hi << 16;
|
|
}
|
|
|
|
// associate chunks
|
|
if (secdef_number > 0 && secdef_number <= sect_count) {
|
|
LNK_Chunk *head_chunk = chunk_table[secdef_number-1];
|
|
LNK_Chunk *associate_chunk = chunk_table[parsed_symbol.section_number-1];
|
|
lnk_chunk_associate(head_chunk, associate_chunk);
|
|
} else {
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "symbol %S (No. %llx) has out of bounds section definition number %u", parsed_symbol.name, symbol_idx, secdef_number);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (chunk->type == LNK_Chunk_List) {
|
|
LNK_Chunk *closest_chunk = chunk->u.list->last->data;
|
|
U64 offset_cursor = 0;
|
|
for (LNK_ChunkNode *c = chunk->u.list->first; c != 0; c = c->next) {
|
|
if (offset_cursor + c->data->u.leaf.size > offset) {
|
|
closest_chunk = c->data;
|
|
break;
|
|
}
|
|
offset_cursor += c->data->u.leaf.size;
|
|
}
|
|
Assert(offset >= offset_cursor);
|
|
offset -= offset_cursor;
|
|
chunk = closest_chunk;
|
|
}
|
|
Assert(chunk->type == LNK_Chunk_Leaf);
|
|
|
|
lnk_init_defined_symbol_chunk(&symbol_array.v[symbol_idx], parsed_symbol.name, visibility, flags, chunk, offset, selection, check_sum);
|
|
symbol_array.v[symbol_idx].obj = obj;
|
|
} break;
|
|
case COFF_SymbolValueInterp_Weak: {
|
|
if (parsed_symbol.aux_symbol_count == 0) {
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "weak symbol \"%S (%u)\" must at least one aux symbol", parsed_symbol.name, symbol_idx);
|
|
}
|
|
|
|
COFF_SymbolWeakExt *weak_ext = aux_symbols;
|
|
if (weak_ext->tag_index >= symbol_count) {
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "weak symbol \"%S (%u)\" points to out of bounds symbol", parsed_symbol.name, symbol_idx);
|
|
}
|
|
|
|
LNK_Symbol *symbol = &symbol_array.v[symbol_idx];
|
|
LNK_Symbol *fallback_symbol = &symbol_array.v[weak_ext->tag_index];
|
|
lnk_init_weak_symbol(symbol, parsed_symbol.name, weak_ext->characteristics, fallback_symbol);
|
|
|
|
symbol->obj = obj;
|
|
fallback_symbol->obj = obj;
|
|
} break;
|
|
case COFF_SymbolValueInterp_Undefined: {
|
|
LNK_Symbol *symbol = &symbol_array.v[symbol_idx];
|
|
lnk_init_undefined_symbol(symbol, parsed_symbol.name, LNK_SymbolScopeFlag_Main);
|
|
symbol->obj = obj;
|
|
} break;
|
|
case COFF_SymbolValueInterp_Common: {
|
|
// :common_block
|
|
//
|
|
// TODO: sort chunks on size to reduce bss usage
|
|
LNK_Chunk *chunk = push_array(arena, LNK_Chunk, 1);
|
|
chunk->align = Min(32, u64_up_to_pow2(parsed_symbol.value)); // link.exe caps align at 32 bytes
|
|
chunk->type = LNK_Chunk_Leaf;
|
|
chunk->flags = master_common_block->flags;
|
|
chunk->u.leaf = str8(0, parsed_symbol.value);
|
|
chunk->obj = obj;
|
|
lnk_chunk_set_debugf(arena, chunk, "common block %S", parsed_symbol.name);
|
|
lnk_chunk_list_push(arena, master_common_block->u.list, chunk);
|
|
|
|
LNK_DefinedSymbolFlags flags = 0;
|
|
if (COFF_SymbolType_IsFunc(parsed_symbol.type)) {
|
|
flags |= LNK_DefinedSymbolFlag_IsFunc;
|
|
}
|
|
|
|
LNK_Symbol *symbol = &symbol_array.v[symbol_idx];
|
|
lnk_init_defined_symbol_chunk(symbol, parsed_symbol.name, LNK_DefinedSymbolVisibility_Extern, flags, chunk, 0, COFF_ComdatSelect_Largest, 0);
|
|
symbol->obj = obj;
|
|
} 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 (parsed_symbol.storage_class == COFF_SymStorageClass_External) {
|
|
visibility = LNK_DefinedSymbolVisibility_Extern;
|
|
}
|
|
|
|
LNK_Symbol *symbol = &symbol_array.v[symbol_idx];
|
|
lnk_init_defined_symbol_va(symbol, parsed_symbol.name, visibility, 0, parsed_symbol.value);
|
|
symbol->obj = obj;
|
|
} break;
|
|
case COFF_SymbolValueInterp_Debug: {
|
|
} break;
|
|
}
|
|
}
|
|
|
|
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_ChunkPtr *chunk_ptr_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) {
|
|
COFF_SectionHeader *coff_sect_header = &coff_sect_arr[sect_idx];
|
|
COFF_RelocInfo coff_reloc_info = coff_reloc_info_from_section_header(coff_data, coff_sect_header);
|
|
COFF_Reloc *coff_reloc_v = (COFF_Reloc *)(coff_data.str + coff_reloc_info.array_off);
|
|
LNK_Chunk *sect_chunk = chunk_ptr_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 void
|
|
lnk_parse_msvc_linker_directive(Arena *arena, String8 obj_path, String8 lib_path, LNK_DirectiveInfo *directive_info, String8 buffer)
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
local_persist B32 init_table = 1;
|
|
local_persist B8 is_legal[LNK_CmdSwitch_Count];
|
|
if (init_table) {
|
|
init_table = 0;
|
|
is_legal[LNK_CmdSwitch_AlternateName] = 1;
|
|
is_legal[LNK_CmdSwitch_DefaultLib] = 1;
|
|
is_legal[LNK_CmdSwitch_DisallowLib] = 1;
|
|
is_legal[LNK_CmdSwitch_EditAndContinue] = 1;
|
|
is_legal[LNK_CmdSwitch_Entry] = 1;
|
|
is_legal[LNK_CmdSwitch_Export] = 1;
|
|
is_legal[LNK_CmdSwitch_FailIfMismatch] = 1;
|
|
is_legal[LNK_CmdSwitch_GuardSym] = 1;
|
|
is_legal[LNK_CmdSwitch_Include] = 1;
|
|
is_legal[LNK_CmdSwitch_InferAsanLibs] = 1;
|
|
is_legal[LNK_CmdSwitch_InferAsanLibsNo] = 1;
|
|
is_legal[LNK_CmdSwitch_ManifestDependency] = 1;
|
|
is_legal[LNK_CmdSwitch_Merge] = 1;
|
|
is_legal[LNK_CmdSwitch_NoDefaultLib] = 1;
|
|
is_legal[LNK_CmdSwitch_Release] = 1;
|
|
is_legal[LNK_CmdSwitch_Section] = 1;
|
|
is_legal[LNK_CmdSwitch_Stack] = 1;
|
|
is_legal[LNK_CmdSwitch_SubSystem] = 1;
|
|
is_legal[LNK_CmdSwitch_ThrowingNew] = 1;
|
|
}
|
|
|
|
String8 to_parse;
|
|
{
|
|
local_persist const U8 bom_sig[] = { 0xEF, 0xBB, 0xBF };
|
|
local_persist const U8 ascii_sig[] = { 0x20, 0x20, 0x20 };
|
|
if (MemoryMatch(buffer.str, &bom_sig[0], sizeof(bom_sig))) {
|
|
to_parse = str8_zero();
|
|
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "TODO: support for BOM encoding");
|
|
} else if (MemoryMatch(buffer.str, &ascii_sig[0], sizeof(ascii_sig))) {
|
|
to_parse = str8_skip(buffer, sizeof(ascii_sig));
|
|
} else {
|
|
to_parse = buffer;
|
|
}
|
|
}
|
|
|
|
String8List arg_list = lnk_arg_list_parse_windows_rules(scratch.arena, to_parse);
|
|
LNK_CmdLine cmd_line = lnk_cmd_line_parse_windows_rules(scratch.arena, arg_list);
|
|
|
|
for (LNK_CmdOption *opt = cmd_line.first_option; opt != 0; opt = opt->next) {
|
|
LNK_CmdSwitchType type = lnk_cmd_switch_type_from_string(opt->string);
|
|
|
|
if (type == LNK_CmdSwitch_Null) {
|
|
lnk_error_with_loc(LNK_Warning_UnknownDirective, obj_path, lib_path, "unknown directive \"%S\"", opt->string);
|
|
continue;
|
|
}
|
|
if (!is_legal[type]) {
|
|
lnk_error_with_loc(LNK_Warning_IllegalDirective, obj_path, lib_path, "illegal directive \"%S\"", opt->string);
|
|
continue;
|
|
}
|
|
|
|
LNK_Directive *directive = push_array_no_zero(arena, LNK_Directive, 1);
|
|
directive->next = 0;
|
|
directive->id = push_str8_copy(arena, opt->string);
|
|
directive->value_list = str8_list_copy(arena, &opt->value_strings);
|
|
|
|
LNK_DirectiveList *directive_list = &directive_info->v[type];
|
|
SLLQueuePush(directive_list->first, directive_list->last, directive);
|
|
++directive_list->count;
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
internal LNK_DirectiveInfo
|
|
lnk_directive_info_from_sections(Arena *arena,
|
|
String8 obj_path,
|
|
String8 lib_path,
|
|
U64 chunk_count,
|
|
LNK_RelocList *reloc_list_arr,
|
|
String8 *sect_name_arr,
|
|
LNK_Chunk *chunk_arr)
|
|
{
|
|
LNK_DirectiveInfo directive_info = {0};
|
|
for (U64 chunk_idx = 0; chunk_idx < chunk_count; ++chunk_idx) {
|
|
String8 sect_name = sect_name_arr[chunk_idx];
|
|
LNK_Chunk *sect_chunk = chunk_arr + chunk_idx;
|
|
if (str8_match_lit(".drectve", sect_name, 0)) {
|
|
if (sect_chunk->type == LNK_Chunk_Leaf) {
|
|
if (sect_chunk->u.leaf.size >= 3) {
|
|
if (~sect_chunk->flags & COFF_SectionFlag_LnkInfo) {
|
|
lnk_error_with_loc(LNK_Warning_IllData, obj_path, lib_path, "%S missing COFF_SectionFlag_LnkInfo", sect_name);
|
|
}
|
|
if (reloc_list_arr[chunk_idx].count > 0) {
|
|
lnk_error_with_loc(LNK_Warning_DirectiveSectionWithRelocs, obj_path, lib_path, "directive section %S(%#x) has relocations", sect_name, (chunk_idx+1));
|
|
}
|
|
lnk_parse_msvc_linker_directive(arena, obj_path, lib_path, &directive_info, sect_chunk->u.leaf);
|
|
} else {
|
|
lnk_error_with_loc(LNK_Warning_IllData, obj_path, lib_path, "unable to parse %S", sect_name);
|
|
}
|
|
} else {
|
|
Assert(!"linker directive section chunk must be of leaf type");
|
|
}
|
|
}
|
|
}
|
|
return directive_info;
|
|
}
|
|
|
|
internal MSCRT_FeatFlags
|
|
lnk_obj_get_features(LNK_Obj *obj)
|
|
{
|
|
MSCRT_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;
|
|
}
|
|
|