diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 6c75ca0c..df5523af 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -2201,6 +2201,44 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S U64 objs_count = obj_list.count; LNK_Obj **objs = lnk_obj_arr_from_list(scratch.arena, obj_list); + ProfBegin("Remove Associatives"); + { + 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); diff --git a/src/linker/lnk_error.h b/src/linker/lnk_error.h index 6d728231..6936777d 100644 --- a/src/linker/lnk_error.h +++ b/src/linker/lnk_error.h @@ -38,6 +38,7 @@ typedef enum LNK_Error_IllegalRelocation, LNK_Error_CircularMerge, LNK_Error_UnresolvedSymbol, + LNK_Error_AssociativeLoop, LNK_Error_StopLast, LNK_Error_First, diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index 8446ca41..155e32a9 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -116,6 +116,35 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) } } + // + // error check symbols + // + { + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(input->data, header.section_table_range).str; + String8 string_table = str8_substr(input->data, header.string_table_range); + String8 symbol_table = str8_substr(input->data, header.symbol_table_range); + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = coff_parse_symbol(header, string_table, symbol_table, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + if (symbol.section_number == 0 || symbol.section_number > header.section_count_no_null) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "symbol %S (No. 0x%x) points to an out of bounds section 0x%x", symbol.name, symbol_idx, symbol.section_number); + } + if (symbol.storage_class == COFF_SymStorageClass_Static && symbol.aux_symbol_count > 0) { + COFF_ComdatSelectType select; + U32 section_number = 0; + coff_parse_secdef(symbol, header.is_big_obj, &select, §ion_number, 0, 0); + if (select == COFF_ComdatSelect_Associative) { + if (section_number == 0 || section_number > header.section_count_no_null) { + lnk_error_with_loc(LNK_Error_IllData, input->path, input->lib_path, "section definition symbol %S (No. 0x%x) associates with an out of bounds section 0x%x", symbol.name, symbol_idx, symbol.section_number); + } + } + } + } + } + } + // // create symbol links to COMDAT sections // @@ -128,11 +157,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) String8 symbol_table = str8_substr(input->data, header.symbol_table_range); COFF_ParsedSymbol symbol; for (U64 symbol_idx = 0; symbol_idx < header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - if (header.is_big_obj) { - symbol = coff_parse_symbol32(string_table, (COFF_Symbol32 *)symbol_table.str + symbol_idx); - } else { - symbol = coff_parse_symbol16(string_table, (COFF_Symbol16 *)symbol_table.str + symbol_idx); - } + symbol = coff_parse_symbol(header, string_table, symbol_table, symbol_idx); COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); if (interp == COFF_SymbolValueInterp_Regular) { @@ -162,6 +187,57 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) } } + // + // COMDAT loop checker + // + { + Temp scratch = scratch_begin(&arena, 1); + + String8 string_table = str8_substr(input->data, header.string_table_range); + String8 symbol_table = str8_substr(input->data, header.symbol_table_range); + HashTable *visited_sections = hash_table_init(scratch.arena, 32); + for (U64 sect_idx = 0; sect_idx < header.section_count_no_null; sect_idx += 1) { + for (U32 curr_section = sect_idx;;) { + U32 symbol_idx = comdats[curr_section]; + + // is section COMDAT? + if (symbol_idx == max_U32) { + break; + } + + // extract COMDAT info for current section + COFF_ParsedSymbol symbol = coff_parse_symbol(header, string_table, symbol_table, symbol_idx); + COFF_ComdatSelectType select = COFF_ComdatSelect_Null; + U32 section_number = 0; + coff_parse_secdef(symbol, header.is_big_obj, &select, §ion_number, 0, 0); + + if (select != COFF_ComdatSelect_Associative) { + // section terminates at non-associative COMDAT -- no loop + break; + } + + // was section visited? -- loop found + if (hash_table_search_u64(visited_sections, curr_section)) { + COFF_ParsedSymbol symbol = coff_parse_symbol(header, string_table, symbol_table, comdats[sect_idx]); + lnk_error_with_loc(LNK_Error_AssociativeLoop, input->path, input->lib_path, "section symbol %S (No. 0x%x) does not terminate on a non-associate COMDAT symbol", symbol.name, comdats[sect_idx]); + break; + } + + // track visited sections + hash_table_push_u64_u64(scratch.arena, visited_sections, curr_section, 0); + + // follow association + Assert(section_number > 0); + curr_section = section_number-1; + } + + // purge hash table for next run + hash_table_purge(visited_sections); + } + + scratch_end(scratch); + } + // fill out obj obj->data = input->data; obj->path = push_str8_copy(arena, input->path); diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index 8cff8fab..b61ea002 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -229,13 +229,11 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) can_replace = 0; } // weak vs regular,common,abs - else if (dst_interp == COFF_SymbolValueInterp_Weak && - (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common || src_interp == COFF_SymbolValueInterp_Abs)) { + else if (dst_interp == COFF_SymbolValueInterp_Weak && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common || src_interp == COFF_SymbolValueInterp_Abs)) { can_replace = 1; } // regular,common vs regular,common - else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Common) && - (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common)) { + else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Common) && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common)) { U32 dst_comdat_symbol_idx = dst_obj->comdats[dst_parsed.section_number-1]; U32 src_comdat_symbol_idx = src_obj->comdats[src_parsed.section_number-1]; if (dst_comdat_symbol_idx == ~0 || src_comdat_symbol_idx == ~0) {