From 7466da14792e06ef1b8040482323104421f790c0 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Fri, 8 Aug 2025 15:53:55 -0700 Subject: [PATCH] mark referenced symbols as live for unresolved-symbol detection --- src/coff/coff_parse.c | 7 ++++ src/coff/coff_parse.h | 1 + src/linker/lnk.c | 24 +++++++------ src/linker/lnk.h | 6 ++-- src/linker/lnk_obj.c | 14 ++++---- src/torture/torture.c | 83 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 22 deletions(-) diff --git a/src/coff/coff_parse.c b/src/coff/coff_parse.c index f8b2a3a4..e3bd1dff 100644 --- a/src/coff/coff_parse.c +++ b/src/coff/coff_parse.c @@ -285,6 +285,13 @@ coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol) return coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); } +internal B32 +coff_is_undefined_data_symbol(COFF_ParsedSymbol symbol) +{ + COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol); + return interp == COFF_SymbolValueInterp_Undefined && symbol.storage_class == COFF_SymStorageClass_External; +} + internal void coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out) { diff --git a/src/coff/coff_parse.h b/src/coff/coff_parse.h index 92106867..3125c1f8 100644 --- a/src/coff/coff_parse.h +++ b/src/coff/coff_parse.h @@ -268,6 +268,7 @@ internal COFF_Symbol16Node *coff_symbol16_list_push(Arena *arena, COFF_Symbol16L internal COFF_SymbolValueInterpType coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_class); internal COFF_SymbolValueInterpType coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol); +internal B32 coff_is_undefined_data_symbol(COFF_ParsedSymbol symbol); internal void coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out); internal COFF_SymbolWeakExt * coff_parse_weak_tag(COFF_ParsedSymbol symbol, B32 is_big_obj); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 1ce0b985..54ee97a0 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -1101,7 +1101,7 @@ lnk_find_refs(Arena *arena, } // - // walk relocations and mark referenced sections as live + // walk relocations and mark referenced sections with live flag // for (; task_stack; ) { struct Task *t = task_stack; SLLStackPop(task_stack); @@ -1115,12 +1115,10 @@ lnk_find_refs(Arena *arena, for (;;) { COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx); COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed); - if (ref_interp == COFF_SymbolValueInterp_Weak) { LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, ref_parsed.name); COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn); COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed); - if (defn_interp == COFF_SymbolValueInterp_Weak) { LNK_Symbol *member_symbol = 0; COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(ref_parsed, ref_symbol.obj->header.is_big_obj); @@ -1172,12 +1170,11 @@ lnk_find_refs(Arena *arena, } } else if (ref_interp == COFF_SymbolValueInterp_Undefined) { - MemoryZeroStruct(&ref_symbol); - if (ref_parsed.storage_class == COFF_SymStorageClass_External) { LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, ref_parsed.name); COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn); COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed); + if (defn_interp == COFF_SymbolValueInterp_Undefined) { LNK_Symbol *member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, ref_parsed.name); if (member_symbol) { @@ -1208,6 +1205,13 @@ lnk_find_refs(Arena *arena, COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed); LNK_Obj *ref_obj = ref_symbol.obj; + // mark referenced symbol live + if (ref_parsed.storage_class == COFF_SymStorageClass_External || + ref_parsed.storage_class == COFF_SymStorageClass_WeakExternal) { + LNK_Symbol *ref_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, ref_parsed.name); + lnk_mark_symbol_live(ref_symbol); + } + if (ref_interp == COFF_SymbolValueInterp_Regular) { // make section number list (reloc section + associates) U32Node *section_number_list = push_array(scratch.arena, U32Node, 1); @@ -2122,9 +2126,8 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) for EachIndex(i, chunk->count) { LNK_Symbol *symbol = chunk->v[i].symbol; if (symbol->is_live) { - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); - if (symbol_interp == COFF_SymbolValueInterp_Undefined) { + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); + if (coff_is_undefined_data_symbol(symbol_parsed)) { undefs_count += 1; } } @@ -2138,9 +2141,8 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) for EachIndex(i, chunk->count) { LNK_Symbol *symbol = chunk->v[i].symbol; if (symbol->is_live) { - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); - if (symbol_interp == COFF_SymbolValueInterp_Undefined) { + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); + if (coff_is_undefined_data_symbol(symbol_parsed)) { undefs[undefs_cursor++] = chunk->v[i].symbol; } } diff --git a/src/linker/lnk.h b/src/linker/lnk.h index d25c18da..d2150f06 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -5,6 +5,9 @@ // --- Link -------------------------------------------------------------------- +#define LNK_IMPORT_STUB "*** RAD_IMPORT_STUB ***" +#define LNK_SECTION_FLAG_IS_LIVE (1 << 0) + typedef struct LNK_LinkContext { LNK_SymbolTable *symtab; @@ -15,12 +18,9 @@ typedef struct LNK_LinkContext // -- Image -------------------------------------------------------------------- -#define LNK_IMPORT_STUB "*** RAD_IMPORT_STUB ***" #define LNK_REMOVED_SECTION_NUMBER_32 (U32)-3 #define LNK_REMOVED_SECTION_NUMBER_16 (U16)-3 -#define LNK_SECTION_FLAG_IS_LIVE (1 << 0) - typedef struct LNK_ImageContext { String8 image_data; diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index 8af306fa..f27d7088 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -345,11 +345,9 @@ THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table) { LNK_InputCoffSymbolTable *task = raw_task; LNK_Obj *obj = &task->objs.v[task_id].data; - COFF_ParsedSymbol symbol = {0}; 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_from_parsed_symbol(symbol); switch (interp) { case COFF_SymbolValueInterp_Regular: { @@ -366,17 +364,17 @@ THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table) LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); } break; - case COFF_SymbolValueInterp_Common: { - LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); - lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); - } break; - case COFF_SymbolValueInterp_Abs: { + case COFF_SymbolValueInterp_Undefined: { if (symbol.storage_class == COFF_SymStorageClass_External) { LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); } } break; - case COFF_SymbolValueInterp_Undefined: { + case COFF_SymbolValueInterp_Common: { + LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); + lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); + } break; + case COFF_SymbolValueInterp_Abs: { if (symbol.storage_class == COFF_SymStorageClass_External) { LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); diff --git a/src/torture/torture.c b/src/torture/torture.c index 0aa3f83d..ebbdb025 100644 --- a/src/torture/torture.c +++ b/src/torture/torture.c @@ -743,6 +743,87 @@ exit:; return result; } +internal T_Result +t_link_undef(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 undef_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); + undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("undef.obj"), undef_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // try linking unresolved symbol and see if linker picks up on that + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj undef.obj"); + if (linker_exit_code != LNK_Error_UnresolvedSymbol) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_link_unref_undef(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 undef_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); + undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("undef.obj"), undef_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // try linking unreferenced unresolved symbol, this must link + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj undef.obj"); + if (linker_exit_code != 0) { goto exit; } + + result = T_Result_Pass; + exit:; + scratch_end(scratch); + return result; +} + internal T_Result t_weak_vs_weak(void) { @@ -3910,6 +3991,8 @@ entry_point(CmdLine *cmdline) { "merge", t_merge }, { "undef_section", t_undef_section }, { "undef_reloc_section", t_undef_reloc_section }, + { "link_undef", t_link_undef }, + { "link_unref_undef", t_link_unref_undef }, { "weak_vs_weak", t_weak_vs_weak }, { "weak_vs_common", t_weak_vs_common }, { "abs_vs_weak", t_abs_vs_weak },