diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 58f0b588..6b2b1c1f 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -243,7 +243,7 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) } // init config - LNK_Config *config = lnk_config_from_cmd_line(arena, cmd_line); + LNK_Config *config = lnk_config_from_cmd_line(arena, raw_cmd_line, cmd_line); #if PROFILE_TELEMETRY { @@ -1071,2195 +1071,6 @@ lnk_queue_lib_member_input(Arena *arena, } } -internal int -lnk_section_contrib_ptr_is_before(void *raw_a, void *raw_b) -{ - // Grouped Sections (PE Format) - // "All contributions with the same object-section name are allocated contiguously in the image, - // and the blocks of contributions are sorted in lexical order by object-section name." - LNK_SectionContrib *a = *(LNK_SectionContrib **)raw_a; - LNK_SectionContrib *b = *(LNK_SectionContrib **)raw_b; - - int cmp; - - if (a->u.sort_idx_size <= 1 && b->u.sort_idx_size <= 1) { - if (a->u.sort_idx_size == b->u.sort_idx_size) { - cmp = u32_compar(&a->u.obj_idx, &b->u.obj_idx); - if (cmp == 0) { - cmp = u32_compar(&a->u.obj_sect_idx, &b->u.obj_sect_idx); - } - } else { - // place sections without sort postfix first - cmp = a->u.sort_idx_size < b->u.sort_idx_size; - } - } else { - // sort on section postfix - String8 a_sort_idx = str8(a->u.sort_idx, a->u.sort_idx_size); - String8 b_sort_idx = str8(b->u.sort_idx, b->u.sort_idx_size); - cmp = str8_compar_case_sensitive(&a_sort_idx, &b_sort_idx); - - // sort on obj position on command line - if (cmp == 0) { - cmp = u32_compar(&a->u.obj_idx, &b->u.obj_idx); - - // sort on section index - if (cmp == 0) { - cmp = u32_compar(&a->u.obj_sect_idx, &b->u.obj_sect_idx); - } - } - } - - int is_before = cmp < 0; - return is_before; -} - -internal int -lnk_common_block_contrib_is_before(void *raw_a, void *raw_b) -{ - LNK_CommonBlockContrib *a = raw_a; - LNK_CommonBlockContrib *b = raw_b; - - int is_before; - if (a->u.size == b->u.size) { - LNK_Symbol *a_symbol = a->symbol; - LNK_Symbol *b_symbol = b->symbol; - if (a_symbol->u.defined.obj->input_idx == b_symbol->u.defined.obj->input_idx) { - is_before = a_symbol->u.defined.symbol_idx < b_symbol->u.defined.symbol_idx; - } else { - is_before = a_symbol->u.defined.obj->input_idx < b_symbol->u.defined.obj->input_idx; - } - } else { - is_before = a->u.size > b->u.size; - } - - return is_before; -} - -internal U64 -lnk_compute_win32_image_header_size(LNK_Config *config, U64 sect_count) -{ - U64 image_header_size = 0; - image_header_size += sizeof(PE_DosHeader) + pe_dos_program.size; - image_header_size += sizeof(U32); // PE_MAGIC - image_header_size += sizeof(COFF_FileHeader); - image_header_size += pe_has_plus_header(config->machine) ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32); - image_header_size += sizeof(PE_DataDirectory) * config->data_dir_count; - image_header_size += sizeof(COFF_SectionHeader) * sect_count; - return image_header_size; -} - -internal -THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) -{ - LNK_ObjRelocPatcher *task = raw_task; - LNK_Obj *obj = task->objs[task_id]; - - COFF_FileHeaderInfo obj_header = obj->header; - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj_header.section_table_range).str; - String8 symbol_table = str8_substr(obj->data, obj_header.symbol_table_range); - String8 string_table = str8_substr(obj->data, obj_header.string_table_range); - - for (U64 sect_idx = 0; sect_idx < obj_header.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *section_header = §ion_table[sect_idx]; - - // was section removed? - if (section_header->flags & COFF_SectionFlag_LnkRemove) { - continue; - } - if (section_header->flags & COFF_SectionFlag_CntUninitializedData) { - continue; - } - - // get section file range - Rng1U64 section_frange = rng_1u64(section_header->foff, section_header->foff + section_header->fsize); - - // get section bytes - String8 section_data; - if (lnk_is_coff_section_debug(obj, sect_idx)) { - section_data = str8_substr(obj->data, section_frange); - } else { - section_data = str8_substr(task->image_data, section_frange); - } - - // find section relocs - COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, section_header); - COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); - - // apply relocs - for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { - COFF_Reloc *reloc = &relocs[reloc_idx]; - - // error check relocation - if (obj->header.machine == COFF_MachineType_X64) { - if (reloc->type > COFF_Reloc_X64_Last) { - lnk_error_obj(LNK_Error_IllegalRelocation, obj, "unknown relocation 0x%x", reloc->type); - } - } else if (obj->header.machine != COFF_MachineType_Unknown) { - NotImplemented; - } - - // compute virtual offsets - U64 reloc_voff = section_header->voff + reloc->apply_off; - - // compute symbol location values - U32 symbol_secnum = 0; - U32 symbol_secoff = 0; - S64 symbol_voff = 0; - { - COFF_ParsedSymbol symbol; - if (obj_header.is_big_obj) { - symbol = coff_parse_symbol32(string_table, (COFF_Symbol32 *)symbol_table.str + reloc->isymbol); - } else { - symbol = coff_parse_symbol16(string_table, (COFF_Symbol16 *)symbol_table.str + reloc->isymbol); - } - - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular) { - symbol_secnum = symbol.section_number; - symbol_secoff = symbol.value; - symbol_voff = safe_cast_u32((U64)task->image_section_table[symbol.section_number]->voff + (U64)symbol_secoff); - } else if (interp == COFF_SymbolValueInterp_Abs) { - // There aren't enough bits in COFF symbol to store full image base address, - // so we special case __ImageBase. A better solution would be to add - // a 64-bit symbol format to COFF. - if (str8_match(symbol.name, str8_lit("__ImageBase"), 0)) { - symbol.value = task->image_base; - } - - symbol_secnum = 0; - symbol_secoff = 0; - symbol_voff = (S64)symbol.value - (S64)task->image_base; - } else if (interp == COFF_SymbolValueInterp_Weak) { - // unresolved weak - } else if (interp == COFF_SymbolValueInterp_Undefined) { - // unresolved undefined - } else { - InvalidPath; - } - } - - // pick reloc value - COFF_RelocValue reloc_value = {0}; - switch (obj_header.machine) { - case COFF_MachineType_Unknown: {} break; - case COFF_MachineType_X64: { reloc_value = coff_pick_reloc_value_x64(reloc->type, task->image_base, reloc_voff, symbol_secnum, symbol_secoff, symbol_voff); } break; - default: { NotImplemented; } break; - } - - // read addend - Assert(reloc_value.size <= section_data.size); - U64 raw_addend = 0; - str8_deserial_read(section_data, reloc->apply_off, &raw_addend, reloc_value.size, 1); - - // compute new reloc value - S64 addend = extend_sign64(raw_addend, reloc_value.size); - U64 reloc_result = reloc_value.value + addend; - - // commit new reloc value - MemoryCopy(section_data.str + reloc->apply_off, &reloc_result, reloc_value.size); - } - } -} - -internal void -lnk_patch_weak_external_symbol(B32 is_big_obj, void *symbol, COFF_ParsedSymbol parsed_symbol) -{ - COFF_SymbolValueInterpType parsed_symbol_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); - switch (parsed_symbol_interp) { - case COFF_SymbolValueInterp_Regular: { - if (is_big_obj) { - COFF_Symbol32 *symbol32 = symbol; - symbol32->section_number = parsed_symbol.section_number; - symbol32->value = parsed_symbol.value; - symbol32->type = parsed_symbol.type; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = symbol; - symbol16->section_number = safe_cast_u16(parsed_symbol.section_number); - symbol16->value = parsed_symbol.value; - symbol16->type = parsed_symbol.type; - symbol16->storage_class = COFF_SymStorageClass_Static; - } - } break; - case COFF_SymbolValueInterp_Common: { - InvalidPath; - } break; - case COFF_SymbolValueInterp_Abs: { - if (is_big_obj) { - COFF_Symbol32 *symbol32 = symbol; - symbol32->section_number = COFF_Symbol_AbsSection32; - symbol32->value = parsed_symbol.value; - symbol32->type = parsed_symbol.type; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = symbol; - symbol16->section_number = COFF_Symbol_AbsSection16; - symbol16->value = parsed_symbol.value; - symbol16->type = parsed_symbol.type; - symbol16->storage_class = COFF_SymStorageClass_Static; - } - } break; - case COFF_SymbolValueInterp_Weak: - case COFF_SymbolValueInterp_Debug: - case COFF_SymbolValueInterp_Undefined: { - InvalidPath; - } break; - default: { NotImplemented; } break; - } -} - -internal int -lnk_section_definition_is_before(void *raw_a, void *raw_b) -{ - LNK_SectionDefinition **a = raw_a; - LNK_SectionDefinition **b = raw_b; - int is_before; - if ((*a)->obj->input_idx == (*b)->obj->input_idx) { - is_before = (*a)->obj_sect_idx < (*b)->obj_sect_idx; - } else { - is_before = (*a)->obj->input_idx < (*b)->obj->input_idx; - } - return is_before; -} - -internal void -lnk_push_coff_symbols_from_data(Arena *arena, LNK_SymbolList *symbol_list, String8 data, LNK_SymbolArray obj_symbols) -{ - if (data.size % sizeof(U32)) { - // TODO: report invalid data size - } - U64 count = data.size / sizeof(U32); - for (U32 *ptr = (U32*)data.str, *opl = ptr + count; ptr < opl; ++ptr) { - U32 coff_symbol_idx = *ptr; - if (coff_symbol_idx >= obj_symbols.count) { - // TODO: report invalid symbol index - continue; - } - Assert(coff_symbol_idx < obj_symbols.count); - LNK_Symbol *symbol = obj_symbols.v + coff_symbol_idx; - lnk_symbol_list_push(arena, symbol_list, symbol); - } -} - -internal String8 -lnk_build_guard_data(Arena *arena, U64Array voff_arr, U64 stride) -{ - Assert(stride >= sizeof(U32)); - - // check for duplicates -#if DEBUG - for (U64 i = 1; i < voff_arr.count; ++i) { - Assert(voff_arr.[i-1] != voff_ptr[i]); - } -#endif - - U64 buffer_size = stride * voff_arr.count; - U8 *buffer = push_array(arena, U8, buffer_size); - for (U64 i = 0; i < voff_arr.count; ++i) { - U32 *voff_ptr = (U32*)(buffer + i * stride); - *voff_ptr = voff_arr.v[i]; - } - - String8 guard_data = str8(buffer, buffer_size); - return guard_data; -} - -internal String8List -lnk_build_guard_tables(TP_Context *tp, - LNK_SectionTable *sectab, - LNK_SymbolTable *symtab, - U64 objs_count, - LNK_Obj **objs, - COFF_MachineType machine, - String8 entry_point_name, - LNK_GuardFlags guard_flags, - B32 emit_suppress_flag) -{ - NotImplemented; - String8List result = {0}; - return result; -#if 0 - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, sectab); - - enum { GUARD_FIDS, GUARD_IATS, GUARD_LJMP, GUARD_EHCONT, GUARD_COUNT }; - LNK_SymbolList guard_symbol_list_table[GUARD_COUNT]; MemoryZeroStruct(&guard_symbol_list_table[0]); - - // collect symbols from objs - for (LNK_ObjNode *obj_node = obj_list.first; obj_node != NULL; obj_node = obj_node->next) { - LNK_Obj *obj = &obj_node->data; - MSCRT_FeatFlags feat_flags = lnk_obj_get_features(obj); - B32 has_guard_flags = (feat_flags & MSCRT_FeatFlag_GUARD_CF) || (feat_flags & MSCRT_FeatFlag_GUARD_EH_CONT); - if (has_guard_flags) { - LNK_SymbolArray symbol_arr = lnk_symbol_array_from_list(scratch.arena, obj->symbol_list); - if (guard_flags & LNK_Guard_Cf) { - String8List gfids_list = lnk_collect_obj_chunks(scratch.arena, obj, str8_lit(".gfids"), str8_zero(), 1); - for (String8Node *node = gfids_list.first; node != 0; node = node->next) { - lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], node->string, symbol_arr); - } - String8List giats_list = lnk_collect_obj_chunks(scratch.arena, obj, str8_lit(".giats"), str8_zero(), 1); - for (String8Node *node = giats_list.first; node != 0; node = node->next) { - lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_IATS], node->string, symbol_arr); - } - } - if (guard_flags & LNK_Guard_LongJmp) { - String8List gljmp_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gljmp"), str8_zero(), 1); - for (String8Node *node = gljmp_list.first; node != 0; node = node->next) { - lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_LJMP], node->string, symbol_arr); - } - } - if (guard_flags & LNK_Guard_EhCont) { - String8List gehcont_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gehcont"), str8_zero(), 1); - for (String8Node *node = gehcont_list.first; node != 0; node = node->next) { - lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_EHCONT], node->string, symbol_arr); - } - } - } else { - // TODO: loop over COFF relocs - NotImplemented; -#if 0 - // use relocation data in code sections to get function symbols - for (U64 isect = 0; isect < obj->sect_count; ++isect) { - LNK_Chunk *chunk = obj->chunk_arr[isect]; - if (!chunk) { - continue; - } - if (lnk_chunk_is_discarded(chunk)) { - continue; - } - if (~chunk->flags & COFF_SectionFlag_CntCode) { - continue; - } - Assert(chunk->type == LNK_Chunk_Leaf); - for (LNK_Reloc *reloc = obj->sect_reloc_list_arr[isect].first; reloc != 0; reloc = reloc->next) { - LNK_Symbol *symbol = lnk_resolve_symbol(symtab, reloc->symbol); - if (!LNK_Symbol_IsDefined(symbol->type)) { - continue; - } - LNK_DefinedSymbol *defined_symbol = &symbol->u.defined; - if (~defined_symbol->flags & LNK_DefinedSymbolFlag_IsFunc) { - continue; - } - LNK_Chunk *symbol_chunk = defined_symbol->u.chunk; - if (!symbol_chunk) { - continue; - } - if (symbol_chunk->type != LNK_Chunk_Leaf) { - continue; - } - if (~symbol_chunk->flags & COFF_SectionFlag_CntCode) { - continue; - } - lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], symbol); - } - } -#endif - } - } - - // entry point - LNK_Symbol *entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, entry_point_name); - lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], entry_point_symbol); - - // push exports - { - Temp temp = temp_begin(scratch.arena); - KeyValuePair *raw_exports = key_value_pairs_from_hash_table(temp.arena, exptab->name_export_ht); - for (U64 i = 0; i < exptab->name_export_ht->count; ++i) { - LNK_Export *exp = raw_exports[i].value_raw; - lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], exp->symbol); - } - scratch_end(temp); - } - - // TODO: push noname exports - - NotImplemented; -#if 0 - // push thunks - LNK_SymbolScope scope_array[] = { LNK_SymbolScope_Defined, LNK_SymbolScope_Internal }; - for (U64 iscope = 0; iscope < ArrayCount(scope_array); ++iscope) { - LNK_SymbolScope scope = scope_array[iscope]; - for (U64 ibucket = 0; ibucket < symtab->bucket_count[scope]; ++ibucket) { - for (LNK_SymbolNode *symbol_node = symtab->buckets[scope][ibucket].first; - symbol_node != NULL; - symbol_node = symbol_node->next) { - LNK_Symbol *symbol = symbol_node->data; - if (!LNK_Symbol_IsDefined(symbol->type)) continue; - LNK_DefinedSymbol *defined_symbol = &symbol->u.defined; - if (~defined_symbol->flags & LNK_DefinedSymbolFlag_IsThunk) continue; - lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], symbol); - } - } - } -#endif - - // build section data - lnk_section_table_build_data(tp, sectab, machine); - lnk_section_table_assign_virtual_offsets(sectab); - - // compute symbols virtual offsets - U64Array guard_voff_arr_table[GUARD_COUNT]; - for (U64 i = 0; i < ArrayCount(guard_symbol_list_table); ++i) { - U64List voff_list; MemoryZeroStruct(&voff_list); - LNK_SymbolList symbol_list = guard_symbol_list_table[i]; - for (LNK_SymbolNode *symbol_node = symbol_list.first; symbol_node != NULL; symbol_node = symbol_node->next) { - LNK_Symbol *symbol = lnk_resolve_symbol(symtab, symbol_node->data); - if (!LNK_Symbol_IsDefined(symbol->type)) { - continue; - } - LNK_DefinedSymbol *defined_symbol = &symbol->u.defined; - LNK_Chunk *chunk = defined_symbol->u.chunk; - if (!chunk) { - continue; - } - if (lnk_chunk_is_discarded(chunk)) { - continue; - } - U64 chunk_voff = lnk_virt_off_from_chunk_ref(sect_id_map, chunk->ref); - U64 symbol_voff = chunk_voff + defined_symbol->u.chunk_offset; - Assert(symbol_voff != 0); - u64_list_push(scratch.arena, &voff_list, symbol_voff); - } - U64Array voff_arr = u64_array_from_list(scratch.arena, &voff_list); - radsort(voff_arr.v, voff_arr.count, u64_compar_is_before); - guard_voff_arr_table[i] = u64_array_remove_duplicates(scratch.arena, voff_arr); - } - - // push guard sections - static struct { - char *name; - char *symbol; - int flags; - } sect_layout[] = { - { ".gfids", LNK_GFIDS_SYMBOL_NAME, LNK_GFIDS_SECTION_FLAGS }, - { ".giats", LNK_GIATS_SYMBOL_NAME, LNK_GIATS_SECTION_FLAGS }, - { ".gljmp", LNK_GLJMP_SYMBOL_NAME, LNK_GLJMP_SECTION_FLAGS }, - { ".gehcont", LNK_GEHCONT_SYMBOL_NAME, LNK_GEHCONT_SECTION_FLAGS }, - }; - for (U64 i = 0; i < ArrayCount(sect_layout); ++i) { - LNK_Section *sect = lnk_section_table_push(sectab, str8_cstring(sect_layout[i].name), sect_layout[i].flags); - } - - // TODO: emit table for SEH on X86 - if (machine == COFF_MachineType_X86) { - lnk_not_implemented("__safe_se_handler_table"); - lnk_not_implemented("__safe_se_handler_count"); - } - - LNK_Symbol *gfids_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GFIDS_SYMBOL_NAME)); - LNK_Symbol *giats_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GIATS_SYMBOL_NAME)); - LNK_Symbol *gljmp_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GLJMP_SYMBOL_NAME)); - LNK_Symbol *gehcont_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GEHCONT_SYMBOL_NAME)); - - LNK_Section *gfids_sect = lnk_section_table_search_id(sectab, gfids_symbol->u.defined.u.chunk->ref.sect_id); - LNK_Section *giats_sect = lnk_section_table_search_id(sectab, giats_symbol->u.defined.u.chunk->ref.sect_id); - LNK_Section *gljmp_sect = lnk_section_table_search_id(sectab, gljmp_symbol->u.defined.u.chunk->ref.sect_id); - LNK_Section *gehcont_sect = lnk_section_table_search_id(sectab, gehcont_symbol->u.defined.u.chunk->ref.sect_id); - - LNK_Chunk *gfids_array_chunk = gfids_sect->root; - LNK_Chunk *giats_array_chunk = giats_sect->root; - LNK_Chunk *gljmp_array_chunk = gljmp_sect->root; - LNK_Chunk *gehcont_array_chunk = gehcont_sect->root; - - // first 4 bytes are call's destination virtual offset - U64 entry_stride = sizeof(U32); - if (emit_suppress_flag) { - // 4th byte tells kernel what to do when destination VA is not in the bitmap. - // If byte is 1 exception is suppressed and program keeps running. - // If zero then exception is raised with nt!_KiRaiseSecurityCheckFailure(FAST_FAIL_GUARD_ICALL_CHECK_FAILURE) and exception code 0xA. - entry_stride = 5; - } - - // make guard data from virtual offsets - String8 gfids_data = lnk_build_guard_data(gfids_sect->arena, guard_voff_arr_table[GUARD_FIDS], entry_stride); - String8 giats_data = lnk_build_guard_data(giats_sect->arena, guard_voff_arr_table[GUARD_IATS], entry_stride); - String8 gljmp_data = lnk_build_guard_data(gljmp_sect->arena, guard_voff_arr_table[GUARD_LJMP], entry_stride); - String8 gehcont_data = lnk_build_guard_data(gehcont_sect->arena, guard_voff_arr_table[GUARD_EHCONT], entry_stride); - - // push guard data - lnk_section_push_chunk_data(gfids_sect, gfids_array_chunk, gfids_data, str8_zero()); - lnk_section_push_chunk_data(giats_sect, giats_array_chunk, giats_data, str8_zero()); - lnk_section_push_chunk_data(gljmp_sect, gljmp_array_chunk, gljmp_data, str8_zero()); - lnk_section_push_chunk_data(gehcont_sect, gehcont_array_chunk, gehcont_data, str8_zero()); - - LNK_Symbol *gflags_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_FLAGS_SYMBOL_NAME)); - LNK_Symbol *gfids_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_FIDS_TABLE_SYMBOL_NAME)); - LNK_Symbol *gfids_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_FIDS_COUNT_SYMBOL_NAME)); - LNK_Symbol *giats_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_IAT_TABLE_SYMBOL_NAME)); - LNK_Symbol *giats_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_IAT_COUNT_SYMBOL_NAME)); - LNK_Symbol *gljmp_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_LONGJMP_TABLE_SYMBOL_NAME)); - LNK_Symbol *gljmp_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_LONGJMP_COUNT_SYMBOL_NAME)); - LNK_Symbol *gehcont_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_EHCONT_TABLE_SYMBOL_NAME)); - LNK_Symbol *gehcont_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_EHCONT_COUNT_SYMBOL_NAME)); - - LNK_DefinedSymbol *gflags_def = &gflags_symbol->u.defined; - LNK_DefinedSymbol *gfids_table_def = &gfids_table_symbol->u.defined; - LNK_DefinedSymbol *gfids_count_def = &gfids_count_symbol->u.defined; - LNK_DefinedSymbol *giats_table_def = &giats_table_symbol->u.defined; - LNK_DefinedSymbol *giats_count_def = &giats_count_symbol->u.defined; - LNK_DefinedSymbol *gljmp_table_def = &gljmp_table_symbol->u.defined; - LNK_DefinedSymbol *gljmp_count_def = &gljmp_count_symbol->u.defined; - LNK_DefinedSymbol *gehcont_table_def = &gehcont_table_symbol->u.defined; - LNK_DefinedSymbol *gehcont_count_def = &gehcont_count_symbol->u.defined; - - // guard flags - gflags_def->value_type = LNK_DefinedSymbolValue_VA; - gflags_def->u.va = PE_LoadConfigGuardFlags_CF_INSTRUMENTED; - if ((guard_flags & LNK_Guard_Cf)) { - gflags_def->u.va |= PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_PRESENT; - } - if ((guard_flags & LNK_Guard_LongJmp) && guard_voff_arr_table[GUARD_LJMP].count) { - gflags_def->u.va |= PE_LoadConfigGuardFlags_CF_LONGJUMP_TABLE_PRESENT; - } - if ((guard_flags & LNK_Guard_EhCont) && guard_voff_arr_table[GUARD_EHCONT].count) { - gflags_def->u.va |= PE_LoadConfigGuardFlags_EH_CONTINUATION_TABLE_PRESENT; - } - { - LNK_Section *didat_sect = lnk_section_table_search(sectab, str8_lit(".didat")); - if (didat_sect) { - gflags_def->u.va |= PE_LoadConfigGuardFlags_DELAYLOAD_IAT_IN_ITS_OWN_SECTION; - } - } - if (entry_stride > sizeof(U32)) { - U64 size_bit = (entry_stride - 5); - if (emit_suppress_flag) { - gflags_def->u.va |= PE_LoadConfigGuardFlags_CF_EXPORT_SUPPRESSION_INFO_PRESENT; - } - gflags_def->u.va |= (1 << size_bit) << PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_SHIFT; - } - - // gfids - if (guard_voff_arr_table[GUARD_FIDS].count) { - gfids_table_def->value_type = LNK_DefinedSymbolValue_Chunk; - gfids_table_def->u.chunk = gfids_array_chunk; - } - gfids_count_def->value_type = LNK_DefinedSymbolValue_VA; - gfids_count_def->u.va = guard_voff_arr_table[GUARD_FIDS].count; - - // giats - if (guard_voff_arr_table[GUARD_IATS].count) { - giats_table_def->value_type = LNK_DefinedSymbolValue_Chunk; - giats_table_def->u.chunk = giats_array_chunk; - } - giats_count_def->value_type = LNK_DefinedSymbolValue_VA; - giats_count_def->u.va = guard_voff_arr_table[GUARD_IATS].count; - - // gljmp - if (guard_voff_arr_table[GUARD_LJMP].count) { - gljmp_table_def->value_type = LNK_DefinedSymbolValue_Chunk; - gljmp_table_def->u.chunk = gljmp_array_chunk; - } - gljmp_count_def->value_type = LNK_DefinedSymbolValue_VA; - gljmp_count_def->u.va = guard_voff_arr_table[GUARD_LJMP].count; - - // gehcont - if (guard_voff_arr_table[GUARD_EHCONT].count) { - gehcont_table_def->value_type = LNK_DefinedSymbolValue_Chunk; - gehcont_table_def->u.chunk = gehcont_array_chunk; - } - gehcont_count_def->value_type = LNK_DefinedSymbolValue_VA; - gehcont_count_def->u.va = guard_voff_arr_table[GUARD_EHCONT].count; - - scratch_end(scratch); - ProfEnd(); -#endif -} - -internal -THREAD_POOL_TASK_FUNC(lnk_emit_base_relocs_from_objs_task) -{ - ProfBeginFunction(); - - LNK_ObjBaseRelocTask *task = raw_task; - Rng1U64 range = task->ranges[task_id]; - - HashTable *page_ht = task->page_ht_arr[task_id]; - LNK_BaseRelocPageList *page_list = &task->list_arr[task_id]; - - for (U64 obj_idx = range.min; obj_idx < range.max; ++obj_idx) { - LNK_Obj *obj = task->obj_arr[obj_idx]; - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; - for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - - if (sect_header->flags & COFF_SectionFlag_LnkRemove) { - continue; - } - - COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, sect_header); - COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); - - for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { - COFF_Reloc *r = &relocs[reloc_idx]; - - COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - B32 is_symbol_address = symbol_interp != COFF_SymbolValueInterp_Abs; - - if (is_symbol_address) { - B32 is_addr32 = 0, is_addr64 = 0; - switch (obj->header.machine) { - case COFF_MachineType_Unknown: {} break; - case COFF_MachineType_X64: { - is_addr32 = r->type == COFF_Reloc_X64_Addr32; - is_addr64 = r->type == COFF_Reloc_X64_Addr64; - } break; - default: { NotImplemented; } break; - } - - if (is_addr32 || is_addr64) { - U64 reloc_voff = sect_header->voff + r->apply_off; - U64 page_voff = AlignDownPow2(reloc_voff, task->page_size); - - LNK_BaseRelocPageNode *page; - { - KeyValuePair *is_page_present = hash_table_search_u64(page_ht, page_voff); - if (is_page_present) { - page = is_page_present->value_raw; - } else { - // fill out page - page = push_array(arena, LNK_BaseRelocPageNode, 1); - page->v.voff = page_voff; - - // push page - SLLQueuePush(page_list->first, page_list->last, page); - page_list->count += 1; - - // register page voff - hash_table_push_u64_raw(arena, page_ht, page_voff, page); - } - } - - if (is_addr32) { - if (task->is_large_addr_aware) { - COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); - lnk_error_obj(LNK_Error_LargeAddrAwareRequired, obj, "found out of range ADDR32 relocation for '%S', link with /LARGEADDRESSAWARE:NO", symbol.name); - } else { - u64_list_push(arena, &page->v.entries_addr32, reloc_voff); - } - } else { - u64_list_push(arena, &page->v.entries_addr64, reloc_voff); - } - } - } - } - } - } - - ProfEnd(); -} - -int -lnk_base_reloc_page_compar(const void *raw_a, const void *raw_b) -{ - const LNK_BaseRelocPage *a = raw_a; - const LNK_BaseRelocPage *b = raw_b; - return u64_compar(&a->voff, &b->voff); -} - -int -lnk_base_reloc_page_is_before(void *raw_a, void *raw_b) -{ - LNK_BaseRelocPage* a = raw_a; - LNK_BaseRelocPage* b = raw_b; - return a->voff < b->voff; -} - -internal String8List -lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, U64 objs_count, LNK_Obj **objs) -{ - ProfBeginFunction(); - - Arena *arena = tp_arena->v[0]; - Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); - tp_arena->v[0] = scratch.arena; - TP_Temp tp_temp = tp_temp_begin(tp_arena); - - LNK_BaseRelocPageArray page_arr; - { - LNK_BaseRelocPageList *page_list_arr = push_array(scratch.arena, LNK_BaseRelocPageList, tp->worker_count); - HashTable **page_ht_arr = push_array_no_zero(scratch.arena, HashTable *, tp->worker_count); - for (U64 i = 0; i < tp->worker_count; ++i) { - page_ht_arr[i] = hash_table_init(scratch.arena, 1024); - } - - { - ProfBegin("Emit Relocs From Objs"); - LNK_ObjBaseRelocTask task = {0}; - task.ranges = tp_divide_work(scratch.arena, objs_count, tp->worker_count); - task.page_size = config->page_size; - task.page_ht_arr = page_ht_arr; - task.list_arr = page_list_arr; - task.obj_arr = objs; - task.is_large_addr_aware = !!(config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE); - tp_for_parallel(tp, tp_arena, tp->worker_count, lnk_emit_base_relocs_from_objs_task, &task); - ProfEnd(); - } - - LNK_BaseRelocPageList *main_page_list = &page_list_arr[0]; - { - ProfBegin("Merge Worker Page Lists"); - HashTable *main_ht = page_ht_arr[0]; - for (U64 list_idx = 1; list_idx < tp->worker_count; ++list_idx) { - LNK_BaseRelocPageList src = page_list_arr[list_idx]; - - for (LNK_BaseRelocPageNode *src_page = src.first, *src_next; src_page != 0; src_page = src_next) { - src_next = src_page->next; - - KeyValuePair *is_page_present = hash_table_search_u64(main_ht, src_page->v.voff); - if (is_page_present) { - // page exists concat voffs - LNK_BaseRelocPageNode *page = is_page_present->value_raw; - Assert(page != src_page); - u64_list_concat_in_place(&page->v.entries_addr32, &src_page->v.entries_addr32); - u64_list_concat_in_place(&page->v.entries_addr64, &src_page->v.entries_addr64); - } else { - // push page to main list - SLLQueuePush(main_page_list->first, main_page_list->last, src_page); - main_page_list->count += 1; - - // store lookup voff - hash_table_push_u64_raw(scratch.arena, main_ht, src_page->v.voff, src_page); - } - } - } - ProfEnd(); - } - - ProfBegin("Page List -> Array"); - page_arr.count = 0; - page_arr.v = push_array_no_zero(scratch.arena, LNK_BaseRelocPage, main_page_list->count); - for (LNK_BaseRelocPageNode* n = main_page_list->first; n != 0; n = n->next) { - page_arr.v[page_arr.count++] = n->v; - } - ProfEnd(); - - ProfBegin("Sort Pages on VOFF"); - //radsort(page_arr.v, page_arr.count, lnk_base_reloc_page_is_before); - qsort(page_arr.v, page_arr.count, sizeof(page_arr.v[0]), lnk_base_reloc_page_compar); - ProfEnd(); - } - - String8List result = {0}; - if (page_arr.count) { - ProfBegin("Serialize Pages"); - HashTable *voff_ht = hash_table_init(scratch.arena, config->page_size); - for (U64 page_idx = 0; page_idx < page_arr.count; ++page_idx) { - LNK_BaseRelocPage *page = &page_arr.v[page_idx]; - - U64 total_entry_count = 0; - total_entry_count += page->entries_addr32.count; - total_entry_count += page->entries_addr64.count; - - U32 *page_voff_ptr; - U32 *block_size_ptr; - U16 *reloc_arr_base; - - // push buffer - U64 buf_size = AlignPow2(sizeof(*page_voff_ptr) + sizeof(*block_size_ptr) + sizeof(*reloc_arr_base)*total_entry_count, sizeof(U32)); - void *buf = push_array_no_zero(arena, U8, buf_size); - - // setup pointers into buffer - page_voff_ptr = buf; - block_size_ptr = page_voff_ptr + 1; - reloc_arr_base = (U16*)(block_size_ptr + 1); - - // write 32-bit relocations - U16 *reloc_arr_ptr = reloc_arr_base; - for (U64Node *i = page->entries_addr32.first; i != 0; i = i->next) { - // was base reloc_entry made? - if (hash_table_search_u64(voff_ht, i->data)) { - continue; - } - hash_table_push_u64_u64(scratch.arena, voff_ht, i->data, 0); - - // write entry - U64 rel_off = i->data - page->voff; - Assert(rel_off <= config->page_size); - *reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_HIGHLOW, rel_off); - } - - // write 64-bit relocations - for (U64Node *i = page->entries_addr64.first; i != 0; i = i->next) { - // was base reloc entry made? - if (hash_table_search_u64(voff_ht, i->data)) { - continue; - } - hash_table_push_u64_u64(scratch.arena, voff_ht, i->data, 0); - - // write entry - U64 rel_off = i->data - page->voff; - Assert(rel_off <= config->page_size); - *reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_DIR64, rel_off); - } - - // write pad - U64 pad_reloc_count = AlignPadPow2(total_entry_count, sizeof(reloc_arr_ptr[0])); - MemoryZeroTyped(reloc_arr_ptr, pad_reloc_count); // fill pad with PE_BaseRelocKind_ABSOLUTE - reloc_arr_ptr += pad_reloc_count; - - // compute block size - U64 reloc_arr_size = (U64)((U8*)reloc_arr_ptr - (U8*)reloc_arr_base); - U64 block_size = sizeof(*page_voff_ptr) + sizeof(*block_size_ptr) + reloc_arr_size; - - // write header - *page_voff_ptr = safe_cast_u32(page->voff); - *block_size_ptr = safe_cast_u32(block_size); - Assert(*block_size_ptr <= buf_size); - - // push page - str8_list_push(arena, &result, str8(buf, buf_size)); - - // purge voffs for next page - hash_table_purge(voff_ht); - } - ProfEnd(); - } - - tp_temp_end(tp_temp); // scratch is cleared here - tp_arena->v[0] = arena; - - ProfEnd(); - return result; -} - -internal String8List -lnk_build_win32_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sects, U64 expected_image_header_size) -{ - ProfBeginFunction(); - - String8List result = {0}; - - // - // DOS header - // - U32 dos_stub_size = sizeof(PE_DosHeader) + pe_dos_program.size; - { - PE_DosHeader *dos_header = push_array(arena, PE_DosHeader, 1); - dos_header->magic = PE_DOS_MAGIC; - dos_header->last_page_size = dos_stub_size % 512; - dos_header->page_count = CeilIntegerDiv(dos_stub_size, 512); - dos_header->paragraph_header_size = sizeof(PE_DosHeader) / 16; - dos_header->min_paragraph = 0; - dos_header->max_paragraph = 0; - dos_header->init_ss = 0; - dos_header->init_sp = 0; - dos_header->checksum = 0; - dos_header->init_ip = 0xFFFF; - dos_header->init_cs = 0; - dos_header->reloc_table_file_off = sizeof(PE_DosHeader); - dos_header->overlay_number = 0; - MemoryZeroStruct(dos_header->reserved); - dos_header->oem_id = 0; - dos_header->oem_info = 0; - MemoryZeroArray(dos_header->reserved2); - dos_header->coff_file_offset = dos_stub_size; - - str8_list_push(arena, &result, str8_struct(dos_header)); - str8_list_push(arena, &result, pe_dos_program); - } - - // - // PE magic - // - U32 *pe_magic = push_array(arena, U32, 1); - *pe_magic = PE_MAGIC; - str8_list_push(arena, &result, str8_struct(pe_magic)); - - // - // determine PE optional header type - // - B32 has_pe_plus_header = pe_has_plus_header(config->machine); - - // - // COFF file header - // - { - COFF_FileHeader *file_header = push_array_no_zero(arena, COFF_FileHeader, 1); - file_header->machine = config->machine; - file_header->time_stamp = config->time_stamp; - file_header->symbol_table_foff = 0; - file_header->symbol_count = 0; - file_header->section_count = sects.count; - file_header->optional_header_size = (has_pe_plus_header ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32)) + (sizeof(PE_DataDirectory) * config->data_dir_count); - file_header->flags = config->file_characteristics; - str8_list_push(arena, &result, str8_struct(file_header)); - } - - // - // compute code/inited/uninited sizes - // - U64 code_base = 0; - U64 sizeof_code = 0; - U64 sizeof_inited_data = 0; - U64 sizeof_uninited_data = 0; - U64 sizeof_image = 0; - for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { - LNK_Section *sect = sects.v[sect_idx]; - if (code_base == 0 && sect->flags & COFF_SectionFlag_CntCode) { - code_base = sect->voff; - } - if (sect->flags & COFF_SectionFlag_CntUninitializedData) { - sizeof_uninited_data += sect->vsize; - } - if ((sect->flags & COFF_SectionFlag_CntInitializedData) || (sect->flags & COFF_SectionFlag_CntCode)) { - sizeof_inited_data += sect->fsize; - } - if (sect->flags & COFF_SectionFlag_CntCode) { - sizeof_code += sect->fsize; - } - sizeof_image = Max(sizeof_image, sects.v[sect_idx]->voff + sects.v[sect_idx]->vsize); - } - sizeof_code = AlignPow2(sizeof_code, config->file_align); - sizeof_inited_data = AlignPow2(sizeof_inited_data, config->file_align); - sizeof_uninited_data = AlignPow2(sizeof_uninited_data, config->file_align); - sizeof_image = AlignPow2(sizeof_image, 4096); - - // - // compute image headers size - // - U64 sizeof_image_headers = 0; - sizeof_image_headers += dos_stub_size; - sizeof_image_headers += sizeof(COFF_FileHeader); - sizeof_image_headers += has_pe_plus_header ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32); - sizeof_image_headers += sizeof(PE_DataDirectory) * config->data_dir_count; - sizeof_image_headers += sizeof(COFF_SectionHeader) * sects.count; - sizeof_image_headers = AlignPow2(sizeof_image_headers, config->file_align); - - // - // fill out PE optional header - // - U32 *entry_point_va; - U32 *check_sum; - if (has_pe_plus_header) { - PE_OptionalHeader32Plus *opt_header = push_array_no_zero(arena, PE_OptionalHeader32Plus, 1); - opt_header->magic = PE_PE32PLUS_MAGIC; - opt_header->major_linker_version = config->link_ver.major; - opt_header->minor_linker_version = config->link_ver.minor; - opt_header->sizeof_code = safe_cast_u32(sizeof_code); - opt_header->sizeof_inited_data = safe_cast_u32(sizeof_inited_data); - opt_header->sizeof_uninited_data = safe_cast_u32(sizeof_uninited_data); - opt_header->entry_point_va = 0; - opt_header->code_base = code_base; - opt_header->image_base = lnk_get_base_addr(config); - opt_header->section_alignment = config->sect_align; - opt_header->file_alignment = config->file_align; - opt_header->major_os_ver = config->os_ver.major; - opt_header->minor_os_ver = config->os_ver.minor; - opt_header->major_img_ver = config->image_ver.major; - opt_header->minor_img_ver = config->image_ver.minor; - opt_header->major_subsystem_ver = config->subsystem_ver.major; - opt_header->minor_subsystem_ver = config->subsystem_ver.minor; - opt_header->win32_version_value = 0; // MSVC writes zero - opt_header->sizeof_image = sizeof_image; - opt_header->sizeof_headers = safe_cast_u32(sizeof_image_headers); - opt_header->check_sum = 0; // :check_sum - opt_header->subsystem = config->subsystem; - opt_header->dll_characteristics = config->dll_characteristics; - opt_header->sizeof_stack_reserve = config->stack_reserve; - opt_header->sizeof_stack_commit = config->stack_commit; - opt_header->sizeof_heap_reserve = config->heap_reserve; - opt_header->sizeof_heap_commit = config->heap_commit; - opt_header->loader_flags = 0; // for dynamic linker, always zero - opt_header->data_dir_count = safe_cast_u32(config->data_dir_count); - - entry_point_va = &opt_header->entry_point_va; - check_sum = &opt_header->check_sum; - - str8_list_push(arena, &result, str8_struct(opt_header)); - } else { - NotImplemented; - } - - // - // PE directories - // - PE_DataDirectory *directory_array; - { - directory_array = push_array(arena, PE_DataDirectory, config->data_dir_count); - str8_list_push(arena, &result, str8_array(directory_array, config->data_dir_count)); - } - - // - // COFF section table - // - COFF_SectionHeader *coff_section_table = push_array(arena, COFF_SectionHeader, sects.count); - U64 coff_section_table_count = 0; - { - for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { - LNK_Section *sect = sects.v[sect_idx]; - - COFF_SectionHeader *coff_section = &coff_section_table[sect_idx]; - - // TODO: for objs we can store long name in string table and write here /offset - if (sect->name.size > sizeof(coff_section->name)) { - lnk_error(LNK_Warning_LongSectionName, "not enough space in COFF section header to store entire name \"%S\"", sect->name); - } - - MemorySet(&coff_section->name[0], 0, sizeof(coff_section->name)); - MemoryCopy(&coff_section->name[0], sect->name.str, Min(sect->name.size, sizeof(coff_section->name))); - coff_section->vsize = sect->vsize; - coff_section->voff = sect->voff; - coff_section->fsize = sect->fsize; - coff_section->foff = sect->foff; - coff_section->relocs_foff = 0; // not present in image - coff_section->lines_foff = 0; // obsolete - coff_section->reloc_count = 0; // not present in image - coff_section->line_count = 0; // obsolete - coff_section->flags = sect->flags; - - coff_section_table_count += 1; - } - - str8_list_push(arena, &result, str8_array(coff_section_table, coff_section_table_count)); - } - - // align image headers - { - U64 image_headers_align_size = AlignPadPow2(result.total_size, config->file_align); - U8 *image_headers_align = push_array(arena, U8, image_headers_align_size); - str8_list_push(arena, &result, str8(image_headers_align, image_headers_align_size)); - } - - // - // entry point - // - { - Temp scratch = scratch_begin(&arena, 1); - - COFF_SectionHeader **section_table = push_array(arena, COFF_SectionHeader *, coff_section_table_count + 1); - for (U64 i = 1; i <= coff_section_table_count; i += 1) { section_table[i] = &coff_section_table[i-1]; } - - LNK_Symbol *entry_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, config->entry_point_name); - if (entry_symbol) { - *entry_point_va = safe_cast_u32(lnk_virt_off_from_symbol(section_table, entry_symbol)); - } - - scratch_end(scratch); - } - - Assert(result.total_size == expected_image_header_size); - ProfEnd(); - return result; -} - -internal LNK_ImageContext -lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs) -{ - ProfBegin("Image"); - lnk_timer_begin(LNK_Timer_Image); - - Temp scratch = scratch_begin(arena->v, arena->count); - - // init section table - LNK_SectionTable *sectab = lnk_section_table_alloc(); - lnk_section_table_push(sectab, str8_lit(".text"), PE_TEXT_SECTION_FLAGS); - lnk_section_table_push(sectab, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS); - lnk_section_table_push(sectab, str8_lit(".data"), PE_DATA_SECTION_FLAGS); - - { - ProfBegin("Remove Associative Sections"); - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - String8 string_table = str8_substr(obj->data, obj->header.string_table_range); - String8 symbol_table = str8_substr(obj->data, obj->header.symbol_table_range); - for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { - // find associate section head section index - U32 head_sect_idx = max_U32; - for (U64 current_sect_idx = sect_idx;;) { - U32 symbol_idx = obj->comdats[current_sect_idx]; - if (symbol_idx == max_U32) { - break; - } - - COFF_ParsedSymbol symbol = coff_parse_symbol(obj->header, string_table, symbol_table, symbol_idx); - COFF_ComdatSelectType selection = COFF_ComdatSelect_Null; - U32 section_number = 0; - coff_parse_secdef(symbol, obj->header.is_big_obj, &selection, §ion_number, 0, 0); - if (selection != COFF_ComdatSelect_Associative) { - head_sect_idx = current_sect_idx; - break; - } - - current_sect_idx = section_number-1; - } - - if (head_sect_idx != max_U32) { - // flag current section with remove if head section was removed - COFF_SectionHeader *head_sect_header = lnk_coff_section_header_from_section_number(obj, head_sect_idx+1); - COFF_SectionHeader *curr_sect_header = lnk_coff_section_header_from_section_number(obj, sect_idx+1); - curr_sect_header->flags |= (head_sect_header->flags & COFF_SectionFlag_LnkRemove); - } - } - } - ProfEnd(); - } - - { - ProfBegin("Define And Count Sections"); - Temp temp = temp_begin(scratch.arena); - - HashTable **sect_defn_ht_arr = push_array(temp.arena, HashTable *, tp->worker_count); - for (U64 i = 0; i < tp->worker_count; i += 1) sect_defn_ht_arr[i] = hash_table_init(temp.arena, 128); - HashTable *sect_defn_ht = sect_defn_ht_arr[0]; - - ProfBegin("Gather Section Definitions"); - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; - String8 string_table = str8_substr(obj->data, obj->header.string_table_range); - - for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - - // remove section - if (sect_header->flags & COFF_SectionFlag_LnkRemove) { - continue; - } - - // parse section name - String8 full_sect_name = coff_name_from_section_header(string_table, sect_header); - String8 sect_name, sect_sort_idx; - coff_parse_section_name(full_sect_name, §_name, §_sort_idx); - - // was section defined? - COFF_SectionFlags sect_flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; - String8 sect_name_with_flags = lnk_make_name_with_flags(temp.arena, sect_name, sect_flags); - LNK_SectionDefinition *sect_defn = 0; - hash_table_search_string_raw(sect_defn_ht, sect_name_with_flags, §_defn); - - // create section definition - if (sect_defn == 0) { - sect_defn = push_array(temp.arena, LNK_SectionDefinition, 1); - sect_defn->name = sect_name; - sect_defn->flags = sect_flags; - sect_defn->obj = obj; - sect_defn->obj_sect_idx = sect_idx; - hash_table_push_string_raw(temp.arena, sect_defn_ht, sect_name_with_flags, sect_defn); - } - - // acc contrib count - sect_defn->contribs_count += 1; - } - } - ProfEnd(); - - ProfBegin("Merge Section Definitions"); - HashTable *main_sect_defns_ht = sect_defn_ht_arr[0]; - for (U64 worker_idx = 1; worker_idx < tp->worker_count; worker_idx += 1) { - U64 sect_defns_count = sect_defn_ht_arr[worker_idx]->count; - LNK_SectionDefinition **sect_defns = values_from_hash_table_raw(temp.arena, sect_defn_ht_arr[worker_idx]); - radsort(sect_defns, sect_defns_count, lnk_section_definition_is_before); - - for (U64 i = 0; i < sect_defns_count; i += 1) { - String8 name_with_flags = lnk_make_name_with_flags(temp.arena, sect_defns[i]->name, sect_defns[i]->flags); - LNK_SectionDefinition *main_defn = 0; - hash_table_search_string_raw(main_sect_defns_ht, name_with_flags, &main_defn); - if (main_defn == 0) { - main_defn = sect_defns[i]; - hash_table_push_string_raw(temp.arena, main_sect_defns_ht, name_with_flags, main_defn); - } else { - main_defn->contribs_count += sect_defns[i]->contribs_count; - } - } - } - ProfEnd(); - - ProfBegin("Push Sections And Reserve Section Contrib Memory"); - U64 sect_defns_count = main_sect_defns_ht->count; - LNK_SectionDefinition **sect_defns = values_from_hash_table_raw(temp.arena, main_sect_defns_ht); - radsort(sect_defns, sect_defns_count, lnk_section_definition_is_before); - - for (U64 defn_idx = 0; defn_idx < sect_defns_count; defn_idx += 1) { - LNK_SectionDefinition *sect_defn = sect_defns[defn_idx]; - - // do not create definitions for sections that are removed from the image - { - B32 skip = 0; - for (String8Node *name_n = config->remove_sections.first; name_n != 0; name_n = name_n->next) { - if (str8_match(sect_defn->name, name_n->string, 0)) { - skip = 1; - break; - } - } - if (skip) { continue; } - } - - // warn about conflicting section flags - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - LNK_Section *sect = §_n->data; - if (str8_match(sect->name, sect_defn->name, 0)) { - if (sect->flags != sect_defn->flags) { - LNK_Obj *obj = sect_defn->obj; - U32 sect_number = sect_defn->obj_sect_idx + 1; - COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, sect_number); - String8 sect_name = coff_name_from_section_header(str8_substr(obj->data, obj->header.string_table_range), sect_header); - String8 expected_flags_str = coff_string_from_section_flags(temp.arena, sect->flags); - String8 current_flags_str = coff_string_from_section_flags(temp.arena, sect_defn->flags); - lnk_error_obj(LNK_Warning_SectionFlagsConflict, sect_defn->obj, "detected section flags conflict in %S(No. %X); expected {%S} but got {%S}", sect_name, sect_number, expected_flags_str, current_flags_str); - } - } - } - - // reserve chunk for contribs - LNK_Section *sect = lnk_section_table_search(sectab, sect_defn->name, sect_defn->flags); - if (sect == 0) { - sect = lnk_section_table_push(sectab, sect_defn->name, sect_defn->flags); - } - AssertAlways(sect->contribs.chunk_count == 0); - lnk_section_contrib_chunk_list_push_chunk(sectab->arena, §->contribs, sect_defn->contribs_count); - } - ProfEnd(); - - temp_end(temp); - ProfEnd(); - } - - U64 expected_image_header_size; - { - // gather section contribs - LNK_SectionContrib ***sect_map = push_array(scratch.arena, LNK_SectionContrib **, objs_count); - { - U16 default_align = lnk_default_align_from_machine(config->machine); - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; - String8 string_table = str8_substr(obj->data, obj->header.string_table_range); - - sect_map[obj_idx] = push_array(scratch.arena, LNK_SectionContrib *, obj->header.section_count_no_null); - - for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - - if (sect_header->flags & COFF_SectionFlag_LnkRemove) { - continue; - } - - // parse section name - String8 full_sect_name = coff_name_from_section_header(string_table, sect_header); - String8 sect_name, sect_sort_idx; - coff_parse_section_name(full_sect_name, §_name, §_sort_idx); - - // search for section to contribute - COFF_SectionFlags flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; - LNK_Section *sect = lnk_section_table_search(sectab, sect_name, flags); - - LNK_SectionContrib *sc; - if (sect) { - // extract align - U16 sc_align = coff_align_size_from_section_flags(sect_header->flags); - if (sc_align == 0) { - sc_align = default_align; - } - - // extract section bytes - String8 sect_data = str8_substr(obj->data, rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize)); - - String8Node *data_n = push_array(sectab->arena, String8Node, 1); - data_n->string = sect_data; - - // fill out contrib - sc = lnk_section_contrib_chunk_push(sect->contribs.first, 1); - sc->align = sc_align; - sc->data_list = data_n; - sc->u.obj_idx = obj_idx; - sc->u.obj_sect_idx = sect_idx; - sc->u.sort_idx_size = (U16)sect_sort_idx.size; - sc->u.sort_idx = sect_sort_idx.str; - } else { - // section was removed, fill slot with pointer to null contrib - sc = &g_null_sc; - } - - sect_map[obj_idx][sect_idx] = sc; - } - } - } - - // build obj section map - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular && symbol.storage_class == COFF_SymStorageClass_External && symbol.value == 0) { - COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); - if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { - // replace contrib with leader - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - sect_map[obj_idx][symbol.section_number - 1] = sect_map[defn->u.defined.obj->input_idx][defn_symbol.section_number - 1]; - } - } - } - } - - // finalize sections layouts - { - ProfBegin("Finalize Sections Layout"); - - // merge sections - if (config->flags & LNK_ConfigFlag_Merge) { - lnk_section_table_merge(sectab, config->merge_list); - } - - // sort contribs - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - for (LNK_SectionContribChunk *sc_chunk = sect_n->data.contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { - Assert(sc_chunk->count == sc_chunk->cap); - radsort(sc_chunk->v, sc_chunk->count, lnk_section_contrib_ptr_is_before); - } - } - - // assign contribs offsets, sizes, and section indices - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - lnk_finalize_section_layout(sectab, §_n->data, config->file_align); - } - - // remove empty sections - { - String8List empty_sect_list = {0}; - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - LNK_Section *sect = §_n->data; - if (sect->vsize == 0) { - str8_list_push(scratch.arena, &empty_sect_list, sect->name); - } - } - for (String8Node *name_n = empty_sect_list.first; name_n != 0; name_n = name_n->next) { - lnk_section_table_remove(sectab, name_n->string); - } - } - - // assign section indices to sections - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - lnk_assign_section_index(§_n->data, sectab->next_sect_idx++); - } - - // assing layout offsets and sizes to merged sections - for (LNK_SectionNode *sect_n = sectab->merge_list.first; sect_n != 0; sect_n = sect_n->next) { - LNK_Section *sect = §_n->data; - LNK_SectionContrib *first_sc = lnk_get_first_section_contrib(sect); - LNK_SectionContrib *last_sc = lnk_get_last_section_contrib(sect); - LNK_Section *final_sect = lnk_finalized_section_from_id(sectab, sect->merge_id); - sect->voff = final_sect->voff + first_sc->u.off; - sect->vsize = (last_sc->u.off - first_sc->u.off) + last_sc->u.size; - sect->foff = final_sect->foff + first_sc->u.off; - sect->fsize = (last_sc->u.off - first_sc->u.off) + last_sc->u.size; - sect->sect_idx = final_sect->sect_idx; - } - - ProfEnd(); - } - - // build common block - // - // TODO: build common block in .bss and merge with .data - U64 common_block_contribs_count; - LNK_CommonBlockContrib *common_block_contribs; - LNK_Section *common_block_sect; - { - ProfBegin("Build Common Block"); - - ProfBegin("Count Contribs"); - common_block_contribs_count = 0; - for (U64 worker_id = 0; worker_id < tp->worker_count; worker_id += 1) { - for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; - chunk != 0; - chunk = chunk->next) { - for (U64 i = 0; i < chunk->count; i += 1) { - LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); - COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); - if (parsed_interp == COFF_SymbolValueInterp_Common) { - common_block_contribs_count += 1; - } - } - } - } - ProfEnd(); - - ProfBegin("Gather Contribs"); - common_block_contribs = push_array(scratch.arena, LNK_CommonBlockContrib, common_block_contribs_count); - { - U64 cursor = 0; - for (U64 worker_id = 0; worker_id < tp->worker_count; worker_id += 1) { - for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; - chunk != 0; - chunk = chunk->next) { - for (U64 i = 0; i < chunk->count; i += 1) { - LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); - COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); - if (parsed_interp == COFF_SymbolValueInterp_Common) { - LNK_CommonBlockContrib *contrib = &common_block_contribs[cursor++]; - contrib->symbol = chunk->v[i].symbol; - contrib->u.size = parsed_symbol.value; - } - } - } - } - } - ProfEnd(); - - if (common_block_contribs_count) { - ProfBeginV("Assign Common Block Offsets [count %llu]", common_block_contribs_count); - - // search/push .data - common_block_sect = lnk_section_table_search(sectab, str8_lit(".data"), PE_DATA_SECTION_FLAGS); - if (common_block_sect == 0) { - common_block_sect = lnk_section_table_push(sectab, str8_lit(".data"), PE_DATA_SECTION_FLAGS); - } - - // sort common blocks from largest to smallest for tighter packing - radsort(common_block_contribs, common_block_contribs_count, lnk_common_block_contrib_is_before); - - // compute common block offsets - for (U64 contrib_idx = 0; contrib_idx < common_block_contribs_count; contrib_idx += 1) { - LNK_CommonBlockContrib *contrib = &common_block_contribs[contrib_idx]; - U32 size = contrib->u.size; - U32 align = Min(32, u64_up_to_pow2(size)); // link.exe caps align at 32 bytes - common_block_sect->vsize = AlignPow2(common_block_sect->vsize, align); - contrib->u.offset = common_block_sect->vsize; - common_block_sect->vsize += size; - } - ProfEnd(); - } - - ProfEnd(); - } - - // - // section list -> array - // - LNK_SectionArray sects = lnk_section_array_from_list(scratch.arena, sectab->list); - - // patch symbol tables - { - ProfBegin("Patch Symbol Tables"); - - ProfBegin("Patch Common Block Leaders"); - B8 **was_patched = push_array(scratch.arena, B8 *, objs_count); - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - was_patched[obj_idx] = push_array(scratch.arena, B8, objs[obj_idx]->header.symbol_count); - } - - for (U64 contrib_idx = 0; contrib_idx < common_block_contribs_count; contrib_idx += 1) { - LNK_CommonBlockContrib *contrib = &common_block_contribs[contrib_idx]; - LNK_Symbol *symbol = contrib->symbol; - LNK_Obj *obj = symbol->u.defined.obj; - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol->u.defined.symbol_idx); - - was_patched[obj->input_idx][symbol->u.defined.symbol_idx] = 1; - - if (obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = parsed_symbol.raw_symbol; - symbol32->value = contrib->u.offset; - symbol32->section_number = safe_cast_u32(common_block_sect->sect_idx + 1); - } else { - COFF_Symbol16 *symbol16 = parsed_symbol.raw_symbol; - symbol16->value = contrib->u.offset; - symbol16->section_number = safe_cast_u16(common_block_sect->sect_idx + 1); - } - } - ProfEnd(); - - ProfBegin("Patch Regular Symbols"); - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - - if (was_patched[obj_idx][symbol_idx]) { - continue; - } - - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular) { - U32 section_number = 0; - U32 value = 0; - { - COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); - if (~sect_header->flags & COFF_SectionFlag_LnkRemove) { - LNK_SectionContrib *sc = sect_map[obj_idx][symbol.section_number-1]; - section_number = safe_cast_u32(sc->u.sect_idx + 1); - value = sc->u.off + symbol.value; - } - } - - if (obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = symbol.raw_symbol; - symbol32->section_number = section_number; - symbol32->value = value; - } else { - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - symbol16->section_number = safe_cast_u16(section_number); - symbol16->value = value; - } - } - } - } - ProfEnd(); - - ProfBegin("Patch common blocks"); - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - - if (interp == COFF_SymbolValueInterp_Common) { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - if (defn) { - LNK_SectionContrib *sc = sect_map[defn->u.defined.obj->input_idx][defn_parsed.section_number-1]; - if (obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = symbol.raw_symbol; - symbol32->section_number = safe_cast_u32(sc->u.sect_idx + 1); - symbol32->value = safe_cast_u32(sc->u.off); - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - symbol16->section_number = safe_cast_u16(sc->u.sect_idx + 1); - symbol16->value = safe_cast_u32(sc->u.off); - symbol16->storage_class = COFF_SymStorageClass_Static; - } - } - } - } - } - ProfEnd(); - - ProfBegin("Patch Abs"); - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - - if (interp == COFF_SymbolValueInterp_Abs && symbol.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); - - if (defn == 0) { - continue; - } - if (defn->u.defined.obj == obj && defn->u.defined.symbol_idx == symbol_idx) { - continue; - } - - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); - if (defn_interp == COFF_SymbolValueInterp_Regular) { - if (defn->u.defined.obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = symbol.raw_symbol; - symbol32->section_number = defn_symbol.section_number; - symbol32->value = defn_symbol.value; - symbol32->type = defn_symbol.type; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - symbol16->section_number = defn_symbol.section_number; - symbol16->value = defn_symbol.value; - symbol16->type = defn_symbol.type; - symbol16->storage_class = COFF_SymStorageClass_Static; - } - } else { - InvalidPath; - } - } - } - } - ProfEnd(); - - // patch undefined symbols - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Undefined) { - if (symbol.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); - if (defn) { - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - - if (defn_symbol.storage_class == COFF_SymStorageClass_WeakExternal) { - continue; - } - - lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); - } else { - // TODO: collect unresolved undefined - } - } - } - } - } - - // patch weak symbols with strong definition - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Weak) { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol.name); - if (defn) { - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); - if (defn_interp != COFF_SymbolValueInterp_Weak) { - lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); - } - } - } - } - } - - // patch weak with fallback definition - { - HashTable *visited_symbols_ht = hash_table_init(scratch.arena, 32); - struct LookupLocation { - struct LookupLocation *next; - LNK_Obj *obj; - U64 symbol_idx; - }; - struct LookupLocation *lookup_first = 0; - struct LookupLocation *lookup_last = 0; - struct LookupLocation *lookup_free_list = 0; - - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Undefined && symbol.storage_class == COFF_SymStorageClass_External) { - String8 lookup_name = symbol.name; - LNK_Obj *lookup_obj = obj; - U64 lookup_symbol_idx = symbol_idx; - for (;;) { - // lookup definition - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, lookup_name); - if (defn == 0) { - break; - } - - // not external symbol? patch and move to next symbol - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - if (defn_parsed.storage_class != COFF_SymStorageClass_WeakExternal) { - lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_parsed); - break; - } - - // check against cyclic refs - struct LookupLocation *was_visited = 0; - hash_table_search_string_raw(visited_symbols_ht, lookup_name, &was_visited); - if (was_visited != 0) { - Temp temp = temp_begin(scratch.arena); - - String8List list = {0}; - for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { - COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->obj, l->symbol_idx); - str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx) =>", l->obj->path, loc_symbol.name, l->symbol_idx); - } - { - COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(was_visited->obj, was_visited->symbol_idx); - str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx)", was_visited->obj->path, loc_symbol.name, was_visited->symbol_idx); - } - - String8 loc_string = str8_list_join(temp.arena, &list, &(StringJoin){.sep = str8_lit("\n") }); - lnk_error_obj(LNK_Error_WeakCycle, obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", symbol.name, loc_string); - - temp_end(temp); - break; - } - struct LookupLocation *loc = lookup_free_list; - if (lookup_free_list) { - SLLStackPop(lookup_free_list); - } else { - loc = push_array(scratch.arena, struct LookupLocation, 1); - } - loc->obj = lookup_obj; - loc->symbol_idx = symbol_idx; - SLLQueuePush(lookup_first, lookup_last, loc); - hash_table_push_string_raw(scratch.arena, visited_symbols_ht, lookup_name, loc); - - // fallback to weak tag for definition - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(defn_parsed, defn->u.defined.obj->header.is_big_obj); - COFF_ParsedSymbol parsed_tag = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, weak_ext->tag_index); - lookup_name = parsed_tag.name; - lookup_obj = defn->u.defined.obj; - lookup_symbol_idx = weak_ext->tag_index; - } - - hash_table_purge(visited_symbols_ht); - lookup_free_list = lookup_first; - lookup_first = 0; - lookup_last = 0; - } - } - } - } - - // patch weak symbols which have undefined tag - { - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Weak) { - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol, obj->header.is_big_obj); - AssertAlways(weak_ext->tag_index < symbol_idx); - - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, weak_ext->tag_index); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); - if (defn_interp != COFF_SymbolValueInterp_Undefined) { - lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); - } - } - } - } - } - - ProfEnd(); - } - - expected_image_header_size = lnk_compute_win32_image_header_size(config, sects.count); - - // assign virtual space - U64 voff_cursor = AlignPow2(expected_image_header_size + sizeof(COFF_SectionHeader), config->sect_align); - for (U64 i = 0; i < sects.count; i += 1) { - lnk_assign_section_virtual_space(sects.v[i], config->sect_align, &voff_cursor); - } - - // patch virtual offset and size in section headers - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; - for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - if (~sect_header->flags & COFF_SectionFlag_LnkRemove) { - LNK_SectionContrib *sc = sect_map[obj_idx][sect_idx]; - LNK_Section *sect = sects.v[sc->u.sect_idx]; - sect_header->vsize = sc->u.size; - sect_header->voff = sect->voff + sc->u.off; - } - } - } - - // build base relocs - if (~config->flags & LNK_ConfigFlag_Fixed) { - String8List base_relocs_data = lnk_build_base_relocs(tp, arena, config, objs_count, objs); - if (base_relocs_data.total_size) { - LNK_Section *reloc = lnk_section_table_push(sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS); - LNK_SectionContribChunk *first_sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &reloc->contribs, 1); - LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(first_sc_chunk, 1); - sc->data_list = base_relocs_data.first; - sc->align = 1; - sc->u.sort_idx_size = 0; - sc->u.obj_idx = max_U32; - - lnk_finalize_section_layout(sectab, reloc, config->file_align); - lnk_assign_section_virtual_space(reloc, config->sect_align, &voff_cursor); - lnk_assign_section_index(reloc, sectab->next_sect_idx++); - - sects = lnk_section_array_from_list(scratch.arena, sectab->list); - expected_image_header_size = lnk_compute_win32_image_header_size(config, sects.count); - } - } - - // assign file space - U64 foff_cursor = AlignPow2(expected_image_header_size, config->file_align); - for (U64 i = 0; i < sects.count; i += 1) { - lnk_assign_section_file_space(sects.v[i], &foff_cursor); - } - - // patch file offset and size in section headers - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; - for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - B32 patch_section_header = (~sect_header->flags & COFF_SectionFlag_LnkRemove) && - !lnk_is_coff_section_debug(obj, sect_idx); - if (patch_section_header) { - LNK_SectionContrib *sc = sect_map[obj_idx][sect_idx]; - LNK_Section *sect = sects.v[sc->u.sect_idx]; - if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { - sect_header->fsize = sc->u.size; - sect_header->foff = sect->foff + sc->u.off; - } - } - } - } - } - - // build win32 image header - LNK_SectionArray sects; - { - sects = lnk_section_array_from_list(scratch.arena, sectab->list); - String8List image_header_data = lnk_build_win32_header(scratch.arena, symtab, config, sects, AlignPow2(expected_image_header_size, config->file_align)); - - LNK_Section *image_header_sect = lnk_section_table_push(sectab, str8_lit(".rad_linker_image_header_section"), 0); - LNK_SectionContribChunk *image_header_sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &image_header_sect->contribs, 1); - LNK_SectionContrib *image_header_sc = lnk_section_contrib_chunk_push(image_header_sc_chunk, 1); - - image_header_sc->align = config->file_align; - image_header_sc->data_list = image_header_data.first; - image_header_sc->u.size = safe_cast_u32(image_header_data.total_size); - - lnk_finalize_section_layout(sectab, image_header_sect, config->file_align); - - sects = lnk_section_array_from_list(scratch.arena, sectab->list); - } - - // patch section symbols - { - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Undefined) { - if (symbol.storage_class == COFF_SymStorageClass_Section) { - LNK_Section *sect = lnk_section_table_search(sectab, symbol.name, symbol.value); - if (sect) { - if (~sect->flags & COFF_SectionFlag_MemDiscardable) { - LNK_SectionContrib *first_sc = lnk_get_first_section_contrib(sect); - if (obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = symbol.raw_symbol; - symbol32->section_number = safe_cast_u32(first_sc->u.sect_idx + 1); - symbol32->value = first_sc->u.off; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - symbol16->section_number = safe_cast_u16(first_sc->u.sect_idx + 1); - symbol16->value = first_sc->u.off; - symbol16->storage_class = COFF_SymStorageClass_Static; - } - } else { - lnk_error_obj(LNK_Error_SectRefsDiscardedMemory, obj, "symbol %S (No. 0x%llx) references section with discard flag", symbol.name, symbol_idx); - } - } else { - lnk_error_obj(LNK_Error_UnresolvedSymbol, obj, "undefined section symbol %S (No 0x%llx) refers to an image section that doesn't exist", symbol.name, symbol_idx); - } - } - } - } - } - } - - String8 image_data = {0}; - { - ProfBegin("Image Fill"); - - U64 image_size = 0; - for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { - image_size += sects.v[sect_idx]->fsize; - } - - image_data.size = image_size; - image_data.str = push_array_no_zero(arena->v[0], U8, image_size); - - for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { - LNK_Section *sect = sects.v[sect_idx]; - - if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { - // pick fill pick - U8 fill_byte = 0; - if (sect->flags & COFF_SectionFlag_CntCode) { - fill_byte = lnk_code_align_byte_from_machine(config->machine); - } - - // copy section contribution - U64 prev_sc_opl = 0; - for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { - for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { - LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; - - // fill align bytes - Assert(sc->u.off >= prev_sc_opl); - U64 fill_size = sc->u.off - prev_sc_opl; - MemorySet(image_data.str + sect->foff + prev_sc_opl, fill_byte, fill_size); - prev_sc_opl = sc->u.off + sc->u.size; - - // copy contrib contents - { - U64 cursor = 0; - for (String8Node *data_n = sc->data_list; data_n != 0; data_n = data_n->next) { - Assert(sc->u.off + data_n->string.size <= sect->vsize); - MemoryCopy(image_data.str + sect->foff + sc->u.off + cursor, data_n->string.str, data_n->string.size); - cursor += data_n->string.size; - } - } - } - } - - // fill section align bytes - { - U64 fill_size = sect->fsize - prev_sc_opl; - MemorySet(image_data.str + sect->foff + prev_sc_opl, fill_byte, fill_size); - } - } - } - - ProfEnd(); - } - - { - ProfBegin("Image Patch"); - - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data); - COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range); - - // patch relocs - { - ProfBegin("Patch Relocs"); - LNK_ObjRelocPatcher task = {0}; - task.image_data = image_data; - task.objs = objs; - task.image_base = pe.image_base; - task.image_section_table = image_section_table; - tp_for_parallel(tp, 0, objs_count, lnk_obj_reloc_patcher, &task); - ProfEnd(); - } - - // patch load config - { - LNK_Symbol *load_config_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME)); - if (load_config_symbol) { - U64 load_config_foff = lnk_file_off_from_symbol(image_section_table, load_config_symbol); - String8 load_config_data = str8_skip(image_data, load_config_foff); - - U32 load_config_size = 0; - if (sizeof(load_config_size) <= load_config_data.size) { - PE_DataDirectory *load_config_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_LOAD_CONFIG); - load_config_dir->virt_off = lnk_virt_off_from_symbol(image_section_table, load_config_symbol); - load_config_dir->virt_size = load_config_size; - } else { - // TODO: report corrupted load config - } - } - } - - // patch exceptions - { - LNK_Section *pdata_sect = lnk_section_table_search(sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS); - if (pdata_sect) { - String8 raw_pdata = str8_substr(image_data, rng_1u64(pdata_sect->foff, pdata_sect->foff + pdata_sect->vsize)); - pe_pdata_sort(config->machine, raw_pdata); - - PE_DataDirectory *pdata_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_EXCEPTIONS); - pdata_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, pdata_sect); - pdata_dir->virt_size = lnk_get_section_contrib_size(pdata_sect); - } - } - - // patch export - { - LNK_Section *edata_sect = lnk_section_table_search(sectab, str8_lit(".edata"), PE_EDATA_SECTION_FLAGS); - if (edata_sect) { - PE_DataDirectory *export_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_EXPORT); - LNK_SectionContrib *edata_first_contrib = lnk_get_first_section_contrib(edata_sect); - LNK_SectionContrib *edata_last_contrib = lnk_get_last_section_contrib(edata_sect); - export_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, edata_sect); - export_dir->virt_size = lnk_get_section_contrib_size(edata_sect); - } - } - - // patch base relocs - { - LNK_Section *reloc_sect = lnk_section_table_search(sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS); - if (reloc_sect) { - PE_DataDirectory *reloc_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_BASE_RELOC); - reloc_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, reloc_sect); - reloc_dir->virt_size = lnk_get_section_contrib_size(reloc_sect); - } - } - - // patch import and import addr - { - LNK_Section *idata_sect = lnk_section_table_search(sectab, str8_lit(".idata"), PE_IDATA_SECTION_FLAGS); - LNK_Symbol *null_import_desc = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "__NULL_IMPORT_DESCRIPTOR"); - LNK_Symbol *null_thunk_data = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "\x7f%S_NULL_THUNK_DATA", lnk_get_image_name(config)); - if (idata_sect && null_import_desc && null_thunk_data) { - COFF_ParsedSymbol null_import_desc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_import_desc->u.defined.obj, null_import_desc->u.defined.symbol_idx); - LNK_SectionContrib *idata_first_contrib = lnk_get_first_section_contrib(idata_sect); - PE_DataDirectory *import_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_IMPORT); - import_dir->virt_off = image_section_table[idata_first_contrib->u.sect_idx + 1]->voff + idata_first_contrib->u.off; - import_dir->virt_size = null_import_desc_parsed.value - idata_first_contrib->u.off; - - COFF_ParsedSymbol null_thunk_data_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_thunk_data->u.defined.obj, null_thunk_data->u.defined.symbol_idx); - U64 null_thunk_data_voff = image_section_table[null_thunk_data_parsed.section_number]->voff + null_thunk_data_parsed.value; - U64 first_import_foff = image_section_table[idata_first_contrib->u.sect_idx+1]->foff + idata_first_contrib->u.off; - PE_ImportEntry *first_import = str8_deserial_get_raw_ptr(image_data, first_import_foff, sizeof(*first_import)); - PE_DataDirectory *import_addr_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_IMPORT_ADDR); - import_addr_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, idata_sect); - import_addr_dir->virt_size = null_thunk_data_voff - first_import->import_addr_table_voff /* null */ + coff_word_size_from_machine(config->machine); - } - } - - // patch delay imports - { - LNK_Section *didat_sect = lnk_section_table_search(sectab, str8_lit(".didat"), PE_IDATA_SECTION_FLAGS); - LNK_Symbol *null_import_desc = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("__NULL_DELAY_IMPORT_DESCRIPTOR")); - LNK_Symbol *last_null_thunk = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "\x7f%S_NULL_THUNK_DATA_DLA", lnk_get_image_name(config)); - if (didat_sect && null_import_desc && last_null_thunk) { - COFF_ParsedSymbol null_import_desc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_import_desc->u.defined.obj, null_import_desc->u.defined.symbol_idx); - LNK_SectionContrib *didat_first_contrib = lnk_get_first_section_contrib(didat_sect); - PE_DataDirectory *import_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_DELAY_IMPORT); - import_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, didat_sect); - import_dir->virt_size = lnk_get_section_contrib_size(didat_sect); - } - } - - // patch TLS - { - LNK_Symbol *tls_used_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, MSCRT_TLS_SYMBOL_NAME); - if (tls_used_symbol) { - ProfBegin("Patch TLS"); - - // find max align in .tls - U64 tls_align = 0; - LNK_Section *tls_sect = lnk_section_table_search(sectab, str8_lit(".tls"), PE_TLS_SECTION_FLAGS); - for (LNK_SectionContribChunk *sc_chunk = tls_sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { - for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { - Assert(IsPow2(sc_chunk->v[sc_idx]->align)); - tls_align = Max(tls_align, sc_chunk->v[sc_idx]->align); - } - } - - // patch-in align - U64 tls_header_foff = lnk_file_off_from_symbol(image_section_table, tls_used_symbol); - B32 is_tls_header64 = coff_word_size_from_machine(config->machine) == 8; - if (is_tls_header64) { - PE_TLSHeader64 *tls_header = str8_deserial_get_raw_ptr(image_data, tls_header_foff, sizeof(*tls_header)); - tls_header->characteristics |= coff_section_flag_from_align_size(tls_align); - } else { - PE_TLSHeader32 *tls_header = str8_deserial_get_raw_ptr(image_data, tls_header_foff, sizeof(*tls_header)); - tls_header->characteristics |= coff_section_flag_from_align_size(tls_align); - } - - // patch directory - PE_DataDirectory *tls_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_TLS); - tls_dir->virt_off = lnk_virt_off_from_symbol(image_section_table, tls_used_symbol); - tls_dir->virt_size = is_tls_header64 ? sizeof(PE_TLSHeader64) : sizeof(PE_TLSHeader32); - - ProfEnd(); - } - } - - // patch debug - { - LNK_Section *debug_dir_sect = lnk_section_table_search(sectab, str8_lit(".RAD_LINK_PE_DEBUG_DIR"), PE_RDATA_SECTION_FLAGS); - if (debug_dir_sect) { - // patch directory - PE_DataDirectory *debug_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_DEBUG); - debug_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, debug_dir_sect); - debug_dir->virt_size = lnk_get_section_contrib_size(debug_dir_sect); - - // find debug directory begin and end pair - LNK_SectionContrib *first_sc = lnk_get_first_section_contrib(debug_dir_sect); - LNK_SectionContrib *last_sc = lnk_get_last_section_contrib(debug_dir_sect); - U64 debug_begin_foff = lnk_foff_from_section_contrib(image_section_table, first_sc); - U64 debug_end_fopl = lnk_fopl_from_section_contrib(image_section_table, last_sc); - - // patch file offsets to the debug directories - for (U64 cursor = debug_begin_foff; cursor + sizeof(PE_DebugDirectory) <= debug_end_fopl; cursor += sizeof(PE_DebugDirectory)) { - PE_DebugDirectory *dir = str8_deserial_get_raw_ptr(image_data, cursor, sizeof(PE_DebugDirectory)); - for (U64 section_number = 1; section_number < pe.section_count+1; section_number += 1) { - if (image_section_table[section_number]->voff <= dir->voff && dir->voff < image_section_table[section_number]->voff + image_section_table[section_number]->vsize) { - dir->foff = image_section_table[section_number]->foff + (dir->voff - image_section_table[section_number]->voff); - } - } - } - } - } - - // patch resources - { - LNK_Section *rsrc_sect = lnk_section_table_search(sectab, str8_lit(".rsrc"), PE_RSRC_SECTION_FLAGS); - if (rsrc_sect) { - PE_DataDirectory *rsrc_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_RESOURCES); - rsrc_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, rsrc_sect); - rsrc_dir->virt_size = lnk_get_section_contrib_size(rsrc_sect); - } - } - - // image checksum - if (config->flags & LNK_ConfigFlag_WriteImageChecksum) { - ProfBegin("Image Checksum"); - *pe.check_sum = pe_compute_checksum(image_data.str, image_data.size); - ProfEnd(); - } - - // compute image guid, and patch PDB and RDI guids - { - LNK_Symbol *guid_pdb_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINK_PE_DEBUG_GUID_PDB")); - LNK_Symbol *guid_rdi_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINK_PE_DEBUG_GUID_RDI")); - - if (guid_pdb_symbol || guid_rdi_symbol) { - switch (config->guid_type) { - case LNK_DebugInfoGuid_Null: break; - case Lnk_DebugInfoGuid_ImageBlake3: { - ProfBegin("Hash Image With Blake3"); - U128 hash = lnk_blake3_hash_parallel(tp, 128, image_data); - MemoryCopy(&config->guid, hash.u8, sizeof(hash.u8)); - ProfEnd(); - } break; - } - } - - if (guid_pdb_symbol) { - U64 cv_guid_foff = lnk_file_off_from_symbol(image_section_table, guid_pdb_symbol); - Guid *cv_guid = str8_deserial_get_raw_ptr(image_data, cv_guid_foff, sizeof(*cv_guid)); - *cv_guid = config->guid; - } - - if (guid_rdi_symbol) { - U64 cv_guid_foff = lnk_file_off_from_symbol(image_section_table, guid_rdi_symbol); - Guid *cv_guid = str8_deserial_get_raw_ptr(image_data, cv_guid_foff, sizeof(*cv_guid)); - *cv_guid = config->guid; - } - } - - ProfEnd(); - } - - LNK_ImageContext image_ctx = {0}; - image_ctx.image_data = image_data; - image_ctx.sectab = sectab; - - lnk_timer_end(LNK_Timer_Image); - ProfEnd(); // :EndImage - scratch_end(scratch); - return image_ctx; -} - -//////////////////////////////// - internal THREAD_POOL_TASK_FUNC(lnk_undef_symbol_finder) { @@ -3410,140 +1221,6 @@ lnk_run_symbol_finder(TP_Context *tp, return result; } -//////////////////////////////// - -internal String8List -lnk_build_rad_map(Arena *arena, String8 image_data, U64 thread_count, U64 objs_count, LNK_Obj **objs, LNK_LibList lib_index[LNK_InputSource_Count], LNK_SectionTable *sectab) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data); - COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range); - - String8List map = {0}; - - ProfBegin("SECTIONS"); - str8_list_pushf(arena, &map, "# SECTIONS\n"); - for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - LNK_Section *sect = §_n->data; - - str8_list_pushf(arena, &map, "%S\n", sect->name); - str8_list_pushf(arena, &map, "%-8s %-8s %-8s %-8s %-16s %-8s %s\n", "FileOff", "VirtOff", "VirtSize", "FileSize", "Blake3", "SC", "Source"); - - for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { - for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { - Temp temp = temp_begin(scratch.arena); - LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; - - U64 file_off = image_section_table[sc->u.sect_idx]->foff + sc->u.off; - U64 virt_off = image_section_table[sc->u.sect_idx]->voff + sc->u.off; - U64 virt_size = sc->u.size; - U64 file_size = sc->u.size; - String8 sc_data = str8_substr(image_data, rng_1u64(file_off, file_off + virt_size)); - - U128 sc_hash = {0}; - if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { - blake3_hasher hasher; blake3_hasher_init(&hasher); - blake3_hasher_update(&hasher, sc_data.str, sc_data.size); - blake3_hasher_finalize(&hasher, (U8 *)&sc_hash, sizeof(sc_hash)); - } - - String8 file_off_str = push_str8f(temp.arena, "%08x", file_off); - String8 virt_off_str = push_str8f(temp.arena, "%08x", virt_off); - String8 virt_size_str = push_str8f(temp.arena, "%08x", virt_size); - String8 file_size_str = push_str8f(temp.arena, "%08x", file_size); - String8 sc_hash_str = push_str8f(temp.arena, "%08x%08x", sc_hash.u64[0], sc_hash.u64[1]); - String8 sc_idx_str = push_str8f(temp.arena, "%llx", sc_idx); - String8 source_str; - { - String8List source_list = {0}; - -#if 0 - // location - if (chunk->obj) { - if (chunk->obj->lib_path.size) { - String8 lib_name = chunk->obj->lib_path; - lib_name = str8_skip_last_slash(lib_name); - lib_name = str8_chop_last_dot(lib_name); - - String8 obj_name = chunk->obj->path; - obj_name = str8_skip_last_slash(obj_name); - - str8_list_pushf(temp.arena, &source_list, "%S:%S", lib_name, obj_name); - } else { - str8_list_push(temp.arena, &source_list, chunk->obj->path); - } - } -#else - str8_list_pushf(temp.arena, &source_list, ""); -#endif - - // string join - source_str = str8_list_join(temp.arena, &source_list, &(StringJoin){.sep=str8_lit(" ")}); - } - - str8_list_pushf(arena, &map, "%-8S %-8S %-8S %-8S %-16S %-8S %S\n", file_off_str, virt_off_str, virt_size_str, file_size_str, sc_hash, sc_idx_str, source_str); - - temp_end(temp); - } - } - str8_list_pushf(arena, &map, "\n"); - } - ProfEnd(); - - - ProfBegin("SYMBOLS"); - str8_list_pushf(arena, &map, "# SYMBOLS\n"); - str8_list_pushf(arena, &map, "%-8s %s\n", "Sect:Idx", "Symbol"); - for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { - LNK_Obj *obj = objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular) { - String8 sc = push_str8f(scratch.arena, "%x:%x", symbol.section_number, symbol.value); - - String8 lib_name = obj->lib_path; - lib_name = str8_skip_last_slash(lib_name); - lib_name = str8_chop_last_dot(lib_name); - - String8 obj_name = obj->path; - obj_name = str8_skip_last_slash(obj_name); - - str8_list_pushf(arena, &map, "%-8S (%S%s%S) %S\n", - sc, - lib_name, lib_name.size ? ":" : "", obj_name, - symbol.name); - } - } - } - str8_list_pushf(arena, &map, "\n"); - ProfEnd(); - - - ProfBegin("LIBS"); - for (U64 input_source = 0; input_source < LNK_InputSource_Count; ++input_source) { - if (lib_index[input_source].count) { - str8_list_pushf(arena, &map, "# LIBS (%S)\n", lnk_string_from_input_source(input_source)); - for (LNK_LibNode *lib_n = lib_index[input_source].first; lib_n != 0; lib_n = lib_n->next) { - str8_list_pushf(arena, &map, "%S\n", lib_n->data.path); - } - } - } - ProfEnd(); - - - scratch_end(scratch); - ProfEnd(); - return map; -} - -//////////////////////////////// - internal LNK_LinkContext lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) { @@ -4448,6 +2125,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) } { + ProfBegin("Build * Debug Directories *"); if (config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null) { LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); input->path = str8_lit("* Debug Directory PDB *"); @@ -4557,6 +2235,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) link_ctx.export_symbol_list = export_symbol_list; MemoryCopyTyped(&link_ctx.lib_index[0], &lib_index[0], ArrayCount(lib_index)); + ProfEnd(); scratch_end(scratch); return link_ctx; @@ -4564,6 +2243,2491 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) #undef state_list_pop } +internal +THREAD_POOL_TASK_FUNC(lnk_remove_associative_sections_task) +{ + LNK_BuildImageTask *task = raw_task; + + LNK_Obj *obj = task->objs[task_id]; + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + String8 symbol_table = str8_substr(obj->data, obj->header.symbol_table_range); + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + // find associate section head section index + U32 head_sect_idx = max_U32; + for (U64 current_sect_idx = sect_idx;;) { + U32 symbol_idx = obj->comdats[current_sect_idx]; + if (symbol_idx == max_U32) { + break; + } + + COFF_ParsedSymbol symbol = coff_parse_symbol(obj->header, string_table, symbol_table, symbol_idx); + COFF_ComdatSelectType selection = COFF_ComdatSelect_Null; + U32 section_number = 0; + coff_parse_secdef(symbol, obj->header.is_big_obj, &selection, §ion_number, 0, 0); + if (selection != COFF_ComdatSelect_Associative) { + head_sect_idx = current_sect_idx; + break; + } + + current_sect_idx = section_number-1; + } + + if (head_sect_idx != max_U32) { + // flag current section with remove if head section was removed + COFF_SectionHeader *head_sect_header = lnk_coff_section_header_from_section_number(obj, head_sect_idx+1); + COFF_SectionHeader *curr_sect_header = lnk_coff_section_header_from_section_number(obj, sect_idx+1); + curr_sect_header->flags |= (head_sect_header->flags & COFF_SectionFlag_LnkRemove); + } + } +} + +internal +THREAD_POOL_TASK_FUNC(lnk_gather_section_definitions_task) +{ + LNK_BuildImageTask *task = raw_task; + + HashTable *sect_defn_ht = task->u.gather_sects.defns[worker_id]; + LNK_Obj *obj = task->objs[task_id]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + + // remove section + if (sect_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + + // parse section name + String8 full_sect_name = coff_name_from_section_header(string_table, sect_header); + String8 sect_name, sect_sort_idx; + coff_parse_section_name(full_sect_name, §_name, §_sort_idx); + + // was section defined? + COFF_SectionFlags sect_flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; + String8 sect_name_with_flags = lnk_make_name_with_flags(arena, sect_name, sect_flags); + LNK_SectionDefinition *sect_defn = 0; + hash_table_search_string_raw(sect_defn_ht, sect_name_with_flags, §_defn); + + // create section definition + if (sect_defn == 0) { + sect_defn = push_array(arena, LNK_SectionDefinition, 1); + sect_defn->name = sect_name; + sect_defn->flags = sect_flags; + sect_defn->obj = obj; + sect_defn->obj_sect_idx = sect_idx; + hash_table_push_string_raw(arena, sect_defn_ht, sect_name_with_flags, sect_defn); + } + + // acc contrib count + sect_defn->contribs_count += 1; + } +} + +internal +THREAD_POOL_TASK_FUNC(lnk_gather_section_contribs_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + + LNK_Obj *obj = task->objs[obj_idx]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + + ProfBeginV("Gather Section Contribs [%S]", obj->path); + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + + if (sect_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + + // parse section name + String8 full_sect_name = coff_name_from_section_header(string_table, sect_header); + String8 sect_name, sect_sort_idx; + coff_parse_section_name(full_sect_name, §_name, §_sort_idx); + + // search for section to contribute + COFF_SectionFlags flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; + LNK_Section *sect = lnk_section_table_search(task->sectab, sect_name, flags); + + LNK_SectionContrib *sc; + if (sect) { + // extract align + U16 sc_align = coff_align_size_from_section_flags(sect_header->flags); + if (sc_align == 0) { + sc_align = task->default_align; + } + + // extract section bytes + String8 sect_data = str8_substr(obj->data, rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize)); + + // fill out contrib + sc = lnk_section_contrib_chunk_push_atomic(sect->contribs.first, 1); + + sc->node.next = 0; + sc->node.string = sect_data; + sc->data_list = &sc->node; + sc->align = sc_align; + sc->u.obj_idx = obj_idx; + sc->u.obj_sect_idx = sect_idx; + sc->u.sort_idx_size = (U16)sect_sort_idx.size; + sc->u.sort_idx = sect_sort_idx.str; + } else { + // section was removed, fill slot with pointer to null contrib + sc = &g_null_sc; + } + + task->sect_map[obj_idx][sect_idx] = sc; + } + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("Set COMDAT Leaders [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular && symbol.storage_class == COFF_SymStorageClass_External && symbol.value == 0) { + COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { + LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + task->sect_map[obj_idx][symbol.section_number - 1] = task->sect_map[defn->u.defined.obj->input_idx][defn_symbol.section_number - 1]; + } + } + } + ProfEnd(); +} + +internal int +lnk_section_contrib_ptr_is_before(void *raw_a, void *raw_b) +{ + // Grouped Sections (PE Format) + // "All contributions with the same object-section name are allocated contiguously in the image, + // and the blocks of contributions are sorted in lexical order by object-section name." + LNK_SectionContrib *a = *(LNK_SectionContrib **)raw_a; + LNK_SectionContrib *b = *(LNK_SectionContrib **)raw_b; + + int cmp; + + if (a->u.sort_idx_size <= 1 && b->u.sort_idx_size <= 1) { + if (a->u.sort_idx_size == b->u.sort_idx_size) { + cmp = u32_compar(&a->u.obj_idx, &b->u.obj_idx); + if (cmp == 0) { + cmp = u32_compar(&a->u.obj_sect_idx, &b->u.obj_sect_idx); + } + } else { + // place sections without sort postfix first + cmp = a->u.sort_idx_size < b->u.sort_idx_size; + } + } else { + // sort on section postfix + String8 a_sort_idx = str8(a->u.sort_idx, a->u.sort_idx_size); + String8 b_sort_idx = str8(b->u.sort_idx, b->u.sort_idx_size); + cmp = str8_compar_case_sensitive(&a_sort_idx, &b_sort_idx); + + // sort on obj position on command line + if (cmp == 0) { + cmp = u32_compar(&a->u.obj_idx, &b->u.obj_idx); + + // sort on section index + if (cmp == 0) { + cmp = u32_compar(&a->u.obj_sect_idx, &b->u.obj_sect_idx); + } + } + } + + int is_before = cmp < 0; + return is_before; +} + +internal +THREAD_POOL_TASK_FUNC(lnk_sort_contribs_task) +{ + LNK_BuildImageTask *task = raw_task; + LNK_SectionContribChunk *chunk = task->u.sort_contribs.chunks[task_id]; + Assert(chunk->count == chunk->cap); + radsort(chunk->v, chunk->count, lnk_section_contrib_ptr_is_before); +} + +internal int +lnk_common_block_contrib_is_before(void *raw_a, void *raw_b) +{ + LNK_CommonBlockContrib *a = raw_a; + LNK_CommonBlockContrib *b = raw_b; + + int is_before; + if (a->u.size == b->u.size) { + LNK_Symbol *a_symbol = a->symbol; + LNK_Symbol *b_symbol = b->symbol; + if (a_symbol->u.defined.obj->input_idx == b_symbol->u.defined.obj->input_idx) { + is_before = a_symbol->u.defined.symbol_idx < b_symbol->u.defined.symbol_idx; + } else { + is_before = a_symbol->u.defined.obj->input_idx < b_symbol->u.defined.obj->input_idx; + } + } else { + is_before = a->u.size > b->u.size; + } + + return is_before; +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_common_block_leaders_task) +{ + ProfBeginFunction(); + + LNK_BuildImageTask *task = raw_task; + Rng1U64 contrib_range = task->u.patch_symtabs.ranges[task_id]; + + for (U64 contrib_idx = contrib_range.min; contrib_idx < contrib_range.max; contrib_idx += 1) { + LNK_CommonBlockContrib *contrib = &task->u.patch_symtabs.common_block_contribs[contrib_idx]; + LNK_Symbol *symbol = contrib->symbol; + LNK_Obj *obj = symbol->u.defined.obj; + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol->u.defined.symbol_idx); + U64 section_number = task->u.patch_symtabs.common_block_sect->sect_idx + 1; + + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = parsed_symbol.raw_symbol; + symbol32->value = contrib->u.offset; + symbol32->section_number = safe_cast_u32(section_number); + } else { + COFF_Symbol16 *symbol16 = parsed_symbol.raw_symbol; + symbol16->value = contrib->u.offset; + symbol16->section_number = safe_cast_u16(section_number); + } + + task->u.patch_symtabs.was_symbol_patched[obj->input_idx][symbol->u.defined.symbol_idx] = 1; + } + + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_regular_symbols_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBegin("Patch Regular Symbols [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + + if (task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx]) { + continue; + } + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + U32 section_number = 0; + U32 value = 0; + { + COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + if (~sect_header->flags & COFF_SectionFlag_LnkRemove) { + LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; + section_number = safe_cast_u32(sc->u.sect_idx + 1); + value = sc->u.off + symbol.value; + } + } + + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = section_number; + symbol32->value = value; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = safe_cast_u16(section_number); + symbol16->value = value; + } + } + } + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_common_block_symbols_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("Patch Common Block Symbols [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + + if (interp == COFF_SymbolValueInterp_Common) { + LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); + COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + if (defn) { + LNK_SectionContrib *sc = task->sect_map[defn->u.defined.obj->input_idx][defn_parsed.section_number-1]; + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = safe_cast_u32(sc->u.sect_idx + 1); + symbol32->value = safe_cast_u32(sc->u.off); + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = safe_cast_u16(sc->u.sect_idx + 1); + symbol16->value = safe_cast_u32(sc->u.off); + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } + } + } + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_abs_symbols_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("Patch Absolute Symbols [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + + if (interp == COFF_SymbolValueInterp_Abs && symbol.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); + + if (defn == 0) { + continue; + } + if (defn->u.defined.obj == obj && defn->u.defined.symbol_idx == symbol_idx) { + continue; + } + + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); + if (defn_interp == COFF_SymbolValueInterp_Regular) { + if (defn->u.defined.obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = defn_symbol.section_number; + symbol32->value = defn_symbol.value; + symbol32->type = defn_symbol.type; + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = defn_symbol.section_number; + symbol16->value = defn_symbol.value; + symbol16->type = defn_symbol.type; + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } else { + InvalidPath; + } + } + } + ProfEnd(); +} + +internal void +lnk_patch_weak_external_symbol(B32 is_big_obj, void *symbol, COFF_ParsedSymbol parsed_symbol) +{ + COFF_SymbolValueInterpType parsed_symbol_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); + switch (parsed_symbol_interp) { + case COFF_SymbolValueInterp_Regular: { + if (is_big_obj) { + COFF_Symbol32 *symbol32 = symbol; + symbol32->section_number = parsed_symbol.section_number; + symbol32->value = parsed_symbol.value; + symbol32->type = parsed_symbol.type; + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol; + symbol16->section_number = safe_cast_u16(parsed_symbol.section_number); + symbol16->value = parsed_symbol.value; + symbol16->type = parsed_symbol.type; + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } break; + case COFF_SymbolValueInterp_Common: { + InvalidPath; + } break; + case COFF_SymbolValueInterp_Abs: { + if (is_big_obj) { + COFF_Symbol32 *symbol32 = symbol; + symbol32->section_number = COFF_Symbol_AbsSection32; + symbol32->value = parsed_symbol.value; + symbol32->type = parsed_symbol.type; + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol; + symbol16->section_number = COFF_Symbol_AbsSection16; + symbol16->value = parsed_symbol.value; + symbol16->type = parsed_symbol.type; + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } break; + case COFF_SymbolValueInterp_Weak: + case COFF_SymbolValueInterp_Debug: + case COFF_SymbolValueInterp_Undefined: { + InvalidPath; + } break; + default: { NotImplemented; } break; + } +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_undefined_symbols_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("Patch Undefined Symbols [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Undefined) { + if (symbol.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); + if (defn) { + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + + if (defn_symbol.storage_class == COFF_SymStorageClass_WeakExternal) { + continue; + } + + lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); + } else { + // TODO: collect unresolved undefined + } + } + } + } + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_weak_symbols_with_strong_definition_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("Patch Weak Symbols [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Weak) { + LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); + if (defn) { + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); + if (defn_interp != COFF_SymbolValueInterp_Weak) { + lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); + } + } + } + } + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_weak_symbols_with_fallback_definition_task) +{ + Temp scratch = scratch_begin(&arena, 1); + + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + HashTable *visited_symbols_ht = hash_table_init(scratch.arena, 32); + struct LookupLocation { + struct LookupLocation *next; + LNK_Obj *obj; + U64 symbol_idx; + }; + struct LookupLocation *lookup_first = 0; + struct LookupLocation *lookup_last = 0; + struct LookupLocation *lookup_free_list = 0; + + ProfBegin("Patch Weak Symbols [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Undefined && symbol.storage_class == COFF_SymStorageClass_External) { + String8 lookup_name = symbol.name; + LNK_Obj *lookup_obj = obj; + U64 lookup_symbol_idx = symbol_idx; + for (;;) { + // lookup definition + LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, lookup_name); + if (defn == 0) { + break; + } + + // not external symbol? patch and move to next symbol + COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + if (defn_parsed.storage_class != COFF_SymStorageClass_WeakExternal) { + lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_parsed); + break; + } + + // check against cyclic refs + struct LookupLocation *was_visited = 0; + hash_table_search_string_raw(visited_symbols_ht, lookup_name, &was_visited); + if (was_visited != 0) { + Temp temp = temp_begin(scratch.arena); + + String8List list = {0}; + for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { + COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->obj, l->symbol_idx); + str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx) =>", l->obj->path, loc_symbol.name, l->symbol_idx); + } + { + COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(was_visited->obj, was_visited->symbol_idx); + str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx)", was_visited->obj->path, loc_symbol.name, was_visited->symbol_idx); + } + + String8 loc_string = str8_list_join(temp.arena, &list, &(StringJoin){.sep = str8_lit("\n") }); + lnk_error_obj(LNK_Error_WeakCycle, obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", symbol.name, loc_string); + + temp_end(temp); + break; + } + struct LookupLocation *loc = lookup_free_list; + if (lookup_free_list) { + SLLStackPop(lookup_free_list); + } else { + loc = push_array(scratch.arena, struct LookupLocation, 1); + } + loc->obj = lookup_obj; + loc->symbol_idx = symbol_idx; + SLLQueuePush(lookup_first, lookup_last, loc); + hash_table_push_string_raw(scratch.arena, visited_symbols_ht, lookup_name, loc); + + // fallback to weak tag for definition + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(defn_parsed, defn->u.defined.obj->header.is_big_obj); + COFF_ParsedSymbol parsed_tag = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, weak_ext->tag_index); + lookup_name = parsed_tag.name; + lookup_obj = defn->u.defined.obj; + lookup_symbol_idx = weak_ext->tag_index; + } + + hash_table_purge(visited_symbols_ht); + lookup_free_list = lookup_first; + lookup_first = 0; + lookup_last = 0; + } + } + ProfEnd(); + + scratch_end(scratch); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_weak_symbols_with_undefined_tag_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("Patch Weak Symbols [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Weak) { + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol, obj->header.is_big_obj); + AssertAlways(weak_ext->tag_index < symbol_idx); + + COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, weak_ext->tag_index); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); + if (defn_interp != COFF_SymbolValueInterp_Undefined) { + lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); + } + } + } + ProfEnd(); +} + +internal U64 +lnk_compute_win32_image_header_size(LNK_Config *config, U64 sect_count) +{ + U64 image_header_size = 0; + image_header_size += sizeof(PE_DosHeader) + pe_dos_program.size; + image_header_size += sizeof(U32); // PE_MAGIC + image_header_size += sizeof(COFF_FileHeader); + image_header_size += pe_has_plus_header(config->machine) ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32); + image_header_size += sizeof(PE_DataDirectory) * config->data_dir_count; + image_header_size += sizeof(COFF_SectionHeader) * sect_count; + return image_header_size; +} + +internal +THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) +{ + LNK_ObjRelocPatcher *task = raw_task; + LNK_Obj *obj = task->objs[task_id]; + + COFF_FileHeaderInfo obj_header = obj->header; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj_header.section_table_range).str; + String8 symbol_table = str8_substr(obj->data, obj_header.symbol_table_range); + String8 string_table = str8_substr(obj->data, obj_header.string_table_range); + + for (U64 sect_idx = 0; sect_idx < obj_header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *section_header = §ion_table[sect_idx]; + + // was section removed? + if (section_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + if (section_header->flags & COFF_SectionFlag_CntUninitializedData) { + continue; + } + + // get section file range + Rng1U64 section_frange = rng_1u64(section_header->foff, section_header->foff + section_header->fsize); + + // get section bytes + String8 section_data; + if (lnk_is_coff_section_debug(obj, sect_idx)) { + section_data = str8_substr(obj->data, section_frange); + } else { + section_data = str8_substr(task->image_data, section_frange); + } + + // find section relocs + COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, section_header); + COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); + + // apply relocs + for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { + COFF_Reloc *reloc = &relocs[reloc_idx]; + + // error check relocation + if (obj->header.machine == COFF_MachineType_X64) { + if (reloc->type > COFF_Reloc_X64_Last) { + lnk_error_obj(LNK_Error_IllegalRelocation, obj, "unknown relocation 0x%x", reloc->type); + } + } else if (obj->header.machine != COFF_MachineType_Unknown) { + NotImplemented; + } + + // compute virtual offsets + U64 reloc_voff = section_header->voff + reloc->apply_off; + + // compute symbol location values + U32 symbol_secnum = 0; + U32 symbol_secoff = 0; + S64 symbol_voff = 0; + { + COFF_ParsedSymbol symbol; + if (obj_header.is_big_obj) { + symbol = coff_parse_symbol32(string_table, (COFF_Symbol32 *)symbol_table.str + reloc->isymbol); + } else { + symbol = coff_parse_symbol16(string_table, (COFF_Symbol16 *)symbol_table.str + reloc->isymbol); + } + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + symbol_secnum = symbol.section_number; + symbol_secoff = symbol.value; + symbol_voff = safe_cast_u32((U64)task->image_section_table[symbol.section_number]->voff + (U64)symbol_secoff); + } else if (interp == COFF_SymbolValueInterp_Abs) { + // There aren't enough bits in COFF symbol to store full image base address, + // so we special case __ImageBase. A better solution would be to add + // a 64-bit symbol format to COFF. + if (str8_match(symbol.name, str8_lit("__ImageBase"), 0)) { + symbol.value = task->image_base; + } + + symbol_secnum = 0; + symbol_secoff = 0; + symbol_voff = (S64)symbol.value - (S64)task->image_base; + } else if (interp == COFF_SymbolValueInterp_Weak) { + // unresolved weak + } else if (interp == COFF_SymbolValueInterp_Undefined) { + // unresolved undefined + } else { + InvalidPath; + } + } + + // pick reloc value + COFF_RelocValue reloc_value = {0}; + switch (obj_header.machine) { + case COFF_MachineType_Unknown: {} break; + case COFF_MachineType_X64: { reloc_value = coff_pick_reloc_value_x64(reloc->type, task->image_base, reloc_voff, symbol_secnum, symbol_secoff, symbol_voff); } break; + default: { NotImplemented; } break; + } + + // read addend + Assert(reloc_value.size <= section_data.size); + U64 raw_addend = 0; + str8_deserial_read(section_data, reloc->apply_off, &raw_addend, reloc_value.size, 1); + + // compute new reloc value + S64 addend = extend_sign64(raw_addend, reloc_value.size); + U64 reloc_result = reloc_value.value + addend; + + // commit new reloc value + MemoryCopy(section_data.str + reloc->apply_off, &reloc_result, reloc_value.size); + } + } +} + +internal int +lnk_section_definition_is_before(void *raw_a, void *raw_b) +{ + LNK_SectionDefinition **a = raw_a; + LNK_SectionDefinition **b = raw_b; + int is_before; + if ((*a)->obj->input_idx == (*b)->obj->input_idx) { + is_before = (*a)->obj_sect_idx < (*b)->obj_sect_idx; + } else { + is_before = (*a)->obj->input_idx < (*b)->obj->input_idx; + } + return is_before; +} + +internal void +lnk_push_coff_symbols_from_data(Arena *arena, LNK_SymbolList *symbol_list, String8 data, LNK_SymbolArray obj_symbols) +{ + if (data.size % sizeof(U32)) { + // TODO: report invalid data size + } + U64 count = data.size / sizeof(U32); + for (U32 *ptr = (U32*)data.str, *opl = ptr + count; ptr < opl; ++ptr) { + U32 coff_symbol_idx = *ptr; + if (coff_symbol_idx >= obj_symbols.count) { + // TODO: report invalid symbol index + continue; + } + Assert(coff_symbol_idx < obj_symbols.count); + LNK_Symbol *symbol = obj_symbols.v + coff_symbol_idx; + lnk_symbol_list_push(arena, symbol_list, symbol); + } +} + +internal String8 +lnk_build_guard_data(Arena *arena, U64Array voff_arr, U64 stride) +{ + Assert(stride >= sizeof(U32)); + + // check for duplicates +#if DEBUG + for (U64 i = 1; i < voff_arr.count; ++i) { + Assert(voff_arr.[i-1] != voff_ptr[i]); + } +#endif + + U64 buffer_size = stride * voff_arr.count; + U8 *buffer = push_array(arena, U8, buffer_size); + for (U64 i = 0; i < voff_arr.count; ++i) { + U32 *voff_ptr = (U32*)(buffer + i * stride); + *voff_ptr = voff_arr.v[i]; + } + + String8 guard_data = str8(buffer, buffer_size); + return guard_data; +} + +internal String8List +lnk_build_guard_tables(TP_Context *tp, + LNK_SectionTable *sectab, + LNK_SymbolTable *symtab, + U64 objs_count, + LNK_Obj **objs, + COFF_MachineType machine, + String8 entry_point_name, + LNK_GuardFlags guard_flags, + B32 emit_suppress_flag) +{ + NotImplemented; + String8List result = {0}; + return result; +#if 0 + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + + LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, sectab); + + enum { GUARD_FIDS, GUARD_IATS, GUARD_LJMP, GUARD_EHCONT, GUARD_COUNT }; + LNK_SymbolList guard_symbol_list_table[GUARD_COUNT]; MemoryZeroStruct(&guard_symbol_list_table[0]); + + // collect symbols from objs + for (LNK_ObjNode *obj_node = obj_list.first; obj_node != NULL; obj_node = obj_node->next) { + LNK_Obj *obj = &obj_node->data; + MSCRT_FeatFlags feat_flags = lnk_obj_get_features(obj); + B32 has_guard_flags = (feat_flags & MSCRT_FeatFlag_GUARD_CF) || (feat_flags & MSCRT_FeatFlag_GUARD_EH_CONT); + if (has_guard_flags) { + LNK_SymbolArray symbol_arr = lnk_symbol_array_from_list(scratch.arena, obj->symbol_list); + if (guard_flags & LNK_Guard_Cf) { + String8List gfids_list = lnk_collect_obj_chunks(scratch.arena, obj, str8_lit(".gfids"), str8_zero(), 1); + for (String8Node *node = gfids_list.first; node != 0; node = node->next) { + lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], node->string, symbol_arr); + } + String8List giats_list = lnk_collect_obj_chunks(scratch.arena, obj, str8_lit(".giats"), str8_zero(), 1); + for (String8Node *node = giats_list.first; node != 0; node = node->next) { + lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_IATS], node->string, symbol_arr); + } + } + if (guard_flags & LNK_Guard_LongJmp) { + String8List gljmp_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gljmp"), str8_zero(), 1); + for (String8Node *node = gljmp_list.first; node != 0; node = node->next) { + lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_LJMP], node->string, symbol_arr); + } + } + if (guard_flags & LNK_Guard_EhCont) { + String8List gehcont_list = lnk_obj_search_chunks(scratch.arena, obj, str8_lit(".gehcont"), str8_zero(), 1); + for (String8Node *node = gehcont_list.first; node != 0; node = node->next) { + lnk_push_coff_symbols_from_data(scratch.arena, &guard_symbol_list_table[GUARD_EHCONT], node->string, symbol_arr); + } + } + } else { + // TODO: loop over COFF relocs + NotImplemented; +#if 0 + // use relocation data in code sections to get function symbols + for (U64 isect = 0; isect < obj->sect_count; ++isect) { + LNK_Chunk *chunk = obj->chunk_arr[isect]; + if (!chunk) { + continue; + } + if (lnk_chunk_is_discarded(chunk)) { + continue; + } + if (~chunk->flags & COFF_SectionFlag_CntCode) { + continue; + } + Assert(chunk->type == LNK_Chunk_Leaf); + for (LNK_Reloc *reloc = obj->sect_reloc_list_arr[isect].first; reloc != 0; reloc = reloc->next) { + LNK_Symbol *symbol = lnk_resolve_symbol(symtab, reloc->symbol); + if (!LNK_Symbol_IsDefined(symbol->type)) { + continue; + } + LNK_DefinedSymbol *defined_symbol = &symbol->u.defined; + if (~defined_symbol->flags & LNK_DefinedSymbolFlag_IsFunc) { + continue; + } + LNK_Chunk *symbol_chunk = defined_symbol->u.chunk; + if (!symbol_chunk) { + continue; + } + if (symbol_chunk->type != LNK_Chunk_Leaf) { + continue; + } + if (~symbol_chunk->flags & COFF_SectionFlag_CntCode) { + continue; + } + lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], symbol); + } + } +#endif + } + } + + // entry point + LNK_Symbol *entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, entry_point_name); + lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], entry_point_symbol); + + // push exports + { + Temp temp = temp_begin(scratch.arena); + KeyValuePair *raw_exports = key_value_pairs_from_hash_table(temp.arena, exptab->name_export_ht); + for (U64 i = 0; i < exptab->name_export_ht->count; ++i) { + LNK_Export *exp = raw_exports[i].value_raw; + lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], exp->symbol); + } + scratch_end(temp); + } + + // TODO: push noname exports + + NotImplemented; +#if 0 + // push thunks + LNK_SymbolScope scope_array[] = { LNK_SymbolScope_Defined, LNK_SymbolScope_Internal }; + for (U64 iscope = 0; iscope < ArrayCount(scope_array); ++iscope) { + LNK_SymbolScope scope = scope_array[iscope]; + for (U64 ibucket = 0; ibucket < symtab->bucket_count[scope]; ++ibucket) { + for (LNK_SymbolNode *symbol_node = symtab->buckets[scope][ibucket].first; + symbol_node != NULL; + symbol_node = symbol_node->next) { + LNK_Symbol *symbol = symbol_node->data; + if (!LNK_Symbol_IsDefined(symbol->type)) continue; + LNK_DefinedSymbol *defined_symbol = &symbol->u.defined; + if (~defined_symbol->flags & LNK_DefinedSymbolFlag_IsThunk) continue; + lnk_symbol_list_push(scratch.arena, &guard_symbol_list_table[GUARD_FIDS], symbol); + } + } + } +#endif + + // build section data + lnk_section_table_build_data(tp, sectab, machine); + lnk_section_table_assign_virtual_offsets(sectab); + + // compute symbols virtual offsets + U64Array guard_voff_arr_table[GUARD_COUNT]; + for (U64 i = 0; i < ArrayCount(guard_symbol_list_table); ++i) { + U64List voff_list; MemoryZeroStruct(&voff_list); + LNK_SymbolList symbol_list = guard_symbol_list_table[i]; + for (LNK_SymbolNode *symbol_node = symbol_list.first; symbol_node != NULL; symbol_node = symbol_node->next) { + LNK_Symbol *symbol = lnk_resolve_symbol(symtab, symbol_node->data); + if (!LNK_Symbol_IsDefined(symbol->type)) { + continue; + } + LNK_DefinedSymbol *defined_symbol = &symbol->u.defined; + LNK_Chunk *chunk = defined_symbol->u.chunk; + if (!chunk) { + continue; + } + if (lnk_chunk_is_discarded(chunk)) { + continue; + } + U64 chunk_voff = lnk_virt_off_from_chunk_ref(sect_id_map, chunk->ref); + U64 symbol_voff = chunk_voff + defined_symbol->u.chunk_offset; + Assert(symbol_voff != 0); + u64_list_push(scratch.arena, &voff_list, symbol_voff); + } + U64Array voff_arr = u64_array_from_list(scratch.arena, &voff_list); + radsort(voff_arr.v, voff_arr.count, u64_compar_is_before); + guard_voff_arr_table[i] = u64_array_remove_duplicates(scratch.arena, voff_arr); + } + + // push guard sections + static struct { + char *name; + char *symbol; + int flags; + } sect_layout[] = { + { ".gfids", LNK_GFIDS_SYMBOL_NAME, LNK_GFIDS_SECTION_FLAGS }, + { ".giats", LNK_GIATS_SYMBOL_NAME, LNK_GIATS_SECTION_FLAGS }, + { ".gljmp", LNK_GLJMP_SYMBOL_NAME, LNK_GLJMP_SECTION_FLAGS }, + { ".gehcont", LNK_GEHCONT_SYMBOL_NAME, LNK_GEHCONT_SECTION_FLAGS }, + }; + for (U64 i = 0; i < ArrayCount(sect_layout); ++i) { + LNK_Section *sect = lnk_section_table_push(sectab, str8_cstring(sect_layout[i].name), sect_layout[i].flags); + } + + // TODO: emit table for SEH on X86 + if (machine == COFF_MachineType_X86) { + lnk_not_implemented("__safe_se_handler_table"); + lnk_not_implemented("__safe_se_handler_count"); + } + + LNK_Symbol *gfids_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GFIDS_SYMBOL_NAME)); + LNK_Symbol *giats_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GIATS_SYMBOL_NAME)); + LNK_Symbol *gljmp_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GLJMP_SYMBOL_NAME)); + LNK_Symbol *gehcont_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Internal, str8_lit(LNK_GEHCONT_SYMBOL_NAME)); + + LNK_Section *gfids_sect = lnk_section_table_search_id(sectab, gfids_symbol->u.defined.u.chunk->ref.sect_id); + LNK_Section *giats_sect = lnk_section_table_search_id(sectab, giats_symbol->u.defined.u.chunk->ref.sect_id); + LNK_Section *gljmp_sect = lnk_section_table_search_id(sectab, gljmp_symbol->u.defined.u.chunk->ref.sect_id); + LNK_Section *gehcont_sect = lnk_section_table_search_id(sectab, gehcont_symbol->u.defined.u.chunk->ref.sect_id); + + LNK_Chunk *gfids_array_chunk = gfids_sect->root; + LNK_Chunk *giats_array_chunk = giats_sect->root; + LNK_Chunk *gljmp_array_chunk = gljmp_sect->root; + LNK_Chunk *gehcont_array_chunk = gehcont_sect->root; + + // first 4 bytes are call's destination virtual offset + U64 entry_stride = sizeof(U32); + if (emit_suppress_flag) { + // 4th byte tells kernel what to do when destination VA is not in the bitmap. + // If byte is 1 exception is suppressed and program keeps running. + // If zero then exception is raised with nt!_KiRaiseSecurityCheckFailure(FAST_FAIL_GUARD_ICALL_CHECK_FAILURE) and exception code 0xA. + entry_stride = 5; + } + + // make guard data from virtual offsets + String8 gfids_data = lnk_build_guard_data(gfids_sect->arena, guard_voff_arr_table[GUARD_FIDS], entry_stride); + String8 giats_data = lnk_build_guard_data(giats_sect->arena, guard_voff_arr_table[GUARD_IATS], entry_stride); + String8 gljmp_data = lnk_build_guard_data(gljmp_sect->arena, guard_voff_arr_table[GUARD_LJMP], entry_stride); + String8 gehcont_data = lnk_build_guard_data(gehcont_sect->arena, guard_voff_arr_table[GUARD_EHCONT], entry_stride); + + // push guard data + lnk_section_push_chunk_data(gfids_sect, gfids_array_chunk, gfids_data, str8_zero()); + lnk_section_push_chunk_data(giats_sect, giats_array_chunk, giats_data, str8_zero()); + lnk_section_push_chunk_data(gljmp_sect, gljmp_array_chunk, gljmp_data, str8_zero()); + lnk_section_push_chunk_data(gehcont_sect, gehcont_array_chunk, gehcont_data, str8_zero()); + + LNK_Symbol *gflags_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_FLAGS_SYMBOL_NAME)); + LNK_Symbol *gfids_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_FIDS_TABLE_SYMBOL_NAME)); + LNK_Symbol *gfids_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_FIDS_COUNT_SYMBOL_NAME)); + LNK_Symbol *giats_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_IAT_TABLE_SYMBOL_NAME)); + LNK_Symbol *giats_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_IAT_COUNT_SYMBOL_NAME)); + LNK_Symbol *gljmp_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_LONGJMP_TABLE_SYMBOL_NAME)); + LNK_Symbol *gljmp_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_LONGJMP_COUNT_SYMBOL_NAME)); + LNK_Symbol *gehcont_table_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_EHCONT_TABLE_SYMBOL_NAME)); + LNK_Symbol *gehcont_count_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Main, str8_lit(MSCRT_GUARD_EHCONT_COUNT_SYMBOL_NAME)); + + LNK_DefinedSymbol *gflags_def = &gflags_symbol->u.defined; + LNK_DefinedSymbol *gfids_table_def = &gfids_table_symbol->u.defined; + LNK_DefinedSymbol *gfids_count_def = &gfids_count_symbol->u.defined; + LNK_DefinedSymbol *giats_table_def = &giats_table_symbol->u.defined; + LNK_DefinedSymbol *giats_count_def = &giats_count_symbol->u.defined; + LNK_DefinedSymbol *gljmp_table_def = &gljmp_table_symbol->u.defined; + LNK_DefinedSymbol *gljmp_count_def = &gljmp_count_symbol->u.defined; + LNK_DefinedSymbol *gehcont_table_def = &gehcont_table_symbol->u.defined; + LNK_DefinedSymbol *gehcont_count_def = &gehcont_count_symbol->u.defined; + + // guard flags + gflags_def->value_type = LNK_DefinedSymbolValue_VA; + gflags_def->u.va = PE_LoadConfigGuardFlags_CF_INSTRUMENTED; + if ((guard_flags & LNK_Guard_Cf)) { + gflags_def->u.va |= PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_PRESENT; + } + if ((guard_flags & LNK_Guard_LongJmp) && guard_voff_arr_table[GUARD_LJMP].count) { + gflags_def->u.va |= PE_LoadConfigGuardFlags_CF_LONGJUMP_TABLE_PRESENT; + } + if ((guard_flags & LNK_Guard_EhCont) && guard_voff_arr_table[GUARD_EHCONT].count) { + gflags_def->u.va |= PE_LoadConfigGuardFlags_EH_CONTINUATION_TABLE_PRESENT; + } + { + LNK_Section *didat_sect = lnk_section_table_search(sectab, str8_lit(".didat")); + if (didat_sect) { + gflags_def->u.va |= PE_LoadConfigGuardFlags_DELAYLOAD_IAT_IN_ITS_OWN_SECTION; + } + } + if (entry_stride > sizeof(U32)) { + U64 size_bit = (entry_stride - 5); + if (emit_suppress_flag) { + gflags_def->u.va |= PE_LoadConfigGuardFlags_CF_EXPORT_SUPPRESSION_INFO_PRESENT; + } + gflags_def->u.va |= (1 << size_bit) << PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_SHIFT; + } + + // gfids + if (guard_voff_arr_table[GUARD_FIDS].count) { + gfids_table_def->value_type = LNK_DefinedSymbolValue_Chunk; + gfids_table_def->u.chunk = gfids_array_chunk; + } + gfids_count_def->value_type = LNK_DefinedSymbolValue_VA; + gfids_count_def->u.va = guard_voff_arr_table[GUARD_FIDS].count; + + // giats + if (guard_voff_arr_table[GUARD_IATS].count) { + giats_table_def->value_type = LNK_DefinedSymbolValue_Chunk; + giats_table_def->u.chunk = giats_array_chunk; + } + giats_count_def->value_type = LNK_DefinedSymbolValue_VA; + giats_count_def->u.va = guard_voff_arr_table[GUARD_IATS].count; + + // gljmp + if (guard_voff_arr_table[GUARD_LJMP].count) { + gljmp_table_def->value_type = LNK_DefinedSymbolValue_Chunk; + gljmp_table_def->u.chunk = gljmp_array_chunk; + } + gljmp_count_def->value_type = LNK_DefinedSymbolValue_VA; + gljmp_count_def->u.va = guard_voff_arr_table[GUARD_LJMP].count; + + // gehcont + if (guard_voff_arr_table[GUARD_EHCONT].count) { + gehcont_table_def->value_type = LNK_DefinedSymbolValue_Chunk; + gehcont_table_def->u.chunk = gehcont_array_chunk; + } + gehcont_count_def->value_type = LNK_DefinedSymbolValue_VA; + gehcont_count_def->u.va = guard_voff_arr_table[GUARD_EHCONT].count; + + scratch_end(scratch); + ProfEnd(); +#endif +} + +internal +THREAD_POOL_TASK_FUNC(lnk_emit_base_relocs_from_objs_task) +{ + ProfBeginFunction(); + + LNK_ObjBaseRelocTask *task = raw_task; + Rng1U64 range = task->ranges[task_id]; + + HashTable *page_ht = task->page_ht_arr[task_id]; + LNK_BaseRelocPageList *page_list = &task->list_arr[task_id]; + + for (U64 obj_idx = range.min; obj_idx < range.max; ++obj_idx) { + LNK_Obj *obj = task->obj_arr[obj_idx]; + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + + if (sect_header->flags & COFF_SectionFlag_LnkRemove) { + continue; + } + + COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, sect_header); + COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); + + for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { + COFF_Reloc *r = &relocs[reloc_idx]; + + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + B32 is_symbol_address = symbol_interp != COFF_SymbolValueInterp_Abs; + + if (is_symbol_address) { + B32 is_addr32 = 0, is_addr64 = 0; + switch (obj->header.machine) { + case COFF_MachineType_Unknown: {} break; + case COFF_MachineType_X64: { + is_addr32 = r->type == COFF_Reloc_X64_Addr32; + is_addr64 = r->type == COFF_Reloc_X64_Addr64; + } break; + default: { NotImplemented; } break; + } + + if (is_addr32 || is_addr64) { + U64 reloc_voff = sect_header->voff + r->apply_off; + U64 page_voff = AlignDownPow2(reloc_voff, task->page_size); + + LNK_BaseRelocPageNode *page; + { + KeyValuePair *is_page_present = hash_table_search_u64(page_ht, page_voff); + if (is_page_present) { + page = is_page_present->value_raw; + } else { + // fill out page + page = push_array(arena, LNK_BaseRelocPageNode, 1); + page->v.voff = page_voff; + + // push page + SLLQueuePush(page_list->first, page_list->last, page); + page_list->count += 1; + + // register page voff + hash_table_push_u64_raw(arena, page_ht, page_voff, page); + } + } + + if (is_addr32) { + if (task->is_large_addr_aware) { + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); + lnk_error_obj(LNK_Error_LargeAddrAwareRequired, obj, "found out of range ADDR32 relocation for '%S', link with /LARGEADDRESSAWARE:NO", symbol.name); + } else { + u64_list_push(arena, &page->v.entries_addr32, reloc_voff); + } + } else { + u64_list_push(arena, &page->v.entries_addr64, reloc_voff); + } + } + } + } + } + } + + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_virtual_offsets_and_sizes_in_obj_section_headers_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("Patch Virtual Offset And Size In Section Headers [%S]", obj->path); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + if (~sect_header->flags & COFF_SectionFlag_LnkRemove) { + LNK_SectionContrib *sc = task->sect_map[obj_idx][sect_idx]; + LNK_Section *sect = task->image_sects.v[sc->u.sect_idx]; + sect_header->vsize = sc->u.size; + sect_header->voff = sect->voff + sc->u.off; + } + } + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_file_offsets_and_sizes_in_obj_section_headers_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("Patch File Offsets And Sizes In Obj Section Headers [%S]", obj->path); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; + for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + B32 patch_section_header = (~sect_header->flags & COFF_SectionFlag_LnkRemove) && + !lnk_is_coff_section_debug(obj, sect_idx); + if (patch_section_header) { + LNK_SectionContrib *sc = task->sect_map[obj_idx][sect_idx]; + LNK_Section *sect = task->image_sects.v[sc->u.sect_idx]; + if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { + sect_header->fsize = sc->u.size; + sect_header->foff = sect->foff + sc->u.off; + } + } + } + ProfEnd(); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_section_symbols_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBegin("Patch Section Symbols [%S]", obj->path); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Undefined) { + if (symbol.storage_class == COFF_SymStorageClass_Section) { + LNK_Section *sect = lnk_section_table_search(task->sectab, symbol.name, symbol.value); + if (sect) { + if (~sect->flags & COFF_SectionFlag_MemDiscardable) { + LNK_SectionContrib *first_sc = lnk_get_first_section_contrib(sect); + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = safe_cast_u32(first_sc->u.sect_idx + 1); + symbol32->value = first_sc->u.off; + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = safe_cast_u16(first_sc->u.sect_idx + 1); + symbol16->value = first_sc->u.off; + symbol16->storage_class = COFF_SymStorageClass_Static; + } + } else { + lnk_error_obj(LNK_Error_SectRefsDiscardedMemory, obj, "symbol %S (No. 0x%llx) references section with discard flag", symbol.name, symbol_idx); + } + } else { + lnk_error_obj(LNK_Error_UnresolvedSymbol, obj, "undefined section symbol %S (No 0x%llx) refers to an image section that doesn't exist", symbol.name, symbol_idx); + } + } + } + } + ProfEnd(); +} + +int +lnk_base_reloc_page_compar(const void *raw_a, const void *raw_b) +{ + const LNK_BaseRelocPage *a = raw_a; + const LNK_BaseRelocPage *b = raw_b; + return u64_compar(&a->voff, &b->voff); +} + +int +lnk_base_reloc_page_is_before(void *raw_a, void *raw_b) +{ + LNK_BaseRelocPage* a = raw_a; + LNK_BaseRelocPage* b = raw_b; + return a->voff < b->voff; +} + +internal String8List +lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, U64 objs_count, LNK_Obj **objs) +{ + ProfBeginFunction(); + + Arena *arena = tp_arena->v[0]; + Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); + tp_arena->v[0] = scratch.arena; + TP_Temp tp_temp = tp_temp_begin(tp_arena); + + LNK_BaseRelocPageArray page_arr; + { + LNK_BaseRelocPageList *page_list_arr = push_array(scratch.arena, LNK_BaseRelocPageList, tp->worker_count); + HashTable **page_ht_arr = push_array_no_zero(scratch.arena, HashTable *, tp->worker_count); + for (U64 i = 0; i < tp->worker_count; ++i) { + page_ht_arr[i] = hash_table_init(scratch.arena, 1024); + } + + { + ProfBegin("Emit Relocs From Objs"); + LNK_ObjBaseRelocTask task = {0}; + task.ranges = tp_divide_work(scratch.arena, objs_count, tp->worker_count); + task.page_size = config->machine_page_size; + task.page_ht_arr = page_ht_arr; + task.list_arr = page_list_arr; + task.obj_arr = objs; + task.is_large_addr_aware = !!(config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE); + tp_for_parallel(tp, tp_arena, tp->worker_count, lnk_emit_base_relocs_from_objs_task, &task); + ProfEnd(); + } + + LNK_BaseRelocPageList *main_page_list = &page_list_arr[0]; + { + ProfBegin("Merge Worker Page Lists"); + HashTable *main_ht = page_ht_arr[0]; + for (U64 list_idx = 1; list_idx < tp->worker_count; ++list_idx) { + LNK_BaseRelocPageList src = page_list_arr[list_idx]; + + for (LNK_BaseRelocPageNode *src_page = src.first, *src_next; src_page != 0; src_page = src_next) { + src_next = src_page->next; + + KeyValuePair *is_page_present = hash_table_search_u64(main_ht, src_page->v.voff); + if (is_page_present) { + // page exists concat voffs + LNK_BaseRelocPageNode *page = is_page_present->value_raw; + Assert(page != src_page); + u64_list_concat_in_place(&page->v.entries_addr32, &src_page->v.entries_addr32); + u64_list_concat_in_place(&page->v.entries_addr64, &src_page->v.entries_addr64); + } else { + // push page to main list + SLLQueuePush(main_page_list->first, main_page_list->last, src_page); + main_page_list->count += 1; + + // store lookup voff + hash_table_push_u64_raw(scratch.arena, main_ht, src_page->v.voff, src_page); + } + } + } + ProfEnd(); + } + + ProfBegin("Page List -> Array"); + page_arr.count = 0; + page_arr.v = push_array_no_zero(scratch.arena, LNK_BaseRelocPage, main_page_list->count); + for (LNK_BaseRelocPageNode* n = main_page_list->first; n != 0; n = n->next) { + page_arr.v[page_arr.count++] = n->v; + } + ProfEnd(); + + ProfBegin("Sort Pages on VOFF"); + //radsort(page_arr.v, page_arr.count, lnk_base_reloc_page_is_before); + qsort(page_arr.v, page_arr.count, sizeof(page_arr.v[0]), lnk_base_reloc_page_compar); + ProfEnd(); + } + + String8List result = {0}; + if (page_arr.count) { + ProfBegin("Serialize Pages"); + HashTable *voff_ht = hash_table_init(scratch.arena, config->machine_page_size); + for (U64 page_idx = 0; page_idx < page_arr.count; ++page_idx) { + LNK_BaseRelocPage *page = &page_arr.v[page_idx]; + + U64 total_entry_count = 0; + total_entry_count += page->entries_addr32.count; + total_entry_count += page->entries_addr64.count; + + U32 *page_voff_ptr; + U32 *block_size_ptr; + U16 *reloc_arr_base; + + // push buffer + U64 buf_size = AlignPow2(sizeof(*page_voff_ptr) + sizeof(*block_size_ptr) + sizeof(*reloc_arr_base)*total_entry_count, sizeof(U32)); + void *buf = push_array_no_zero(arena, U8, buf_size); + + // setup pointers into buffer + page_voff_ptr = buf; + block_size_ptr = page_voff_ptr + 1; + reloc_arr_base = (U16*)(block_size_ptr + 1); + + // write 32-bit relocations + U16 *reloc_arr_ptr = reloc_arr_base; + for (U64Node *i = page->entries_addr32.first; i != 0; i = i->next) { + // was base reloc_entry made? + if (hash_table_search_u64(voff_ht, i->data)) { + continue; + } + hash_table_push_u64_u64(scratch.arena, voff_ht, i->data, 0); + + // write entry + U64 rel_off = i->data - page->voff; + Assert(rel_off <= config->machine_page_size); + *reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_HIGHLOW, rel_off); + } + + // write 64-bit relocations + for (U64Node *i = page->entries_addr64.first; i != 0; i = i->next) { + // was base reloc entry made? + if (hash_table_search_u64(voff_ht, i->data)) { + continue; + } + hash_table_push_u64_u64(scratch.arena, voff_ht, i->data, 0); + + // write entry + U64 rel_off = i->data - page->voff; + Assert(rel_off <= config->machine_page_size); + *reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_DIR64, rel_off); + } + + // write pad + U64 pad_reloc_count = AlignPadPow2(total_entry_count, sizeof(reloc_arr_ptr[0])); + MemoryZeroTyped(reloc_arr_ptr, pad_reloc_count); // fill pad with PE_BaseRelocKind_ABSOLUTE + reloc_arr_ptr += pad_reloc_count; + + // compute block size + U64 reloc_arr_size = (U64)((U8*)reloc_arr_ptr - (U8*)reloc_arr_base); + U64 block_size = sizeof(*page_voff_ptr) + sizeof(*block_size_ptr) + reloc_arr_size; + + // write header + *page_voff_ptr = safe_cast_u32(page->voff); + *block_size_ptr = safe_cast_u32(block_size); + Assert(*block_size_ptr <= buf_size); + + // push page + str8_list_push(arena, &result, str8(buf, buf_size)); + + // purge voffs for next page + hash_table_purge(voff_ht); + } + ProfEnd(); + } + + tp_temp_end(tp_temp); // scratch is cleared here + tp_arena->v[0] = arena; + + ProfEnd(); + return result; +} + +internal String8List +lnk_build_win32_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sects, U64 expected_image_header_size) +{ + ProfBeginFunction(); + + String8List result = {0}; + + // + // DOS header + // + U32 dos_stub_size = sizeof(PE_DosHeader) + pe_dos_program.size; + { + PE_DosHeader *dos_header = push_array(arena, PE_DosHeader, 1); + dos_header->magic = PE_DOS_MAGIC; + dos_header->last_page_size = dos_stub_size % 512; + dos_header->page_count = CeilIntegerDiv(dos_stub_size, 512); + dos_header->paragraph_header_size = sizeof(PE_DosHeader) / 16; + dos_header->min_paragraph = 0; + dos_header->max_paragraph = 0; + dos_header->init_ss = 0; + dos_header->init_sp = 0; + dos_header->checksum = 0; + dos_header->init_ip = 0xFFFF; + dos_header->init_cs = 0; + dos_header->reloc_table_file_off = sizeof(PE_DosHeader); + dos_header->overlay_number = 0; + MemoryZeroStruct(dos_header->reserved); + dos_header->oem_id = 0; + dos_header->oem_info = 0; + MemoryZeroArray(dos_header->reserved2); + dos_header->coff_file_offset = dos_stub_size; + + str8_list_push(arena, &result, str8_struct(dos_header)); + str8_list_push(arena, &result, pe_dos_program); + } + + // + // PE magic + // + U32 *pe_magic = push_array(arena, U32, 1); + *pe_magic = PE_MAGIC; + str8_list_push(arena, &result, str8_struct(pe_magic)); + + // + // determine PE optional header type + // + B32 has_pe_plus_header = pe_has_plus_header(config->machine); + + // + // COFF file header + // + { + COFF_FileHeader *file_header = push_array_no_zero(arena, COFF_FileHeader, 1); + file_header->machine = config->machine; + file_header->time_stamp = config->time_stamp; + file_header->symbol_table_foff = 0; + file_header->symbol_count = 0; + file_header->section_count = sects.count; + file_header->optional_header_size = (has_pe_plus_header ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32)) + (sizeof(PE_DataDirectory) * config->data_dir_count); + file_header->flags = config->file_characteristics; + str8_list_push(arena, &result, str8_struct(file_header)); + } + + // + // compute code/inited/uninited sizes + // + U64 code_base = 0; + U64 sizeof_code = 0; + U64 sizeof_inited_data = 0; + U64 sizeof_uninited_data = 0; + U64 sizeof_image = 0; + for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + LNK_Section *sect = sects.v[sect_idx]; + if (code_base == 0 && sect->flags & COFF_SectionFlag_CntCode) { + code_base = sect->voff; + } + if (sect->flags & COFF_SectionFlag_CntUninitializedData) { + sizeof_uninited_data += sect->vsize; + } + if ((sect->flags & COFF_SectionFlag_CntInitializedData) || (sect->flags & COFF_SectionFlag_CntCode)) { + sizeof_inited_data += sect->fsize; + } + if (sect->flags & COFF_SectionFlag_CntCode) { + sizeof_code += sect->fsize; + } + sizeof_image = Max(sizeof_image, sects.v[sect_idx]->voff + sects.v[sect_idx]->vsize); + } + sizeof_code = AlignPow2(sizeof_code, config->file_align); + sizeof_inited_data = AlignPow2(sizeof_inited_data, config->file_align); + sizeof_uninited_data = AlignPow2(sizeof_uninited_data, config->file_align); + sizeof_image = AlignPow2(sizeof_image, 4096); + + // + // compute image headers size + // + U64 sizeof_image_headers = 0; + sizeof_image_headers += dos_stub_size; + sizeof_image_headers += sizeof(COFF_FileHeader); + sizeof_image_headers += has_pe_plus_header ? sizeof(PE_OptionalHeader32Plus) : sizeof(PE_OptionalHeader32); + sizeof_image_headers += sizeof(PE_DataDirectory) * config->data_dir_count; + sizeof_image_headers += sizeof(COFF_SectionHeader) * sects.count; + sizeof_image_headers = AlignPow2(sizeof_image_headers, config->file_align); + + // + // fill out PE optional header + // + U32 *entry_point_va; + U32 *check_sum; + if (has_pe_plus_header) { + PE_OptionalHeader32Plus *opt_header = push_array_no_zero(arena, PE_OptionalHeader32Plus, 1); + opt_header->magic = PE_PE32PLUS_MAGIC; + opt_header->major_linker_version = config->link_ver.major; + opt_header->minor_linker_version = config->link_ver.minor; + opt_header->sizeof_code = safe_cast_u32(sizeof_code); + opt_header->sizeof_inited_data = safe_cast_u32(sizeof_inited_data); + opt_header->sizeof_uninited_data = safe_cast_u32(sizeof_uninited_data); + opt_header->entry_point_va = 0; + opt_header->code_base = code_base; + opt_header->image_base = lnk_get_base_addr(config); + opt_header->section_alignment = config->sect_align; + opt_header->file_alignment = config->file_align; + opt_header->major_os_ver = config->os_ver.major; + opt_header->minor_os_ver = config->os_ver.minor; + opt_header->major_img_ver = config->image_ver.major; + opt_header->minor_img_ver = config->image_ver.minor; + opt_header->major_subsystem_ver = config->subsystem_ver.major; + opt_header->minor_subsystem_ver = config->subsystem_ver.minor; + opt_header->win32_version_value = 0; // MSVC writes zero + opt_header->sizeof_image = sizeof_image; + opt_header->sizeof_headers = safe_cast_u32(sizeof_image_headers); + opt_header->check_sum = 0; // :check_sum + opt_header->subsystem = config->subsystem; + opt_header->dll_characteristics = config->dll_characteristics; + opt_header->sizeof_stack_reserve = config->stack_reserve; + opt_header->sizeof_stack_commit = config->stack_commit; + opt_header->sizeof_heap_reserve = config->heap_reserve; + opt_header->sizeof_heap_commit = config->heap_commit; + opt_header->loader_flags = 0; // for dynamic linker, always zero + opt_header->data_dir_count = safe_cast_u32(config->data_dir_count); + + entry_point_va = &opt_header->entry_point_va; + check_sum = &opt_header->check_sum; + + str8_list_push(arena, &result, str8_struct(opt_header)); + } else { + NotImplemented; + } + + // + // PE directories + // + PE_DataDirectory *directory_array; + { + directory_array = push_array(arena, PE_DataDirectory, config->data_dir_count); + str8_list_push(arena, &result, str8_array(directory_array, config->data_dir_count)); + } + + // + // COFF section table + // + COFF_SectionHeader *coff_section_table = push_array(arena, COFF_SectionHeader, sects.count); + U64 coff_section_table_count = 0; + { + for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + LNK_Section *sect = sects.v[sect_idx]; + + COFF_SectionHeader *coff_section = &coff_section_table[sect_idx]; + + // TODO: for objs we can store long name in string table and write here /offset + if (sect->name.size > sizeof(coff_section->name)) { + lnk_error(LNK_Warning_LongSectionName, "not enough space in COFF section header to store entire name \"%S\"", sect->name); + } + + MemorySet(&coff_section->name[0], 0, sizeof(coff_section->name)); + MemoryCopy(&coff_section->name[0], sect->name.str, Min(sect->name.size, sizeof(coff_section->name))); + coff_section->vsize = sect->vsize; + coff_section->voff = sect->voff; + coff_section->fsize = sect->fsize; + coff_section->foff = sect->foff; + coff_section->relocs_foff = 0; // not present in image + coff_section->lines_foff = 0; // obsolete + coff_section->reloc_count = 0; // not present in image + coff_section->line_count = 0; // obsolete + coff_section->flags = sect->flags; + + coff_section_table_count += 1; + } + + str8_list_push(arena, &result, str8_array(coff_section_table, coff_section_table_count)); + } + + // align image headers + { + U64 image_headers_align_size = AlignPadPow2(result.total_size, config->file_align); + U8 *image_headers_align = push_array(arena, U8, image_headers_align_size); + str8_list_push(arena, &result, str8(image_headers_align, image_headers_align_size)); + } + + // + // entry point + // + { + Temp scratch = scratch_begin(&arena, 1); + + COFF_SectionHeader **section_table = push_array(arena, COFF_SectionHeader *, coff_section_table_count + 1); + for (U64 i = 1; i <= coff_section_table_count; i += 1) { section_table[i] = &coff_section_table[i-1]; } + + LNK_Symbol *entry_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, config->entry_point_name); + if (entry_symbol) { + *entry_point_va = safe_cast_u32(lnk_virt_off_from_symbol(section_table, entry_symbol)); + } + + scratch_end(scratch); + } + + Assert(result.total_size == expected_image_header_size); + ProfEnd(); + return result; +} + +internal LNK_ImageContext +lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs) +{ + ProfBegin("Image"); + lnk_timer_begin(LNK_Timer_Image); + + Temp scratch = scratch_begin(arena->v, arena->count); + + // init section table + LNK_SectionTable *sectab = lnk_section_table_alloc(); + lnk_section_table_push(sectab, str8_lit(".text"), PE_TEXT_SECTION_FLAGS); + lnk_section_table_push(sectab, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS); + lnk_section_table_push(sectab, str8_lit(".data"), PE_DATA_SECTION_FLAGS); + + LNK_BuildImageTask task = {0}; + task.symtab = symtab; + task.sectab = sectab; + task.default_align = lnk_default_align_from_machine(config->machine); + task.objs_count = objs_count; + task.objs = objs; + task.sect_map = 0; + + ProfBegin("Remove Associative Sections"); + tp_for_parallel(tp, 0, objs_count, lnk_remove_associative_sections_task, &task); + ProfEnd(); + + { + ProfBegin("Define And Count Sections"); + TP_Temp temp = tp_temp_begin(arena); + + // init hash tables for gathering section definitions + task.u.gather_sects.defns = push_array(arena->v[0], HashTable *, tp->worker_count); + for (U64 worker_idx = 0; worker_idx < tp->worker_count; worker_idx += 1) task.u.gather_sects.defns[worker_idx] = hash_table_init(arena->v[0], 128); + + ProfBegin("Gather Section Definitions"); + tp_for_parallel(tp, arena, objs_count, lnk_gather_section_definitions_task, &task); + ProfEnd(); + + ProfBegin("Merge Section Definitions Hash Tables"); + for (U64 worker_idx = 1; worker_idx < tp->worker_count; worker_idx += 1) { + U64 sect_defns_count = task.u.gather_sects.defns[worker_idx]->count; + LNK_SectionDefinition **sect_defns = values_from_hash_table_raw(arena->v[0], task.u.gather_sects.defns[worker_idx]); + radsort(sect_defns, sect_defns_count, lnk_section_definition_is_before); + + for (U64 defn_idx = 0; defn_idx < sect_defns_count; defn_idx += 1) { + String8 name_with_flags = lnk_make_name_with_flags(arena->v[0], sect_defns[defn_idx]->name, sect_defns[defn_idx]->flags); + LNK_SectionDefinition *main_defn = 0; + hash_table_search_string_raw(task.u.gather_sects.defns[0], name_with_flags, &main_defn); + if (main_defn == 0) { + main_defn = sect_defns[defn_idx]; + hash_table_push_string_raw(arena->v[0], task.u.gather_sects.defns[0], name_with_flags, main_defn); + } else { + main_defn->contribs_count += sect_defns[defn_idx]->contribs_count; + } + } + } + ProfEnd(); + + U64 sect_defns_count = task.u.gather_sects.defns[0]->count; + LNK_SectionDefinition **sect_defns = values_from_hash_table_raw(arena->v[0], task.u.gather_sects.defns[0]); + + ProfBegin("Sort Sections Definitions"); + radsort(sect_defns, sect_defns_count, lnk_section_definition_is_before); + ProfEnd(); + + ProfBegin("Push Sections And Reserve Section Contrib Memory"); + for (U64 defn_idx = 0; defn_idx < sect_defns_count; defn_idx += 1) { + LNK_SectionDefinition *sect_defn = sect_defns[defn_idx]; + + // do not create definitions for sections that are removed from the image + { + B32 skip = 0; + for (String8Node *name_n = config->remove_sections.first; name_n != 0; name_n = name_n->next) { + if (str8_match(sect_defn->name, name_n->string, 0)) { + skip = 1; + break; + } + } + if (skip) { continue; } + } + + // warn about conflicting section flags + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + LNK_Section *sect = §_n->data; + if (str8_match(sect->name, sect_defn->name, 0)) { + if (sect->flags != sect_defn->flags) { + LNK_Obj *obj = sect_defn->obj; + U32 sect_number = sect_defn->obj_sect_idx + 1; + COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, sect_number); + String8 sect_name = coff_name_from_section_header(str8_substr(obj->data, obj->header.string_table_range), sect_header); + String8 expected_flags_str = coff_string_from_section_flags(arena->v[0], sect->flags); + String8 current_flags_str = coff_string_from_section_flags(arena->v[0], sect_defn->flags); + lnk_error_obj(LNK_Warning_SectionFlagsConflict, sect_defn->obj, "detected section flags conflict in %S(No. %X); expected {%S} but got {%S}", sect_name, sect_number, expected_flags_str, current_flags_str); + } + } + } + + ProfBeginV("Reserve Section Contrib Chunks [%S]", sect_defn->name); + LNK_Section *sect = lnk_section_table_search(sectab, sect_defn->name, sect_defn->flags); + if (sect == 0) { + sect = lnk_section_table_push(sectab, sect_defn->name, sect_defn->flags); + } + AssertAlways(sect->contribs.chunk_count == 0); + lnk_section_contrib_chunk_list_push_chunk(sectab->arena, §->contribs, sect_defn->contribs_count); + ProfEnd(); + + } + ProfEnd(); + + tp_temp_end(temp); + ProfEnd(); + } + + U64 expected_image_header_size; + { + ProfBegin("Alloc Section Map"); + task.sect_map = push_array(scratch.arena, LNK_SectionContrib **, objs_count); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) task.sect_map[obj_idx] = push_array(scratch.arena, LNK_SectionContrib *, objs[obj_idx]->header.section_count_no_null); + ProfEnd(); + + ProfBegin("Gather Section Contribs"); + tp_for_parallel(tp, 0, objs_count, lnk_gather_section_contribs_task, &task); + ProfEnd(); + + ProfBegin("Set COMDAT Leaders"); + tp_for_parallel(tp, 0, objs_count, lnk_set_comdat_leaders_task, &task); + ProfEnd(); + + // finalize sections layouts + { + ProfBegin("Finalize Sections Layout"); + + // merge sections + if (config->flags & LNK_ConfigFlag_Merge) { + lnk_section_table_merge(sectab, config->merge_list); + } + + ProfBegin("Sort Section Contribs"); + { + U64 total_chunk_count = 0; + LNK_SectionContribChunk **chunks = 0; + { + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + total_chunk_count += sect_n->data.contribs.chunk_count; + } + U64 cursor = 0; + chunks = push_array(scratch.arena, LNK_SectionContribChunk *, total_chunk_count); + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + for (LNK_SectionContribChunk *chunk_n = sect_n->data.contribs.first; chunk_n != 0; chunk_n = chunk_n->next) { + chunks[cursor++] = chunk_n; + } + } + Assert(cursor == total_chunk_count); + } + task.u.sort_contribs.chunks = chunks; + tp_for_parallel(tp, 0, total_chunk_count, lnk_sort_contribs_task, &task); + } + ProfEnd(); + + // assign contribs offsets, sizes, and section indices + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + lnk_finalize_section_layout(sectab, §_n->data, config->file_align); + } + + // remove empty sections + { + String8List empty_sect_list = {0}; + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + LNK_Section *sect = §_n->data; + if (sect->vsize == 0) { + str8_list_push(scratch.arena, &empty_sect_list, sect->name); + } + } + for (String8Node *name_n = empty_sect_list.first; name_n != 0; name_n = name_n->next) { + lnk_section_table_remove(sectab, name_n->string); + } + } + + // assign section indices to sections + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + lnk_assign_section_index(§_n->data, sectab->next_sect_idx++); + } + + // assing layout offsets and sizes to merged sections + for (LNK_SectionNode *sect_n = sectab->merge_list.first; sect_n != 0; sect_n = sect_n->next) { + LNK_Section *sect = §_n->data; + LNK_SectionContrib *first_sc = lnk_get_first_section_contrib(sect); + LNK_SectionContrib *last_sc = lnk_get_last_section_contrib(sect); + LNK_Section *final_sect = lnk_finalized_section_from_id(sectab, sect->merge_id); + sect->voff = final_sect->voff + first_sc->u.off; + sect->vsize = (last_sc->u.off - first_sc->u.off) + last_sc->u.size; + sect->foff = final_sect->foff + first_sc->u.off; + sect->fsize = (last_sc->u.off - first_sc->u.off) + last_sc->u.size; + sect->sect_idx = final_sect->sect_idx; + } + + ProfEnd(); + } + + // build common block + // + // TODO: build common block in .bss and merge with .data + U64 common_block_contribs_count; + LNK_CommonBlockContrib *common_block_contribs; + LNK_Section *common_block_sect; + { + ProfBegin("Build Common Block"); + + ProfBegin("Count Contribs"); + common_block_contribs_count = 0; + for (U64 worker_id = 0; worker_id < tp->worker_count; worker_id += 1) { + for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; + chunk != 0; + chunk = chunk->next) { + for (U64 i = 0; i < chunk->count; i += 1) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); + if (parsed_interp == COFF_SymbolValueInterp_Common) { + common_block_contribs_count += 1; + } + } + } + } + ProfEnd(); + + ProfBegin("Gather Contribs"); + common_block_contribs = push_array(scratch.arena, LNK_CommonBlockContrib, common_block_contribs_count); + { + U64 cursor = 0; + for (U64 worker_id = 0; worker_id < tp->worker_count; worker_id += 1) { + for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; + chunk != 0; + chunk = chunk->next) { + for (U64 i = 0; i < chunk->count; i += 1) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); + if (parsed_interp == COFF_SymbolValueInterp_Common) { + LNK_CommonBlockContrib *contrib = &common_block_contribs[cursor++]; + contrib->symbol = chunk->v[i].symbol; + contrib->u.size = parsed_symbol.value; + } + } + } + } + } + ProfEnd(); + + if (common_block_contribs_count) { + ProfBeginV("Assign Common Block Offsets [count %llu]", common_block_contribs_count); + + // search/push .data + common_block_sect = lnk_section_table_search(sectab, str8_lit(".data"), PE_DATA_SECTION_FLAGS); + if (common_block_sect == 0) { + common_block_sect = lnk_section_table_push(sectab, str8_lit(".data"), PE_DATA_SECTION_FLAGS); + } + + // sort common blocks from largest to smallest for tighter packing + radsort(common_block_contribs, common_block_contribs_count, lnk_common_block_contrib_is_before); + + // compute common block offsets + for (U64 contrib_idx = 0; contrib_idx < common_block_contribs_count; contrib_idx += 1) { + LNK_CommonBlockContrib *contrib = &common_block_contribs[contrib_idx]; + U32 size = contrib->u.size; + U32 align = Min(32, u64_up_to_pow2(size)); // link.exe caps align at 32 bytes + common_block_sect->vsize = AlignPow2(common_block_sect->vsize, align); + contrib->u.offset = common_block_sect->vsize; + common_block_sect->vsize += size; + } + ProfEnd(); + } + + ProfEnd(); + } + + // + // patch symbol tables + // + { + ProfBegin("Patch Symbol Tables"); + Temp temp = temp_begin(scratch.arena); + + ProfBegin("Alloc Patch Flags"); + task.u.patch_symtabs.was_symbol_patched = push_array(scratch.arena, B8 *, objs_count); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + task.u.patch_symtabs.was_symbol_patched[obj_idx] = push_array(temp.arena, B8, objs[obj_idx]->header.symbol_count); + } + task.u.patch_symtabs.common_block_sect = common_block_sect; + task.u.patch_symtabs.ranges = tp_divide_work(scratch.arena, common_block_contribs_count, tp->worker_count); + task.u.patch_symtabs.common_block_contribs = common_block_contribs; + ProfEnd(); + + ProfBegin("Patch Common Block Leaders"); + tp_for_parallel(tp, 0, tp->worker_count, lnk_patch_common_block_leaders_task, &task); + ProfEnd(); + + ProfBegin("Patch Regular Symbols"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_regular_symbols_task, &task); + ProfEnd(); + + ProfBegin("Patch Common blocks"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_common_block_symbols_task, &task); + ProfEnd(); + + ProfBegin("Patch Absolute Symbols"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_abs_symbols_task, &task); + ProfEnd(); + + ProfBegin("Patch Undefined Symbols"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_undefined_symbols_task, &task); + ProfEnd(); + + ProfBegin("Patch Weak Symbols With Strong Definition"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_weak_symbols_with_strong_definition_task, &task); + ProfEnd(); + + ProfBegin("Patch Weak Symbols With Fallback Definition"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_weak_symbols_with_fallback_definition_task, &task); + ProfEnd(); + + ProfBegin("Patch Weak Symbols With Undefined Tag"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_weak_symbols_with_undefined_tag_task, &task); + ProfEnd(); + + temp_end(temp); + ProfEnd(); + } + + // section list -> array + task.image_sects = lnk_section_array_from_list(scratch.arena, sectab->list); + + expected_image_header_size = lnk_compute_win32_image_header_size(config, task.image_sects.count); + + // assign virtual space + U64 voff_cursor = AlignPow2(expected_image_header_size + sizeof(COFF_SectionHeader), config->sect_align); + for (U64 i = 0; i < task.image_sects.count; i += 1) { + lnk_assign_section_virtual_space(task.image_sects.v[i], config->sect_align, &voff_cursor); + } + + ProfBegin("Patch Virtual Offsets and SIzes in Obj Section Headers"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_virtual_offsets_and_sizes_in_obj_section_headers_task, &task); + ProfEnd(); + + // build base relocs + if (~config->flags & LNK_ConfigFlag_Fixed) { + String8List base_relocs_data = lnk_build_base_relocs(tp, arena, config, objs_count, objs); + if (base_relocs_data.total_size) { + LNK_Section *reloc = lnk_section_table_push(sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS); + LNK_SectionContribChunk *first_sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &reloc->contribs, 1); + LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(first_sc_chunk, 1); + sc->data_list = base_relocs_data.first; + sc->align = 1; + sc->u.sort_idx_size = 0; + sc->u.obj_idx = max_U32; + + lnk_finalize_section_layout(sectab, reloc, config->file_align); + lnk_assign_section_virtual_space(reloc, config->sect_align, &voff_cursor); + lnk_assign_section_index(reloc, sectab->next_sect_idx++); + + task.image_sects = lnk_section_array_from_list(scratch.arena, sectab->list); + expected_image_header_size = lnk_compute_win32_image_header_size(config, task.image_sects.count); + } + } + + // assign file space + U64 foff_cursor = AlignPow2(expected_image_header_size, config->file_align); + for (U64 i = 0; i < task.image_sects.count; i += 1) { + lnk_assign_section_file_space(task.image_sects.v[i], &foff_cursor); + } + + ProfBegin("Patch File Offsets And Sizes In Section Headers"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_file_offsets_and_sizes_in_obj_section_headers_task, &task); + ProfEnd(); + } + + // build win32 image header + { + String8List image_header_data = lnk_build_win32_header(scratch.arena, symtab, config, task.image_sects, AlignPow2(expected_image_header_size, config->file_align)); + + LNK_Section *image_header_sect = lnk_section_table_push(sectab, str8_lit(".rad_linker_image_header_section"), 0); + LNK_SectionContribChunk *image_header_sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &image_header_sect->contribs, 1); + LNK_SectionContrib *image_header_sc = lnk_section_contrib_chunk_push(image_header_sc_chunk, 1); + + image_header_sc->align = config->file_align; + image_header_sc->data_list = image_header_data.first; + image_header_sc->u.size = safe_cast_u32(image_header_data.total_size); + + lnk_finalize_section_layout(sectab, image_header_sect, config->file_align); + } + + ProfBegin("Patch Section Symbols"); + tp_for_parallel(tp, 0, task.objs_count, lnk_patch_section_symbols_task, &task); + ProfEnd(); + + String8 image_data = {0}; + { + ProfBegin("Image Fill"); + + LNK_SectionArray sects = lnk_section_array_from_list(scratch.arena, sectab->list); + + U64 image_size = 0; + for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + image_size += sects.v[sect_idx]->fsize; + } + + image_data.size = image_size; + image_data.str = push_array_no_zero(arena->v[0], U8, image_size); + + for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + LNK_Section *sect = sects.v[sect_idx]; + + if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { + // pick fill pick + U8 fill_byte = 0; + if (sect->flags & COFF_SectionFlag_CntCode) { + fill_byte = lnk_code_align_byte_from_machine(config->machine); + } + + // copy section contribution + U64 prev_sc_opl = 0; + for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; + + // fill align bytes + Assert(sc->u.off >= prev_sc_opl); + U64 fill_size = sc->u.off - prev_sc_opl; + MemorySet(image_data.str + sect->foff + prev_sc_opl, fill_byte, fill_size); + prev_sc_opl = sc->u.off + sc->u.size; + + // copy contrib contents + { + U64 cursor = 0; + for (String8Node *data_n = sc->data_list; data_n != 0; data_n = data_n->next) { + Assert(sc->u.off + data_n->string.size <= sect->vsize); + MemoryCopy(image_data.str + sect->foff + sc->u.off + cursor, data_n->string.str, data_n->string.size); + cursor += data_n->string.size; + } + } + } + } + + // fill section align bytes + { + U64 fill_size = sect->fsize - prev_sc_opl; + MemorySet(image_data.str + sect->foff + prev_sc_opl, fill_byte, fill_size); + } + } + } + + ProfEnd(); + } + + { + ProfBegin("Image Patch"); + + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data); + COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range); + + // patch relocs + { + ProfBegin("Patch Relocs"); + LNK_ObjRelocPatcher task = {0}; + task.image_data = image_data; + task.objs = objs; + task.image_base = pe.image_base; + task.image_section_table = image_section_table; + tp_for_parallel(tp, 0, objs_count, lnk_obj_reloc_patcher, &task); + ProfEnd(); + } + + // patch load config + { + LNK_Symbol *load_config_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME)); + if (load_config_symbol) { + U64 load_config_foff = lnk_file_off_from_symbol(image_section_table, load_config_symbol); + String8 load_config_data = str8_skip(image_data, load_config_foff); + + U32 load_config_size = 0; + if (sizeof(load_config_size) <= load_config_data.size) { + PE_DataDirectory *load_config_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_LOAD_CONFIG); + load_config_dir->virt_off = lnk_virt_off_from_symbol(image_section_table, load_config_symbol); + load_config_dir->virt_size = load_config_size; + } else { + // TODO: report corrupted load config + } + } + } + + // patch exceptions + { + LNK_Section *pdata_sect = lnk_section_table_search(sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS); + if (pdata_sect) { + String8 raw_pdata = str8_substr(image_data, rng_1u64(pdata_sect->foff, pdata_sect->foff + pdata_sect->vsize)); + pe_pdata_sort(config->machine, raw_pdata); + + PE_DataDirectory *pdata_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_EXCEPTIONS); + pdata_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, pdata_sect); + pdata_dir->virt_size = lnk_get_section_contrib_size(pdata_sect); + } + } + + // patch export + { + LNK_Section *edata_sect = lnk_section_table_search(sectab, str8_lit(".edata"), PE_EDATA_SECTION_FLAGS); + if (edata_sect) { + PE_DataDirectory *export_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_EXPORT); + LNK_SectionContrib *edata_first_contrib = lnk_get_first_section_contrib(edata_sect); + LNK_SectionContrib *edata_last_contrib = lnk_get_last_section_contrib(edata_sect); + export_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, edata_sect); + export_dir->virt_size = lnk_get_section_contrib_size(edata_sect); + } + } + + // patch base relocs + { + LNK_Section *reloc_sect = lnk_section_table_search(sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS); + if (reloc_sect) { + PE_DataDirectory *reloc_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_BASE_RELOC); + reloc_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, reloc_sect); + reloc_dir->virt_size = lnk_get_section_contrib_size(reloc_sect); + } + } + + // patch import and import addr + { + LNK_Section *idata_sect = lnk_section_table_search(sectab, str8_lit(".idata"), PE_IDATA_SECTION_FLAGS); + LNK_Symbol *null_import_desc = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "__NULL_IMPORT_DESCRIPTOR"); + LNK_Symbol *null_thunk_data = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "\x7f%S_NULL_THUNK_DATA", lnk_get_image_name(config)); + if (idata_sect && null_import_desc && null_thunk_data) { + COFF_ParsedSymbol null_import_desc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_import_desc->u.defined.obj, null_import_desc->u.defined.symbol_idx); + LNK_SectionContrib *idata_first_contrib = lnk_get_first_section_contrib(idata_sect); + PE_DataDirectory *import_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_IMPORT); + import_dir->virt_off = image_section_table[idata_first_contrib->u.sect_idx + 1]->voff + idata_first_contrib->u.off; + import_dir->virt_size = null_import_desc_parsed.value - idata_first_contrib->u.off; + + COFF_ParsedSymbol null_thunk_data_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_thunk_data->u.defined.obj, null_thunk_data->u.defined.symbol_idx); + U64 null_thunk_data_voff = image_section_table[null_thunk_data_parsed.section_number]->voff + null_thunk_data_parsed.value; + U64 first_import_foff = image_section_table[idata_first_contrib->u.sect_idx+1]->foff + idata_first_contrib->u.off; + PE_ImportEntry *first_import = str8_deserial_get_raw_ptr(image_data, first_import_foff, sizeof(*first_import)); + PE_DataDirectory *import_addr_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_IMPORT_ADDR); + import_addr_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, idata_sect); + import_addr_dir->virt_size = null_thunk_data_voff - first_import->import_addr_table_voff /* null */ + coff_word_size_from_machine(config->machine); + } + } + + // patch delay imports + { + LNK_Section *didat_sect = lnk_section_table_search(sectab, str8_lit(".didat"), PE_IDATA_SECTION_FLAGS); + LNK_Symbol *null_import_desc = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("__NULL_DELAY_IMPORT_DESCRIPTOR")); + LNK_Symbol *last_null_thunk = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "\x7f%S_NULL_THUNK_DATA_DLA", lnk_get_image_name(config)); + if (didat_sect && null_import_desc && last_null_thunk) { + COFF_ParsedSymbol null_import_desc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_import_desc->u.defined.obj, null_import_desc->u.defined.symbol_idx); + LNK_SectionContrib *didat_first_contrib = lnk_get_first_section_contrib(didat_sect); + PE_DataDirectory *import_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_DELAY_IMPORT); + import_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, didat_sect); + import_dir->virt_size = lnk_get_section_contrib_size(didat_sect); + } + } + + // patch TLS + { + LNK_Symbol *tls_used_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, MSCRT_TLS_SYMBOL_NAME); + if (tls_used_symbol) { + ProfBegin("Patch TLS"); + + // find max align in .tls + U64 tls_align = 0; + LNK_Section *tls_sect = lnk_section_table_search(sectab, str8_lit(".tls"), PE_TLS_SECTION_FLAGS); + for (LNK_SectionContribChunk *sc_chunk = tls_sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + Assert(IsPow2(sc_chunk->v[sc_idx]->align)); + tls_align = Max(tls_align, sc_chunk->v[sc_idx]->align); + } + } + + // patch-in align + U64 tls_header_foff = lnk_file_off_from_symbol(image_section_table, tls_used_symbol); + B32 is_tls_header64 = coff_word_size_from_machine(config->machine) == 8; + if (is_tls_header64) { + PE_TLSHeader64 *tls_header = str8_deserial_get_raw_ptr(image_data, tls_header_foff, sizeof(*tls_header)); + tls_header->characteristics |= coff_section_flag_from_align_size(tls_align); + } else { + PE_TLSHeader32 *tls_header = str8_deserial_get_raw_ptr(image_data, tls_header_foff, sizeof(*tls_header)); + tls_header->characteristics |= coff_section_flag_from_align_size(tls_align); + } + + // patch directory + PE_DataDirectory *tls_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_TLS); + tls_dir->virt_off = lnk_virt_off_from_symbol(image_section_table, tls_used_symbol); + tls_dir->virt_size = is_tls_header64 ? sizeof(PE_TLSHeader64) : sizeof(PE_TLSHeader32); + + ProfEnd(); + } + } + + // patch debug + { + LNK_Section *debug_dir_sect = lnk_section_table_search(sectab, str8_lit(".RAD_LINK_PE_DEBUG_DIR"), PE_RDATA_SECTION_FLAGS); + if (debug_dir_sect) { + // patch directory + PE_DataDirectory *debug_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_DEBUG); + debug_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, debug_dir_sect); + debug_dir->virt_size = lnk_get_section_contrib_size(debug_dir_sect); + + // find debug directory begin and end pair + LNK_SectionContrib *first_sc = lnk_get_first_section_contrib(debug_dir_sect); + LNK_SectionContrib *last_sc = lnk_get_last_section_contrib(debug_dir_sect); + U64 debug_begin_foff = lnk_foff_from_section_contrib(image_section_table, first_sc); + U64 debug_end_fopl = lnk_fopl_from_section_contrib(image_section_table, last_sc); + + // patch file offsets to the debug directories + for (U64 cursor = debug_begin_foff; cursor + sizeof(PE_DebugDirectory) <= debug_end_fopl; cursor += sizeof(PE_DebugDirectory)) { + PE_DebugDirectory *dir = str8_deserial_get_raw_ptr(image_data, cursor, sizeof(PE_DebugDirectory)); + for (U64 section_number = 1; section_number < pe.section_count+1; section_number += 1) { + if (image_section_table[section_number]->voff <= dir->voff && dir->voff < image_section_table[section_number]->voff + image_section_table[section_number]->vsize) { + dir->foff = image_section_table[section_number]->foff + (dir->voff - image_section_table[section_number]->voff); + } + } + } + } + } + + // patch resources + { + LNK_Section *rsrc_sect = lnk_section_table_search(sectab, str8_lit(".rsrc"), PE_RSRC_SECTION_FLAGS); + if (rsrc_sect) { + PE_DataDirectory *rsrc_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_RESOURCES); + rsrc_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, rsrc_sect); + rsrc_dir->virt_size = lnk_get_section_contrib_size(rsrc_sect); + } + } + + // image checksum + if (config->flags & LNK_ConfigFlag_WriteImageChecksum) { + ProfBegin("Image Checksum"); + *pe.check_sum = pe_compute_checksum(image_data.str, image_data.size); + ProfEnd(); + } + + // compute image guid, and patch PDB and RDI guids + { + LNK_Symbol *guid_pdb_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINK_PE_DEBUG_GUID_PDB")); + LNK_Symbol *guid_rdi_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINK_PE_DEBUG_GUID_RDI")); + + if (guid_pdb_symbol || guid_rdi_symbol) { + switch (config->guid_type) { + case LNK_DebugInfoGuid_Null: break; + case Lnk_DebugInfoGuid_ImageBlake3: { + ProfBegin("Hash Image With Blake3"); + U128 hash = lnk_blake3_hash_parallel(tp, 128, image_data); + MemoryCopy(&config->guid, hash.u8, sizeof(hash.u8)); + ProfEnd(); + } break; + } + } + + if (guid_pdb_symbol) { + U64 cv_guid_foff = lnk_file_off_from_symbol(image_section_table, guid_pdb_symbol); + Guid *cv_guid = str8_deserial_get_raw_ptr(image_data, cv_guid_foff, sizeof(*cv_guid)); + *cv_guid = config->guid; + } + + if (guid_rdi_symbol) { + U64 cv_guid_foff = lnk_file_off_from_symbol(image_section_table, guid_rdi_symbol); + Guid *cv_guid = str8_deserial_get_raw_ptr(image_data, cv_guid_foff, sizeof(*cv_guid)); + *cv_guid = config->guid; + } + } + + ProfEnd(); + } + + LNK_ImageContext image_ctx = {0}; + image_ctx.image_data = image_data; + image_ctx.sectab = sectab; + + lnk_timer_end(LNK_Timer_Image); + ProfEnd(); // :EndImage + scratch_end(scratch); + return image_ctx; +} + +internal String8List +lnk_build_rad_map(Arena *arena, String8 image_data, U64 thread_count, U64 objs_count, LNK_Obj **objs, LNK_LibList lib_index[LNK_InputSource_Count], LNK_SectionTable *sectab) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(&arena, 1); + + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data); + COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range); + + String8List map = {0}; + + ProfBegin("SECTIONS"); + str8_list_pushf(arena, &map, "# SECTIONS\n"); + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + LNK_Section *sect = §_n->data; + + str8_list_pushf(arena, &map, "%S\n", sect->name); + str8_list_pushf(arena, &map, "%-8s %-8s %-8s %-8s %-16s %-8s %s\n", "FileOff", "VirtOff", "VirtSize", "FileSize", "Blake3", "SC", "Source"); + + for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { + for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + Temp temp = temp_begin(scratch.arena); + LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; + + U64 file_off = image_section_table[sc->u.sect_idx]->foff + sc->u.off; + U64 virt_off = image_section_table[sc->u.sect_idx]->voff + sc->u.off; + U64 virt_size = sc->u.size; + U64 file_size = sc->u.size; + String8 sc_data = str8_substr(image_data, rng_1u64(file_off, file_off + virt_size)); + + U128 sc_hash = {0}; + if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { + blake3_hasher hasher; blake3_hasher_init(&hasher); + blake3_hasher_update(&hasher, sc_data.str, sc_data.size); + blake3_hasher_finalize(&hasher, (U8 *)&sc_hash, sizeof(sc_hash)); + } + + String8 file_off_str = push_str8f(temp.arena, "%08x", file_off); + String8 virt_off_str = push_str8f(temp.arena, "%08x", virt_off); + String8 virt_size_str = push_str8f(temp.arena, "%08x", virt_size); + String8 file_size_str = push_str8f(temp.arena, "%08x", file_size); + String8 sc_hash_str = push_str8f(temp.arena, "%08x%08x", sc_hash.u64[0], sc_hash.u64[1]); + String8 sc_idx_str = push_str8f(temp.arena, "%llx", sc_idx); + String8 source_str; + { + String8List source_list = {0}; + +#if 0 + // location + if (chunk->obj) { + if (chunk->obj->lib_path.size) { + String8 lib_name = chunk->obj->lib_path; + lib_name = str8_skip_last_slash(lib_name); + lib_name = str8_chop_last_dot(lib_name); + + String8 obj_name = chunk->obj->path; + obj_name = str8_skip_last_slash(obj_name); + + str8_list_pushf(temp.arena, &source_list, "%S:%S", lib_name, obj_name); + } else { + str8_list_push(temp.arena, &source_list, chunk->obj->path); + } + } +#else + str8_list_pushf(temp.arena, &source_list, ""); +#endif + + // string join + source_str = str8_list_join(temp.arena, &source_list, &(StringJoin){.sep=str8_lit(" ")}); + } + + str8_list_pushf(arena, &map, "%-8S %-8S %-8S %-8S %-16S %-8S %S\n", file_off_str, virt_off_str, virt_size_str, file_size_str, sc_hash, sc_idx_str, source_str); + + temp_end(temp); + } + } + str8_list_pushf(arena, &map, "\n"); + } + ProfEnd(); + + + ProfBegin("SYMBOLS"); + str8_list_pushf(arena, &map, "# SYMBOLS\n"); + str8_list_pushf(arena, &map, "%-8s %s\n", "Sect:Idx", "Symbol"); + for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { + LNK_Obj *obj = objs[obj_idx]; + + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + String8 sc = push_str8f(scratch.arena, "%x:%x", symbol.section_number, symbol.value); + + String8 lib_name = obj->lib_path; + lib_name = str8_skip_last_slash(lib_name); + lib_name = str8_chop_last_dot(lib_name); + + String8 obj_name = obj->path; + obj_name = str8_skip_last_slash(obj_name); + + str8_list_pushf(arena, &map, "%-8S (%S%s%S) %S\n", + sc, + lib_name, lib_name.size ? ":" : "", obj_name, + symbol.name); + } + } + } + str8_list_pushf(arena, &map, "\n"); + ProfEnd(); + + + ProfBegin("LIBS"); + for (U64 input_source = 0; input_source < LNK_InputSource_Count; ++input_source) { + if (lib_index[input_source].count) { + str8_list_pushf(arena, &map, "# LIBS (%S)\n", lnk_string_from_input_source(input_source)); + for (LNK_LibNode *lib_n = lib_index[input_source].first; lib_n != 0; lib_n = lib_n->next) { + str8_list_pushf(arena, &map, "%S\n", lib_n->data.path); + } + } + } + ProfEnd(); + + + scratch_end(scratch); + ProfEnd(); + return map; +} + internal void lnk_write_thread(void *raw_ctx) { diff --git a/src/linker/lnk.h b/src/linker/lnk.h index ad7fca18..e7772cbe 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -50,6 +50,31 @@ typedef struct LNK_BaseRelocPageArray // --- Workers Contexts -------------------------------------------------------- +typedef struct +{ + LNK_SymbolTable *symtab; + LNK_SectionTable *sectab; + U64 default_align; + U64 objs_count; + LNK_Obj **objs; + LNK_SectionContrib ***sect_map; + LNK_SectionArray image_sects; + union { + struct { + HashTable **defns; + } gather_sects; + struct { + LNK_SectionContribChunk **chunks; + } sort_contribs; + struct { + B8 **was_symbol_patched; + LNK_Section *common_block_sect; + Rng1U64 *ranges; + LNK_CommonBlockContrib *common_block_contribs; + } patch_symtabs; + } u; +} LNK_BuildImageTask; + typedef struct { U64 page_size; @@ -145,7 +170,7 @@ internal String8 lnk_manifest_from_inputs(Arena *arena, String8 mt_path, String8 internal String8 lnk_make_res_obj(Arena *arena, String8List res_file_list, String8List res_path_list, COFF_MachineType machine, U32 time_stamp, String8 work_dir, PathStyle system_path_style, String8 obj_name); internal String8 lnk_make_linker_coff_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 cwd_path, String8 exe_path, String8 pdb_path, String8 cmd_line, String8 obj_name); -// --- Symbol Resolver --------------------------------------------------------- +// --- Link Context ------------------------------------------------------------ internal String8 lnk_get_lib_name(String8 path); internal B32 lnk_is_lib_disallowed(HashTable *disallow_lib_ht, String8 path); @@ -156,9 +181,10 @@ internal void lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, Str internal LNK_InputObjList lnk_push_linker_symbols(Arena *arena, LNK_Config *config); internal void lnk_queue_lib_member_input(Arena *arena, PathStyle path_style, LNK_SymbolLib *symbol, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list); +internal LNK_LinkContext lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config); + // --- Win32 Image ------------------------------------------------------------- -internal LNK_LinkContext lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config); internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag); internal String8List lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_temp, LNK_Config *config, U64 objs_count, LNK_Obj **objs); internal String8List lnk_build_win32_image_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sect_arr, U64 expected_image_header_size); diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index a1fca3da..893c77f4 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -1740,7 +1740,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Rad_PageSize: { - lnk_cmd_switch_parse_u64(obj_path, lib_path, cmd_switch, value_strings, &config->page_size, 0); + lnk_cmd_switch_parse_u64(obj_path, lib_path, cmd_switch, value_strings, &config->machine_page_size, 0); } break; case LNK_CmdSwitch_Rad_PathStyle: { @@ -1935,13 +1935,13 @@ lnk_unwrap_rsp(Arena *arena, String8List arg_list) } internal LNK_Config * -lnk_config_from_cmd_line(Arena *arena, LNK_CmdLine cmd_line) +lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd_line) { ProfBeginFunction(); Temp scratch = scratch_begin(&arena, 1); LNK_Config *config = push_array(arena, LNK_Config, 1); - config->raw_cmd_line = str8_list_copy(arena, &cmd_line.raw_cmd_line); + config->raw_cmd_line = str8_list_copy(arena, &raw_cmd_line); config->work_dir = os_get_current_path(arena); config->build_imp_lib = 1; config->build_exp = 1; diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index fdf7b4ae..b5377b92 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -319,7 +319,7 @@ typedef struct LNK_Config U64 heap_commit; U64 user_base_addr; U64 max_image_size; - U64 page_size; + U64 machine_page_size; U64 pdb_page_size; U64 worker_count; U64 max_worker_count; @@ -583,6 +583,3 @@ internal B32 lnk_parse_merge_directive(String8 string, LNK_MergeDirective *parse internal void lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 name, String8List value_list, String8 obj_path, String8 lib_path); -internal LNK_Config * lnk_config_from_raw_cmd_line(Arena *arena, String8List raw_cmd_line); -internal LNK_Config * lnk_build_config (Arena *arena, int argc, char **argv); -