diff --git a/src/base/base_strings.c b/src/base/base_strings.c index fde890c5..ab34f01a 100644 --- a/src/base/base_strings.c +++ b/src/base/base_strings.c @@ -522,6 +522,17 @@ push_str8f(Arena *arena, char *fmt, ...){ return(result); } +internal String8 +push_cstr(Arena *arena, String8 str) +{ + U64 buffer_size = str.size + 1; + U8 *buffer = push_array_no_zero(arena, U8, buffer_size); + MemoryCopy(buffer, str.str, str.size); + buffer[str.size] = 0; + String8 result = str8(buffer, buffer_size); + return result; +} + //////////////////////////////// //~ rjf: String <=> Integer Conversions diff --git a/src/base/base_strings.h b/src/base/base_strings.h index 7b9b3d20..14e71c74 100644 --- a/src/base/base_strings.h +++ b/src/base/base_strings.h @@ -232,6 +232,7 @@ internal String8 push_str8_cat(Arena *arena, String8 s1, String8 s2); internal String8 push_str8_copy(Arena *arena, String8 s); internal String8 push_str8fv(Arena *arena, char *fmt, va_list args); internal String8 push_str8f(Arena *arena, char *fmt, ...); +internal String8 push_cstr(Arena *arena, String8 str); //////////////////////////////// //~ rjf: String <=> Integer Conversions diff --git a/src/coff/coff.c b/src/coff/coff.c index 1d68d786..a474f698 100644 --- a/src/coff/coff.c +++ b/src/coff/coff.c @@ -253,18 +253,6 @@ coff_pick_reloc_value_x64(COFF_Reloc_X64 type, return result; } -internal COFF_RelocType -coff_virt_off_reloc_from_machine(COFF_MachineType machine) -{ - COFF_RelocType result = 0; - switch (machine) { - case COFF_MachineType_Unknown: break; - case COFF_MachineType_X64: result = COFF_Reloc_X64_Addr32Nb; break; - default: { NotImplemented; } break; - } - return result; -} - internal String8 coff_make_import_lookup(Arena *arena, U16 hint, String8 name) { diff --git a/src/coff/coff.h b/src/coff/coff.h index 5d1473b9..3cbe8f8e 100644 --- a/src/coff/coff.h +++ b/src/coff/coff.h @@ -603,7 +603,6 @@ internal U64 coff_apply_size_from_reloc_x64(COFF_Reloc_X64 x); internal U64 coff_apply_size_from_reloc_x86(COFF_Reloc_X86 x); internal COFF_RelocValue coff_pick_reloc_value_x64(COFF_Reloc_X64 type, U64 reloc_virtual_offset, U32 symbol_section_number, U32 symbol_section_offset, U32 symbol_virtual_offset, U64 symbol_address); -internal COFF_RelocType coff_virt_off_reloc_from_machine(COFF_MachineType machine); //////////////////////////////// // Import diff --git a/src/coff/coff_lib_writer.c b/src/coff/coff_lib_writer.c index 6ad24044..e29370d5 100644 --- a/src/coff/coff_lib_writer.c +++ b/src/coff/coff_lib_writer.c @@ -128,11 +128,9 @@ coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_d } } -internal COFF_LibWriterSymbolNode * +internal void coff_lib_writer_push_export(COFF_LibWriter *writer, String8 raw_import_header) { - COFF_LibWriterSymbolNode *result = 0; - U64 member_idx = writer->member_list.count; COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(raw_import_header); @@ -147,35 +145,36 @@ coff_lib_writer_push_export(COFF_LibWriter *writer, String8 raw_import_header) COFF_LibWriterSymbol def_symbol = {0}; def_symbol.name = push_str8_copy(writer->arena, import_header.func_name); def_symbol.member_idx = member_idx; - result = coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, def_symbol); + coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, def_symbol); + + COFF_LibWriterSymbol imp_symbol = {0}; + imp_symbol.name = push_str8f(writer->arena, "__imp_%S", import_header.func_name); + imp_symbol.member_idx = member_idx; + coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, def_symbol); } break; case COFF_ImportHeader_Data: { COFF_LibWriterSymbol imp_symbol = {0}; imp_symbol.name = push_str8f(writer->arena, "__imp_%S", import_header.func_name); imp_symbol.member_idx = member_idx; - result = coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, imp_symbol); - } break; - case COFF_ImportHeader_Const: { - NotImplemented; + coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, imp_symbol); } break; + case COFF_ImportHeader_Const: { NotImplemented; } break; default: { InvalidPath; } break; } - - return result; } -internal COFF_LibWriterSymbolNode * +internal void coff_lib_writer_push_export_by_ordinal(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, U16 ordinal) { String8 import_header = coff_make_import_header_by_ordinal(lib_writer->arena, machine, time_stamp, dll_name, ordinal, import_type); - return coff_lib_writer_push_export(lib_writer, import_header); + coff_lib_writer_push_export(lib_writer, import_header); } -internal COFF_LibWriterSymbolNode * +internal void coff_lib_writer_push_export_by_name(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, String8 name, U16 hint) { String8 import_header = coff_make_import_header_by_name(lib_writer->arena, machine, time_stamp, dll_name, name, hint, import_type); - return coff_lib_writer_push_export(lib_writer, import_header); + coff_lib_writer_push_export(lib_writer, import_header); } internal String8List diff --git a/src/coff/coff_lib_writer.h b/src/coff/coff_lib_writer.h index 0b2c166c..76dee152 100644 --- a/src/coff/coff_lib_writer.h +++ b/src/coff/coff_lib_writer.h @@ -12,8 +12,8 @@ typedef struct COFF_LibWriterMember typedef struct COFF_LibWriterMemberNode { - struct COFF_LibWriterMemberNode *next; COFF_LibWriterMember data; + struct COFF_LibWriterMemberNode *next; } COFF_LibWriterMemberNode; typedef struct COFF_LibWriterMemberList @@ -31,8 +31,8 @@ typedef struct COFF_LibWriterSymbol typedef struct COFF_LibWriterSymbolNode { - struct COFF_LibWriterSymbolNode *next; COFF_LibWriterSymbol data; + struct COFF_LibWriterSymbolNode *next; } COFF_LibWriterSymbolNode; typedef struct COFF_LibWriterSymbolList @@ -60,9 +60,9 @@ internal void coff_lib_writer_symbol_array_sort(COFF_LibWriterSymbol *arr, U64 c internal COFF_LibWriter * coff_lib_writer_alloc(void); internal void coff_lib_writer_release(COFF_LibWriter **writer_ptr); internal void coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_data); -internal COFF_LibWriterSymbolNode * coff_lib_writer_push_export(COFF_LibWriter *writer, String8 raw_import_header); -internal COFF_LibWriterSymbolNode * coff_lib_writer_push_export_by_ordinal(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, U16 ordinal); -internal COFF_LibWriterSymbolNode * coff_lib_writer_push_export_by_name(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, String8 name, U16 hint); +internal void coff_lib_writer_push_export(COFF_LibWriter *writer, String8 raw_import_header); +internal void coff_lib_writer_push_export_by_ordinal(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, U16 ordinal); +internal void coff_lib_writer_push_export_by_name(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, String8 name, U16 hint); internal String8List coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member); #endif // COFF_LIB_WRITER_H diff --git a/src/coff/coff_obj_writer.c b/src/coff/coff_obj_writer.c index faa0dd29..9b730d9c 100644 --- a/src/coff/coff_obj_writer.c +++ b/src/coff/coff_obj_writer.c @@ -16,6 +16,24 @@ coff_obj_writer_release(COFF_ObjWriter **obj_writer) *obj_writer = 0; } +internal COFF_ObjSection * +coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data) +{ + COFF_ObjSectionNode *sect_n = push_array(obj_writer->arena, COFF_ObjSectionNode, 1); + SLLQueuePush(obj_writer->sect_first, obj_writer->sect_last, sect_n); + obj_writer->sect_count += 1; + + COFF_ObjSection *sect = §_n->v; + sect->name = name; + sect->flags = flags; + + if (data.size) { + str8_list_push(obj_writer->arena, §->data, data); + } + + return sect; +} + internal COFF_ObjSymbol * coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymbolLocation loc, COFF_SymbolType type, COFF_SymStorageClass storage_class) { @@ -104,15 +122,6 @@ coff_obj_writer_push_symbol_undef(COFF_ObjWriter *obj_writer, String8 name) return s; } -internal COFF_ObjSymbol * -coff_obj_writer_push_symbol_undef_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags) -{ - COFF_SymbolType type = {0}; - COFF_SymbolLocation loc = { COFF_SymbolLocation_Undef }; - COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, flags, loc, type, COFF_SymStorageClass_Section); - return s; -} - internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_func(COFF_ObjWriter *obj_writer, String8 name) { @@ -159,24 +168,6 @@ coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32 return s; } -internal COFF_ObjSection * -coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data) -{ - COFF_ObjSectionNode *sect_n = push_array(obj_writer->arena, COFF_ObjSectionNode, 1); - SLLQueuePush(obj_writer->sect_first, obj_writer->sect_last, sect_n); - obj_writer->sect_count += 1; - - COFF_ObjSection *sect = §_n->v; - sect->name = name; - sect->flags = flags; - - if (data.size) { - str8_list_push(obj_writer->arena, §->data, data); - } - - return sect; -} - internal COFF_ObjReloc* coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType type) { @@ -192,6 +183,30 @@ coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection * return reloc; } +internal COFF_ObjReloc * +coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol) +{ + COFF_RelocType reloc_type = 0; + switch (obj_writer->machine) { + case COFF_MachineType_Unknown: break; + case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr64; break; + default: { NotImplemented; } break; + } + return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type); +} + +internal COFF_ObjReloc * +coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol) +{ + COFF_RelocType reloc_type = 0; + switch (obj_writer->machine) { + case COFF_MachineType_Unknown: break; + case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr32Nb; break; + default: { NotImplemented; } break; + } + return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type); +} + internal void coff_obj_writer_push_directive(COFF_ObjWriter *obj_writer, String8 directive) { diff --git a/src/coff/coff_obj_writer.h b/src/coff/coff_obj_writer.h index 56cf47e2..e537a632 100644 --- a/src/coff/coff_obj_writer.h +++ b/src/coff/coff_obj_writer.h @@ -94,11 +94,26 @@ typedef struct COFF_ObjWriter //////////////////////////////// -internal COFF_ObjWriter* coff_obj_writer_alloc(COFF_TimeStamp time_stamp, COFF_MachineType machine); -internal void coff_obj_writer_release(COFF_ObjWriter **obj_writer); -internal COFF_ObjSection* coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data); +internal COFF_ObjWriter * coff_obj_writer_alloc(COFF_TimeStamp time_stamp, COFF_MachineType machine); +internal void coff_obj_writer_release(COFF_ObjWriter **obj_writer); + +internal COFF_ObjSection * coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data); + internal COFF_ObjSymbol* coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymbolLocation loc, COFF_SymbolType type, COFF_SymStorageClass storage_class); -internal COFF_ObjReloc* coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType reloc_type); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_extern(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_static(COFF_ObjWriter *obj_writer, String8 name, U32 off, COFF_ObjSection *section); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_secdef(COFF_ObjWriter *obj_writer, COFF_ObjSection *section, COFF_ComdatSelectType selection); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_weak(COFF_ObjWriter *obj_writer, String8 name, COFF_WeakExtType characteristics, COFF_ObjSymbol *tag); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_abs(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymStorageClass storage_class); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef(COFF_ObjWriter *obj_writer, String8 name); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_func(COFF_ObjWriter *obj_writer, String8 name); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_sect(COFF_ObjWriter *obj_writer, String8 name, U32 value); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_sect(COFF_ObjWriter *obj_writer, String8 name, COFF_ObjSection *sect); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32 size); + +internal COFF_ObjReloc * coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType reloc_type); +internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol); +internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol); #endif // COFF_OBJ_WRITER_H diff --git a/src/linker/base_ext/base_arena.c b/src/linker/base_ext/base_arena.c index 79471dfb..669cf47f 100644 --- a/src/linker/base_ext/base_arena.c +++ b/src/linker/base_ext/base_arena.c @@ -1,17 +1,6 @@ // Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal String8 -push_cstr(Arena *arena, String8 str) -{ - U64 buffer_size = str.size + 1; - U8 *buffer = push_array_no_zero(arena, U8, buffer_size); - MemoryCopy(buffer, str.str, str.size); - buffer[str.size] = 0; - String8 result = str8(buffer, buffer_size); - return result; -} - internal U32 * push_u32(Arena *arena, U32 value) { diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index bdc32ac3..964506aa 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -329,6 +329,19 @@ keys_from_hash_table_u64(Arena *arena, HashTable *ht) return result; } +internal String8 * +keys_from_hash_table_string(Arena *arena, HashTable *ht) +{ + String8 *result = push_array_no_zero(arena, String8, ht->count); + for (U64 bucket_idx = 0, cursor = 0; bucket_idx < ht->cap; ++bucket_idx) { + for (BucketNode *n = ht->buckets[bucket_idx].first; n != 0; n = n->next) { + Assert(cursor < ht->count); + result[cursor++] = n->v.key_string; + } + } + return result; +} + internal KeyValuePair * key_value_pairs_from_hash_table(Arena *arena, HashTable *ht) { diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index cc65cbf5..504aedc6 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -79,6 +79,7 @@ internal B32 hash_table_search_path_u64(HashTable *ht, String8 key, U64 *value_o internal U32 * keys_from_hash_table_u32 (Arena *arena, HashTable *ht); internal U64 * keys_from_hash_table_u64 (Arena *arena, HashTable *ht); +internal String8 keys_from_hash_table_str8 (Arena *arena, HashTable *ht); internal KeyValuePair * key_value_pairs_from_hash_table(Arena *arena, HashTable *ht); internal void * values_from_hash_table_raw(Arena *arena, HashTable *ht); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index ecf53d44..7fec2c93 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -41,6 +41,9 @@ #include "coff/coff_obj_writer.h" #include "coff/coff_lib_writer.h" #include "pe/pe.h" +#include "pe/pe_section_flags.h" +#include "pe/pe_make_import_table.h" +#include "pe/pe_make_export_table.h" #include "codeview/codeview.h" #include "codeview/codeview_parse.h" #include "codeview/codeview_enum.h" @@ -59,6 +62,8 @@ #include "coff/coff_obj_writer.c" #include "coff/coff_lib_writer.c" #include "pe/pe.c" +#include "pe/pe_make_import_table.c" +#include "pe/pe_make_export_table.c" #include "codeview/codeview.c" #include "codeview/codeview_enum.c" #include "codeview/codeview_parse.c" @@ -119,9 +124,6 @@ #include "lnk_io.h" #include "lnk_cmd_line.h" #include "lnk_input.h" -#include "lnk_image_section_flags.h" -#include "lnk_import_table.h" -#include "lnk_export_table.h" #include "lnk_config.h" #include "lnk_symbol_table.h" #include "lnk_section_table.h" @@ -137,8 +139,6 @@ #include "lnk_io.c" #include "lnk_cmd_line.c" #include "lnk_input.c" -#include "lnk_import_table.c" -#include "lnk_export_table.c" #include "lnk_config.c" #include "lnk_symbol_table.c" #include "lnk_section_table.c" @@ -425,8 +425,8 @@ lnk_serialize_pe_resource_tree(COFF_ObjWriter *obj_writer, PE_ResourceDir *root_ stack->coff_entry_arr[1] = 0; } - COFF_ObjSection *rsrc1 = coff_obj_writer_push_section(obj_writer, str8_lit(".rsrc$01"), LNK_RSRC1_SECTION_FLAGS, str8_zero()); - COFF_ObjSection *rsrc2 = coff_obj_writer_push_section(obj_writer, str8_lit(".rsrc$02"), LNK_RSRC2_SECTION_FLAGS, str8_zero()); + COFF_ObjSection *rsrc1 = coff_obj_writer_push_section(obj_writer, str8_lit(".rsrc$01"), PE_RSRC1_SECTION_FLAGS, str8_zero()); + COFF_ObjSection *rsrc2 = coff_obj_writer_push_section(obj_writer, str8_lit(".rsrc$02"), PE_RSRC2_SECTION_FLAGS, str8_zero()); for (; stack; ) { for (; stack->arr_idx < ArrayCount(stack->res_arr); stack->arr_idx += 1) { @@ -635,7 +635,7 @@ lnk_add_resource_debug_s(COFF_ObjWriter *obj_writer, str8_serial_push_align(scratch.arena, &sub_sect_srl, CV_C13SubSectionAlign); String8 sub_sect_data = str8_serial_end(obj_writer->arena, &sub_sect_srl); - coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), LNK_DEBUG_SECTION_FLAGS, sub_sect_data); + coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS, sub_sect_data); scratch_end(scratch); ProfEnd(); @@ -749,7 +749,7 @@ lnk_make_linker_coff_obj(Arena *arena, String8 obj; { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); - coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), LNK_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); + coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); obj = coff_obj_writer_serialize(arena, obj_writer); coff_obj_writer_release(&obj_writer); } @@ -773,7 +773,7 @@ lnk_push_pe_debug_data_directory(COFF_ObjWriter *obj_writer, dir->size = data_size; //dir->voff = 0; // relocated through 'data_symbol' //dir->foff = 0; // relocated through 'data_symbol' - COFF_ObjSection *debug_dir_sect = coff_obj_writer_push_section(obj_writer, dir_name, LNK_DATA_SECTION_FLAGS, str8_struct(dir)); + COFF_ObjSection *debug_dir_sect = coff_obj_writer_push_section(obj_writer, dir_name, PE_DATA_SECTION_FLAGS, str8_struct(dir)); coff_obj_writer_section_push_reloc(obj_writer, debug_dir_sect, OffsetOf(PE_DebugDirectory, voff), data_symbol, COFF_Reloc_X64_Addr32Nb); coff_obj_writer_section_push_reloc(obj_writer, debug_dir_sect, OffsetOf(PE_DebugDirectory, foff), data_symbol, COFF_Reloc_X64_Abs); } @@ -782,8 +782,8 @@ internal String8 lnk_make_debug_directory_obj(Arena *arena, LNK_Config *config) { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, config->machine); - COFF_ObjSection *sect_a = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$a"), LNK_DATA_SECTION_FLAGS, str8_zero()); - COFF_ObjSection *sect_z = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$z"), LNK_DATA_SECTION_FLAGS, str8_zero()); + COFF_ObjSection *sect_a = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$a"), PE_DATA_SECTION_FLAGS, str8_zero()); + COFF_ObjSection *sect_z = coff_obj_writer_push_section(obj_writer, str8_lit(".RAD_LINKER_DEBUG_DIR$z"), PE_DATA_SECTION_FLAGS, str8_zero()); String8 obj = coff_obj_writer_serialize(arena, obj_writer); coff_obj_writer_release(&obj_writer); return obj; @@ -794,7 +794,7 @@ lnk_make_debug_directory_pdb_obj(Arena *arena, LNK_Config *config) { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, config->machine); String8 debug_pdb_data = pe_make_debug_header_pdb70(obj_writer->arena, config->guid, config->age, config->pdb_alt_path); - COFF_ObjSection *debug_pdb_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), LNK_DATA_SECTION_FLAGS, debug_pdb_data); + COFF_ObjSection *debug_pdb_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), PE_DATA_SECTION_FLAGS, debug_pdb_data); COFF_ObjSymbol *debug_pdb_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("PDB_DEBUG_HEADER_70"), 0, debug_pdb_sect); lnk_push_pe_debug_data_directory(obj_writer, debug_pdb_symbol, debug_pdb_data.size, str8_lit(".RAD_LINKER_DEBUG_DIR$PDB"), PE_DebugDirectoryType_CODEVIEW, config->time_stamp); String8 obj = coff_obj_writer_serialize(arena, obj_writer); @@ -806,7 +806,7 @@ lnk_make_debug_directory_rdi_obj(Arena *arena, LNK_Config *config) { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, COFF_MachineType_Unknown); String8 debug_rdi_data = pe_make_debug_header_rdi(obj_writer->arena, config->guid, config->rad_debug_alt_path); - COFF_ObjSection *debug_rdi_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), LNK_DATA_SECTION_FLAGS, debug_rdi_data); + COFF_ObjSection *debug_rdi_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), PE_DATA_SECTION_FLAGS, debug_rdi_data); COFF_ObjSymbol *debug_rdi_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("RDI_DEBUG_HEADER"), 0, debug_rdi_sect); lnk_push_pe_debug_data_directory(obj_writer, debug_rdi_symbol, debug_rdi_data.size, str8_lit("RAD_LINKER_DEBUG_DIR$RDI"), PE_DebugDirectoryType_CODEVIEW, config->time_stamp); String8 obj = coff_obj_writer_serialize(arena, obj_writer); @@ -871,10 +871,10 @@ lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path) } internal void -lnk_push_export(Arena *arena, HashTable *export_ht, LNK_ExportParseList *export_list, String8List *include_symbol_list, LNK_ExportParse export_parse) +lnk_push_export(Arena *arena, HashTable *export_ht, PE_ExportParseList *export_list, String8List *include_symbol_list, PE_ExportParse export_parse) { - LNK_ExportParseNode *exp_n = 0; - String8 export_name = lnk_name_from_export_parse(&export_parse); + PE_ExportParseNode *exp_n = 0; + String8 export_name = pe_name_from_export_parse(&export_parse); hash_table_search_string_raw(export_ht, export_name, &exp_n); if (exp_n == 0) { @@ -884,12 +884,12 @@ lnk_push_export(Arena *arena, HashTable *export_ht, LNK_ExportParseList *export_ } // push new export - exp_n = lnk_export_parse_list_push(arena, export_list, export_parse); + exp_n = pe_export_parse_list_push(arena, export_list, export_parse); hash_table_push_string_raw(arena, export_ht, export_name, exp_n); } else { B32 is_ambiguous = 1; - LNK_ExportParse *extant_export = &exp_n->data; + PE_ExportParse *extant_export = &exp_n->data; if (extant_export->alias.size && export_parse.alias.size && !str8_match(extant_export->alias, export_parse.alias, 0)) { goto report; @@ -2066,9 +2066,9 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S // init section table // LNK_SectionTable *sectab = lnk_section_table_alloc(); - lnk_section_table_push(sectab, str8_lit(".text"), LNK_TEXT_SECTION_FLAGS); - lnk_section_table_push(sectab, str8_lit(".data"), LNK_DATA_SECTION_FLAGS); - lnk_section_table_push(sectab, str8_lit(".rdata"), LNK_RDATA_SECTION_FLAGS); + lnk_section_table_push(sectab, str8_lit(".text"), PE_TEXT_SECTION_FLAGS); + lnk_section_table_push(sectab, str8_lit(".data"), PE_DATA_SECTION_FLAGS); + lnk_section_table_push(sectab, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS); // // obj list -> array @@ -2392,9 +2392,9 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S 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"), LNK_DATA_SECTION_FLAGS); + 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"), LNK_DATA_SECTION_FLAGS); + 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 @@ -2742,7 +2742,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S 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"), LNK_RELOC_SECTION_FLAGS); + 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; @@ -2938,7 +2938,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S // patch .pdata { - LNK_Section *pdata_sect = lnk_section_table_search(sectab, str8_lit(".pdata"), LNK_PDATA_SECTION_FLAGS); + LNK_Section *pdata_sect = lnk_section_table_search(sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS); if (pdata_sect) { String8 pdata = str8_substr(image_data, rng_1u64(pdata_sect->foff, pdata_sect->foff + pdata_sect->vsize)); @@ -2964,7 +2964,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S // patch export { - LNK_Section *edata_sect = lnk_section_table_search(sectab, str8_lit(".edata"), LNK_EDATA_SECTION_FLAGS); + LNK_Section *edata_sect = lnk_section_table_search(sectab, str8_lit(".edata"), PE_EDATA_SECTION_FLAGS); if (edata_sect) { PE_DataDirectory *export_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_EXPORT, sizeof(PE_DataDirectory)); export_dir->virt_off = edata_sect->voff; @@ -2974,7 +2974,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S // patch base relocs { - LNK_Section *reloc_sect = lnk_section_table_search(sectab, str8_lit(".reloc"), LNK_RELOC_SECTION_FLAGS); + LNK_Section *reloc_sect = lnk_section_table_search(sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS); if (reloc_sect) { PE_DataDirectory *reloc_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_BASE_RELOC, sizeof(PE_DataDirectory)); reloc_dir->virt_off = reloc_sect->voff; @@ -3033,7 +3033,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S ProfBegin("Patch TLS Align"); // loop over .tls sections and extract max alignment - LNK_Section *tls_sect = lnk_section_table_search(sectab, str8_lit(".tls"), LNK_RDATA_SECTION_FLAGS); + LNK_Section *tls_sect = lnk_section_table_search(sectab, str8_lit(".tls"), PE_RDATA_SECTION_FLAGS); U64 tls_align = 0; for (LNK_SectionContribChunk *sc_chunk = tls_sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { @@ -3070,7 +3070,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S // patch Debug { - LNK_Section *debug_dir_sect = lnk_section_table_search(sectab, str8_lit(".RAD_LINKER_DEBUG_DIR"), LNK_RDATA_SECTION_FLAGS); + LNK_Section *debug_dir_sect = lnk_section_table_search(sectab, str8_lit(".RAD_LINKER_DEBUG_DIR"), PE_RDATA_SECTION_FLAGS); if (debug_dir_sect) { PE_DataDirectory *debug_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_DEBUG, sizeof(PE_DataDirectory)); debug_dir->virt_off = debug_dir_sect->voff; @@ -3100,7 +3100,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S // patch resources { - LNK_Section *rsrc_sect = lnk_section_table_search(sectab, str8_lit(".rsrc"), LNK_RSRC_SECTION_FLAGS); + LNK_Section *rsrc_sect = lnk_section_table_search(sectab, str8_lit(".rsrc"), PE_RSRC_SECTION_FLAGS); if (rsrc_sect) { PE_DataDirectory *rsrc_dir = str8_deserial_get_raw_ptr(image_data, pe.data_dir_range.min + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_RESOURCES, sizeof(PE_DataDirectory)); rsrc_dir->virt_off = rsrc_sect->voff; @@ -3538,7 +3538,7 @@ lnk_run(int argc, char **argv) String8List input_disallow_lib_list = config->disallow_lib_list; String8List input_manifest_path_list = str8_list_copy(scratch.arena, &config->input_list[LNK_Input_Manifest]); String8List manifest_dep_list = str8_list_copy(scratch.arena, &config->manifest_dependency_list); - LNK_ExportParseList export_symbol_list = config->export_symbol_list; + PE_ExportParseList export_symbol_list = config->export_symbol_list; HashTable *export_ht = hash_table_init(scratch.arena, max_U16/2); LNK_AltNameList alt_name_list = config->alt_name_list; LNK_InputObjList input_obj_list = {0}; @@ -3559,11 +3559,12 @@ lnk_run(int argc, char **argv) // state LNK_SymbolTable *symtab = lnk_symbol_table_init(tp_arena); LNK_SectionTable *sectab = 0; - LNK_ImportTable *imptab_static = lnk_import_table_alloc(); - LNK_ImportTable *imptab_delayed = lnk_import_table_alloc(); + HashTable *static_imports = hash_table_init(scratch.arena, 512); + HashTable *delayed_imports = hash_table_init(scratch.arena, 512); LNK_ObjList obj_list = {0}; LNK_LibList lib_index[LNK_InputSource_Count] = {0}; Arena *ht_arena = arena_alloc(); + String8 delay_load_helper_name = {0}; HashTable *disallow_lib_ht = hash_table_init(scratch.arena, 0x100); HashTable *delay_load_dll_ht = hash_table_init(scratch.arena, 0x100); HashTable *loaded_lib_ht = hash_table_init(scratch.arena, 0x100); @@ -3618,7 +3619,7 @@ lnk_run(int argc, char **argv) // // Push config exports // - for (LNK_ExportParseNode *exp_n = config->export_symbol_list.first; exp_n != 0; exp_n = exp_n->next) { + for (PE_ExportParseNode *exp_n = config->export_symbol_list.first; exp_n != 0; exp_n = exp_n->next) { lnk_push_export(scratch.arena, export_ht, &export_symbol_list, &include_symbol_list, exp_n->data); } @@ -3651,24 +3652,56 @@ lnk_run(int argc, char **argv) for (LNK_InputImport *input = input_import_list.first; input != 0; input = input->next) { COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(input->coff_import); + // import machine compat check if (import_header.machine != config->machine) { - lnk_error(LNK_Error_IncompatibleMachine, "symbol pulls in an import with incompatible machine %S (expected %S)", + lnk_error(LNK_Error_IncompatibleMachine, "symbol %S pulled in import with incompatible machine %S (expected %S)", + import_header.func_name, coff_string_from_machine_type(import_header.machine), coff_string_from_machine_type(config->machine)); + continue; } - LNK_Symbol *thunk_symbol = push_array(symtab->arena->v[0], LNK_Symbol, 1); - thunk_symbol->type = LNK_Symbol_Import; - thunk_symbol->name = import_header.func_name; - thunk_symbol->u.coff_import = input->coff_import; + // create import stubs (later replaced with acutal imports generated by linker) + LNK_Symbol *thunk_symbol; + { + thunk_symbol = push_array(symtab->arena->v[0], LNK_Symbol, 1); + thunk_symbol->type = LNK_Symbol_Import; + thunk_symbol->name = import_header.func_name; + thunk_symbol->u.coff_import = input->coff_import; - LNK_Symbol *iat_symbol = push_array(symtab->arena->v[0], LNK_Symbol, 1); - iat_symbol->type = LNK_Symbol_Import; - iat_symbol->name = push_str8f(symtab->arena->v[0], "__imp_%S", import_header.func_name); - iat_symbol->u.coff_import = input->coff_import; + LNK_Symbol *iat_symbol = push_array(symtab->arena->v[0], LNK_Symbol, 1); + iat_symbol->type = LNK_Symbol_Import; + iat_symbol->name = push_str8f(symtab->arena->v[0], "__imp_%S", import_header.func_name); + iat_symbol->u.coff_import = input->coff_import; - lnk_symbol_table_push(symtab, thunk_symbol); - lnk_symbol_table_push(symtab, iat_symbol); + lnk_symbol_table_push(symtab, thunk_symbol); + lnk_symbol_table_push(symtab, iat_symbol); + } + + // pick imports hash table + HashTable *imports_ht; + { + B32 is_delay_load_dll = hash_table_search_path_u64(delay_load_dll_ht, import_header.dll_name, 0); + if (is_delay_load_dll) { + imports_ht = delayed_imports; + } else { + imports_ht = static_imports; + } + } + + // search DLL symbol list + String8List *import_symbols; + { + import_symbols = hash_table_search_path_raw(imports_ht, import_header.dll_name); + if (import_symbols == 0) { + import_symbols = push_array(scratch.arena, String8List, 1); + hash_table_push_path_raw(scratch.arena, imports_ht, import_header.dll_name, import_symbols); + } + hash_table_push_path_raw(scratch.arena, imports_ht, import_header.dll_name, import_symbols); + } + + // push symbol + str8_list_push(scratch.arena, import_symbols, input->coff_import); } // reset input @@ -3816,7 +3849,7 @@ lnk_run(int argc, char **argv) // /EXPORT { for (LNK_Directive *dir = directive_info.v[LNK_CmdSwitch_Export].first; dir != 0; dir = dir->next) { - LNK_ExportParse export_parse = {0}; + PE_ExportParse export_parse = {0}; lnk_parse_export_directive_ex(scratch.arena, dir->value_list, obj->path, obj->lib_path, &export_parse); lnk_push_export(scratch.arena, export_ht, &export_symbol_list, &include_symbol_list, export_parse); } @@ -3991,14 +4024,13 @@ lnk_run(int argc, char **argv) case State_PushDllHelperUndefSymbol: { ProfBegin("Push Dll Helper Undef Symbol"); - String8 delay_helper_name = str8_zero(); switch (config->machine) { - case COFF_MachineType_X86: delay_helper_name = str8_cstring(LNK_DELAY_LOAD_HELPER2_X86_SYMBOL_NAME); break; - case COFF_MachineType_X64: delay_helper_name = str8_cstring(LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME); break; - default: NotImplemented; + case COFF_MachineType_X86: delay_load_helper_name = str8_cstring(LNK_DELAY_LOAD_HELPER2_X86_SYMBOL_NAME); break; + case COFF_MachineType_X64: delay_load_helper_name = str8_cstring(LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME); break; + default: { NotImplemented; } break; } - str8_list_push(scratch.arena, &include_symbol_list, delay_helper_name); + str8_list_push(scratch.arena, &include_symbol_list, delay_load_helper_name); ProfEnd(); } break; case State_PushLinkerSymbols: { @@ -4176,52 +4208,96 @@ lnk_run(int argc, char **argv) } } break; case State_InputImportObjs: { - if (input_import_list.count) { - ProfBegin("Build Import Table"); + ProfBegin("Build Import Table"); - // warn about unused delayloads - if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { - if (imptab_delayed) { - for (String8Node *node = config->delay_load_dll_list.first; node != 0; node = node->next) { - LNK_ImportDLL *dll = lnk_import_table_search_dll(imptab_delayed, node->string); - if (dll == 0) { - lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", node->string); - } - } + // warn about unused delayloads + if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { + for (String8Node *dll_name_n = config->delay_load_dll_list.first; dll_name_n != 0; dll_name_n = dll_name_n->next) { + if (!hash_table_search_string_raw(delayed_imports, dll_name_n->string, 0)) { + lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", dll_name_n->string); } } + } - // make and input static imports - String8Array import_objs_static = lnk_make_import_dlls_static(scratch.arena, imptab_static, config->machine, str8_skip_last_slash(config->image_name)); - for (U64 i = 0; i < import_objs_static.count; i += 1) { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* Import Obj *"); - input->input_idx = i; - input->data = import_objs_static.v[i]; - } - - // make and input delayed imports + // make and input delayed imports + if (delayed_imports->count) { + COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; B32 emit_biat = config->import_table_emit_biat == LNK_SwitchState_Yes; B32 emit_uiat = config->import_table_emit_uiat == LNK_SwitchState_Yes; - String8Array import_objs_delayed = lnk_make_import_dlls_delayed(scratch.arena, imptab_delayed, config->machine, str8_skip_last_slash(config->image_name), emit_biat, emit_uiat); - for (U64 i = 0; i < import_objs_delayed.count; i += 1) { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* Import Obj *"); - input->input_idx = import_objs_static.count + i; - input->data = import_objs_delayed.v[i]; - } + String8 *dll_names = keys_from_hash_table_string(scratch.arena, delayed_imports); + String8List *dll_import_headers = values_from_hash_table_raw(scratch.arena, delayed_imports); - ProfEnd(); + for (U64 dll_idx = 0; dll_idx < delayed_imports->count; dll_idx += 1) { + String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->input_idx = input_obj_list.count; + input->data = pe_make_import_dll_obj_delayed(scratch.arena, time_stamp, config->machine, dll_names[dll_idx], delay_load_helper_name, import_debug_symbols, dll_import_headers[dll_idx], emit_biat, emit_uiat); + input->path = dll_names[dll_idx]; + } + String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); + { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->input_idx = input_obj_list.count; + input->data = pe_make_import_entry_obj(scratch.arena, config->image_name, time_stamp, config->machine, linker_debug_symbols); + input->path = str8_lit("* Delayed Import Entry Obj *"); + } + { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->input_idx = input_obj_list.count; + input->data = pe_make_null_import_descriptor_obj(scratch.arena, time_stamp, config->machine, linker_debug_symbols); + input->path = str8_lit("* Delayed Null Import Descriptor *"); + } + { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->input_idx = input_obj_list.count; + input->data = pe_make_null_thunk_data_obj(scratch.arena, config->image_name, time_stamp, config->machine, linker_debug_symbols); + input->path = str8_lit("* Delayed Null Thunk Data *"); + } } + + // make and input static imports + if (static_imports->count) { + COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; + String8 *dll_names = keys_from_hash_table_string(scratch.arena, static_imports); + String8List *dll_import_headers = values_from_hash_table_raw(scratch.arena, static_imports); + for (U64 dll_idx = 0; dll_idx < static_imports->count; dll_idx += 1) { + String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->input_idx = input_obj_list.count; + input->data = pe_make_import_dll_obj_static(scratch.arena, time_stamp, config->machine, dll_names[dll_idx], import_debug_symbols, dll_import_headers[dll_idx]); + input->path = dll_names[dll_idx]; + } + String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); + { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->input_idx = input_obj_list.count; + input->data = pe_make_import_entry_obj(scratch.arena, config->image_name, time_stamp, config->machine, linker_debug_symbols); + input->path = str8_lit("* Import Entry Obj *"); + } + { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->input_idx = input_obj_list.count; + input->data = pe_make_null_import_descriptor_obj(scratch.arena, time_stamp, config->machine, linker_debug_symbols); + input->path = str8_lit("* Null Import Descriptor *"); + } + { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->input_idx = input_obj_list.count; + input->data = pe_make_null_thunk_data_obj(scratch.arena, config->image_name, time_stamp, config->machine, linker_debug_symbols); + input->path = str8_lit("* Null Thunk Data *"); + } + } + + ProfEnd(); } break; case State_InputExportObjs: { if (export_symbol_list.count) { ProfBegin("Build Export Table"); - LNK_ExportParseList resolved_exports = {0}; - for (LNK_ExportParseNode *exp_n = export_symbol_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { + PE_ExportParseList resolved_exports = {0}; + for (PE_ExportParseNode *exp_n = export_symbol_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { exp_n_next = exp_n->next; - LNK_ExportParse *exp = &exp_n->data; + PE_ExportParse *exp = &exp_n->data; if (!exp->is_forwarder) { // filter out unresolved exports @@ -4255,15 +4331,16 @@ lnk_run(int argc, char **argv) } // push resolved export - lnk_export_parse_list_push_node(&resolved_exports, exp_n); + pe_export_parse_list_push_node(&resolved_exports, exp_n); } - String8 edata_obj = lnk_make_edata_obj(scratch.arena, symtab, str8_skip_last_slash(config->image_name), config->machine, resolved_exports); + PE_FinalizedExports finalized_exports = pe_finalize_export_list(scratch.arena, resolved_exports); + String8 edata_obj = pe_make_edata_obj(scratch.arena, str8_skip_last_slash(config->image_name), COFF_TimeStamp_Max, config->machine, finalized_exports); LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* Exports *"); - input->dedup_id = input->path; - input->data = edata_obj; + input->path = str8_lit("* Exports *"); + input->dedup_id = input->path; + input->data = edata_obj; ProfEnd(); } @@ -4420,7 +4497,8 @@ lnk_run(int argc, char **argv) case State_BuildImpLib: { ProfBegin("Build Imp Lib"); lnk_timer_begin(LNK_Timer_Lib); - String8List lib_list = lnk_build_import_lib(tp_arena->v[0], config->machine, config->time_stamp, config->image_name, export_symbol_list); + String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); + String8List lib_list = pe_make_import_lib(tp_arena->v[0], config->machine, config->time_stamp, str8_skip_last_slash(config->image_name), linker_debug_symbols, export_symbol_list); lnk_write_data_list_to_file_path(config->imp_lib_name, str8_zero(), lib_list); lnk_timer_end(LNK_Timer_Lib); ProfEnd(); diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 2a1b1808..89842710 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -31,27 +31,6 @@ // PE_TLSHeader32 or PE_TLSHeader64, according to machine type. #define LNK_TLS_SYMBOL_NAME "_tls_used" -// --- Default Section Flags -------------------------------------------------- - -#define LNK_TEXT_SECTION_FLAGS (COFF_SectionFlag_CntCode|COFF_SectionFlag_MemExecute|COFF_SectionFlag_MemRead) -#define LNK_DATA_SECTION_FLAGS (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite) -#define LNK_RDATA_SECTION_FLAGS (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead) -#define LNK_BSS_SECTION_FLAGS (COFF_SectionFlag_CntUninitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite) -#define LNK_IDATA_SECTION_FLAGS LNK_DATA_SECTION_FLAGS -#define LNK_DEBUG_DIR_SECTION_FLAGS LNK_DATA_SECTION_FLAGS -#define LNK_RSRC_SECTION_FLAGS LNK_DATA_SECTION_FLAGS -#define LNK_RSRC1_SECTION_FLAGS (LNK_DATA_SECTION_FLAGS | COFF_SectionFlag_Align4Bytes) -#define LNK_RSRC2_SECTION_FLAGS (LNK_DATA_SECTION_FLAGS | COFF_SectionFlag_Align4Bytes) -#define LNK_XDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_PDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_EDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_GFIDS_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_GIATS_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_GLJMP_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_GEHCONT_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_RELOC_SECTION_FLAGS (LNK_RDATA_SECTION_FLAGS | COFF_SectionFlag_MemDiscardable) -#define LNK_DEBUG_SECTION_FLAGS (LNK_RDATA_SECTION_FLAGS | COFF_SectionFlag_MemDiscardable) - // --- Base Reloc -------------------------------------------------------------- typedef struct LNK_BaseRelocPage diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 7df1713a..632147b9 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -801,6 +801,122 @@ lnk_parse_alt_name_directive_list(Arena *arena, String8List list, LNK_AltNameLis return 0; } +internal B32 +lnk_parse_export_directive_ex(Arena *arena, String8List directive, String8 obj_path, String8 lib_path, PE_ExportParse *export_out) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(&arena, 1); + B32 is_parsed = 0; + + // parse "alias=name" + String8 name = {0}; + String8 alias = {0}; + String8List flags = {0}; + { + String8List alias_name_split = str8_split_by_string_chars(scratch.arena, directive.first->string, str8_lit("="), 0); + if (alias_name_split.node_count == 2) { + alias = alias_name_split.first->string; + name = alias_name_split.last->string; + } else if (alias_name_split.node_count == 1) { + name = alias_name_split.first->string; + } else { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); + goto exit; + } + + flags = directive; + str8_list_pop_front(&flags); + } + + // discard alias to itself + if (str8_match(name, alias, 0)) { + alias = str8_zero(); + } + + // does directive have ordinal? + U16 ordinal16 = 0; + String8 ordinal = {0}; + String8 noname_flag = {0}; + if (str8_match(str8_prefix(str8_list_first(&flags), 1), str8_lit("@"), 0)) { + // parse ordinal + ordinal = str8_skip(str8_list_pop_front(&flags)->string, 1); + if (str8_is_integer(ordinal, 10)) { + U64 ordinal64 = u64_from_str8(ordinal, 10); + if (ordinal64 <= max_U16) { + ordinal16 = (U16)ordinal64; + } else { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "ordinal value must fit into 16-bit integer, \"/EXPORT:%S\"", d); + goto exit; + } + } else { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); + goto exit; + } + + // detect NONAME flag + if (str8_match(str8_list_first(&flags), str8_lit("NONAME"), StringMatchFlag_CaseInsensitive)) { + noname_flag = str8_list_pop_front(&flags)->string; + } + } + + // detect PRIVATE flag + String8 private_flag = {0}; + if (str8_match(str8_list_first(&flags), str8_lit("PRIVATE"), StringMatchFlag_CaseInsensitive)) { + private_flag = str8_list_pop_front(&flags)->string; + } + + // parse export type + COFF_ImportType type = COFF_ImportHeader_Code; + if (flags.node_count) { + type = coff_import_header_type_from_string(str8_list_pop_front(&flags)->string); + if (type == COFF_ImportType_Invalid) { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); + goto exit; + } + } + + // are there leftover nodes? + if (flags.node_count != 0) { + String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); + lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); + goto exit; + } + + // fill out export + export_out->obj_path = obj_path; + export_out->lib_path = lib_path; + export_out->name = push_str8_copy(arena, name); + export_out->alias = push_str8_copy(arena, alias); + export_out->type = type; + export_out->ordinal = ordinal16; + export_out->is_ordinal_assigned = ordinal.size > 0; + export_out->is_noname_present = noname_flag.size > 0; + export_out->is_private = private_flag.size > 0; + export_out->is_forwarder = str8_find_needle(name, 0, str8_lit("."), 0) < name.size; + + is_parsed = 1; + +exit:; + scratch_end(scratch); + ProfEnd(); + return is_parsed; +} + +internal B32 +lnk_parse_export_directive(Arena *arena, String8 directive, String8 obj_path, String8 lib_path, PE_ExportParse *export_out) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List split_directive = str8_split_by_string_chars(scratch.arena, directive, str8_lit(","), 0); + B32 is_parsed = lnk_parse_export_directive_ex(arena, split_directive, obj_path, lib_path, export_out); + scratch_end(scratch); + return is_parsed; +} + + internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data) { @@ -1062,9 +1178,9 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Export: { - LNK_ExportParse export_parse = {0}; + PE_ExportParse export_parse = {0}; if (lnk_parse_export_directive_ex(arena, value_strings, obj_path, lib_path, &export_parse)) { - lnk_export_parse_list_push(arena, &config->export_symbol_list, export_parse); + pe_export_parse_list_push(arena, &config->export_symbol_list, export_parse); } } break; diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 20946905..d139cf2d 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -336,7 +336,7 @@ typedef struct LNK_Config LNK_TypeNameHashMode pdb_hash_type_names; String8 pdb_hash_type_name_map; U64 pdb_hash_type_name_length; - LNK_ExportParseList export_symbol_list; + PE_ExportParseList export_symbol_list; String8List input_list[LNK_Input_Count]; String8List input_default_lib_list; String8List disallow_lib_list; @@ -559,6 +559,9 @@ internal void lnk_alt_name_list_concat_in_place(LNK_AltNameList *list, LNK_ internal B32 lnk_parse_alt_name_directive (Arena *arena, String8 input, LNK_AltNameList *list_out); internal String8 * lnk_parse_alt_name_directive_list(Arena *arena, String8List list, LNK_AltNameList *list_out); +internal B32 lnk_parse_export_directive_ex(Arena *arena, String8List directive, String8 obj_path, String8 lib_path, PE_ExportParse *export_out); +internal B32 lnk_parse_export_directive(Arena *arena, String8 directive, String8 obj_path, String8 lib_path, PE_ExportParse *export_out); + internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data); internal B32 lnk_parse_merge_directive(String8 string, LNK_MergeDirective *parse_out); diff --git a/src/linker/lnk_debug_helper.c b/src/linker/lnk_debug_helper.c index 3e964c76..b4190db4 100644 --- a/src/linker/lnk_debug_helper.c +++ b/src/linker/lnk_debug_helper.c @@ -36,4 +36,43 @@ lnk_make_debug_s(Arena *arena, CV_SymbolList symbol_list) return debug_s_data; } +internal String8 +lnk_make_linker_debug_symbols(Arena *arena, COFF_MachineType machine) +{ + Temp scratch = scratch_begin(&arena, 1); + CV_SymbolList symbol_list = { .signature = CV_Signature_C13 }; + String8 comp3_data = lnk_make_linker_compile3(scratch.arena, machine); + cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_COMPILE3, comp3_data); + String8 debug_symbols = lnk_make_debug_s(arena, symbol_list); + scratch_end(scratch); + return debug_symbols; +} + +internal String8 +lnk_make_dll_import_debug_symbols(Arena *arena, COFF_MachineType machine, String8 dll_name) +{ + Temp scratch = scratch_begin(&arena,1); + + CV_SymbolList symbol_list = { .signature = CV_Signature_C13 }; + + // S_OBJ + String8 obj_data = cv_make_obj_name(scratch.arena, dll_name, 0); + cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_OBJNAME, obj_data); + + // S_COMPILE3 + String8 comp3_data = lnk_make_linker_compile3(scratch.arena, machine); + cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_COMPILE3, comp3_data); + + // S_END + cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_END, str8_zero()); + + // TODO: add thunks + + // serialize symbols + String8 debug_symbols = lnk_make_debug_s(arena, symbol_list); + + scratch_end(scratch); + return debug_symbols; +} + diff --git a/src/linker/lnk_export_table.c b/src/linker/lnk_export_table.c deleted file mode 100644 index e3866ab9..00000000 --- a/src/linker/lnk_export_table.c +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright (c) 2025 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal String8 -lnk_name_from_export_parse(LNK_ExportParse *exp) -{ - String8 name; - if (exp->is_forwarder) { - name = exp->alias; - } else if (exp->alias.size) { - name = exp->alias; - } else { - name = exp->name; - } - return name; -} - -internal B32 -lnk_parse_export_directive_ex(Arena *arena, String8List directive, String8 obj_path, String8 lib_path, LNK_ExportParse *export_out) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - B32 is_parsed = 0; - - // parse "alias=name" - String8 name = {0}; - String8 alias = {0}; - String8List flags = {0}; - { - String8List alias_name_split = str8_split_by_string_chars(scratch.arena, directive.first->string, str8_lit("="), 0); - if (alias_name_split.node_count == 2) { - alias = alias_name_split.first->string; - name = alias_name_split.last->string; - } else if (alias_name_split.node_count == 1) { - name = alias_name_split.first->string; - } else { - String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); - lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); - goto exit; - } - - flags = directive; - str8_list_pop_front(&flags); - } - - // discard alias to itself - if (str8_match(name, alias, 0)) { - alias = str8_zero(); - } - - // does directive have ordinal? - U16 ordinal16 = 0; - String8 ordinal = {0}; - String8 noname_flag = {0}; - if (str8_match(str8_prefix(str8_list_first(&flags), 1), str8_lit("@"), 0)) { - // parse ordinal - ordinal = str8_skip(str8_list_pop_front(&flags)->string, 1); - if (str8_is_integer(ordinal, 10)) { - U64 ordinal64 = u64_from_str8(ordinal, 10); - if (ordinal64 <= max_U16) { - ordinal16 = (U16)ordinal64; - } else { - String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); - lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "ordinal value must fit into 16-bit integer, \"/EXPORT:%S\"", d); - goto exit; - } - } else { - String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); - lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); - goto exit; - } - - // detect NONAME flag - if (str8_match(str8_list_first(&flags), str8_lit("NONAME"), StringMatchFlag_CaseInsensitive)) { - noname_flag = str8_list_pop_front(&flags)->string; - } - } - - // detect PRIVATE flag - String8 private_flag = {0}; - if (str8_match(str8_list_first(&flags), str8_lit("PRIVATE"), StringMatchFlag_CaseInsensitive)) { - private_flag = str8_list_pop_front(&flags)->string; - } - - // parse export type - COFF_ImportType type = COFF_ImportHeader_Code; - if (flags.node_count) { - type = coff_import_header_type_from_string(str8_list_pop_front(&flags)->string); - if (type == COFF_ImportType_Invalid) { - String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); - lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); - goto exit; - } - } - - // are there leftover nodes? - if (flags.node_count != 0) { - String8 d = str8_list_join(scratch.arena, &directive, &(StringJoin){.sep=str8_lit(",")}); - lnk_error_with_loc(LNK_Error_IllExport, obj_path, lib_path, "invalid export directive \"/EXPORT:%S\"", d); - goto exit; - } - - // fill out export - export_out->obj_path = obj_path; - export_out->lib_path = lib_path; - export_out->name = push_str8_copy(arena, name); - export_out->alias = push_str8_copy(arena, alias); - export_out->type = type; - export_out->ordinal = ordinal16; - export_out->is_ordinal_assigned = ordinal.size > 0; - export_out->is_noname_present = noname_flag.size > 0; - export_out->is_private = private_flag.size > 0; - export_out->is_forwarder = str8_find_needle(name, 0, str8_lit("."), 0) < name.size; - - is_parsed = 1; - -exit:; - scratch_end(scratch); - ProfEnd(); - return is_parsed; -} - -internal B32 -lnk_parse_export_directive(Arena *arena, String8 directive, String8 obj_path, String8 lib_path, LNK_ExportParse *export_out) -{ - Temp scratch = scratch_begin(&arena, 1); - String8List split_directive = str8_split_by_string_chars(scratch.arena, directive, str8_lit(","), 0); - B32 is_parsed = lnk_parse_export_directive_ex(arena, split_directive, obj_path, lib_path, export_out); - scratch_end(scratch); - return is_parsed; -} - -internal LNK_ExportParsePtrArray -lnk_array_from_export_list(Arena *arena, LNK_ExportParseList list) -{ - LNK_ExportParsePtrArray result = {0}; - result.v = push_array_no_zero(arena, LNK_ExportParse *, list.count); - for (LNK_ExportParseNode *exp = list.first; exp != 0; exp = exp->next) { - result.v[result.count++] = &exp->data; - } - return result; -} - -internal void -lnk_export_parse_list_push_node(LNK_ExportParseList *list, LNK_ExportParseNode *node) -{ - SLLQueuePush(list->first, list->last, node); - list->count += 1; -} - -internal LNK_ExportParseNode * -lnk_export_parse_list_push(Arena *arena, LNK_ExportParseList *list, LNK_ExportParse data) -{ - LNK_ExportParseNode *node = push_array(arena, LNK_ExportParseNode, 1); - node->data = data; - lnk_export_parse_list_push_node(list, node); - return node; -} - -internal void -lnk_export_parse_list_concat_in_place(LNK_ExportParseList *list, LNK_ExportParseList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal int -lnk_named_export_is_before(void *raw_a, void *raw_b) -{ - LNK_ExportParse *a = *(LNK_ExportParse **)raw_a; - LNK_ExportParse *b = *(LNK_ExportParse **)raw_b; - int cmp = str8_compar_case_sensitive(&a->name, &b->name); - return cmp < 0; -} - -internal int -lnk_ordinal_export_is_before(void *raw_a, void *raw_b) -{ - LNK_ExportParse *a = raw_a; - LNK_ExportParse *b = raw_b; - return a->ordinal < b->ordinal; -} - -internal String8 -lnk_make_edata_obj(Arena *arena, - LNK_SymbolTable *symtab, - String8 image_name, - COFF_MachineType machine, - LNK_ExportParseList export_list) -{ - Temp scratch = scratch_begin(&arena, 1); - - // compute max ordinal and used ordinal flag array - U64 ordinal_low = max_U64; - B8 *is_ordinal_used = push_array(arena, B8, max_U16); - for (LNK_ExportParseNode *exp_n = export_list.first; exp_n != 0; exp_n = exp_n->next) { - LNK_ExportParse *exp = &exp_n->data; - if (exp->is_ordinal_assigned) { - ordinal_low = Min(ordinal_low, exp->ordinal); - is_ordinal_used[exp->ordinal] = 1; - } - } - - LNK_ExportParsePtrArray named_exports = {0}; - LNK_ExportParsePtrArray noname_exports = {0}; - LNK_ExportParsePtrArray forwarder_exports = {0}; - { - // group exports based on flags - LNK_ExportParseList named_exports_list = {0}; - LNK_ExportParseList noname_exports_list = {0}; - LNK_ExportParseList forwarder_exports_list = {0}; - for (LNK_ExportParseNode *exp_n = export_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { - exp_n_next = exp_n->next; - if (exp_n->data.is_forwarder) { - lnk_export_parse_list_push_node(&forwarder_exports_list, exp_n); - } else if (exp_n->data.is_noname_present) { - AssertAlways(exp_n->data.is_ordinal_assigned); - lnk_export_parse_list_push_node(&noname_exports_list, exp_n); - } else { - lnk_export_parse_list_push_node(&named_exports_list, exp_n); - } - } - - // list -> array - named_exports = lnk_array_from_export_list(scratch.arena, named_exports_list); - noname_exports = lnk_array_from_export_list(scratch.arena, noname_exports_list); - forwarder_exports = lnk_array_from_export_list(scratch.arena, forwarder_exports_list); - - // sort exports - radsort(named_exports.v, named_exports.count, lnk_named_export_is_before); - radsort(noname_exports.v, noname_exports.count, lnk_ordinal_export_is_before); - radsort(forwarder_exports.v, forwarder_exports.count, lnk_named_export_is_before); - - MemoryZeroStruct(&export_list); - lnk_export_parse_list_concat_in_place(&export_list, &named_exports_list); - lnk_export_parse_list_concat_in_place(&export_list, &forwarder_exports_list); - lnk_export_parse_list_concat_in_place(&export_list, &noname_exports_list); - } - - // assign omitted ordinals - { - U16 last_ordinal = ordinal_low; - for (U64 exp_idx = 0; exp_idx < named_exports.count; exp_idx += 1) { - LNK_ExportParse *exp = named_exports.v[exp_idx]; - if (!exp->is_ordinal_assigned) { - for (; last_ordinal < max_U16 && is_ordinal_used[last_ordinal] != 0; last_ordinal += 1); - exp->ordinal = last_ordinal; - exp->is_ordinal_assigned = 1; - is_ordinal_used[last_ordinal] = 1; - } - } - for (U64 exp_idx = 0; exp_idx < forwarder_exports.count; exp_idx += 1) { - LNK_ExportParse *exp = forwarder_exports.v[exp_idx]; - if (!exp->is_ordinal_assigned) { - for (; last_ordinal < max_U16 && is_ordinal_used[last_ordinal] != 0; last_ordinal += 1); - exp->ordinal = last_ordinal; - exp->is_ordinal_assigned = 1; - is_ordinal_used[last_ordinal] = 1; - } - } - for (U64 exp_idx = 0; exp_idx < noname_exports.count; exp_idx += 1) { - LNK_ExportParse *exp = noname_exports.v[exp_idx]; - if (!exp->is_ordinal_assigned) { - exp->ordinal = last_ordinal; - exp->is_ordinal_assigned = 1; - is_ordinal_used[last_ordinal] = 1; - } - } - } - - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, machine); - - // push sections - COFF_ObjSection *voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$2"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero()); - COFF_ObjSection *name_voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$3"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero()); - COFF_ObjSection *ordinal_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$4"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); - COFF_ObjSection *string_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$5"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); - COFF_ObjSection *image_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$6"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, push_cstr(obj_writer->arena, image_name)); - - ProfBegin("Virtual Offset Table"); - { - B8 *is_ordinal_bound = push_array(scratch.arena, B8, max_U16); - LNK_ExportParsePtrArray *all_exports[] = { &named_exports, &forwarder_exports, &noname_exports }; - - for (U64 arr_idx = 0; arr_idx < ArrayCount(all_exports); arr_idx += 1) { - for (U64 exp_idx = 0; exp_idx < all_exports[arr_idx]->count; exp_idx += 1) { - LNK_ExportParse *exp = all_exports[arr_idx]->v[exp_idx]; - if (is_ordinal_bound[exp->ordinal] == 0) { - // alloc only one slot per ordinal, so it's possible to map ordinal to a virtual offset - is_ordinal_bound[exp->ordinal] = 1; - - // create slot for the ordinal virtual offset - U64 voff_offset = voff_table_sect->data.total_size; - U32 *voff = push_array(obj_writer->arena, U32, 1); - str8_list_push(obj_writer->arena, &voff_table_sect->data, str8_struct(voff)); - - COFF_ObjSymbol *exp_symbol; - if (exp->is_forwarder) { - U64 forwarder_name_offset = string_table_sect->data.total_size; - String8 forwarder_name_cstr = push_cstr(obj_writer->arena, exp->name); - str8_list_push(obj_writer->arena, &string_table_sect->data, forwarder_name_cstr); - // symbol to the name string - exp_symbol = coff_obj_writer_push_symbol_static(obj_writer, exp->name, forwarder_name_offset, string_table_sect); - } else { - // function or global var symbol - exp_symbol = coff_obj_writer_push_symbol_undef(obj_writer, exp->name); - } - - U16 ordinal_nb = exp->ordinal - ordinal_low; - coff_obj_writer_section_push_reloc(obj_writer, voff_table_sect, ordinal_nb*sizeof(U32), exp_symbol, coff_virt_off_reloc_from_machine(machine)); - } - } - } - } - ProfEnd(); - - ProfBegin("Named & Forwarder Exports"); - { - LNK_ExportParsePtrArray *exports_with_names[] = { &named_exports, &forwarder_exports }; - - // assign hints - for (U64 arr_idx = 0, hint = 0; arr_idx < ArrayCount(exports_with_names); arr_idx += 1) { - for (U64 exp_idx = 0; exp_idx < exports_with_names[arr_idx]->count; exp_idx += 1, hint += 1) { - LNK_ExportParse *exp = exports_with_names[arr_idx]->v[exp_idx]; - exp->hint = hint; - } - } - - for (U64 arr_idx = 0; arr_idx < ArrayCount(exports_with_names); arr_idx += 1) { - LNK_ExportParsePtrArray *exports = exports_with_names[arr_idx]; - for (U64 exp_idx = 0; exp_idx < exports->count; exp_idx += 1) { - LNK_ExportParse *exp = exports->v[exp_idx]; - - String8 name = lnk_name_from_export_parse(exp); - - // store symbol name string - U64 export_name_offset = string_table_sect->data.total_size; - String8 export_name_cstr = push_cstr(obj_writer->arena, name); - str8_list_push(obj_writer->arena, &string_table_sect->data, export_name_cstr); - - // create symbol for the name string - String8 export_name_symbol_name = push_str8f(obj_writer->arena, "RAD_NAME:%S", name); - COFF_ObjSymbol *export_name_symbol = coff_obj_writer_push_symbol_extern(obj_writer, export_name_symbol_name, export_name_offset, string_table_sect); - - // create slot for export virtual offset - U64 export_name_voff_offset = name_voff_table_sect->data.total_size; - U8 *export_name_voff = push_array(obj_writer->arena, U8, sizeof(U32)); - str8_list_push(obj_writer->arena, &name_voff_table_sect->data, str8_array(export_name_voff, sizeof(U32))); - - // write string's virtual offset - coff_obj_writer_section_push_reloc(obj_writer, name_voff_table_sect, export_name_voff_offset, export_name_symbol, coff_virt_off_reloc_from_machine(machine)); - - // create and store export's ordinal - U16 *ordinal = push_array(obj_writer->arena, U16, 1); - *ordinal = exp->ordinal - ordinal_low; - str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal)); - } - } - } - ProfEnd(); - - ProfBegin("NONAME Exports"); - { - for (U64 exp_idx = 0; exp_idx < noname_exports.count; exp_idx += 1) { - // create and store export's ordinal - LNK_ExportParse *exp = noname_exports.v[exp_idx]; - U16 *ordinal = push_array(obj_writer->arena, U16, 1); - *ordinal = exp->ordinal - ordinal_low; - str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal)); - } - } - ProfEnd(); - - // fill out export table header - PE_ExportTableHeader *header = push_array(obj_writer->arena, PE_ExportTableHeader, 1); - header->time_stamp = COFF_TimeStamp_Max; - header->ordinal_base = safe_cast_u16(ordinal_low); - header->export_address_table_count = safe_cast_u32(voff_table_sect->data.node_count); - header->name_pointer_table_count = safe_cast_u32(name_voff_table_sect->data.node_count); - - // push header field's symbols - COFF_ObjSymbol *image_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_NAME_VOFF"), 0, image_name_sect); - COFF_ObjSymbol *address_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_ADDRESS_TABLE_VOFF"), 0, voff_table_sect); - COFF_ObjSymbol *name_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_NAME_POINTER_VOFF"), 0, name_voff_table_sect); - COFF_ObjSymbol *ordinal_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_ORDINAL_TABLE_VOFF"), 0, ordinal_table_sect); - - // push export table header section - COFF_ObjSection *header_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$1"), LNK_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_struct(header)); - coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_HEADER"), 0, header_sect); - - // patch export table header - COFF_RelocType virt_off_reloc_type = coff_virt_off_reloc_from_machine(machine); - coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_voff), image_name_symbol, virt_off_reloc_type); - coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, export_address_table_voff), address_table_symbol, virt_off_reloc_type); - coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_pointer_table_voff), name_table_symbol, virt_off_reloc_type); - coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, ordinal_table_voff), ordinal_table_symbol, virt_off_reloc_type); - - String8 obj = coff_obj_writer_serialize(arena, obj_writer); - coff_obj_writer_release(&obj_writer); - - os_write_data_to_file_path(str8_lit("foo.obj"), obj); - - scratch_end(scratch); - return obj; -} - -internal String8List -lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportParseList export_list) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - dll_name = str8_skip_last_slash(dll_name); - - // These objects appear in first three members of any lib that linker produces with /dll. - // Objects are used by MSVC linker to build import table. - String8 import_entry_obj = lnk_build_import_entry_obj(scratch.arena, dll_name, time_stamp, machine); - String8 null_import_descriptor_obj = lnk_build_null_import_descriptor_obj(scratch.arena, time_stamp, machine); - String8 null_thunk_data_obj = lnk_build_null_thunk_data_obj(scratch.arena, dll_name, time_stamp, machine); - - COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); - - // push import table nulls - coff_lib_writer_push_obj(lib_writer, dll_name, import_entry_obj); - coff_lib_writer_push_obj(lib_writer, dll_name, null_import_descriptor_obj); - coff_lib_writer_push_obj(lib_writer, dll_name, null_thunk_data_obj); - - // push exports - for (LNK_ExportParseNode *exp_n = export_list.first; exp_n != 0; exp_n = exp_n->next) { - LNK_ExportParse *exp = &exp_n->data; - if (exp->is_noname_present) { - coff_lib_writer_push_export_by_ordinal(lib_writer, machine, time_stamp, dll_name, exp->type, exp->ordinal); - } else { - String8 name = lnk_name_from_export_parse(exp); - COFF_LibWriterSymbolNode *member_symbol = coff_lib_writer_push_export_by_name(lib_writer, machine, time_stamp, dll_name, exp->type, name, exp->hint); - - COFF_LibWriterSymbol imp_symbol = {0}; - imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name); - imp_symbol.member_idx = member_symbol->data.member_idx; - coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, imp_symbol); - } - } - - // serialize lib - String8List lib = coff_lib_writer_serialize(arena, lib_writer, COFF_TimeStamp_Max, 0, /* emit second member: */ 1); - coff_lib_writer_release(&lib_writer); - - scratch_end(scratch); - ProfEnd(); - return lib; -} - diff --git a/src/linker/lnk_export_table.h b/src/linker/lnk_export_table.h deleted file mode 100644 index 78474d61..00000000 --- a/src/linker/lnk_export_table.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2025 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -typedef struct LNK_ExportParse -{ - String8 obj_path; - String8 lib_path; - String8 name; - String8 alias; - COFF_ImportType type; - U16 ordinal; - U16 hint; - B32 is_ordinal_assigned; - B32 is_noname_present; - B32 is_private; - B32 is_forwarder; -} LNK_ExportParse; - -typedef struct LNK_ExportParseNode -{ - LNK_ExportParse data; - struct LNK_ExportParseNode *next; -} LNK_ExportParseNode; - -typedef struct LNK_ExportParseList -{ - U64 count; - LNK_ExportParseNode *first; - LNK_ExportParseNode *last; -} LNK_ExportParseList; - -typedef struct LNK_ExportParsePtrArray -{ - U64 count; - LNK_ExportParse **v; -} LNK_ExportParsePtrArray; - -//////////////////////////////// - -internal B32 lnk_parse_export_directive_ex(Arena *arena, String8List directive, String8 obj_path, String8 lib_path, LNK_ExportParse *export_out); -internal B32 lnk_parse_export_directive(Arena *arena, String8 directive, String8 obj_path, String8 lib_path, LNK_ExportParse *parse_out); -internal LNK_ExportParsePtrArray lnk_array_from_export_list(Arena *arena, LNK_ExportParseList list); -internal LNK_ExportParseNode * lnk_export_parse_list_push(Arena *arena, LNK_ExportParseList *list, LNK_ExportParse data); -internal String8List lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportParseList export_list); diff --git a/src/linker/lnk_image_section_flags.h b/src/linker/lnk_image_section_flags.h deleted file mode 100644 index a237d7ce..00000000 --- a/src/linker/lnk_image_section_flags.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2025 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -// --- Default Section Flags -------------------------------------------------- - -#define LNK_TEXT_SECTION_FLAGS (COFF_SectionFlag_CntCode|COFF_SectionFlag_MemExecute|COFF_SectionFlag_MemRead) -#define LNK_DATA_SECTION_FLAGS (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite) -#define LNK_RDATA_SECTION_FLAGS (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead) -#define LNK_BSS_SECTION_FLAGS (COFF_SectionFlag_CntUninitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite) -#define LNK_IDATA_SECTION_FLAGS LNK_DATA_SECTION_FLAGS -#define LNK_DEBUG_DIR_SECTION_FLAGS LNK_DATA_SECTION_FLAGS -#define LNK_RSRC_SECTION_FLAGS LNK_DATA_SECTION_FLAGS -#define LNK_RSRC1_SECTION_FLAGS (LNK_DATA_SECTION_FLAGS | COFF_SectionFlag_Align4Bytes) -#define LNK_RSRC2_SECTION_FLAGS (LNK_DATA_SECTION_FLAGS | COFF_SectionFlag_Align4Bytes) -#define LNK_XDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_PDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_EDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_GFIDS_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_GIATS_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_GLJMP_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_GEHCONT_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS -#define LNK_RELOC_SECTION_FLAGS (LNK_RDATA_SECTION_FLAGS | COFF_SectionFlag_MemDiscardable) -#define LNK_DEBUG_SECTION_FLAGS (LNK_RDATA_SECTION_FLAGS | COFF_SectionFlag_MemDiscardable) - diff --git a/src/linker/lnk_import_table.h b/src/linker/lnk_import_table.h deleted file mode 100644 index 6dcd300b..00000000 --- a/src/linker/lnk_import_table.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2025 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -typedef struct LNK_ImportFunc -{ - struct LNK_ImportFunc *next; - struct LNK_Symbol *symbol; -} LNK_ImportFunc; - -typedef struct LNK_ImportDLL -{ - struct LNK_ImportDLL *next; - struct LNK_ImportFunc *first_func; - struct LNK_ImportFunc *last_func; - String8 name; -} LNK_ImportDLL; - -typedef struct LNK_ImportTable -{ - Arena *arena; - LNK_ImportDLL *first_dll; - LNK_ImportDLL *last_dll; - U64 dll_count; - HashTable *dll_ht; -} LNK_ImportTable; - -//////////////////////////////// - -internal LNK_ImportTable * lnk_import_table_alloc(void); -internal void lnk_import_table_release(LNK_ImportTable **imptab_ptr); -internal LNK_ImportDLL * lnk_import_table_push_dll(LNK_ImportTable *imptab, String8 dll_name, COFF_MachineType machine); -internal LNK_ImportDLL * lnk_import_table_search_dll(LNK_ImportTable *imptab, String8 dll_name); -internal LNK_ImportFunc * lnk_import_table_push_func(LNK_ImportTable *imptab, LNK_ImportDLL *dll, struct LNK_Symbol *symbol); - -internal COFF_ObjSymbol * lnk_emit_indirect_jump_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *iat_symbol, String8 thunk_name); -internal COFF_ObjSymbol * lnk_emit_load_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *imp_addr_ptr, COFF_ObjSymbol *tail_merge, String8 func_name); -internal COFF_ObjSymbol * lnk_emit_tail_merge_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, String8 dll_name, COFF_ObjSymbol *dll_import_descriptor); - -internal String8 lnk_build_import_entry_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine); -internal String8 lnk_build_null_import_descriptor_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine); -internal String8 lnk_build_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine); - -internal void lnk_emit_dll_import_debug_symbols(COFF_ObjWriter *obj_writer, String8 dll_name); -internal String8 lnk_obj_from_import_dll_static(Arena *arena, COFF_MachineType machine, LNK_ImportDLL *dll); -internal String8 lnk_obj_from_import_dll_delayed(Arena *arena, COFF_MachineType machine, LNK_ImportDLL *dll, B32 emit_biat, B32 emit_uiat); - -internal String8Array lnk_make_import_dlls_static(Arena *arena, LNK_ImportTable *imptab, COFF_MachineType machine, String8 image_name); -internal String8Array lnk_make_import_dlls_delayed(Arena *arena, LNK_ImportTable *imptab, COFF_MachineType machine, String8 image_name, B32 emit_biat, B32 emit_uiat); - diff --git a/src/natvis/base.natvis b/src/natvis/base.natvis index 644b583c..ce938f33 100644 --- a/src/natvis/base.natvis +++ b/src/natvis/base.natvis @@ -151,6 +151,8 @@ + + {{ count={count} first={first} }} count diff --git a/src/pe/pe_make_export_table.c b/src/pe/pe_make_export_table.c new file mode 100644 index 00000000..e4d37e02 --- /dev/null +++ b/src/pe/pe_make_export_table.c @@ -0,0 +1,343 @@ +// Copyright (c) 2025 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal String8 +pe_name_from_export_parse(PE_ExportParse *exp) +{ + String8 name; + if (exp->is_forwarder) { + name = exp->alias; + } else if (exp->alias.size) { + name = exp->alias; + } else { + name = exp->name; + } + return name; +} + +internal PE_ExportParsePtrArray +pe_array_from_export_list(Arena *arena, PE_ExportParseList list) +{ + PE_ExportParsePtrArray result = {0}; + result.v = push_array_no_zero(arena, PE_ExportParse *, list.count); + for (PE_ExportParseNode *exp = list.first; exp != 0; exp = exp->next) { + result.v[result.count++] = &exp->data; + } + return result; +} + +internal void +pe_export_parse_list_push_node(PE_ExportParseList *list, PE_ExportParseNode *node) +{ + SLLQueuePush(list->first, list->last, node); + list->count += 1; +} + +internal PE_ExportParseNode * +pe_export_parse_list_push(Arena *arena, PE_ExportParseList *list, PE_ExportParse data) +{ + PE_ExportParseNode *node = push_array(arena, PE_ExportParseNode, 1); + node->data = data; + pe_export_parse_list_push_node(list, node); + return node; +} + +internal void +pe_export_parse_list_concat_in_place(PE_ExportParseList *list, PE_ExportParseList *to_concat) +{ + if (to_concat->count) { + if (list->count) { + list->last->next = to_concat->first; + list->last = to_concat->last; + } else { + list->first = to_concat->first; + list->last = to_concat->last; + } + list->count += to_concat->count; + MemoryZeroStruct(to_concat); + } +} + +internal int +pe_named_export_is_before(void *raw_a, void *raw_b) +{ + PE_ExportParse *a = *(PE_ExportParse **)raw_a; + PE_ExportParse *b = *(PE_ExportParse **)raw_b; + int cmp = str8_compar_case_sensitive(&a->name, &b->name); + return cmp < 0; +} + +internal int +pe_ordinal_export_is_before(void *raw_a, void *raw_b) +{ + PE_ExportParse *a = raw_a; + PE_ExportParse *b = raw_b; + return a->ordinal < b->ordinal; +} + +internal PE_FinalizedExports +pe_finalize_export_list(Arena *arena, PE_ExportParseList export_list) +{ + // compute max ordinal and used ordinal flag array + U64 ordinal_low = max_U64; + B8 *is_ordinal_used = push_array(arena, B8, max_U16); + for (PE_ExportParseNode *exp_n = export_list.first; exp_n != 0; exp_n = exp_n->next) { + PE_ExportParse *exp = &exp_n->data; + if (exp->is_ordinal_assigned) { + ordinal_low = Min(ordinal_low, exp->ordinal); + is_ordinal_used[exp->ordinal] = 1; + } + } + + PE_ExportParsePtrArray named_exports = {0}; + PE_ExportParsePtrArray ordinal_exports = {0}; + PE_ExportParsePtrArray forwarder_exports = {0}; + { + // group exports based on flags + PE_ExportParseList named_exports_list = {0}; + PE_ExportParseList ordinal_exports_list = {0}; + PE_ExportParseList forwarder_exports_list = {0}; + for (PE_ExportParseNode *exp_n = export_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { + exp_n_next = exp_n->next; + if (exp_n->data.is_forwarder) { + pe_export_parse_list_push_node(&forwarder_exports_list, exp_n); + } else if (exp_n->data.is_noname_present) { + AssertAlways(exp_n->data.is_ordinal_assigned); + pe_export_parse_list_push_node(&ordinal_exports_list, exp_n); + } else { + pe_export_parse_list_push_node(&named_exports_list, exp_n); + } + } + + // list -> array + named_exports = pe_array_from_export_list(arena, named_exports_list); + forwarder_exports = pe_array_from_export_list(arena, forwarder_exports_list); + ordinal_exports = pe_array_from_export_list(arena, ordinal_exports_list); + + // sort exports + radsort(named_exports.v, named_exports.count, pe_named_export_is_before); + radsort(ordinal_exports.v, ordinal_exports.count, pe_ordinal_export_is_before); + radsort(forwarder_exports.v, forwarder_exports.count, pe_named_export_is_before); + + MemoryZeroStruct(&export_list); + pe_export_parse_list_concat_in_place(&export_list, &named_exports_list); + pe_export_parse_list_concat_in_place(&export_list, &forwarder_exports_list); + pe_export_parse_list_concat_in_place(&export_list, &ordinal_exports_list); + } + + // assign omitted ordinals + { + U16 last_ordinal = ordinal_low; + for (U64 exp_idx = 0; exp_idx < named_exports.count; exp_idx += 1) { + PE_ExportParse *exp = named_exports.v[exp_idx]; + if (!exp->is_ordinal_assigned) { + for (; last_ordinal < max_U16 && is_ordinal_used[last_ordinal] != 0; last_ordinal += 1); + exp->ordinal = last_ordinal; + exp->is_ordinal_assigned = 1; + is_ordinal_used[last_ordinal] = 1; + } + } + for (U64 exp_idx = 0; exp_idx < forwarder_exports.count; exp_idx += 1) { + PE_ExportParse *exp = forwarder_exports.v[exp_idx]; + if (!exp->is_ordinal_assigned) { + for (; last_ordinal < max_U16 && is_ordinal_used[last_ordinal] != 0; last_ordinal += 1); + exp->ordinal = last_ordinal; + exp->is_ordinal_assigned = 1; + is_ordinal_used[last_ordinal] = 1; + } + } + for (U64 exp_idx = 0; exp_idx < ordinal_exports.count; exp_idx += 1) { + PE_ExportParse *exp = ordinal_exports.v[exp_idx]; + if (!exp->is_ordinal_assigned) { + exp->ordinal = last_ordinal; + exp->is_ordinal_assigned = 1; + is_ordinal_used[last_ordinal] = 1; + } + } + } + + // assign hints + { + U64 hint = 0; + for (U64 exp_idx = 0; exp_idx < named_exports.count; exp_idx += 1, hint += 1) { + named_exports.v[exp_idx]->hint = hint; + } + for (U64 exp_idx = 0; exp_idx < forwarder_exports.count; exp_idx += 1, hint += 1) { + forwarder_exports.v[exp_idx]->hint = hint; + } + } + + PE_FinalizedExports result = {0}; + result.named_exports = named_exports; + result.forwarder_exports = forwarder_exports; + result.ordinal_exports = ordinal_exports; + + return result; +} + +internal String8 +pe_make_edata_obj(Arena *arena, + String8 image_name, + COFF_TimeStamp time_stamp, + COFF_MachineType machine, + PE_FinalizedExports finalized_exports) +{ + Temp scratch = scratch_begin(&arena, 1); + + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); + + // push sections + COFF_ObjSection *voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$2"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero()); + COFF_ObjSection *name_voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$3"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero()); + COFF_ObjSection *ordinal_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$4"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); + COFF_ObjSection *string_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$5"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); + COFF_ObjSection *image_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$6"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, push_cstr(obj_writer->arena, image_name)); + + ProfBegin("Virtual Offset Table"); + { + B8 *is_ordinal_bound = push_array(scratch.arena, B8, max_U16); + + for (U64 arr_idx = 0; arr_idx < ArrayCount(finalized_exports.all); arr_idx += 1) { + for (U64 exp_idx = 0; exp_idx < finalized_exports.all[arr_idx].count; exp_idx += 1) { + PE_ExportParse *exp = finalized_exports.all[arr_idx].v[exp_idx]; + if (is_ordinal_bound[exp->ordinal] == 0) { + // alloc only one slot per ordinal, so it's possible to map ordinal to a virtual offset + is_ordinal_bound[exp->ordinal] = 1; + + // create slot for the ordinal virtual offset + U64 voff_offset = voff_table_sect->data.total_size; + U32 *voff = push_array(obj_writer->arena, U32, 1); + str8_list_push(obj_writer->arena, &voff_table_sect->data, str8_struct(voff)); + + COFF_ObjSymbol *exp_symbol; + if (exp->is_forwarder) { + U64 forwarder_name_offset = string_table_sect->data.total_size; + String8 forwarder_name_cstr = push_cstr(obj_writer->arena, exp->name); + str8_list_push(obj_writer->arena, &string_table_sect->data, forwarder_name_cstr); + // symbol to the name string + exp_symbol = coff_obj_writer_push_symbol_static(obj_writer, exp->name, forwarder_name_offset, string_table_sect); + } else { + // function or global var symbol + exp_symbol = coff_obj_writer_push_symbol_undef(obj_writer, exp->name); + } + + U16 ordinal_nb = exp->ordinal - finalized_exports.ordinal_low; + coff_obj_writer_section_push_reloc_voff(obj_writer, voff_table_sect, ordinal_nb*sizeof(U32), exp_symbol); + } + } + } + } + ProfEnd(); + + ProfBegin("Named & Forwarder Exports"); + { + for (U64 arr_idx = 0; arr_idx < ArrayCount(finalized_exports.exports_with_names); arr_idx += 1) { + PE_ExportParsePtrArray exports = finalized_exports.exports_with_names[arr_idx]; + for (U64 exp_idx = 0; exp_idx < exports.count; exp_idx += 1) { + PE_ExportParse *exp = exports.v[exp_idx]; + + String8 name = pe_name_from_export_parse(exp); + + // store symbol name string + U64 export_name_offset = string_table_sect->data.total_size; + String8 export_name_cstr = push_cstr(obj_writer->arena, name); + str8_list_push(obj_writer->arena, &string_table_sect->data, export_name_cstr); + + // create symbol for the name string + String8 export_name_symbol_name = push_str8f(obj_writer->arena, "RAD_NAME:%S", name); + COFF_ObjSymbol *export_name_symbol = coff_obj_writer_push_symbol_extern(obj_writer, export_name_symbol_name, export_name_offset, string_table_sect); + + // create slot for export virtual offset + U64 export_name_voff_offset = name_voff_table_sect->data.total_size; + U8 *export_name_voff = push_array(obj_writer->arena, U8, sizeof(U32)); + str8_list_push(obj_writer->arena, &name_voff_table_sect->data, str8_array(export_name_voff, sizeof(U32))); + + // write string's virtual offset + coff_obj_writer_section_push_reloc_voff(obj_writer, name_voff_table_sect, export_name_voff_offset, export_name_symbol); + + // create and store export's ordinal + U16 *ordinal = push_array(obj_writer->arena, U16, 1); + *ordinal = exp->ordinal - finalized_exports.ordinal_low; + str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal)); + } + } + } + ProfEnd(); + + ProfBegin("Ordinal Exports"); + { + for (U64 exp_idx = 0; exp_idx < finalized_exports.ordinal_exports.count; exp_idx += 1) { + // create and store export's ordinal + PE_ExportParse *exp = finalized_exports.ordinal_exports.v[exp_idx]; + U16 *ordinal = push_array(obj_writer->arena, U16, 1); + *ordinal = exp->ordinal - finalized_exports.ordinal_low; + str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal)); + } + } + ProfEnd(); + + // fill out export table header + PE_ExportTableHeader *header = push_array(obj_writer->arena, PE_ExportTableHeader, 1); + header->time_stamp = time_stamp; + header->ordinal_base = safe_cast_u16(finalized_exports.ordinal_low); + header->export_address_table_count = safe_cast_u32(voff_table_sect->data.node_count); + header->name_pointer_table_count = safe_cast_u32(name_voff_table_sect->data.node_count); + + // push header field's symbols + COFF_ObjSymbol *image_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_NAME_VOFF"), 0, image_name_sect); + COFF_ObjSymbol *address_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_ADDRESS_TABLE_VOFF"), 0, voff_table_sect); + COFF_ObjSymbol *name_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_NAME_POINTER_VOFF"), 0, name_voff_table_sect); + COFF_ObjSymbol *ordinal_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_ORDINAL_TABLE_VOFF"), 0, ordinal_table_sect); + + // push export table header section + COFF_ObjSection *header_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$1"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_struct(header)); + coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_HEADER"), 0, header_sect); + + // patch export table header + coff_obj_writer_section_push_reloc_voff(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_voff), image_name_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, export_address_table_voff), address_table_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_pointer_table_voff), name_table_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, ordinal_table_voff), ordinal_table_symbol); + + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); + + scratch_end(scratch); + return obj; +} + +internal String8List +pe_make_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, String8 debug_symbols, PE_ExportParseList export_list) +{ + ProfBeginFunction(); + + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + + // These objects appear in first three members of any lib that linker produces with /dll. + // Objects are used by MSVC linker to build import table. + String8 import_entry_obj = pe_make_import_entry_obj(lib_writer->arena, dll_name, time_stamp, machine, debug_symbols); + String8 null_import_descriptor_obj = pe_make_null_import_descriptor_obj(lib_writer->arena, time_stamp, machine, debug_symbols); + String8 null_thunk_data_obj = pe_make_null_thunk_data_obj(lib_writer->arena, dll_name, time_stamp, machine, debug_symbols); + + // push import table nulls + coff_lib_writer_push_obj(lib_writer, dll_name, import_entry_obj); + coff_lib_writer_push_obj(lib_writer, dll_name, null_import_descriptor_obj); + coff_lib_writer_push_obj(lib_writer, dll_name, null_thunk_data_obj); + + // push exports + for (PE_ExportParseNode *exp_n = export_list.first; exp_n != 0; exp_n = exp_n->next) { + PE_ExportParse *exp = &exp_n->data; + if (exp->is_private) { + continue; + } + String8 name = pe_name_from_export_parse(exp); + coff_lib_writer_push_export_by_name(lib_writer, machine, time_stamp, dll_name, exp->type, name, exp->hint); + } + + // serialize lib + String8List lib = coff_lib_writer_serialize(arena, lib_writer, COFF_TimeStamp_Max, 0, /* emit second member: */ 1); + coff_lib_writer_release(&lib_writer); + + ProfEnd(); + return lib; +} diff --git a/src/pe/pe_make_export_table.h b/src/pe/pe_make_export_table.h new file mode 100644 index 00000000..e5a5e6ad --- /dev/null +++ b/src/pe/pe_make_export_table.h @@ -0,0 +1,61 @@ +// Copyright (c) 2025 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef PE_MAKE_EXPORT_TABLE_H +#define PE_MAKE_EXPORT_TABLE_H + +typedef struct PE_ExportParse +{ + String8 obj_path; + String8 lib_path; + String8 name; + String8 alias; + COFF_ImportType type; + U16 ordinal; + U16 hint; + B32 is_ordinal_assigned; + B32 is_noname_present; + B32 is_private; + B32 is_forwarder; +} PE_ExportParse; + +typedef struct PE_ExportParseNode +{ + PE_ExportParse data; + struct PE_ExportParseNode *next; +} PE_ExportParseNode; + +typedef struct PE_ExportParseList +{ + U64 count; + PE_ExportParseNode *first; + PE_ExportParseNode *last; +} PE_ExportParseList; + +typedef struct PE_ExportParsePtrArray +{ + U64 count; + PE_ExportParse **v; +} PE_ExportParsePtrArray; + +typedef struct PE_FinalizedExports +{ + U64 ordinal_low; + union { + struct { + PE_ExportParsePtrArray named_exports; + PE_ExportParsePtrArray forwarder_exports; + PE_ExportParsePtrArray ordinal_exports; + }; + PE_ExportParsePtrArray exports_with_names[2]; + PE_ExportParsePtrArray all[3]; + }; +} PE_FinalizedExports; + +//////////////////////////////// + +internal PE_ExportParsePtrArray pe_array_from_export_list(Arena *arena, PE_ExportParseList list); +internal PE_ExportParseNode * pe_export_parse_list_push(Arena *arena, PE_ExportParseList *list, PE_ExportParse data); +internal String8List pe_make_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, String8 debug_symbols, PE_ExportParseList export_list); + +#endif // COFF_EXPORT_TABLE_H diff --git a/src/linker/lnk_import_table.c b/src/pe/pe_make_import_table.c similarity index 51% rename from src/linker/lnk_import_table.c rename to src/pe/pe_make_import_table.c index 9beccc87..8935e0a9 100644 --- a/src/linker/lnk_import_table.c +++ b/src/pe/pe_make_import_table.c @@ -1,58 +1,8 @@ // Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal LNK_ImportTable * -lnk_import_table_alloc(void) -{ - Arena *arena = arena_alloc(); - LNK_ImportTable *imptab = push_array(arena, LNK_ImportTable, 1); - imptab->arena = arena; - imptab->dll_ht = hash_table_init(arena, 512); - return imptab; -} - -internal void -lnk_import_table_release(LNK_ImportTable **imptab_ptr) -{ - ProfBeginFunction(); - arena_release((*imptab_ptr)->arena); - *imptab_ptr = 0; - ProfEnd(); -} - -internal LNK_ImportDLL * -lnk_import_table_push_dll(LNK_ImportTable *imptab, String8 dll_name, COFF_MachineType machine) -{ - LNK_ImportDLL *dll = push_array(imptab->arena, LNK_ImportDLL, 1); - - // update list - SLLQueuePush(imptab->first_dll, imptab->last_dll, dll); - - // update name -> dll hash table - hash_table_push_path_raw(imptab->arena, imptab->dll_ht, dll->name, dll); - - return dll; -} - -internal LNK_ImportDLL * -lnk_import_table_search_dll(LNK_ImportTable *imptab, String8 dll_name) -{ - LNK_ImportDLL *dll = 0; - hash_table_search_string_raw(imptab->dll_ht, dll_name, &dll); - return dll; -} - -internal LNK_ImportFunc * -lnk_import_table_push_func(LNK_ImportTable *imptab, LNK_ImportDLL *dll, struct LNK_Symbol *symbol) -{ - LNK_ImportFunc *func = push_array(imptab->arena, LNK_ImportFunc, 1); - func->symbol = symbol; - lnk_import_table_push_func_node(imptab, dll, func); - return func; -} - internal COFF_ObjSymbol * -lnk_emit_indirect_jump_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *iat_symbol, String8 thunk_name) +pe_make_indirect_jump_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *iat_symbol, String8 thunk_name) { ProfBeginFunction(); @@ -74,7 +24,7 @@ lnk_emit_indirect_jump_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *co } internal COFF_ObjSymbol * -lnk_emit_load_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *imp_addr_ptr, COFF_ObjSymbol *tail_merge, String8 func_name) +pe_make_load_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *imp_addr_ptr, COFF_ObjSymbol *tail_merge, String8 func_name) { ProfBeginFunction(); @@ -105,7 +55,7 @@ lnk_emit_load_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, } internal COFF_ObjSymbol * -lnk_emit_tail_merge_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, String8 dll_name, COFF_ObjSymbol *dll_import_descriptor) +pe_make_tail_merge_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, String8 dll_name, String8 delay_load_helper_name, COFF_ObjSymbol *dll_import_descriptor) { ProfBeginFunction(); @@ -143,7 +93,7 @@ lnk_emit_tail_merge_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_ static const U64 LEA_OPERAND_OFFSET = 54; coff_obj_writer_section_push_reloc(obj_writer, code_sect, tail_merge_off + LEA_OPERAND_OFFSET, dll_import_descriptor, COFF_Reloc_X64_Rel32); - COFF_ObjSymbol *delay_load_helper = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit(LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME)); + COFF_ObjSymbol *delay_load_helper = coff_obj_writer_push_symbol_undef(obj_writer, delay_load_helper_name); // patch call __delayLoadHelper2 static const U64 CALL_OPERAND_OFFSET = 59; @@ -158,10 +108,9 @@ lnk_emit_tail_merge_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_ } internal String8 -lnk_build_import_entry_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine) +pe_make_import_entry_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols) { ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); Assert(machine == COFF_MachineType_X64); Assert(str8_match_lit("dll", str8_skip_last_dot(dll_name), StringMatchFlag_CaseInsensitive|StringMatchFlag_RightSideSloppy)); @@ -170,18 +119,10 @@ lnk_build_import_entry_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_s COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); - String8 debug_symbols; - { - CV_SymbolList symbol_list = { .signature = CV_Signature_C13 }; - String8 comp3_data = lnk_make_linker_compile3(scratch.arena, machine); - cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_COMPILE3, comp3_data); - debug_symbols = lnk_make_debug_s(obj_writer->arena, symbol_list); - } - String8 dll_name_cstr = push_cstr(obj_writer->arena, dll_name); - COFF_ObjSection *debugs = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), LNK_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); - COFF_ObjSection *idata2 = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$2"), LNK_DATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero()); - COFF_ObjSection *idata6 = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$6"), LNK_DATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); + COFF_ObjSection *debugs = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); + COFF_ObjSection *idata2 = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$2"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero()); + COFF_ObjSection *idata6 = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$6"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("@comp.id"), 0x1018175, COFF_SymStorageClass_Static); { @@ -210,30 +151,20 @@ lnk_build_import_entry_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_s coff_obj_writer_release(&obj_writer); - scratch_end(scratch); ProfEnd(); return obj; } internal String8 -lnk_build_null_import_descriptor_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine) +pe_make_null_import_descriptor_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols) { ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); - String8 debug_symbols; - { - CV_SymbolList symbol_list = { .signature = CV_Signature_C13 }; - String8 comp3_data = lnk_make_linker_compile3(scratch.arena, machine); - cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_COMPILE3, comp3_data); - debug_symbols = lnk_make_debug_s(obj_writer->arena, symbol_list); - } - PE_ImportEntry *import_desc = push_array(obj_writer->arena, PE_ImportEntry, 1); - COFF_ObjSection *debugs = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), LNK_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); - COFF_ObjSection *idata3 = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$3"), LNK_DATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_struct(import_desc)); + COFF_ObjSection *debugs = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); + COFF_ObjSection *idata3 = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$3"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_struct(import_desc)); coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("@comp.id"), 0x01018175, COFF_SymStorageClass_Static); coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("__NULL_IMPORT_DESCRIPTOR"), 0, idata3); @@ -242,30 +173,20 @@ lnk_build_null_import_descriptor_obj(Arena *arena, COFF_TimeStamp time_stamp, CO coff_obj_writer_release(&obj_writer); - scratch_end(scratch); ProfEnd(); return obj; } internal String8 -lnk_build_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine) +pe_make_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols) { ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); Assert(str8_match_lit("dll", str8_skip_last_dot(dll_name), StringMatchFlag_CaseInsensitive|StringMatchFlag_RightSideSloppy)); COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); - String8 debug_symbols; - { - CV_SymbolList symbol_list = { .signature = CV_Signature_C13 }; - String8 comp3_data = lnk_make_linker_compile3(scratch.arena, machine); - cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_COMPILE3, comp3_data); - debug_symbols = lnk_make_debug_s(obj_writer->arena, symbol_list); - } - - COFF_ObjSection *debugs = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), LNK_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); + COFF_ObjSection *debugs = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); COFF_ObjSection *idata4 = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$4"), COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite, str8_zero()); COFF_ObjSection *idata5 = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$5"), COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite, str8_zero()); @@ -291,74 +212,38 @@ lnk_build_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_TimeStamp tim coff_obj_writer_release(&obj_writer); - scratch_end(scratch); ProfEnd(); return obj; } -internal void -lnk_emit_dll_import_debug_symbols(COFF_ObjWriter *obj_writer, String8 dll_name) -{ - Temp scratch = scratch_begin(0,0); - - CV_SymbolList symbol_list = { .signature = CV_Signature_C13 }; - - // S_OBJ - String8 obj_data = cv_make_obj_name(scratch.arena, dll_name, 0); - cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_OBJNAME, obj_data); - - // S_COMPILE3 - String8 comp3_data = lnk_make_linker_compile3(scratch.arena, obj_writer->machine); - cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_COMPILE3, comp3_data); - - // S_END - cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_END, str8_zero()); - - // TODO: add thunks - - // serialize symbols - String8 debug_symbols = lnk_make_debug_s(obj_writer->arena, symbol_list); - - coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), LNK_DEBUG_SECTION_FLAGS, debug_symbols); - - scratch_end(scratch); -} - internal String8 -lnk_obj_from_import_dll_static(Arena *arena, COFF_MachineType machine, LNK_ImportDLL *dll) +pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 debug_symbols, String8List import_headers) { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, machine); + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); - U64 import_size = coff_word_size_from_machine(machine); + U64 import_size = coff_word_size_from_machine(machine); COFF_SectionFlags import_align = coff_section_flag_from_align_size(import_size); - PE_ImportEntry *impdesc = push_array(obj_writer->arena, PE_ImportEntry, 1); - String8 dll_name_cstr = push_cstr(obj_writer->arena, dll->name); + PE_ImportEntry *impdesc = push_array(obj_writer->arena, PE_ImportEntry, 1); + String8 dll_name_cstr = push_cstr(obj_writer->arena, dll_name); - COFF_ObjSection *dll_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$2"), LNK_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_struct(impdesc)); - COFF_ObjSection *ilt_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$4"), LNK_IDATA_SECTION_FLAGS|import_align, str8_zero()); - COFF_ObjSection *iat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$5"), LNK_IDATA_SECTION_FLAGS|import_align, str8_zero()); - COFF_ObjSection *int_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$6"), LNK_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); - COFF_ObjSection *dll_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$7"), LNK_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); - COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$i"), LNK_TEXT_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); + COFF_ObjSection *dll_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$2"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_struct(impdesc)); + COFF_ObjSection *ilt_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$4"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); + COFF_ObjSection *iat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$5"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); + COFF_ObjSection *int_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$6"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); + COFF_ObjSection *dll_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$7"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); + COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$i"), PE_TEXT_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); COFF_ObjSymbol *ilt_symbol = coff_obj_writer_push_symbol_static(obj_writer, ilt_sect->name, 0, ilt_sect); COFF_ObjSymbol *iat_symbol = coff_obj_writer_push_symbol_static(obj_writer, iat_sect->name, 0, iat_sect); COFF_ObjSymbol *dll_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, dll_name_sect->name, 0, dll_name_sect); - - switch (machine) { - case COFF_MachineType_Unknown: {} break; - case COFF_MachineType_X64: { - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_ImportEntry, lookup_table_voff), ilt_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_ImportEntry, name_voff), dll_name_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_ImportEntry, import_addr_table_voff), iat_symbol, COFF_Reloc_X64_Addr32Nb); - } break; - default: { NotImplemented; } break; - } - for (LNK_ImportFunc *func = dll->first_func; func != 0; func = func->next) { - LNK_Symbol *import_symbol = func->symbol; - COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(import_symbol->u.coff_import); + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_ImportEntry, lookup_table_voff), ilt_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_ImportEntry, name_voff), dll_name_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_ImportEntry, import_addr_table_voff), iat_symbol); + + for (String8Node *import_header_n = import_headers.first; import_header_n != 0; import_header_n = import_header_n->next) { + COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(import_header_n->string); String8 iat_symbol_name = push_str8f(obj_writer->arena, "__imp_%S", import_header.func_name); U64 iat_offset = iat_sect->data.total_size; @@ -391,8 +276,8 @@ lnk_obj_from_import_dll_static(Arena *arena, COFF_MachineType machine, LNK_Impor iat_symbol = coff_obj_writer_push_symbol_extern(obj_writer, iat_symbol_name, iat_offset, iat_sect); // patch IAT and ILT - coff_obj_writer_section_push_reloc(obj_writer, ilt_sect, ilt_offset, int_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, iat_sect, iat_offset, int_symbol, COFF_Reloc_X64_Addr32Nb); + coff_obj_writer_section_push_reloc_voff(obj_writer, ilt_sect, ilt_offset, int_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, iat_sect, iat_offset, int_symbol); } break; case COFF_ImportBy_Undecorate: { NotImplemented; @@ -407,7 +292,7 @@ lnk_obj_from_import_dll_static(Arena *arena, COFF_MachineType machine, LNK_Impor if (import_header.type == COFF_ImportHeader_Code) { switch (import_header.machine) { case COFF_MachineType_Unknown: {} break; - case COFF_MachineType_X64: { jmp_thunk_symbol = lnk_emit_indirect_jump_thunk_x64(obj_writer, code_sect, iat_symbol, import_header.func_name); } break; + case COFF_MachineType_X64: { jmp_thunk_symbol = pe_make_indirect_jump_thunk_x64(obj_writer, code_sect, iat_symbol, import_header.func_name); } break; default: { NotImplemented; } break; } } @@ -419,73 +304,72 @@ lnk_obj_from_import_dll_static(Arena *arena, COFF_MachineType machine, LNK_Impor } internal String8 -lnk_obj_from_import_dll_delayed(Arena *arena, COFF_MachineType machine, LNK_ImportDLL *dll, B32 emit_biat, B32 emit_uiat) +pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 delay_load_helper_name, String8 debug_symbols, String8List import_headers, B32 emit_biat, B32 emit_uiat) { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, machine); + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); // import descriptor PE_DelayedImportEntry *impdesc = push_array(obj_writer->arena, PE_DelayedImportEntry, 1); - impdesc->attributes = 1; + impdesc->attributes = 1; // DLL name cstring - String8 dll_name_cstr = push_cstr(obj_writer->arena, dll->name); + String8 dll_name_cstr = push_cstr(obj_writer->arena, dll_name); // DLL handle U64 handle_size = coff_word_size_from_machine(machine); - U8 *handle = push_array(obj_writer->arena, U8, handle_size); + U8 *handle = push_array(obj_writer->arena, U8, handle_size); // import align - U64 import_size = coff_word_size_from_machine(machine); + U64 import_size = coff_word_size_from_machine(machine); COFF_SectionFlags import_align = coff_section_flag_from_align_size(import_size); - COFF_ObjSection *dll_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$2"), LNK_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_struct(impdesc)); - COFF_ObjSection *ilt_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$4"), LNK_IDATA_SECTION_FLAGS|import_align, str8_zero()); - COFF_ObjSection *iat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$5"), LNK_IDATA_SECTION_FLAGS|import_align, str8_zero()); - COFF_ObjSection *int_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$6"), LNK_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); - COFF_ObjSection *dll_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$7"), LNK_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); - COFF_ObjSection *biat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$8"), LNK_IDATA_SECTION_FLAGS|import_align, str8_zero()); - COFF_ObjSection *uiat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$9"), LNK_IDATA_SECTION_FLAGS|import_align, str8_zero()); - COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$i"), LNK_TEXT_SECTION_FLAGS, str8_zero()); - COFF_ObjSection *handle_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$h"), LNK_DATA_SECTION_FLAGS, str8_array(handle, handle_size)); + // push sections + COFF_ObjSection *dll_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$2"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_struct(impdesc)); + COFF_ObjSection *ilt_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$4"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); + COFF_ObjSection *iat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$5"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); + COFF_ObjSection *int_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$6"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); + COFF_ObjSection *dll_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$7"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); + COFF_ObjSection *biat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$8"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); + COFF_ObjSection *uiat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$9"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); + COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$i"), PE_TEXT_SECTION_FLAGS, str8_zero()); + COFF_ObjSection *handle_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$h"), PE_DATA_SECTION_FLAGS, str8_array(handle, handle_size)); + COFF_ObjSection *debug_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); + // sections symbols COFF_ObjSymbol *dll_symbol = coff_obj_writer_push_symbol_static(obj_writer, dll_sect->name, 0, dll_sect); COFF_ObjSymbol *dll_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, dll_name_sect->name, 0, dll_name_sect); COFF_ObjSymbol *handle_symbol = coff_obj_writer_push_symbol_static(obj_writer, handle_sect->name, 0, handle_sect); COFF_ObjSymbol *iat_symbol = coff_obj_writer_push_symbol_static(obj_writer, iat_sect->name, 0, iat_sect); COFF_ObjSymbol *ilt_symbol = coff_obj_writer_push_symbol_static(obj_writer, ilt_sect->name, 0, ilt_sect); - switch (machine) { - case COFF_MachineType_Unknown: {} break; - case COFF_MachineType_X64: { - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, name_voff), dll_name_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, module_handle_voff), handle_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, iat_voff), iat_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, name_table_voff), ilt_symbol, COFF_Reloc_X64_Addr32Nb); - } break; - default: { NotImplemented; } break; - } + // patch virutal offsets in import header + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, name_voff), dll_name_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, module_handle_voff), handle_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, iat_voff), iat_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, name_table_voff), ilt_symbol); + // patch BIAT virtual offset in import header if (emit_biat) { COFF_ObjSymbol *biat_symbol = coff_obj_writer_push_symbol_static(obj_writer, biat_sect->name, 0, biat_sect); - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, bound_table_voff), biat_symbol, COFF_Reloc_X64_Addr32Nb); + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, bound_table_voff), biat_symbol); } + + // patch UIAT virtual offset in import header if (emit_uiat) { COFF_ObjSymbol *uiat_symbol = coff_obj_writer_push_symbol_static(obj_writer, uiat_sect->name, 0, uiat_sect); - coff_obj_writer_section_push_reloc(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, unload_table_voff), uiat_symbol, COFF_Reloc_X64_Addr32Nb); + coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_DelayedImportEntry, unload_table_voff), uiat_symbol); } // emit tail merge COFF_ObjSymbol *tail_merge_symbol = 0; switch (machine) { case COFF_MachineType_Unknown: {} break; - case COFF_MachineType_X64: { tail_merge_symbol = lnk_emit_tail_merge_thunk_x64(obj_writer, code_sect, dll->name, dll_symbol); } break; + case COFF_MachineType_X64: { tail_merge_symbol = pe_make_tail_merge_thunk_x64(obj_writer, code_sect, dll_name, delay_load_helper_name, dll_symbol); } break; default: { NotImplemented; } break; } - for (LNK_ImportFunc *func = dll->first_func; func != 0; func = func->next) { -#if 0 - LNK_Symbol *import_symbol = func->symbol; - COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(import_symbol->u.coff_import); + for (String8Node *import_header_n = import_headers.first; import_header_n != 0; import_header_n = import_header_n->next) { + COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(import_header_n->string); // emit thunks COFF_ObjSymbol *jmp_thunk_symbol = 0; @@ -493,146 +377,91 @@ lnk_obj_from_import_dll_delayed(Arena *arena, COFF_MachineType machine, LNK_Impo if (import_header.type == COFF_ImportHeader_Code) { switch (machine) { case COFF_MachineType_X64: { - String8 iat_symbol_name = push_str8f(obj_writer->arena, "__imp_%S", header->func_name); + String8 iat_symbol_name = push_str8f(obj_writer->arena, "__imp_%S", import_header.func_name); coff_obj_writer_push_symbol_undef(obj_writer, iat_symbol_name); // emit jmp thunk - jmp_thunk_symbol = lnk_emit_indirect_jump_thunk_x64(obj_writer, code_sect, iat_symbol, import_header.func_name); + jmp_thunk_symbol = pe_make_indirect_jump_thunk_x64(obj_writer, code_sect, iat_symbol, import_header.func_name); // emit load thunk - load_thunk_chunk = lnk_emit_load_thunk_x64(code_sect, code_table_chunk, iat_symbol, dll->tail_merge_symbol); + load_thunk_symbol = pe_make_load_thunk_x64(obj_writer, code_sect, iat_symbol, tail_merge_symbol, import_header.func_name); } break; - default: lnk_not_implemented("TODO: support for machine 0x%X", dll->machine); break; + default: { NotImplemented; } break; } } - U64 import_size = coff_word_size_from_machine(machine); - - switch (header->import_by) { + switch (import_header.import_by) { case COFF_ImportBy_Ordinal: { + U64 iat_offset = iat_sect->data.total_size; String8 ordinal_data = coff_ordinal_data_from_hint(obj_writer->arena, import_header.machine, import_header.hint_or_ordinal); str8_list_push(obj_writer->arena, &ilt_sect->data, ordinal_data); str8_list_push(obj_writer->arena, &iat_sect->data, ordinal_data); + String8 iat_symbol_name = push_str8f(obj_writer->arena, "__imp_%S", import_header.func_name); iat_symbol = coff_obj_writer_push_symbol_extern(obj_writer, iat_symbol_name, iat_offset, iat_sect); + if (emit_biat) { - biat_chunk = lnk_section_push_chunk_bss(data_sect, biat_table_chunk, import_size, sort_index); - lnk_section_push_reloc(data_sect, biat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol); - lnk_section_associate_chunks(data_sect, iat_chunk, biat_chunk); + U64 import_size = coff_word_size_from_machine(machine); + U64 biat_offset = biat_sect->data.total_size; + str8_list_push(obj_writer->arena, &biat_sect->data, str8(0,import_size)); + coff_obj_writer_section_push_reloc_addr(obj_writer, biat_sect, biat_offset, load_thunk_symbol); } if (emit_uiat) { - uiat_chunk = lnk_section_push_chunk_bss(data_sect, uiat_table_chunk, import_size, sort_index); - lnk_section_push_reloc(data_sect, uiat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol); - lnk_section_associate_chunks(data_sect, iat_chunk, uiat_chunk); + U64 import_size = coff_word_size_from_machine(machine); + U64 uiat_offset = uiat_sect->data.total_size; + str8_list_push(obj_writer->arena, &biat_sect->data, str8(0,import_size)); + coff_obj_writer_section_push_reloc_addr(obj_writer, uiat_sect, uiat_offset, load_thunk_symbol); } } break; case COFF_ImportBy_Name: { // put together name look up entry - String8 int_data = coff_make_import_lookup(data_sect->arena, header->hint_or_ordinal, header->func_name); - LNK_Chunk *int_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, int_data, str8_zero()); + String8 int_data = coff_make_import_lookup(obj_writer->arena, import_header.hint_or_ordinal, import_header.func_name); + U64 int_data_offset = int_sect->data.total_size; + str8_list_push(obj_writer->arena, &int_sect->data, int_data); // create symbol for lookup chunk - String8 int_symbol_name = push_str8f(symtab->arena->v[0], "%S.%S.name.delayed", dll->name, header->func_name); - int_symbol = lnk_symbol_table_push_defined_chunk(symtab, int_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, int_chunk, 0, 0, 0); + String8 int_symbol_name = push_str8f(obj_writer->arena, "%S.%S.name.delayed", dll_name, import_header.func_name); + COFF_ObjSymbol *int_symbol = coff_obj_writer_push_symbol_static(obj_writer, int_symbol_name, int_data_offset, int_sect); + + U64 import_size = coff_word_size_from_machine(machine); // dynamic linker patches this voff on DLL load event - ilt_chunk = lnk_section_push_chunk_bss(data_sect, ilt_table_chunk, import_size, sort_index); - lnk_chunk_set_debugf(data_sect->arena, ilt_chunk, "ILT entry (delayed) %S.%S", dll->name, header->func_name); + U64 ilt_data_offset = ilt_sect->data.total_size; + str8_list_push(obj_writer->arena, &ilt_sect->data, str8(0, import_size)); // patch-in ILT with import voff - lnk_section_push_reloc(data_sect, ilt_chunk, LNK_Reloc_VIRT_OFF_32, 0, int_symbol); + coff_obj_writer_section_push_reloc_voff(obj_writer, ilt_sect, ilt_data_offset, int_symbol); // in the file IAT mirrors ILT, dynamic linker later overwrites it with imported function addresses. - iat_chunk = lnk_section_push_chunk_bss(data_sect, iat_table_chunk, import_size, sort_index); - lnk_chunk_set_debugf(data_sect->arena, iat_chunk, "IAT entre (delayed) %S.%S", dll->name, header->func_name); - - // associate chunks - lnk_section_associate_chunks(data_sect, iat_chunk, ilt_chunk); - lnk_section_associate_chunks(data_sect, iat_chunk, int_chunk); + U64 iat_data_offset = iat_sect->data.total_size; + str8_list_push(obj_writer->arena, &iat_sect->data, str8(0, import_size)); // patch-in thunk address - lnk_section_push_reloc(data_sect, iat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol); + coff_obj_writer_section_push_reloc_addr(obj_writer, iat_sect, iat_data_offset, load_thunk_symbol); - if (imptab->flags & LNK_ImportTableFlag_EmitBiat) { - biat_chunk = lnk_section_push_chunk_bss(data_sect, biat_table_chunk, import_size, sort_index); - lnk_chunk_set_debugf(data_sect->arena, biat_chunk, "%S.biat.%S (delayed)", dll->name, header->func_name); + if (emit_biat) { + U64 biat_data_offset = biat_sect->data.total_size; + str8_list_push(obj_writer->arena, &biat_sect->data, str8(0, import_size)); // patch-in thunk address - lnk_section_push_reloc(data_sect, biat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol); + coff_obj_writer_section_push_reloc_addr(obj_writer, biat_sect, biat_data_offset, load_thunk_symbol); } - if (imptab->flags & LNK_ImportTableFlag_EmitUiat) { - uiat_chunk = lnk_section_push_chunk_bss(data_sect, uiat_table_chunk, import_size, sort_index); - lnk_chunk_set_debugf(data_sect->arena, uiat_chunk, "%S.uiat.%S (delayed)", dll->name, header->func_name); + if (emit_uiat) { + U64 uiat_data_offset = uiat_sect->data.total_size; + str8_list_push(obj_writer->arena, &uiat_sect->data, str8(0, import_size)); // patch-in thunk address - lnk_section_push_reloc(data_sect, uiat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol); + coff_obj_writer_section_push_reloc_addr(obj_writer, uiat_sect, uiat_data_offset, load_thunk_symbol); } } break; - case COFF_ImportBy_Undecorate: { - lnk_not_implemented("TODO: COFF_ImportBy_Undecorate"); - } break; - case COFF_ImportBy_NameNoPrefix: { - lnk_not_implemented("TODO: COFF_ImportBy_NameNoPrefix"); - } break; + case COFF_ImportBy_Undecorate: { NotImplemented; } break; + case COFF_ImportBy_NameNoPrefix: { NotImplemented; } break; } - - if (jmp_thunk_chunk) { - lnk_section_associate_chunks(data_sect, iat_chunk, jmp_thunk_chunk); - } - if (load_thunk_chunk) { - lnk_section_associate_chunks(data_sect, iat_chunk, load_thunk_chunk); - } - - String8 iat_symbol_name = push_str8f(symtab->arena->v[0], "__imp_%S", header->func_name); - LNK_Symbol *iat_symbol = lnk_symbol_table_push_defined_chunk(symtab, iat_symbol_name, LNK_DefinedSymbolVisibility_Extern, 0, iat_chunk, 0, 0, 0); - - String8 ilt_symbol_name = push_str8f(symtab->arena->v[0], "%S.%S.ilt.delayed", dll->name, header->func_name); - LNK_Symbol *ilt_symbol = lnk_symbol_table_push_defined_chunk(symtab, ilt_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, ilt_chunk, 0, 0, 0); -#endif } - return str8_zero(); + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); + return obj; } - -internal String8Array -lnk_make_import_dlls_static(Arena *arena, LNK_ImportTable *imptab, COFF_MachineType machine, String8 image_name) -{ - String8Array objs = {0}; - if (imptab->dll_count) { - objs.v = push_array(arena, String8, imptab->dll_count + 3); - - // make objs for each DLL - for (LNK_ImportDLL *dll = imptab->first_dll; dll != 0; dll = dll->next) { - objs.v[objs.count++] = lnk_obj_from_import_dll_static(arena, machine, dll); - } - - // make null terminator objs - objs.v[objs.count++] = lnk_build_import_entry_obj(arena, image_name, COFF_TimeStamp_Max, machine); - objs.v[objs.count++] = lnk_build_null_import_descriptor_obj(arena, COFF_TimeStamp_Max, machine); - objs.v[objs.count++] = lnk_build_null_thunk_data_obj(arena, image_name, COFF_TimeStamp_Max, machine); - } - return objs; -} - -internal String8Array -lnk_make_import_dlls_delayed(Arena *arena, LNK_ImportTable *imptab, COFF_MachineType machine, String8 image_name, B32 emit_biat, B32 emit_uiat) -{ - String8Array objs = {0}; - if (imptab->dll_count) { - objs.v = push_array(arena, String8, imptab->dll_count + 3); - - // make objs for each DLL - for (LNK_ImportDLL *dll = imptab->first_dll; dll != 0; dll = dll->next) { - objs.v[objs.count++] = lnk_obj_from_import_dll_delayed(arena, machine, dll, emit_biat, emit_uiat); - } - - // make null terminator objs - objs.v[objs.count++] = lnk_build_import_entry_obj(arena, image_name, COFF_TimeStamp_Max, machine); - objs.v[objs.count++] = lnk_build_null_import_descriptor_obj(arena, COFF_TimeStamp_Max, machine); - objs.v[objs.count++] = lnk_build_null_thunk_data_obj(arena, image_name, COFF_TimeStamp_Max, machine); - } - return objs; -} - diff --git a/src/pe/pe_make_import_table.h b/src/pe/pe_make_import_table.h new file mode 100644 index 00000000..b88d3905 --- /dev/null +++ b/src/pe/pe_make_import_table.h @@ -0,0 +1,18 @@ +// Copyright (c) 2025 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef PE_MAKE_IMPORT_TABLE_H +#define PE_MAKE_IMPORT_TABLE_H + +internal COFF_ObjSymbol * pe_make_indirect_jump_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *iat_symbol, String8 thunk_name); +internal COFF_ObjSymbol * pe_make_load_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *imp_addr_ptr, COFF_ObjSymbol *tail_merge, String8 func_name); +internal COFF_ObjSymbol * pe_make_tail_merge_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, String8 dll_name, String8 delay_load_helper_name, COFF_ObjSymbol *dll_import_descriptor); + +internal String8 pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stmap, COFF_MachineType machine, String8 dll_name, String8 debug_symbols, String8List import_headers); +internal String8 pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 delay_load_helper_name, String8 debug_symbols, String8List import_headers, B32 emit_biat, B32 emit_uiat); + +internal String8 pe_make_import_entry_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols); +internal String8 pe_make_null_import_descriptor_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols); +internal String8 pe_make_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols); + +#endif // PE_MAKE_IMPORT_TABLE_H \ No newline at end of file diff --git a/src/pe/pe_section_flags.h b/src/pe/pe_section_flags.h new file mode 100644 index 00000000..4d9fdf54 --- /dev/null +++ b/src/pe/pe_section_flags.h @@ -0,0 +1,27 @@ +// Copyright (c) 2025 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef PE_SECTION_FLAGS_H +#define PE_SECTION_FLAGS_H + +#define PE_TEXT_SECTION_FLAGS (COFF_SectionFlag_CntCode|COFF_SectionFlag_MemExecute|COFF_SectionFlag_MemRead) +#define PE_DATA_SECTION_FLAGS (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite) +#define PE_RDATA_SECTION_FLAGS (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead) +#define PE_BSS_SECTION_FLAGS (COFF_SectionFlag_CntUninitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite) +#define PE_IDATA_SECTION_FLAGS PE_DATA_SECTION_FLAGS +#define PE_DEBUG_DIR_SECTION_FLAGS PE_DATA_SECTION_FLAGS +#define PE_RSRC_SECTION_FLAGS PE_DATA_SECTION_FLAGS +#define PE_RSRC1_SECTION_FLAGS (PE_DATA_SECTION_FLAGS | COFF_SectionFlag_Align4Bytes) +#define PE_RSRC2_SECTION_FLAGS (PE_DATA_SECTION_FLAGS | COFF_SectionFlag_Align4Bytes) +#define PE_XDATA_SECTION_FLAGS PE_RDATA_SECTION_FLAGS +#define PE_PDATA_SECTION_FLAGS PE_RDATA_SECTION_FLAGS +#define PE_EDATA_SECTION_FLAGS PE_RDATA_SECTION_FLAGS +#define PE_GFIDS_SECTION_FLAGS PE_RDATA_SECTION_FLAGS +#define PE_GIATS_SECTION_FLAGS PE_RDATA_SECTION_FLAGS +#define PE_GLJMP_SECTION_FLAGS PE_RDATA_SECTION_FLAGS +#define PE_GEHCONT_SECTION_FLAGS PE_RDATA_SECTION_FLAGS +#define PE_RELOC_SECTION_FLAGS (PE_RDATA_SECTION_FLAGS | COFF_SectionFlag_MemDiscardable) +#define PE_DEBUG_SECTION_FLAGS (PE_RDATA_SECTION_FLAGS | COFF_SectionFlag_MemDiscardable) + +#endif // PE_SECTION_FLAGS_H + diff --git a/src/torture/torture.c b/src/torture/torture.c index 64127fec..a8dd2135 100644 --- a/src/torture/torture.c +++ b/src/torture/torture.c @@ -1478,7 +1478,7 @@ t_import_export(void) coff_obj_writer_push_directive(obj_writer, str8_lit("/export:baf=baz.#1")); coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord,@5")); coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord2,@6,DATA")); - coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord3,@7,NONAME")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord3,@7,NONAME,PRIVATE")); coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord4,@8,NONAME,DATA")); COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(export_text)); coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("_DllMainCRTStartup"), 0, text_sect);