pack export table into obj and input back to the linker

This commit is contained in:
Nikita Smith
2025-05-12 16:02:07 -07:00
committed by Ryan Fleury
parent c5164bd490
commit f25dc5cc8d
8 changed files with 525 additions and 403 deletions
+128 -41
View File
@@ -869,6 +869,48 @@ 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_ExportParseNode *exp_n = 0;
String8 export_name = lnk_name_from_export_parse(&export_parse);
hash_table_search_string_raw(export_ht, export_name, &exp_n);
if (exp_n == 0) {
// make sure export is defined
if (!export_parse.is_forwarder) {
str8_list_push(arena, include_symbol_list, export_parse.name);
}
// push new export
exp_n = lnk_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;
if (extant_export->alias.size && export_parse.alias.size && !str8_match(extant_export->alias, export_parse.alias, 0)) {
goto report;
}
if (extant_export->ordinal != export_parse.ordinal) {
goto report;
}
is_ambiguous = 0;
if (extant_export->alias.size == 0 && export_parse.alias.size != 0) {
extant_export->alias = export_parse.alias;
}
report:;
if (is_ambiguous) {
lnk_error_with_loc(LNK_Error_IllExport, export_parse.obj_path, export_parse.lib_path, "ambiguous symbol export %S", export_parse.name);
}
}
}
internal LNK_InputObjList
lnk_push_linker_symbols(Arena *arena, LNK_Config *config)
{
@@ -963,13 +1005,13 @@ lnk_queue_lib_member_input(Arena *arena, PathStyle path_style, LNK_SymbolLib *sy
}
internal int
lnk_section_contrib_is_before(void *raw_a, void *raw_b)
lnk_section_contrib_ptr_is_before(void *raw_a, void *raw_b)
{
// Grouped Sections (PE Format)
// "All contributions with the same object-section name are allocated contiguously in the image,
// and the blocks of contributions are sorted in lexical order by object-section name."
LNK_SectionContrib *a = raw_a;
LNK_SectionContrib *b = raw_b;
LNK_SectionContrib *a = *(LNK_SectionContrib **)raw_a;
LNK_SectionContrib *b = *(LNK_SectionContrib **)raw_b;
int cmp;
@@ -1220,7 +1262,6 @@ internal String8List
lnk_build_guard_tables(TP_Context *tp,
LNK_SectionTable *sectab,
LNK_SymbolTable *symtab,
LNK_ExportTable *exptab,
U64 objs_count,
LNK_Obj **objs,
COFF_MachineType machine,
@@ -2007,7 +2048,7 @@ lnk_pdata_is_before_x8664(void *raw_a, void *raw_b)
}
internal String8
lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, LNK_ObjList obj_list)
lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ObjList obj_list)
{
Temp scratch = scratch_begin(arena->v, arena->count);
@@ -2182,18 +2223,18 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S
COFF_SectionFlags flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags;
LNK_Section *sect = lnk_section_table_search(sectab, sect_name, flags);
String8Node *data_n = push_array(sect->arena, String8Node, 1);
data_n->string = sect_data;
// fill out contrib
LNK_SectionContribChunk *sc_chunk = sect->contribs.first;
LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(sc_chunk, 1);
sc->align = sc_align;
sc->data_list = data_n;
sc->u.obj_idx = obj_idx;
sc->u.sort_idx_size = (U16)sect_sort_idx.size;
sc->u.sort_idx = sect_sort_idx.str;
String8Node *data_n = push_array(sect->arena, String8Node, 1);
data_n->string = sect_data;
SLLStackPush(sc->data_list, data_n);
sect_map[obj_idx][sect_idx] = sc;
}
}
@@ -2242,7 +2283,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S
for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) {
for (LNK_SectionContribChunk *sc_chunk = sect_n->data.contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) {
Assert(sc_chunk->count == sc_chunk->cap);
radsort(sc_chunk->v, sc_chunk->count, lnk_section_contrib_is_before);
radsort(sc_chunk->v, sc_chunk->count, lnk_section_contrib_ptr_is_before);
}
}
@@ -2279,7 +2320,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S
LNK_Section *sect = &sect_n->data;
for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) {
for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) {
sc_chunk->v[sc_idx].u.sect_idx = sect->sect_idx;
sc_chunk->v[sc_idx]->u.sect_idx = sect->sect_idx;
}
}
}
@@ -2417,11 +2458,11 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S
if (obj->header.is_big_obj) {
COFF_Symbol32 *symbol32 = symbol.raw_symbol;
symbol32->section_number = safe_cast_u32(sc->u.sect_idx + 1);
symbol32->value = safe_cast_u32(sc->u.off);
symbol32->value = safe_cast_u32(sc->u.off + symbol32->value);
} else {
COFF_Symbol16 *symbol16 = symbol.raw_symbol;
symbol16->section_number = safe_cast_u16(sc->u.sect_idx + 1);
symbol16->value = safe_cast_u32(sc->u.off);
symbol16->value = safe_cast_u32(sc->u.off + symbol16->value);
}
}
}
@@ -2789,7 +2830,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S
U64 prev_sc_opl = 0;
for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) {
for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) {
LNK_SectionContrib *sc = &sc_chunk->v[sc_idx];
LNK_SectionContrib *sc = sc_chunk->v[sc_idx];
// fill align bytes
Assert(sc->u.off >= prev_sc_opl);
@@ -2961,7 +3002,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S
for (LNK_SectionContribChunk *sc_chunk = tls_sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) {
for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) {
LNK_SectionContrib *sc = &sc_chunk->v[sc_idx];
LNK_SectionContrib *sc = sc_chunk->v[sc_idx];
tls_align = Max(tls_align, sc->align);
}
}
@@ -3080,7 +3121,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S
}
internal String8List
lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportTable *exptab)
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);
@@ -3094,20 +3135,24 @@ lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time
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);
KeyValuePair *raw_export_arr = key_value_pairs_from_hash_table(scratch.arena, exptab->name_export_ht);
for (U64 i = 0; i < exptab->name_export_ht->count; ++i) {
LNK_Export *exp = raw_export_arr[i].value_raw;
if (exp->name.size) {
coff_lib_writer_push_export_by_name(lib_writer, machine, time_stamp, dll_name, exp->type, exp->name, safe_cast_u16(exp->id));
} else {
// 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_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);
@@ -3284,7 +3329,7 @@ lnk_build_rad_chunk_map(Arena *arena, String8 image_data, U64 thread_count, LNK_
for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) {
for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) {
Temp temp = temp_begin(scratch.arena);
LNK_SectionContrib *sc = &sc_chunk->v[sc_idx];
LNK_SectionContrib *sc = sc_chunk->v[sc_idx];
U64 file_off = image_section_table[sc->u.sect_idx]->foff + sc->u.off;
U64 virt_off = image_section_table[sc->u.sect_idx]->voff + sc->u.off;
@@ -3499,6 +3544,7 @@ lnk_run(int argc, char **argv)
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;
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};
LNK_InputImportList input_import_list = {0};
@@ -3520,7 +3566,6 @@ lnk_run(int argc, char **argv)
LNK_SectionTable *sectab = 0;
LNK_ImportTable *imptab_static = lnk_import_table_alloc(0);
LNK_ImportTable *imptab_delayed = lnk_import_table_alloc(config->import_table_flags);
LNK_ExportTable *exptab = lnk_export_table_alloc();
LNK_ObjList obj_list = {0};
LNK_LibList lib_index[LNK_InputSource_Count] = {0};
Arena *ht_arena = arena_alloc();
@@ -3570,10 +3615,17 @@ lnk_run(int argc, char **argv)
//
lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".xdata"), str8_lit_comp(".rdata") });
lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".tls"), str8_lit_comp(".data") });
lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".edata"), str8_lit_comp(".rdata") });
//lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".edata"), str8_lit_comp(".rdata") });
lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".idata"), str8_lit_comp(".rdata") });
lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".didat"), str8_lit_comp(".didat") });
lnk_merge_directive_list_push(scratch.arena, &config->merge_list, (LNK_MergeDirective){ str8_lit_comp(".RAD_LINKER_DEBUG_DIR"), str8_lit_comp(".rdata") });
//
// Push config exports
//
for (LNK_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);
}
ProfBegin("Image"); // :EndImage
lnk_timer_begin(LNK_Timer_Image);
@@ -3792,14 +3844,11 @@ lnk_run(int argc, char **argv)
// /EXPORT
{
LNK_ExportParseList obj_exports = {0};
for (LNK_Directive *dir = directive_info.v[LNK_CmdSwitch_Export].first; dir != 0; dir = dir->next) {
lnk_parse_export_directive(scratch.arena, &obj_exports, dir->value_list, obj->path, obj->lib_path);
LNK_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);
}
for (LNK_ExportParse *exp = obj_exports.first; exp != 0; exp = exp->next) {
str8_list_push(scratch.arena, &include_symbol_list, exp->name);
}
lnk_export_parse_list_concat_in_place(&export_symbol_list, &obj_exports);
}
// /INCLUDESYMBOL
@@ -4145,7 +4194,7 @@ lnk_run(int argc, char **argv)
case State_ReportUnresolvedSymbols: {
// report unresolved symbols
for (LNK_SymbolNode *node = unresolved_undef_list.first; node != 0; node = node->next) {
lnk_error(LNK_Error_UnresolvedSymbol, "unresolved symbol %S", node->data->name);
lnk_error_obj(LNK_Error_UnresolvedSymbol, node->data->u.undef.obj, "unresolved symbol %S", node->data->name);
}
if (unresolved_undef_list.count) {
goto exit;
@@ -4179,14 +4228,52 @@ lnk_run(int argc, char **argv)
if (export_symbol_list.count) {
ProfBegin("Build Export Table");
ProfBeginV("Push Exports [Count %u]", export_symbol_list.count);
for (LNK_ExportParse *exp_parse = export_symbol_list.first; exp_parse != 0; exp_parse = exp_parse->next) {
lnk_export_table_push_export(exptab, symtab, exp_parse);
}
ProfEnd();
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) {
exp_n_next = exp_n->next;
LNK_ExportParse *exp = &exp_n->data;
LNK_InputObjList export_objs = lnk_export_table_serialize(scratch.arena, exptab, str8_skip_last_slash(config->image_name), config->machine);
lnk_input_obj_list_concat_in_place(&input_obj_list, &export_objs);
if (!exp->is_forwarder) {
// filter out unresolved exports
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, exp_n->data.name);
if (symbol == 0) {
lnk_error_with_loc(LNK_Warning_IllExport, exp->obj_path, exp->lib_path, "unresolved export symbol %S\n", exp->name);
continue;
}
// check export type
switch (exp->type) {
case COFF_ImportHeader_Code: {
COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
B32 is_export_data = !COFF_SymbolType_IsFunc(defn.type);
if (is_export_data) {
lnk_error_with_loc(LNK_Warning_IllExport, exp->obj_path, exp->lib_path, "export \"%S\" is DATA but has type CODE", exp->name);
}
} break;
case COFF_ImportHeader_Data: {
COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
B32 is_export_code = COFF_SymbolType_IsFunc(defn.type);
if (is_export_code) {
lnk_error_with_loc(LNK_Warning_IllExport, exp->obj_path, exp->lib_path, "export \"%S\" is CODE but has type DATA", exp->name);
}
} break;
case COFF_ImportHeader_Const: {
lnk_not_implemented("TODO: COFF_ImportHeader_Const");
} break;
default: { InvalidPath; } break;
}
}
// push resolved export
lnk_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);
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;
ProfEnd();
}
@@ -4309,7 +4396,7 @@ lnk_run(int argc, char **argv)
} break;
case State_BuildImage: {
// build image
image_data = lnk_build_win32_image(tp_arena, tp, config, symtab, exptab, obj_list);
image_data = lnk_build_win32_image(tp_arena, tp, config, symtab, obj_list);
// write image to disk in a background thread
{
@@ -4343,7 +4430,7 @@ 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, exptab);
String8List lib_list = lnk_build_import_lib(tp_arena->v[0], config->machine, config->time_stamp, config->image_name, 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();
+3 -3
View File
@@ -187,14 +187,14 @@ internal void lnk_queue_lib_member_input(Arena *arena, PathStyle pat
// --- Win32 Image -------------------------------------------------------------
internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag);
internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag);
internal String8List lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_temp, LNK_Config *config, U64 objs_count, LNK_Obj **objs);
internal String8List lnk_build_win32_image_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sect_arr, U64 expected_image_header_size);
internal String8 lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, LNK_ObjList obj_list);
internal String8 lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ObjList obj_list);
// --- Import Lib --------------------------------------------------------------
internal String8List lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportTable *exptab);
internal String8List lnk_build_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ExportParseList export_list);
// --- Logger ------------------------------------------------------------------
+6 -63
View File
@@ -801,72 +801,13 @@ lnk_parse_alt_name_directive_list(Arena *arena, String8List list, LNK_AltNameLis
return 0;
}
internal void
lnk_export_parse_list_concat_in_place(LNK_ExportParseList *list, LNK_ExportParseList *to_concat)
{
SLLConcatInPlace(list, to_concat);
}
internal LNK_ExportParse *
lnk_parse_export_directive(Arena *arena, LNK_ExportParseList *list, String8List value_list, String8 obj_path, String8 lib_path)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
LNK_ExportParse *parse = 0;
// parse directive
String8 name = str8_zero();
String8 alias = str8_zero();
String8 type = coff_string_from_import_header_type(COFF_ImportHeader_Code);
if (value_list.node_count > 0) {
String8List dir_split = str8_split_by_string_chars(scratch.arena, value_list.first->string, str8_lit("="), 0);
B32 is_export_valid = value_list.node_count <= 2 && value_list.node_count > 0;
if (is_export_valid) {
if (dir_split.node_count > 0) {
name = dir_split.last->string;
}
if (dir_split.node_count == 2) {
alias = dir_split.first->string;
}
if (value_list.node_count == 2) {
type = value_list.last->string;
}
}
}
// prase error check
if (name.size == 0) {
String8 dir = str8_list_join(scratch.arena, &value_list, 0);
lnk_error_with_loc(LNK_Error_IllData, obj_path, lib_path, "invalid export directive \"%S\"", dir);
goto exit;
}
parse = push_array_no_zero(arena, LNK_ExportParse, 1);
parse->next = 0;
parse->name = name;
parse->alias = alias;
parse->type = type;
SLLQueuePush(list->first, list->last, parse);
++list->count;
exit:;
scratch_end(scratch);
ProfEnd();
return parse;
}
internal LNK_MergeDirectiveNode *
lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data)
{
LNK_MergeDirectiveNode *node = push_array_no_zero(arena, LNK_MergeDirectiveNode, 1);
node->data = data;
node->next = 0;
node->data = data;
SLLQueuePush(list->first, list->last, node);
++list->count;
list->count += 1;
return node;
}
@@ -1121,8 +1062,10 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam
} break;
case LNK_CmdSwitch_Export: {
String8List value_strings_copy = str8_list_copy(arena, &value_strings);
lnk_parse_export_directive(arena, &config->export_symbol_list, value_strings_copy, obj_path, lib_path);
LNK_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);
}
} break;
case LNK_CmdSwitch_FastFail: {
+2 -18
View File
@@ -238,21 +238,6 @@ typedef struct LNK_AltNameList
String8List to_list;
} LNK_AltNameList;
typedef struct LNK_ExportParse
{
String8 name;
String8 alias;
String8 type;
struct LNK_ExportParse *next;
} LNK_ExportParse;
typedef struct LNK_ExportParseList
{
U64 count;
LNK_ExportParse *first;
LNK_ExportParse *last;
} LNK_ExportParseList;
typedef struct LNK_MergeDirective
{
String8 src;
@@ -573,10 +558,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 LNK_ExportParse * lnk_parse_export_directive(Arena *arena, LNK_ExportParseList *list, String8List value_list, String8 obj_path, String8 lib_path);
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 *out);
internal B32 lnk_parse_merge_directive(String8 string, LNK_MergeDirective *parse_out);
////////////////////////////////
+347 -244
View File
@@ -1,303 +1,406 @@
// Copyright (c) 2025 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
int
lnk_export_name_compar(const void *a_, const void *b_)
internal String8
lnk_name_from_export_parse(LNK_ExportParse *exp)
{
const LNK_Export *a = (const LNK_Export *)a_;
const LNK_Export *b = (const LNK_Export *)b_;
return str8_compar_case_sensitive(&a->name, &b->name);
String8 name;
if (exp->is_forwarder) {
name = exp->alias;
} else if (exp->alias.size) {
name = exp->alias;
} else {
name = exp->name;
}
return name;
}
int
lnk_export_ordinal_compar(const void *a_, const void *b_)
{
const LNK_Export *a = (const LNK_Export *)a_;
const LNK_Export *b = (const LNK_Export *)b_;
int cmp = u16_compar(&a->ordinal, &b->ordinal);
return cmp;
}
internal LNK_ExportTable *
lnk_export_table_alloc(void)
internal B32
lnk_parse_export_directive_ex(Arena *arena, String8List directive, String8 obj_path, String8 lib_path, LNK_ExportParse *export_out)
{
ProfBeginFunction();
Arena *arena = arena_alloc();
Temp scratch = scratch_begin(&arena, 1);
B32 is_parsed = 0;
LNK_ExportTable *exptab = push_array(arena, LNK_ExportTable, 1);
exptab->arena = arena;
exptab->voff_size = sizeof(U32);
exptab->max_ordinal = max_U16;
exptab->is_ordinal_used = push_array(arena, B8, exptab->max_ordinal);
exptab->name_export_ht = hash_table_init(arena, 0x10000);
exptab->noname_export_ht = hash_table_init(arena, 0x100);
// 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 exptab;
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_table_release(LNK_ExportTable **exptab_ptr)
lnk_export_parse_list_push_node(LNK_ExportParseList *list, LNK_ExportParseNode *node)
{
ProfBeginFunction();
arena_release((*exptab_ptr)->arena);
*exptab_ptr = NULL;
ProfEnd();
SLLQueuePush(list->first, list->last, node);
list->count += 1;
}
internal LNK_Export *
lnk_export_table_search(LNK_ExportTable *exptab, String8 name)
internal LNK_ExportParseNode *
lnk_export_parse_list_push(Arena *arena, LNK_ExportParseList *list, LNK_ExportParse data)
{
KeyValuePair *kv = hash_table_search_string(exptab->name_export_ht, name);
if (kv) {
return kv->value_raw;
}
return 0;
LNK_ExportParseNode *node = push_array(arena, LNK_ExportParseNode, 1);
node->data = data;
lnk_export_parse_list_push_node(list, node);
return node;
}
internal LNK_Export *
lnk_export_table_push_export(LNK_ExportTable *exptab, LNK_SymbolTable *symtab, LNK_ExportParse *exp_parse)
internal void
lnk_export_parse_list_concat_in_place(LNK_ExportParseList *list, LNK_ExportParseList *to_concat)
{
LNK_Export *exp = 0;
// get export symbol
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, exp_parse->name);
if (symbol == 0) {
lnk_error(LNK_Warning_IllExport, "symbol \"%S\" for export doesn't exist", exp_parse->name);
goto exit;
}
if (symbol->type != LNK_Symbol_Defined) {
lnk_error(LNK_Warning_IllExport, "unable to resolve symbol \"%S\" for export", exp_parse->name);
goto exit;
}
// NOTE: It is possible to export a global variable as CODE
// with following snippet:
// int global_bar = 0;
// #pragma comment(linker, "/export:global_bar")
// for some reason MSVC and LLD don't check symbol type and default
// to CODE instead of DATA. But if you try export global variable with:
// #pragma comment(linker, "/export:global_bar,CODE")
// MSVC and LLD issue an error. For compatibility sake we do the same thing too.
COFF_ImportType type = coff_import_header_type_from_string(exp_parse->type);
switch (type) {
case COFF_ImportHeader_Code: {
COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
B32 is_export_data = !COFF_SymbolType_IsFunc(defn.type);
if (is_export_data) {
lnk_error(LNK_Error_IllExport, "export \"%S\" is DATA but has specifier CODE", exp_parse->name);
}
} break;
case COFF_ImportHeader_Data: {
COFF_ParsedSymbol defn = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
B32 is_export_code = COFF_SymbolType_IsFunc(defn.type);
if (is_export_code) {
lnk_error(LNK_Error_IllExport, "export \"%S\" is CODE but has specifier DATA", exp_parse->name);
}
} break;
case COFF_ImportHeader_Const: {
lnk_not_implemented("TODO: COFF_ImportHeader_Const");
} break;
default: {
if (exp_parse->type.size) {
lnk_error(LNK_Error_IllExport, "invalid type \"%S\" for export \"%S\"", exp_parse->type, exp_parse->name);
}
} break;
}
// error check multiple def
exp = lnk_export_table_search(exptab, exp_parse->alias);
if (exp) {
if (exp->type != type) {
lnk_error(LNK_Warning_IllExport, "trying to rexport symbol \"%S\"", exp_parse->alias);
}
goto exit;
}
exp = lnk_export_table_search(exptab, exp_parse->name);
if (exp) {
if (exp->type != type) {
lnk_error(LNK_Warning_IllExport, "multiple export definition for \"%S\"", exp_parse->name);
}
goto exit;
}
// find free ordinal
U16 ordinal;
for (ordinal = 0; ordinal < exptab->max_ordinal; ++ordinal) {
if (!exptab->is_ordinal_used[ordinal]) {
exptab->is_ordinal_used[ordinal] = 1;
break;
}
}
// ordinal alloc error check
if (ordinal >= exptab->max_ordinal) {
lnk_error(LNK_Error_OutOfExportOrdinals, "reached export limit of %u, discarding export %S", exptab->max_ordinal, exp_parse->name);
goto exit;
}
// fill out export
exp = push_array_no_zero(exptab->arena, LNK_Export, 1);
exp->next = 0;
exp->name = push_str8_copy(exptab->arena, exp_parse->alias.size > 0 ? exp_parse->alias : exp_parse->name);
exp->symbol = symbol;
exp->id = exptab->name_export_ht->count;
exp->ordinal = ordinal;
exp->type = type;
exp->is_private = 0; // exports through directives are public
hash_table_push_string_raw(exptab->arena, exptab->name_export_ht, exp->name, exp);
exit:;
return exp;
SLLConcatInPlace(list, to_concat);
}
internal LNK_ExportArray
lnk_export_array_from_list(Arena *arena, LNK_ExportList list)
internal int
lnk_named_export_is_before(void *raw_a, void *raw_b)
{
ProfBeginFunction();
LNK_ExportArray arr;
arr.count = 0;
arr.v = push_array_no_zero(arena, LNK_Export, list.count);
for (LNK_Export *exp = list.first; exp != NULL; exp = exp->next) {
arr.v[arr.count++] = *exp;
}
ProfEnd();
return arr;
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 LNK_InputObjList
lnk_export_table_serialize(Arena *arena, LNK_ExportTable *exptab, String8 image_name, COFF_MachineType machine)
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 ordinal bounds
U64 ordinal_low;
for (ordinal_low = 0; ordinal_low < exptab->max_ordinal; ++ordinal_low) {
if (exptab->is_ordinal_used[ordinal_low]) {
break;
// 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;
}
}
U64 ordinal_high;
for (ordinal_high = exptab->max_ordinal - 1; ordinal_high > 0; --ordinal_high) {
if (exptab->is_ordinal_used[ordinal_high]) {
break;
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);
// fill out export table header
PE_ExportTableHeader *header = push_array(obj_writer->arena, PE_ExportTableHeader, 1);
header->ordinal_base = safe_cast_u16(ordinal_low + 1);
header->export_address_table_count = safe_cast_u32(exptab->name_export_ht->count + exptab->noname_export_ht->count);
header->name_pointer_table_count = safe_cast_u32(exptab->name_export_ht->count);
// make iamge name c-string
String8 image_name_cstr = push_cstr(obj_writer->arena, image_name);
// push sections
COFF_ObjSection *header_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$1"), LNK_EDATA_SECTION_FLAGS, str8_struct(header));
COFF_ObjSection *voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$2"), LNK_EDATA_SECTION_FLAGS, str8_zero());
COFF_ObjSection *name_voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$3"), LNK_EDATA_SECTION_FLAGS, str8_zero());
COFF_ObjSection *ordinal_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$4"), LNK_EDATA_SECTION_FLAGS, str8_zero());
COFF_ObjSection *string_buffer_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$5"), LNK_EDATA_SECTION_FLAGS, str8_zero());
COFF_ObjSection *image_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$6"), LNK_EDATA_SECTION_FLAGS, image_name_cstr);
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));
// push symbols
COFF_ObjSymbol *image_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_NAME_VOFF"), 0, image_name_sect);
COFF_ObjSymbol *address_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_ADDRESS_TABLE_VOFF"), 0, voff_table_sect);
COFF_ObjSymbol *name_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_NAME_POINTER_VOFF"), 0, name_voff_table_sect);
COFF_ObjSymbol *ordinal_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_ORDINAL_TABLE_VOFF"), 0, ordinal_table_sect);
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 };
// patch export table header
switch (machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: {
coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_voff), image_name_symbol, COFF_Reloc_X64_Addr32Nb);
coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, export_address_table_voff), address_table_symbol, COFF_Reloc_X64_Addr32Nb);
coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_pointer_table_voff), name_table_symbol, COFF_Reloc_X64_Addr32Nb);
coff_obj_writer_section_push_reloc(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, ordinal_table_voff), ordinal_table_symbol, COFF_Reloc_X64_Addr32Nb);
} break;
default: { NotImplemented; } break;
}
B8 *is_ordinal_bound = push_array(scratch.arena, B8, exptab->max_ordinal);
HashTable *exp_ht_arr[] = { exptab->name_export_ht, exptab->noname_export_ht };
for (U64 ht_idx = 0; ht_idx < ArrayCount(exp_ht_arr); ht_idx += 1) {
HashTable *ht = exp_ht_arr[ht_idx];
KeyValuePair *kv_arr = key_value_pairs_from_hash_table(scratch.arena, exptab->name_export_ht);
// named exports must be lexically sorted
if (ht_idx == 0) {
sort_key_value_pairs_as_string_sensitive(kv_arr, exptab->name_export_ht->count);
}
for (U64 i = 0; i < ht->count; i += 1) {
LNK_Export *exp = kv_arr[i].value_raw;
{
U64 export_name_offset = string_buffer_sect->data.total_size;
String8 export_name_cstr = push_cstr(obj_writer->arena, exp->name);
str8_list_push(obj_writer->arena, &string_buffer_sect->data, export_name_cstr);
String8 export_name_symbol_name = push_str8f(obj_writer->arena, "EXPORT.%S", exp->name);
COFF_ObjSymbol *export_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, export_name_symbol_name, export_name_offset, string_buffer_sect);
U64 export_name_voff_offset = name_voff_table_sect->data.total_size;
U64 export_name_voff_size = sizeof(U32);
U8 *export_name_voff = push_array(obj_writer->arena, U8, export_name_voff_size);
str8_list_push(obj_writer->arena, &name_voff_table_sect->data, str8_array(export_name_voff, export_name_voff_size));
switch (machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: { coff_obj_writer_section_push_reloc(obj_writer, name_voff_table_sect, export_name_voff_offset, export_name_symbol, COFF_Reloc_X64_Addr32Nb); } break;
default: { NotImplemented; } break;
}
}
{
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));
if ( ! is_ordinal_bound[exp->ordinal]) {
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 *symbol = coff_obj_writer_push_symbol_undef(obj_writer, exp->name);
switch (machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: { coff_obj_writer_section_push_reloc(obj_writer, voff_table_sect, voff_offset, symbol, COFF_Reloc_X64_Addr32Nb); } break;
default: { NotImplemented; } break;
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);
LNK_InputObjList result = {0};
LNK_InputObj *input = lnk_input_obj_list_push(arena, &result);
input->path = str8_lit("* Exports *");
input->dedup_id = input->path;
input->data = obj;
os_write_data_to_file_path(str8_lit("foo.obj"), obj);
scratch_end(scratch);
return result;
return obj;
}
+32 -30
View File
@@ -3,42 +3,44 @@
#pragma once
typedef struct LNK_Export
typedef struct LNK_ExportParse
{
struct LNK_Export *next;
String8 name;
struct LNK_Symbol *symbol;
U32 id;
U16 ordinal;
COFF_ImportType type;
B32 is_private;
} LNK_Export;
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_ExportList
typedef struct LNK_ExportParseNode
{
U64 count;
LNK_Export *first;
LNK_Export *last;
} LNK_ExportList;
LNK_ExportParse data;
struct LNK_ExportParseNode *next;
} LNK_ExportParseNode;
typedef struct LNK_ExportArray
typedef struct LNK_ExportParseList
{
U64 count;
LNK_Export *v;
} LNK_ExportArray;
U64 count;
LNK_ExportParseNode *first;
LNK_ExportParseNode *last;
} LNK_ExportParseList;
typedef struct LNK_ExportTable
typedef struct LNK_ExportParsePtrArray
{
Arena *arena;
HashTable *name_export_ht;
HashTable *noname_export_ht;
U64 voff_size;
U64 max_ordinal;
B8 *is_ordinal_used;
} LNK_ExportTable;
U64 count;
LNK_ExportParse **v;
} LNK_ExportParsePtrArray;
internal LNK_ExportTable * lnk_export_table_alloc(void);
internal void lnk_export_table_release(LNK_ExportTable **exptab_ptr);
internal LNK_Export * lnk_export_table_search(LNK_ExportTable *exptab, String8 name);
internal LNK_InputObjList lnk_export_table_serialize(Arena *arena, LNK_ExportTable *exptab, String8 image_name, COFF_MachineType machine);
////////////////////////////////
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);
+5 -3
View File
@@ -35,7 +35,7 @@ internal LNK_SectionContrib *
lnk_section_contrib_chunk_push(LNK_SectionContribChunk *chunk, U64 count)
{
Assert(chunk->count + count <= chunk->cap);
LNK_SectionContrib *result = &chunk->v[chunk->count];
LNK_SectionContrib *result = chunk->v[chunk->count];
chunk->count += count;
return result;
}
@@ -46,7 +46,9 @@ lnk_section_contrib_chunk_list_push_chunk(Arena *arena, LNK_SectionContribChunkL
LNK_SectionContribChunk *chunk = push_array(arena, LNK_SectionContribChunk, 1);
chunk->count = 0;
chunk->cap = cap;
chunk->v = push_array(arena, LNK_SectionContrib, cap);
chunk->v = push_array(arena, LNK_SectionContrib *, cap);
chunk->v2 = push_array(arena, LNK_SectionContrib, cap);
for (U64 i = 0; i < cap; i += 1) { chunk->v[i] = &chunk->v2[i]; }
SLLQueuePush(list->first, list->last, chunk);
list->chunk_count += 1;
return chunk;
@@ -317,7 +319,7 @@ lnk_finalize_section_layout(LNK_SectionTable *sectab, LNK_Section *sect, U64 fil
U64 cursor = 0;
for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) {
for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) {
LNK_SectionContrib *sc = &sc_chunk->v[sc_idx];
LNK_SectionContrib *sc = sc_chunk->v[sc_idx];
cursor = AlignPow2(cursor, sc->align);
+2 -1
View File
@@ -38,7 +38,8 @@ typedef struct LNK_SectionContribChunk
struct LNK_SectionContribChunk *next;
U64 count;
U64 cap;
LNK_SectionContrib *v;
LNK_SectionContrib **v;
LNK_SectionContrib *v2;
} LNK_SectionContribChunk;
typedef struct LNK_SectionContribChunkList