Files
raddebugger/src/linker/lnk_import_table.c
T
Nikita Smith 4a2e1dd8d0 switch to thread safe hash trie map
Initial implementation of the symbol table allowed us to push symbols
from one thread at a time and required a wonky lock-step and spread out
logic for replacing symbols. And because it was a externally chained
hash table we had to guesstimate optimal capacity for small and big targets.
We erred on the side of larger capacity to prevent degradation and
rehashing, however this meant small targets had to spent unreasonable
amount of time memory allocing slots. With hash trie we can dynamically
scale up to 4 ^ 32 items and atomically replace symbols when needed. Neat!
2024-10-30 15:43:53 -07:00

764 lines
38 KiB
C

// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal LNK_ImportTable *
lnk_import_table_alloc_static(LNK_SectionTable *st, LNK_SymbolTable *symtab, COFF_MachineType machine)
{
ProfBeginFunction();
LNK_Section *data_sect = lnk_section_table_push(st, str8_lit(".idata"), LNK_IDATA_SECTION_FLAGS);
LNK_Section *code_sect = lnk_section_table_search(st, str8_lit(".text"));
LNK_Chunk *dll_table_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *int_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *iat_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *ilt_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *code_chunk = lnk_section_push_chunk_list(code_sect, code_sect->root, str8_zero());
LNK_Chunk *null_dll_import = lnk_section_push_chunk_data(data_sect, dll_table_chunk, str8(0, sizeof(PE_ImportEntry)), str8_lit("zzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_dll_import, "DLL_DIRECTORY_TERMINATOR");
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_IMPORT_DLL_TABLE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, dll_table_chunk, 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_IMPORT_NAME_TABLE_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, int_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_IMPORT_IAT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, iat_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_IMPORT_ILT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, ilt_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_IMPORT_JMP_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, code_chunk , 0, 0, 0);
Arena *arena = arena_alloc();
LNK_ImportTable *imptab = push_array(arena, LNK_ImportTable, 1);
imptab->machine = machine;
imptab->arena = arena;
imptab->data_sect = data_sect;
imptab->code_sect = code_sect;
imptab->dll_table_chunk = dll_table_chunk;
imptab->int_chunk = int_chunk;
imptab->iat_chunk = iat_chunk;
imptab->ilt_chunk = ilt_chunk;
imptab->code_chunk = code_chunk;
imptab->dll_ht = hash_table_init(arena, LNK_IMPORT_DLL_HASH_TABLE_BUCKET_COUNT);
ProfEnd();
return imptab;
}
internal LNK_ImportTable *
lnk_import_table_alloc_delayed(LNK_SectionTable *st, LNK_SymbolTable *symtab, COFF_MachineType machine, B32 is_unloadable, B32 is_bindable)
{
ProfBeginFunction();
LNK_Section *data_sect = lnk_section_table_push(st, str8_lit(".didat"), LNK_DEBUG_DIR_SECTION_FLAGS);
LNK_Section *code_sect = lnk_section_table_search(st, str8_lit(".text"));
LNK_Chunk *dll_table_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *int_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *handle_table_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *iat_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *ilt_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *biat_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *uiat_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *code_chunk = lnk_section_push_chunk_list(code_sect, code_sect->root, str8_zero());
LNK_Chunk *null_dll_import = lnk_section_push_chunk_data(data_sect, dll_table_chunk, str8(0, sizeof(PE_DelayedImportEntry)), str8_lit("~0"));
lnk_chunk_set_debugf(data_sect->arena, null_dll_import, "DLL_DIRECTORY_TERMINATOR");
if (is_unloadable) {
U64 import_size = coff_word_size_from_machine(machine);
LNK_Chunk *null_uiat_chunk = lnk_section_push_chunk_bss(data_sect, uiat_chunk, import_size, str8_lit("~1"));
lnk_chunk_set_debugf(data_sect->arena, null_uiat_chunk, "UIAT_TERMINATOR");
}
if (is_bindable) {
U64 import_size = coff_word_size_from_machine(machine);
LNK_Chunk *null_biat_chunk = lnk_section_push_chunk_bss(data_sect, biat_chunk, import_size, str8_lit("~2"));
lnk_chunk_set_debugf(data_sect->arena, null_biat_chunk, "BIAT_TERMINATOR");
}
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_DELAYED_IMPORT_DLL_TABLE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, dll_table_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_DELAYED_IMPORT_INT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, int_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_DELAYED_IMPORT_HANDLE_TABLE_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, handle_table_chunk, 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_DELAYED_IMPORT_IAT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, iat_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_DELAYED_IMPORT_ILT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, ilt_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_DELAYED_IMPORT_BIAT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, biat_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_DELAYED_IMPORT_UIAT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, uiat_chunk , 0, 0, 0);
lnk_symbol_table_push_defined_chunk(symtab, str8_cstring(LNK_DELAYED_IMPORT_CODE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, code_chunk , 0, 0, 0);
LNK_ImportTableFlags flags = 0;
if (is_unloadable) {
flags |= LNK_ImportTableFlag_EmitUiat;
}
if (is_bindable) {
flags |= LNK_ImportTableFlag_EmitBiat;
}
Arena *arena = arena_alloc();
LNK_ImportTable *imptab = push_array(arena, LNK_ImportTable, 1);
imptab->arena = arena;
imptab->machine = machine;
imptab->data_sect = data_sect;
imptab->code_sect = code_sect;
imptab->dll_table_chunk = dll_table_chunk;
imptab->int_chunk = int_chunk;
imptab->handle_table_chunk = handle_table_chunk;
imptab->iat_chunk = iat_chunk;
imptab->ilt_chunk = ilt_chunk;
imptab->biat_chunk = biat_chunk;
imptab->uiat_chunk = uiat_chunk;
imptab->code_chunk = code_chunk;
imptab->flags = flags;
imptab->dll_ht = hash_table_init(arena, LNK_IMPORT_FUNC_HASH_TABLE_BUCKET_COUNT);
ProfEnd();
return imptab;
}
internal void
lnk_import_table_release(LNK_ImportTable **imptab_ptr)
{
ProfBeginFunction();
arena_release((*imptab_ptr)->arena);
*imptab_ptr = 0;
ProfEnd();
}
internal BucketNode *
lnk_import_table_push_dll_node(LNK_ImportTable *imptab, LNK_ImportDLL *dll)
{
// update list
SLLQueuePush(imptab->first_dll, imptab->last_dll, dll);
// update name -> dll hash table
return hash_table_push_path_raw(imptab->arena, imptab->dll_ht, dll->name, dll);
}
internal BucketNode *
lnk_import_table_push_func_node(LNK_ImportTable *imptab, LNK_ImportDLL *dll, LNK_ImportFunc *func)
{
// update list
SLLQueuePush(dll->first_func, dll->last_func, func);
// update name -> func hash table
return hash_table_push_string_raw(imptab->arena, dll->func_ht, func->name, func);
}
internal LNK_ImportDLL *
lnk_import_table_search_dll(LNK_ImportTable *imptab, String8 name)
{
KeyValuePair *kv = hash_table_search_path(imptab->dll_ht, name);
if (kv) {
Assert(kv->value_raw);
return kv->value_raw;
}
return 0;
}
internal LNK_ImportFunc *
lnk_import_table_search_func(LNK_ImportDLL *dll, String8 name)
{
KeyValuePair *kv = hash_table_search_string(dll->func_ht, name);
if (kv) {
Assert(kv->value_raw);
return kv->value_raw;
}
return 0;
}
internal LNK_ImportDLL *
lnk_import_table_push_dll_static(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, String8 dll_name, COFF_MachineType machine)
{
ProfBeginFunction();
// TODO: error handle
Assert(imptab->machine == machine);
LNK_Section *data_sect = imptab->data_sect;
LNK_Section *code_sect = imptab->code_sect;
LNK_Chunk *int_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->int_chunk, str8_zero());
LNK_Chunk *ilt_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->ilt_chunk, str8_zero());
LNK_Chunk *iat_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->iat_chunk, str8_zero());
LNK_Chunk *code_table_chunk = lnk_section_push_chunk_list(code_sect, imptab->code_chunk, str8_zero());
String8 ilt_symbol_name = push_str8f(symtab->arena->v[0], "%S.lookup_table_voff", dll_name);
LNK_Symbol *ilt_symbol = lnk_symbol_table_push_defined_chunk(symtab, ilt_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, ilt_table_chunk, 0, 0, 0);
String8 iat_symbol_name = push_str8f(symtab->arena->v[0], "%S.import_addr_table_voff", dll_name);
LNK_Symbol *iat_symbol = lnk_symbol_table_push_defined_chunk(symtab, iat_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, iat_table_chunk, 0, 0, 0);
String8 dll_name_cstr = push_cstr(data_sect->arena, dll_name);
LNK_Chunk *dll_name_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, dll_name_cstr, str8_zero());
lnk_chunk_set_debugf(data_sect->arena, dll_name_chunk, "DLL name chunk (%S)", dll_name);
String8 dll_name_voff_name = push_str8f(symtab->arena->v[0], "%S.name_voff", dll_name);
LNK_Symbol *dll_name_voff_symbol = lnk_symbol_table_push_defined_chunk(symtab, dll_name_voff_name, LNK_DefinedSymbolVisibility_Internal, 0, dll_name_chunk, 0, 0, 0);
// chunk for dll directory entry
PE_ImportEntry *dir = push_array(imptab->arena, PE_ImportEntry, 1);
LNK_Chunk *dll_chunk = lnk_section_push_chunk_data(data_sect, imptab->dll_table_chunk, str8_struct(dir), str8_zero());
lnk_chunk_set_debugf(data_sect->arena, dll_chunk, "DLL Directory for %S", dll_name);
// patch dll import fields
lnk_section_push_reloc(data_sect, dll_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ImportEntry, lookup_table_voff), ilt_symbol);
lnk_section_push_reloc(data_sect, dll_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ImportEntry, name_voff), dll_name_voff_symbol);
lnk_section_push_reloc(data_sect, dll_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ImportEntry, import_addr_table_voff), iat_symbol);
U64 import_size = coff_word_size_from_machine(machine);
// null entry to terminate import lookup table array
LNK_Chunk *null_ilt_chunk = lnk_section_push_chunk_data(data_sect, ilt_table_chunk, str8(0, import_size), str8_lit("zzzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_ilt_chunk, "%S: ILT terminator", dll_name);
// null entry to terminate import address table array
LNK_Chunk *null_iat_chunk = lnk_section_push_chunk_data(data_sect, iat_table_chunk, str8(0, import_size), str8_lit("zzzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_iat_chunk, "%S: IAT terminator", dll_name);
// push to list
LNK_ImportDLL *dll = push_array(imptab->arena, LNK_ImportDLL, 1);
dll->name = push_str8_copy(imptab->arena, dll_name);
dll->dll_chunk = dll_chunk;
dll->int_table_chunk = int_table_chunk;
dll->ilt_table_chunk = ilt_table_chunk;
dll->iat_table_chunk = iat_table_chunk;
dll->code_table_chunk = code_table_chunk;
dll->machine = machine;
dll->func_ht = hash_table_init(imptab->arena, LNK_IMPORT_FUNC_HASH_TABLE_BUCKET_COUNT);
lnk_import_table_push_dll_node(imptab, dll);
ProfEnd();
return dll;
}
internal LNK_ImportDLL *
lnk_import_table_push_dll_delayed(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, String8 dll_name, COFF_MachineType machine)
{
ProfBeginFunction();
Assert(imptab->machine == machine);
U64 handle_size = coff_word_size_from_machine(machine);
U64 import_size = coff_word_size_from_machine(machine);
// shortcuts
LNK_Section *data_sect = imptab->data_sect;
LNK_Section *code_sect = imptab->code_sect;
// init DLL entry
PE_DelayedImportEntry *imp_desc = push_array(data_sect->arena, PE_DelayedImportEntry, 1);
imp_desc->attributes = 1;
imp_desc->name_voff = 0; // relocated
imp_desc->module_handle_voff = 0; // relocated
imp_desc->iat_voff = 0; // relocated
imp_desc->name_table_voff = 0; // relocated
imp_desc->bound_table_voff = 0; // relocated
imp_desc->unload_table_voff = 0; // relocated
imp_desc->time_stamp = 0;
// emit entry chunk
String8 imp_desc_data = str8_struct(imp_desc);
LNK_Chunk *imp_desc_chunk = lnk_section_push_chunk_data(data_sect, imptab->dll_table_chunk, imp_desc_data, str8_zero());
// emit entry symbol
String8 imp_desc_name = push_str8f(symtab->arena->v[0], "__DELAY_IMPORT_DESCRIPTOR_%S", dll_name);
LNK_Symbol *imp_desc_symbol = lnk_symbol_table_push_defined_chunk(symtab, imp_desc_name, LNK_DefinedSymbolVisibility_Extern, 0, imp_desc_chunk, 0, 0, 0);
// emit string table chunk
String8 int_table_chunk_debug = push_str8f(data_sect->arena, "delayed.%S.int", dll_name);
LNK_Chunk *int_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->int_chunk, int_table_chunk_debug);
String8 int_table_symbol_name = push_str8f(symtab->arena->v[0], "delayed.%S.int", dll_name);
LNK_Symbol *int_table_symbol = lnk_symbol_table_push_defined_chunk(symtab, int_table_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, int_table_chunk, 0, 0, 0);
LNK_Chunk *null_string_chunk = lnk_section_push_chunk_list(data_sect, int_table_chunk, str8_lit("zzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_string_chunk, "string table null");
// emit DLL name chunk
String8 name_chunk_data = push_cstr(data_sect->arena, dll_name);
LNK_Chunk *name_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, name_chunk_data, str8_zero());
String8 name_symbol_name = push_str8f(symtab->arena->v[0], "delayed.%S.name", dll_name);
LNK_Symbol *name_symbol = lnk_symbol_table_push_defined_chunk(symtab, name_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, name_chunk, 0, 0, 0);
// patch DLL name voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, name_voff), name_symbol);
// emit DLL handle chunk
LNK_Chunk *handle_chunk = lnk_section_push_chunk_bss(data_sect, imptab->handle_table_chunk, handle_size, str8_zero());
String8 handle_name = push_str8f(symtab->arena->v[0], "delayed.%S.handle", dll_name);
LNK_Symbol *handle_symbol = lnk_symbol_table_push_defined_chunk(symtab, handle_name, LNK_DefinedSymbolVisibility_Internal, 0, handle_chunk, 0, 0, 0);
// patch DLL handle voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, module_handle_voff), handle_symbol);
// emit IAT chunk
LNK_Chunk *iat_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->iat_chunk, str8_zero());
String8 iat_table_name = push_str8f(symtab->arena->v[0], "delayed.%S.iat", dll_name);
LNK_Symbol *iat_table_symbol = lnk_symbol_table_push_defined_chunk(symtab, iat_table_name, LNK_DefinedSymbolVisibility_Internal, 0, iat_table_chunk, 0, 0, 0);
LNK_Chunk *null_iat_chunk = lnk_section_push_chunk_bss(data_sect, iat_table_chunk, import_size, str8_lit("zzzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_iat_chunk, "%S: IAT terminator", dll_name);
// emit ILT chunk
LNK_Chunk *ilt_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->ilt_chunk, str8_zero());
LNK_Chunk *null_ilt_chunk = lnk_section_push_chunk_bss(data_sect, ilt_table_chunk, import_size, str8_lit("zzzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_ilt_chunk, "%S: ILT terminator", dll_name);
String8 ilt_table_name = push_str8f(symtab->arena->v[0], "delayed.%S.ilt", dll_name);
LNK_Symbol *ilt_table_symbol = lnk_symbol_table_push_defined_chunk(symtab, ilt_table_name, LNK_DefinedSymbolVisibility_Extern, 0, ilt_table_chunk, 0, 0, 0);
// patch import address table voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, iat_voff), iat_table_symbol);
// patch string table voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, name_table_voff), ilt_table_symbol);
// emit bound table chunk
LNK_Chunk *biat_chunk = 0;
if (imptab->flags & LNK_ImportTableFlag_EmitBiat) {
biat_chunk = lnk_section_push_chunk_list(data_sect, imptab->biat_chunk, str8_zero());
String8 biat_symbol_name = push_str8f(symtab->arena->v[0], "delayed.%S.BIAT", dll_name);
LNK_Symbol *biat_symbol = lnk_symbol_table_push_defined_chunk(symtab, biat_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, biat_chunk, 0, 0, 0);
// patch BIAT field off
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, bound_table_voff), biat_symbol);
}
// emit unload table chunk
LNK_Chunk *uiat_chunk = NULL;
if (imptab->flags & LNK_ImportTableFlag_EmitUiat) {
uiat_chunk = lnk_section_push_chunk_list(data_sect, imptab->uiat_chunk, str8_zero());
String8 uiat_symbol_name = push_str8f(symtab->arena->v[0], "delayed.%S.UIAT", dll_name);
LNK_Symbol *uiat_symbol = lnk_symbol_table_push_defined_chunk(symtab, uiat_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, uiat_chunk, 0, 0, 0);
// patch UIAT field voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, unload_table_voff), uiat_symbol);
}
// emit chunk for DLL thunk/load code
LNK_Chunk *code_chunk = lnk_section_push_chunk_list(code_sect, imptab->code_chunk, str8_zero());
lnk_chunk_set_debugf(code_sect->arena, code_chunk, "code for %S", dll_name);
// emit tail merge
LNK_Chunk *tail_merge_chunk = 0;
switch (machine) {
case COFF_MachineType_X64: {
LNK_Symbol *delay_load_helper_symbol = lnk_make_undefined_symbol(symtab->arena->v[0], str8_lit(LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME), LNK_SymbolScopeFlag_Main);
tail_merge_chunk = lnk_emit_tail_merge_thunk_x64(code_sect, code_chunk, imp_desc_symbol, delay_load_helper_symbol);
} break;
default: {
lnk_not_implemented("TODO: __tailMerge for %S", coff_string_from_machine_type(machine));
} break;
}
// fill out result
LNK_ImportDLL *dll = push_array(imptab->arena, LNK_ImportDLL, 1);
dll->dll_chunk = imp_desc_chunk;
dll->int_table_chunk = int_table_chunk;
dll->iat_table_chunk = iat_table_chunk;
dll->ilt_table_chunk = ilt_table_chunk;
dll->biat_table_chunk = biat_chunk;
dll->uiat_table_chunk = uiat_chunk;
dll->code_table_chunk = code_chunk;
dll->tail_merge_symbol = lnk_emit_tail_merge_symbol(symtab, tail_merge_chunk, dll_name);
dll->name = push_str8_copy(imptab->arena, dll_name);
dll->machine = machine;
dll->func_ht = hash_table_init(imptab->arena, LNK_IMPORT_FUNC_HASH_TABLE_BUCKET_COUNT);
lnk_import_table_push_dll_node(imptab, dll);
ProfEnd();
return dll;
}
internal LNK_ImportFunc *
lnk_import_table_push_func_static(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, LNK_ImportDLL *dll, COFF_ImportHeader *header)
{
ProfBeginFunction();
Assert(header->machine == dll->machine); // TODO: error handling
LNK_Section *data_sect = imptab->data_sect;
LNK_Section *code_sect = imptab->code_sect;
LNK_Chunk *int_table_chunk = dll->int_table_chunk;
LNK_Chunk *ilt_table_chunk = dll->ilt_table_chunk;
LNK_Chunk *iat_table_chunk = dll->iat_table_chunk;
LNK_Chunk *code_table_chunk = dll->code_table_chunk;
LNK_Chunk *ilt_chunk = g_null_chunk_ptr;
LNK_Chunk *iat_chunk = g_null_chunk_ptr;
U64 import_size = coff_word_size_from_machine(dll->machine);
// generate sort index (optional)
String8 sort_index = str8_from_bits_u32(data_sect->arena, header->hint);
switch (header->name_type) {
case COFF_ImportHeaderNameType_ORDINAL: {
String8 ordinal_data = lnk_ordinal_data_from_hint(data_sect->arena, dll->machine, header->hint);
ilt_chunk = lnk_section_push_chunk_data(data_sect, ilt_table_chunk, ordinal_data, sort_index);
iat_chunk = lnk_section_push_chunk_data(data_sect, iat_table_chunk, ordinal_data, sort_index);
// associate chunks
lnk_section_associate_chunks(data_sect, iat_chunk, ilt_chunk);
} break;
case COFF_ImportHeaderNameType_NAME: {
// put together name look up entry
String8 int_data = coff_make_import_lookup(data_sect->arena, header->hint, header->func_name);
LNK_Chunk *int_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, int_data, str8_zero());
// create symbol for lookup chunk
String8 int_symbol_name = push_str8f(symtab->arena->v[0], "static.%S.%S.name", dll->name, header->func_name);
LNK_Symbol *int_symbol = lnk_symbol_table_push_defined_chunk(symtab, int_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, int_chunk, 0, 0, 0);
// in the file IAT mirrors ILT, dynamic linker later overwrites it with imported function addresses.
ilt_chunk = lnk_section_push_chunk_bss(data_sect, ilt_table_chunk, import_size, sort_index);
iat_chunk = lnk_section_push_chunk_bss(data_sect, iat_table_chunk, import_size, sort_index);
lnk_chunk_set_debugf(data_sect->arena, ilt_chunk, "ILT entry for %S.%S", dll->name, header->func_name);
lnk_chunk_set_debugf(data_sect->arena, iat_chunk, "IAT entry for %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);
// patch IAT and ILT
lnk_section_push_reloc(data_sect, ilt_chunk, LNK_Reloc_VIRT_OFF_32, 0, int_symbol);
lnk_section_push_reloc(data_sect, iat_chunk, LNK_Reloc_VIRT_OFF_32, 0, int_symbol);
} break;
case COFF_ImportHeaderNameType_UNDECORATE: {
lnk_not_implemented("TODO: COFF_ImportHeaderNameType_UNDECORATE");
} break;
case COFF_ImportHeaderNameType_NAME_NOPREFIX: {
lnk_not_implemented("TODO: COFF_ImportHeaderNameType_NAME_NOPREFIX");
} break;
}
String8 ilt_symbol_name = push_str8f(symtab->arena->v[0], "static.%S.%S.ilt", dll->name, header->func_name);
String8 iat_symbol_name = push_str8f(symtab->arena->v[0], "__imp_%S", 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);
LNK_Symbol *iat_symbol = lnk_symbol_table_push_defined_chunk(symtab, iat_symbol_name, LNK_DefinedSymbolVisibility_Extern, 0, iat_chunk, 0, 0, 0);
// generate thunks
LNK_Symbol *jmp_thunk_symbol = g_null_symbol_ptr;
if (header->type == COFF_ImportHeaderType_CODE) {
switch (dll->machine) {
case COFF_MachineType_X64: {
// generate jump thunk
LNK_Chunk *jmp_thunk_chunk = lnk_emit_indirect_jump_thunk_x64(code_sect, code_table_chunk, iat_symbol);
lnk_section_associate_chunks(data_sect, iat_chunk, jmp_thunk_chunk);
// push jump thunk symbol
String8 jmp_thunk_symbol_name = push_str8_copy(symtab->arena->v[0], header->func_name);
jmp_thunk_symbol = lnk_emit_jmp_thunk_symbol(symtab, jmp_thunk_chunk, jmp_thunk_symbol_name);
} break;
default: lnk_not_implemented("TODO: support for machine 0x%X", dll->machine); break;
}
}
// fill out import
LNK_ImportFunc *func = push_array(imptab->arena, LNK_ImportFunc, 1);
func->name = push_str8_copy(imptab->arena, header->func_name);
func->thunk_symbol_name = push_str8_copy(imptab->arena, jmp_thunk_symbol->name);
func->iat_symbol_name = push_str8_copy(imptab->arena, iat_symbol->name);
lnk_import_table_push_func_node(imptab, dll, func);
ProfEnd();
return func;
}
internal LNK_ImportFunc *
lnk_import_table_push_func_delayed(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, LNK_ImportDLL *dll, COFF_ImportHeader *header)
{
ProfBeginFunction();
Assert(dll->machine == header->machine); // TODO: error handle
U64 import_size = coff_word_size_from_machine(dll->machine);
LNK_Section *data_sect = imptab->data_sect;
LNK_Section *code_sect = imptab->code_sect;
LNK_Chunk *int_table_chunk = dll->int_table_chunk;
LNK_Chunk *ilt_table_chunk = dll->ilt_table_chunk;
LNK_Chunk *iat_table_chunk = dll->iat_table_chunk;
LNK_Chunk *biat_table_chunk = dll->biat_table_chunk;
LNK_Chunk *uiat_table_chunk = dll->uiat_table_chunk;
LNK_Chunk *code_table_chunk = dll->code_table_chunk;
LNK_Chunk *ilt_chunk = g_null_chunk_ptr;
LNK_Chunk *iat_chunk = g_null_chunk_ptr;
LNK_Chunk *uiat_chunk = g_null_chunk_ptr;
LNK_Chunk *biat_chunk = g_null_chunk_ptr;
LNK_Symbol *int_symbol = 0;
// generate sort index (optional)
String8 sort_index = str8_from_bits_u32(data_sect->arena, header->hint);
// generate thunks
LNK_Symbol *jmp_thunk_symbol = g_null_symbol_ptr;
LNK_Symbol *load_thunk_symbol = g_null_symbol_ptr;
LNK_Chunk *jmp_thunk_chunk = 0;
LNK_Chunk *load_thunk_chunk = 0;
if (header->type == COFF_ImportHeaderType_CODE) {
switch (dll->machine) {
case COFF_MachineType_X64: {
String8 iat_symbol_name = push_str8f(symtab->arena->v[0], "__imp_%S", header->func_name);
LNK_Symbol *iat_symbol = lnk_make_undefined_symbol(symtab->arena->v[0], iat_symbol_name, LNK_SymbolScopeFlag_Main);
// emit jmp thunk chunk
jmp_thunk_chunk = lnk_emit_indirect_jump_thunk_x64(code_sect, code_table_chunk, iat_symbol);
jmp_thunk_symbol = lnk_emit_jmp_thunk_symbol(symtab, jmp_thunk_chunk, 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 = lnk_emit_load_thunk_symbol(symtab, load_thunk_chunk, header->func_name);
} break;
default: lnk_not_implemented("TODO: support for machine 0x%X", dll->machine); break;
}
}
switch (header->name_type) {
case COFF_ImportHeaderNameType_ORDINAL: {
String8 ordinal_data = lnk_ordinal_data_from_hint(data_sect->arena, dll->machine, header->hint);
Assert(ordinal_data.size == import_size);
ilt_chunk = lnk_section_push_chunk_data(data_sect, ilt_table_chunk, ordinal_data, sort_index);
iat_chunk = lnk_section_push_chunk_bss(data_sect, iat_table_chunk, import_size, sort_index);
lnk_section_push_reloc(data_sect, iat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol);
lnk_section_associate_chunks(data_sect, iat_chunk, ilt_chunk);
if (imptab->flags & LNK_ImportTableFlag_EmitBiat) {
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);
}
if (imptab->flags & LNK_ImportTableFlag_EmitUiat) {
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);
}
} break;
case COFF_ImportHeaderNameType_NAME: {
// put together name look up entry
String8 int_data = coff_make_import_lookup(data_sect->arena, header->hint, header->func_name);
LNK_Chunk *int_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, int_data, str8_zero());
// 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);
// 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);
// patch-in ILT with import voff
lnk_section_push_reloc(data_sect, ilt_chunk, LNK_Reloc_VIRT_OFF_32, 0, 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);
// patch-in thunk address
lnk_section_push_reloc(data_sect, iat_chunk, LNK_Reloc_ADDR_64, 0, 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);
// patch-in thunk address
lnk_section_push_reloc(data_sect, biat_chunk, LNK_Reloc_ADDR_64, 0, 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);
// patch-in thunk address
lnk_section_push_reloc(data_sect, uiat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol);
}
} break;
case COFF_ImportHeaderNameType_UNDECORATE: {
lnk_not_implemented("TODO: COFF_ImportHeaderNameType_UNDECORATE");
} break;
case COFF_ImportHeaderNameType_NAME_NOPREFIX: {
lnk_not_implemented("TODO: COFF_ImportHeaderNameType_NAME_NOPREFIX");
} 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);
// fill out import
LNK_ImportFunc *func = push_array(imptab->arena, LNK_ImportFunc, 1);
func->name = push_str8_copy(imptab->arena, header->func_name);
func->thunk_symbol_name = push_str8_copy(imptab->arena, jmp_thunk_symbol->name);
func->iat_symbol_name = push_str8_copy(imptab->arena, iat_symbol->name);
lnk_import_table_push_func_node(imptab, dll, func);
ProfEnd();
return func;
}
internal String8
lnk_ordinal_data_from_hint(Arena *arena, COFF_MachineType machine, U16 hint)
{
String8 ordinal_data = str8_zero();
switch (machine) {
case COFF_MachineType_X64: {
U64 *ordinal = push_array(arena, U64, 1);
*ordinal = coff_make_ordinal_64(hint);
ordinal_data = str8_struct(ordinal);
} break;
case COFF_MachineType_X86: {
U32 *ordinal = push_array(arena, U32, 1);
*ordinal = coff_make_ordinal_32(hint);
ordinal_data = str8_struct(ordinal);
} break;
default: lnk_not_implemented("TODO: support for machine 0x%x", machine);
}
return ordinal_data;
}
internal LNK_Chunk *
lnk_emit_indirect_jump_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *addr_ptr)
{
ProfBeginFunction();
static U8 thunk[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; // jmp [__imp_<FUNC_NAME>]
// emit chunk
String8 jmp_data = push_str8_copy(sect->arena, str8_array_fixed(thunk));
LNK_Chunk *jmp_chunk = lnk_section_push_chunk_data(sect, parent, jmp_data, str8_zero());
// patch thunk with imports address
static const U64 JMP_OPERAND_OFFSET = 2;
lnk_section_push_reloc(sect, jmp_chunk, LNK_Reloc_REL32, JMP_OPERAND_OFFSET, addr_ptr);
ProfEnd();
return jmp_chunk;
}
internal LNK_Chunk *
lnk_emit_load_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *imp_addr_ptr, LNK_Symbol *tail_merge)
{
ProfBeginFunction();
static U8 load_thunk[] = {
0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00, // lea rax, [__imp_<FUNC_NAME>]
0xE9, 0x00, 0x00, 0x00, 0x00 // jmp __tailMerge_<DLL_NAME>
};
// emit load thunk chunk
String8 load_thunk_data = push_str8_copy(sect->arena, str8_array_fixed(load_thunk));
LNK_Chunk *load_thunk_chunk = lnk_section_push_chunk_data(sect, parent, load_thunk_data, str8_zero());
// patch lea with IAT entry
static const U64 LEA_OPERAND_OFFSET = 3;
lnk_section_push_reloc(sect, load_thunk_chunk, LNK_Reloc_REL32, LEA_OPERAND_OFFSET, imp_addr_ptr);
// patch jmp __tailMerge_<DLL_NAME>
static const U64 JMP_OPERAND_OFFSET = 8;
lnk_section_push_reloc(sect, load_thunk_chunk, LNK_Reloc_REL32, JMP_OPERAND_OFFSET, tail_merge);
ProfEnd();
return load_thunk_chunk;
}
internal LNK_Chunk *
lnk_emit_tail_merge_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *dll_import_descriptor, LNK_Symbol *delay_load_helper)
{
ProfBeginFunction();
static U8 tail_merge[] = {
0x48, 0x89, 0x4C, 0x24, 0x08, // mov qword ptr [rsp+8],rcx
0x48, 0x89, 0x54, 0x24, 0x10, // mov qword ptr [rsp+10h],rdx
0x4C, 0x89, 0x44, 0x24, 0x18, // mov qword ptr [rsp+18h],r8
0x4C, 0x89, 0x4C, 0x24, 0x20, // mov qword ptr [rsp+20h],r9
0x48, 0x83, 0xEC, 0x68, // sub rsp,68h
0x66, 0x0F, 0x7F, 0x44, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h],xmm0
0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h],xmm1
0x66, 0x0F, 0x7F, 0x54, 0x24, 0x40, // movdqa xmmword ptr [rsp+40h],xmm2
0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x50, // movdqa xmmword ptr [rsp+50h],xmm3
0x48, 0x8B, 0xD0, // mov rdx,rax
0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00, // lea rcx,[__DELAY_IMPORT_DESCRIPTOR_<DLL_NAME>]
0xE8, 0x00, 0x00, 0x00, 0x00, // call __delayLoadHelper2
0x66, 0x0F, 0x6F, 0x44, 0x24, 0x20, // movdqa xmm0,xmmword ptr [rsp+20h]
0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x30, // movdqa xmm1,xmmword ptr [rsp+30h]
0x66, 0x0F, 0x6F, 0x54, 0x24, 0x40, // movdqa xmm2,xmmword ptr [rsp+40h]
0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x50, // movdqa xmm3,xmmword ptr [rsp+50h]
0x48, 0x8B, 0x4C, 0x24, 0x70, // mov rcx,qword ptr [rsp+70h]
0x48, 0x8B, 0x54, 0x24, 0x78, // mov rdx,qword ptr [rsp+78h]
0x4C, 0x8B, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, // mov r8,qword ptr [rsp+80h]
0x4C, 0x8B, 0x8C, 0x24, 0x88, 0x00, 0x00, 0x00, // mov r9,qword ptr [rsp+88h]
0x48, 0x83, 0xC4, 0x68, // add rsp,68h
0xFF, 0xE0, // jmp rax
};
// emit tail merge chunk
String8 tail_merge_data = push_str8_copy(sect->arena, str8_array_fixed(tail_merge));
LNK_Chunk *tail_merge_chunk = lnk_section_push_chunk_data(sect, parent, tail_merge_data, str8_zero());
// patch lea __DELAY_IMPORT_DESCRIPTOR_<DLL_NAME>
static const U64 LEA_OPERAND_OFFSET = 54;
lnk_section_push_reloc(sect, tail_merge_chunk, LNK_Reloc_REL32, LEA_OPERAND_OFFSET, dll_import_descriptor);
// patch call __delayLoadHelper2
static const U64 CALL_OPERAND_OFFSET = 59;
lnk_section_push_reloc(sect, tail_merge_chunk, LNK_Reloc_REL32, CALL_OPERAND_OFFSET, delay_load_helper);
ProfEnd();
return tail_merge_chunk;
}
internal LNK_Symbol *
lnk_emit_load_thunk_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name)
{
ProfBeginFunction();
// emit load thunk symbol
String8 load_thunk_name = push_str8f(symtab->arena->v[0], "__imp_load_%S", func_name);
LNK_Symbol *load_thunk_symbol = lnk_symbol_table_push_defined_chunk(symtab, load_thunk_name, LNK_DefinedSymbolVisibility_Extern, LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk, chunk, 0, 0, 0);
ProfEnd();
return load_thunk_symbol;
}
internal LNK_Symbol *
lnk_emit_jmp_thunk_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name)
{
ProfBeginFunction();
String8 jmp_thunk_name = push_str8f(symtab->arena->v[0], "%S", func_name);
LNK_Symbol *jmp_thunk_symbol = lnk_symbol_table_push_defined_chunk(symtab, jmp_thunk_name, LNK_DefinedSymbolVisibility_Extern, LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk, chunk, 0, 0, 0);
ProfEnd();
return jmp_thunk_symbol;
}
internal LNK_Symbol *
lnk_emit_tail_merge_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name)
{
ProfBeginFunction();
String8 tail_merge_name = push_str8f(symtab->arena->v[0], "__tailMerge_%S", func_name);
LNK_Symbol *tail_merge_symbol = lnk_symbol_table_push_defined_chunk(symtab, tail_merge_name, LNK_DefinedSymbolVisibility_Extern, LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk, chunk, 0, 0, 0);
ProfEnd();
return tail_merge_symbol;
}