diff --git a/src/coff/coff.c b/src/coff/coff.c index 6257e3fc..34c97aab 100644 --- a/src/coff/coff.c +++ b/src/coff/coff.c @@ -99,6 +99,7 @@ coff_header_info_from_data(String8 data) COFF_HeaderBigObj *big_header = (COFF_HeaderBigObj*)data.str; info.type = COFF_DataType_BIG_OBJ; info.machine = big_header->machine; + info.header_size = sizeof(COFF_HeaderBigObj); info.section_array_off = sizeof(COFF_HeaderBigObj); info.section_count_no_null = big_header->section_count; info.string_table_off = big_header->symbol_table_foff + sizeof(COFF_Symbol32) * big_header->symbol_count; @@ -109,6 +110,7 @@ coff_header_info_from_data(String8 data) COFF_Header *header = (COFF_Header*)data.str; info.type = COFF_DataType_OBJ; info.machine = header->machine; + info.header_size = sizeof(COFF_Header); info.section_array_off = sizeof(COFF_Header); info.section_count_no_null = header->section_count; info.string_table_off = header->symbol_table_foff + sizeof(COFF_Symbol16) * header->symbol_count; diff --git a/src/coff/coff.h b/src/coff/coff.h index ccf905bb..b1251b9c 100644 --- a/src/coff/coff.h +++ b/src/coff/coff.h @@ -634,8 +634,9 @@ typedef U32 COFF_DataType; typedef struct COFF_HeaderInfo { - COFF_MachineType machine; COFF_DataType type; + COFF_MachineType machine; + U64 header_size; U64 section_array_off; U64 section_count_no_null; U64 string_table_off; diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 1f80d3a2..e6a3d845 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -115,7 +115,6 @@ #include "lnk_config.h" #include "lnk_chunk.h" #include "lnk_reloc.h" -#include "lnk_directive.h" #include "lnk_symbol_table.h" #include "lnk_section_table.h" #include "lnk_obj.h" @@ -133,7 +132,6 @@ #include "lnk_config.c" #include "lnk_chunk.c" #include "lnk_reloc.c" -#include "lnk_directive.c" #include "lnk_symbol_table.c" #include "lnk_section_table.c" #include "lnk_obj.c" @@ -3553,6 +3551,8 @@ lnk_run(int argc, char **argv) // derive machine from obj if (config->machine == COFF_MachineType_UNKNOWN) { config->machine = obj->machine; + } else if (config->machine != COFF_MachineType_X64) { + lnk_error_with_loc(LNK_Error_UnsupportedMachine, obj->path, obj->lib_path, "%S machine is supported", coff_string_from_machine_type(obj->machine)); } else { // is obj machine compatible? if (config->machine != obj->machine && @@ -3901,8 +3901,16 @@ lnk_run(int argc, char **argv) } break; case State_BuildExportTable: { ProfBegin("Build Export Table"); - + + // push exports from command line + for (LNK_ExportParse *exp_parse = config->export_symbol_list.first; exp_parse != 0; exp_parse = exp_parse->next) { + lnk_export_table_push_export(exptab, symtab, exp_parse); + } + + // push exports from obj directives lnk_collect_exports_from_obj_directives(exptab, obj_list, symtab); + + // build export table section lnk_build_edata(exptab, st, symtab, config->image_name, config->machine); ProfEnd(); diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index eb754b40..72c7ed4c 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -43,7 +43,7 @@ read_only struct { LNK_CmdSwitch_NotImplemented, "EMITVOLATILEMETADATA", "", "" }, { LNK_CmdSwitch_Entry, "ENTRY", ":FUNCTION", "" }, { LNK_CmdSwitch_Null, "ERRORREPORT", "", "Deprecated starting Windows Vista." }, - { LNK_CmdSwitch_NotImplemented, "EXPORT", ":SYMBOL", "" }, + { LNK_CmdSwitch_Export, "EXPORT", ":SYMBOL", "" }, { LNK_CmdSwitch_NotImplemented, "EXPORTADMIN", "", "" }, { LNK_CmdSwitch_FastFail, "FASTFAIL", "", "Not used." }, { LNK_CmdSwitch_NotImplemented, "FASTGENPROFILE", "", "" }, @@ -773,6 +773,13 @@ lnk_cmd_switch_parse_string_copy(Arena *arena, String8 obj_path, String8 lib_pat //////////////////////////////// +internal void +lnk_alt_name_list_concat_in_place(LNK_AltNameList *list, LNK_AltNameList *to_concat) +{ + str8_list_concat_in_place(&list->from_list, &to_concat->from_list); + str8_list_concat_in_place(&list->to_list, &to_concat->to_list); +} + internal B32 lnk_parse_alt_name_directive(Arena *arena, String8 input, LNK_AltNameList *list_out) { @@ -800,6 +807,86 @@ lnk_parse_alt_name_directive_list(Arena *arena, String8List list, LNK_AltNameLis return 0; } +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_ImportHeaderType_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; + + SLLQueuePush(list->first, list->last, node); + ++list->count; + + return node; +} + +internal B32 +lnk_parse_merge_directive(String8 string, LNK_MergeDirective *out) +{ + Temp scratch = scratch_begin(0, 0); + B32 is_parse_ok = 0; + + String8List list = str8_split_by_string_chars(scratch.arena, string, str8_lit("="), 0); + if (list.node_count == 2) { + out->src = list.first->string; + out->dst = list.last->string; + is_parse_ok = 1; + } + + scratch_end(scratch); + return is_parse_ok; +} + //////////////////////////////// internal void @@ -1028,6 +1115,11 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam config->entry_point_name = new_entry_point_name; } 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); + } break; + case LNK_CmdSwitch_FastFail: { // do nothing } break; diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 54783f7b..595eca07 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -7,7 +7,6 @@ typedef enum { LNK_CmdSwitch_Null, LNK_CmdSwitch_NotImplemented, - LNK_CmdSwitch_Deprecated, LNK_CmdSwitch_Align, LNK_CmdSwitch_AllowBind, @@ -22,6 +21,7 @@ typedef enum LNK_CmdSwitch_Dll, LNK_CmdSwitch_DynamicBase, LNK_CmdSwitch_Entry, + LNK_CmdSwitch_Export, LNK_CmdSwitch_FastFail, LNK_CmdSwitch_FileAlign, LNK_CmdSwitch_Fixed, @@ -77,7 +77,6 @@ typedef enum LNK_CmdSwitch_EditAndContinue, LNK_CmdSwitch_EmitVolatileMetadata, LNK_CmdSwitch_ErrorReport, - LNK_CmdSwitch_Export, LNK_CmdSwitch_ExportAdmin, LNK_CmdSwitch_FastGenProfile, LNK_CmdSwitch_FailIfMismatch, @@ -227,6 +226,42 @@ typedef struct LNK_AltNameList String8List to_list; } LNK_AltNameList; +typedef struct LNK_ExportParse +{ + struct LNK_ExportParse *next; + String8 name; + String8 alias; + String8 type; +} LNK_ExportParse; + +typedef struct LNK_ExportParseList +{ + U64 count; + LNK_ExportParse *first; + LNK_ExportParse *last; +} LNK_ExportParseList; + +typedef struct LNK_MergeDirective +{ + String8 src; + String8 dst; +} LNK_MergeDirective; + +typedef struct LNK_MergeDirectiveNode +{ + struct LNK_MergeDirectiveNode *next; + LNK_MergeDirective data; +} LNK_MergeDirectiveNode; + +typedef struct LNK_MergeDirectiveList +{ + U64 count; + LNK_MergeDirectiveNode *first; + LNK_MergeDirectiveNode *last; +} LNK_MergeDirectiveList; + + + typedef enum { LNK_DebugInfoGuid_Null, @@ -301,6 +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; String8List input_list[LNK_Input_Count]; String8List input_default_lib_list; String8List disallow_lib_list; @@ -506,9 +542,17 @@ internal void lnk_cmd_switch_set_flag_64(String8 obj_path, String8 lib_path, LNK internal B32 lnk_cmd_switch_parse_string(String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, String8List value_strings, String8 *string_out); internal void lnk_cmd_switch_parse_string_copy(Arena *arena, String8 obj_path, String8 lib_path, LNK_CmdSwitchType cmd_switch, String8List value_strings, String8 *string_out); +//////////////////////////////// + +internal void lnk_alt_name_list_concat_in_place(LNK_AltNameList *list, LNK_AltNameList *to_concat); 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 void lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 name, String8List value_list, String8 obj_path, String8 lib_path); diff --git a/src/linker/lnk_directive.c b/src/linker/lnk_directive.c deleted file mode 100644 index c3107942..00000000 --- a/src/linker/lnk_directive.c +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal void -lnk_alt_name_list_concat_in_place(LNK_AltNameList *list, LNK_AltNameList *to_concat) -{ - str8_list_concat_in_place(&list->from_list, &to_concat->from_list); - str8_list_concat_in_place(&list->to_list, &to_concat->to_list); -} - -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; - - SLLQueuePush(list->first, list->last, node); - ++list->count; - - return node; -} - -//////////////////////////////// - -internal B32 -lnk_is_directive_legal(LNK_CmdSwitchType type) -{ - static B32 init_table = 1; - static B8 is_legal[LNK_CmdSwitch_Count]; - if (init_table) { - init_table = 0; - is_legal[LNK_CmdSwitch_AlternateName] = 1; - is_legal[LNK_CmdSwitch_DefaultLib] = 1; - is_legal[LNK_CmdSwitch_DisallowLib] = 1; - is_legal[LNK_CmdSwitch_EditAndContinue] = 1; - is_legal[LNK_CmdSwitch_Entry] = 1; - is_legal[LNK_CmdSwitch_Export] = 1; - is_legal[LNK_CmdSwitch_FailIfMismatch] = 1; - is_legal[LNK_CmdSwitch_GuardSym] = 1; - is_legal[LNK_CmdSwitch_Include] = 1; - is_legal[LNK_CmdSwitch_InferAsanLibs] = 1; - is_legal[LNK_CmdSwitch_InferAsanLibsNo] = 1; - is_legal[LNK_CmdSwitch_ManifestDependency] = 1; - is_legal[LNK_CmdSwitch_Merge] = 1; - is_legal[LNK_CmdSwitch_NoDefaultLib] = 1; - is_legal[LNK_CmdSwitch_Release] = 1; - is_legal[LNK_CmdSwitch_Section] = 1; - is_legal[LNK_CmdSwitch_Stack] = 1; - is_legal[LNK_CmdSwitch_SubSystem] = 1; - is_legal[LNK_CmdSwitch_ThrowingNew] = 1; - } - return is_legal[type]; -} - -internal void -lnk_parse_directives(Arena *arena, LNK_DirectiveInfo *directive_info, String8 buffer, String8 obj_path) -{ - Temp scratch = scratch_begin(&arena, 1); - - String8 to_parse; - { - local_persist const U8 bom_sig[] = { 0xEF, 0xBB, 0xBF }; - local_persist const U8 ascii_sig[] = { 0x20, 0x20, 0x20 }; - if (MemoryMatch(buffer.str, &bom_sig[0], sizeof(bom_sig))) { - to_parse = str8_zero(); - lnk_not_implemented("TODO: support for BOM encoding"); - } else if (MemoryMatch(buffer.str, &ascii_sig[0], sizeof(ascii_sig))) { - to_parse = str8_skip(buffer, sizeof(ascii_sig)); - } else { - to_parse = buffer; - } - } - - String8List arg_list = lnk_arg_list_parse_windows_rules(scratch.arena, to_parse); - LNK_CmdLine cmd_line = lnk_cmd_line_parse_windows_rules(scratch.arena, arg_list); - - for (LNK_CmdOption *opt = cmd_line.first_option; opt != 0; opt = opt->next) { - LNK_CmdSwitchType type = lnk_cmd_switch_type_from_string(opt->string); - - if (type == LNK_CmdSwitch_Null) { - lnk_error(LNK_Warning_UnknownDirective, "%S: unknown directive \"%S\"", obj_path, opt->string); - continue; - } - if (!lnk_is_directive_legal(type)) { - lnk_error(LNK_Warning_IllegalDirective, "%S: illegal directive \"%S\"", obj_path, opt->string); - continue; - } - - LNK_Directive *directive = push_array_no_zero(arena, LNK_Directive, 1); - directive->next = 0; - directive->id = push_str8_copy(arena, opt->string); - directive->value_list = str8_list_copy(arena, &opt->value_strings); - - LNK_DirectiveList *directive_list = &directive_info->v[type]; - SLLQueuePush(directive_list->first, directive_list->last, directive); - ++directive_list->count; - } - - scratch_end(scratch); -} - -internal String8List -lnk_parse_default_lib_directive(Arena *arena, LNK_DirectiveList *dir_list) -{ - ProfBeginFunction(); - String8List default_libs = {0}; - - for (LNK_Directive *dir = dir_list->first; dir != 0; dir = dir->next) { - for (String8Node *i = dir->value_list.first; i != 0; i = i->next) { - String8 lib_path = i->string; - - // is there lib extension? - String8 ext = str8_skip_last_dot(lib_path); - if (ext.size == lib_path.size) { // TODO: fix string_extension_from_path, if there is no extension it should return zero - lib_path = push_str8f(arena, "%S.lib", lib_path); - } else { - lib_path = push_str8_copy(arena, lib_path); - } - - str8_list_push(arena, &default_libs, lib_path); - } - } - - ProfEnd(); - return default_libs; -} - -internal LNK_ExportParse * -lnk_parse_export_direcive(Arena *arena, LNK_ExportParseList *list, String8List value_list, LNK_Obj *obj) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - LNK_ExportParse *parse = 0; - - // parse directive - String8 name = str8(0,0); - String8 alias = str8(0,0); - String8 type = coff_string_from_import_header_type(COFF_ImportHeaderType_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_obj(LNK_Error_IllData, obj, "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 B32 -lnk_parse_merge_directive(String8 string, LNK_MergeDirective *out) -{ - Temp scratch = scratch_begin(0, 0); - B32 is_parse_ok = 0; - - String8List list = str8_split_by_string_chars(scratch.arena, string, str8_lit("="), 0); - if (list.node_count == 2) { - out->src = list.first->string; - out->dst = list.last->string; - is_parse_ok = 1; - } - - scratch_end(scratch); - return is_parse_ok; -} - diff --git a/src/linker/lnk_directive.h b/src/linker/lnk_directive.h deleted file mode 100644 index 64f9e0d2..00000000 --- a/src/linker/lnk_directive.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -typedef struct LNK_Directive -{ - struct LNK_Directive *next; - String8 id; - String8List value_list; -} LNK_Directive; - -typedef struct LNK_DirectiveList -{ - U64 count; - LNK_Directive *first; - LNK_Directive *last; -} LNK_DirectiveList; - -typedef struct LNK_ExportParse -{ - struct LNK_ExportParse *next; - String8 name; - String8 alias; - String8 type; -} LNK_ExportParse; - -typedef struct LNK_ExportParseList -{ - U64 count; - LNK_ExportParse *first; - LNK_ExportParse *last; -} LNK_ExportParseList; - -typedef struct LNK_MergeDirective -{ - String8 src; - String8 dst; -} LNK_MergeDirective; - -typedef struct LNK_MergeDirectiveNode -{ - struct LNK_MergeDirectiveNode *next; - LNK_MergeDirective data; -} LNK_MergeDirectiveNode; - -typedef struct LNK_MergeDirectiveList -{ - U64 count; - LNK_MergeDirectiveNode *first; - LNK_MergeDirectiveNode *last; -} LNK_MergeDirectiveList; - -typedef struct LNK_DirectiveInfo -{ - LNK_DirectiveList v[LNK_CmdSwitch_Count]; -} LNK_DirectiveInfo; - -//////////////////////////////// - -internal void lnk_alt_name_list_concat_in_place(LNK_AltNameList *list, LNK_AltNameList *to_concat); - -internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data); - -//////////////////////////////// - -internal void lnk_parse_directives(Arena *arena, LNK_DirectiveInfo *directive_info, String8 buffer, String8 obj_path); -internal String8List lnk_parse_default_lib_directive(Arena *arena, LNK_DirectiveList *dir_list); -internal B32 lnk_parse_merge_directive(String8 directive, LNK_MergeDirective *out); - - diff --git a/src/linker/lnk_error.h b/src/linker/lnk_error.h index 65cdf96b..2f3cd415 100644 --- a/src/linker/lnk_error.h +++ b/src/linker/lnk_error.h @@ -80,6 +80,7 @@ typedef enum LNK_Warning_LongSectionName, LNK_Warning_UnknownSwitch, LNK_Warning_TLSAlign, + LNK_Warning_DirectiveSectionWithRelocs, LNK_Warning_Last, LNK_Error_Count diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index 2fb22216..03265995 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -219,9 +219,10 @@ THREAD_POOL_TASK_FUNC(lnk_default_lib_collector) Rng1U64 range = task->range_arr[task_id]; String8List *result = &task->out_arr[task_id]; for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) { - LNK_Obj *obj = &task->in_arr.v[obj_idx].data; - String8List list = lnk_parse_default_lib_directive(arena, &obj->directive_info.v[LNK_CmdSwitch_DefaultLib]); - str8_list_concat_in_place(result, &list); + LNK_Obj *obj = &task->in_arr.v[obj_idx].data; + for (LNK_Directive *dir = obj->directive_info.v[LNK_CmdSwitch_DefaultLib].first; dir != 0; dir = dir->next) { + str8_list_concat_in_place(result, &dir->value_list); + } } } @@ -322,19 +323,26 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) LNK_ObjNode *obj_node = task->obj_node_arr + task_id; LNK_Obj *obj = &obj_node->data; - // cache path, we need it for error reports and debug stuff String8 cached_path = push_str8_copy(arena, input->path); String8 cached_lib_path = push_str8_copy(arena, input->lib_path); // parse coff obj - COFF_HeaderInfo coff_info = coff_header_info_from_data(input->data); - COFF_SectionHeader *coff_sect_arr = (COFF_SectionHeader *)(input->data.str + coff_info.section_array_off); - void *coff_symbols = input->data.str + coff_info.symbol_off; + COFF_HeaderInfo coff_info = coff_header_info_from_data(input->data); + Rng1U64 coff_file_header_range = rng_1u64(0, coff_info.header_size); + Rng1U64 coff_sect_arr_range = rng_1u64(coff_info.section_array_off, coff_info.section_array_off + coff_info.section_count_no_null * sizeof(COFF_SectionHeader)); + Rng1U64 coff_symbols_range = rng_1u64(coff_info.symbol_off, coff_info.symbol_off + coff_info.symbol_count * coff_info.symbol_size); + String8 raw_coff_sect_arr = str8_substr(input->data, coff_sect_arr_range); + String8 raw_coff_symbols = str8_substr(input->data, coff_symbols_range); - // handle machines we dont support - if (coff_info.machine != COFF_MachineType_UNKNOWN && coff_info.machine != COFF_MachineType_X64) { - lnk_error(LNK_Error_UnsupportedMachine, "%S: %S machine is supported", input->path, coff_string_from_machine_type(coff_info.machine)); + if (raw_coff_sect_arr.size != dim_1u64(coff_sect_arr_range)) { + lnk_error_with_loc(LNK_Error_IllData, cached_path, cached_lib_path, "corrupted file, unable to read section header table"); } + if (raw_coff_symbols.size != dim_1u64(coff_symbols_range)) { + lnk_error_with_loc(LNK_Error_IllData, cached_path, cached_lib_path, "corrupted file, unable to read symbol table"); + } + + COFF_SectionHeader *coff_sect_arr = (COFF_SectionHeader *)raw_coff_sect_arr.str; + void *coff_symbols = raw_coff_symbols.str; // :function_pad_min U64 function_pad_min; @@ -344,30 +352,13 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) function_pad_min = lnk_get_default_function_pad_min(coff_info.machine); } - U64 chunk_count = 0; - chunk_count += coff_info.section_count_no_null; - chunk_count += 1; // :common_block + U64 chunk_count = 1; // :common_block + chunk_count += coff_info.section_count_no_null; - String8 *sect_name_arr = push_array_no_zero(arena, String8, chunk_count); - String8 *sect_sort_arr = push_array_no_zero(arena, String8, chunk_count); + String8 *sect_name_arr = push_array_no_zero(arena, String8, chunk_count); + String8 *sect_sort_arr = push_array_no_zero(arena, String8, chunk_count); LNK_Chunk *chunk_arr = push_array_no_zero(arena, LNK_Chunk, chunk_count); - // init section name and postfix array - for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *coff_sect = &coff_sect_arr[sect_idx]; - - // read name - String8 sect_name = coff_name_from_section_header(coff_sect, input->data, coff_info.string_table_off); - - // parse section name - String8 name, postfix; - coff_parse_section_name(sect_name, &name, &postfix); - - // fill out - sect_name_arr[sect_idx] = name; - sect_sort_arr[sect_idx] = postfix; - } - // :common_block U64 common_block_idx = chunk_count - 1; sect_name_arr[common_block_idx] = str8_lit(".bss"); @@ -376,11 +367,38 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) { COFF_SectionHeader *coff_sect = &coff_sect_arr[sect_idx]; + // read name + String8 sect_name = coff_name_from_section_header(coff_sect, input->data, coff_info.string_table_off); + + // parse section name + coff_parse_section_name(sect_name, §_name_arr[sect_idx], §_sort_arr[sect_idx]); + String8 data; if (coff_sect->flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA) { data = str8(0, coff_sect->fsize); } else { - data = str8(input->data.str + coff_sect->foff, coff_sect->fsize); + if (coff_sect->fsize > 0) { + Rng1U64 range = rng_1u64(coff_sect->foff, coff_sect->foff + coff_sect->fsize); + data = str8_substr(input->data, range); + + if (contains_1u64(coff_file_header_range, coff_sect->foff) || + (coff_sect->fsize > 0 && contains_1u64(coff_file_header_range, coff_sect->foff + coff_sect->fsize-1))) { + lnk_error_with_loc(LNK_Error_IllData, cached_path, cached_lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into file header)", sect_name, sect_idx+1); + } + if (contains_1u64(coff_sect_arr_range, coff_sect->foff) || + (coff_sect->fsize > 0 && contains_1u64(coff_sect_arr_range, coff_sect->foff + coff_sect->fsize-1))) { + lnk_error_with_loc(LNK_Error_IllData, cached_path, cached_lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into section header table)", sect_name, sect_idx+1); + } + if (contains_1u64(coff_symbols_range, coff_sect->foff) || + (coff_sect->fsize > 0 && contains_1u64(coff_symbols_range, coff_sect->foff + coff_sect->fsize-1))) { + lnk_error_with_loc(LNK_Error_IllData, cached_path, cached_lib_path, "header (%S No. %#llx) defines out of bounds section data (file offsets point into symbol table)", sect_name, sect_idx+1); + } + if (dim_1u64(range) != coff_sect->fsize) { + lnk_error_with_loc(LNK_Error_IllData, cached_path, cached_lib_path, "header (%S No. %#llx) defines out of bounds section data", sect_name, sect_idx+1); + } + } else { + data = str8_zero(); + } } LNK_Chunk *chunk = &chunk_arr[sect_idx]; @@ -418,10 +436,11 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) } // convert from coff - B32 is_big_obj = coff_info.type == COFF_DataType_BIG_OBJ; - LNK_SymbolArray symbol_arr = lnk_symbol_array_from_coff(arena, input->data, obj, cached_path, is_big_obj, function_pad_min, coff_info.string_table_off, coff_info.section_count_no_null, coff_sect_arr, coff_info.symbol_count, coff_symbols, chunk_ptr_arr, master_common_block); - LNK_SymbolList symbol_list = lnk_symbol_list_from_array(arena, symbol_arr); - LNK_RelocList *reloc_list_arr = lnk_reloc_list_array_from_coff(arena, coff_info.machine, input->data, coff_info.section_count_no_null, coff_sect_arr, chunk_ptr_arr, symbol_arr); + B32 is_big_obj = coff_info.type == COFF_DataType_BIG_OBJ; + LNK_SymbolArray symbol_arr = lnk_symbol_array_from_coff(arena, input->data, obj, cached_path, is_big_obj, function_pad_min, coff_info.string_table_off, coff_info.section_count_no_null, coff_sect_arr, coff_info.symbol_count, coff_symbols, chunk_ptr_arr, master_common_block); + LNK_SymbolList symbol_list = lnk_symbol_list_from_array(arena, symbol_arr); + LNK_RelocList *reloc_list_arr = lnk_reloc_list_array_from_coff(arena, coff_info.machine, input->data, coff_info.section_count_no_null, coff_sect_arr, chunk_ptr_arr, symbol_arr); + LNK_DirectiveInfo directive_info = lnk_directive_info_from_sections(arena, cached_path, cached_lib_path, coff_info.section_count_no_null, reloc_list_arr, sect_name_arr, chunk_arr); // fill out obj obj->data = input->data; @@ -436,12 +455,12 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) obj->chunk_arr = chunk_ptr_arr; obj->symbol_list = symbol_list; obj->sect_reloc_list_arr = reloc_list_arr; - obj->directive_info = lnk_init_directives(arena, cached_path, coff_info.section_count_no_null, sect_name_arr, chunk_arr); + obj->directive_info = directive_info; // parse exports LNK_ExportParseList export_parse = {0}; for (LNK_Directive *dir = obj->directive_info.v[LNK_CmdSwitch_Export].first; dir != 0; dir = dir->next) { - lnk_parse_export_direcive(arena, &obj->export_parse, dir->value_list, obj); + lnk_parse_export_directive(arena, &obj->export_parse, dir->value_list, obj->path, obj->lib_path); } // push /export symbols @@ -1029,30 +1048,108 @@ lnk_reloc_list_array_from_coff(Arena *arena, COFF_MachineType machine, String8 c return reloc_list_arr; } +internal void +lnk_parse_msvc_linker_directive(Arena *arena, String8 obj_path, String8 lib_path, LNK_DirectiveInfo *directive_info, String8 buffer) +{ + Temp scratch = scratch_begin(&arena, 1); + + local_persist B32 init_table = 1; + local_persist B8 is_legal[LNK_CmdSwitch_Count]; + if (init_table) { + init_table = 0; + is_legal[LNK_CmdSwitch_AlternateName] = 1; + is_legal[LNK_CmdSwitch_DefaultLib] = 1; + is_legal[LNK_CmdSwitch_DisallowLib] = 1; + is_legal[LNK_CmdSwitch_EditAndContinue] = 1; + is_legal[LNK_CmdSwitch_Entry] = 1; + is_legal[LNK_CmdSwitch_Export] = 1; + is_legal[LNK_CmdSwitch_FailIfMismatch] = 1; + is_legal[LNK_CmdSwitch_GuardSym] = 1; + is_legal[LNK_CmdSwitch_Include] = 1; + is_legal[LNK_CmdSwitch_InferAsanLibs] = 1; + is_legal[LNK_CmdSwitch_InferAsanLibsNo] = 1; + is_legal[LNK_CmdSwitch_ManifestDependency] = 1; + is_legal[LNK_CmdSwitch_Merge] = 1; + is_legal[LNK_CmdSwitch_NoDefaultLib] = 1; + is_legal[LNK_CmdSwitch_Release] = 1; + is_legal[LNK_CmdSwitch_Section] = 1; + is_legal[LNK_CmdSwitch_Stack] = 1; + is_legal[LNK_CmdSwitch_SubSystem] = 1; + is_legal[LNK_CmdSwitch_ThrowingNew] = 1; + } + + String8 to_parse; + { + local_persist const U8 bom_sig[] = { 0xEF, 0xBB, 0xBF }; + local_persist const U8 ascii_sig[] = { 0x20, 0x20, 0x20 }; + if (MemoryMatch(buffer.str, &bom_sig[0], sizeof(bom_sig))) { + to_parse = str8_zero(); + lnk_error_with_loc(LNK_InternalError_NotImplemented, obj_path, lib_path, "TODO: support for BOM encoding"); + } else if (MemoryMatch(buffer.str, &ascii_sig[0], sizeof(ascii_sig))) { + to_parse = str8_skip(buffer, sizeof(ascii_sig)); + } else { + to_parse = buffer; + } + } + + String8List arg_list = lnk_arg_list_parse_windows_rules(scratch.arena, to_parse); + LNK_CmdLine cmd_line = lnk_cmd_line_parse_windows_rules(scratch.arena, arg_list); + + for (LNK_CmdOption *opt = cmd_line.first_option; opt != 0; opt = opt->next) { + LNK_CmdSwitchType type = lnk_cmd_switch_type_from_string(opt->string); + + if (type == LNK_CmdSwitch_Null) { + lnk_error_with_loc(LNK_Warning_UnknownDirective, obj_path, lib_path, "unknown directive \"%S\"", opt->string); + continue; + } + if (!is_legal[type]) { + lnk_error_with_loc(LNK_Warning_IllegalDirective, obj_path, lib_path, "illegal directive \"%S\"", opt->string); + continue; + } + + LNK_Directive *directive = push_array_no_zero(arena, LNK_Directive, 1); + directive->next = 0; + directive->id = push_str8_copy(arena, opt->string); + directive->value_list = str8_list_copy(arena, &opt->value_strings); + + LNK_DirectiveList *directive_list = &directive_info->v[type]; + SLLQueuePush(directive_list->first, directive_list->last, directive); + ++directive_list->count; + } + + scratch_end(scratch); +} + internal LNK_DirectiveInfo -lnk_init_directives(Arena *arena, String8 obj_path, U64 chunk_count, String8 *sect_name_arr, LNK_Chunk *chunk_arr) +lnk_directive_info_from_sections(Arena *arena, + String8 obj_path, + String8 lib_path, + U64 chunk_count, + LNK_RelocList *reloc_list_arr, + String8 *sect_name_arr, + LNK_Chunk *chunk_arr) { LNK_DirectiveInfo directive_info = {0}; - for (U64 chunk_idx = 0; chunk_idx < chunk_count; chunk_idx += 1) { + for (U64 chunk_idx = 0; chunk_idx < chunk_count; ++chunk_idx) { String8 sect_name = sect_name_arr[chunk_idx]; - LNK_Chunk *sect_chunk = &chunk_arr[chunk_idx]; - Assert(sect_chunk->type == LNK_Chunk_Leaf); - - if (!str8_match(sect_name, str8_lit(".drectve"), 0)) { - continue; + LNK_Chunk *sect_chunk = chunk_arr + chunk_idx; + if (str8_match(sect_name, str8_lit(".drectve"), 0)) { + if (sect_chunk->type == LNK_Chunk_Leaf) { + if (sect_chunk->u.leaf.size >= 3) { + if (~sect_chunk->flags & COFF_SectionFlag_LNK_INFO) { + lnk_error_with_loc(LNK_Warning_IllData, obj_path, lib_path, "%S missing COFF_SectionFlag_LNK_INFO", sect_name); + } + if (reloc_list_arr[chunk_idx].count > 0) { + lnk_error_with_loc(LNK_Warning_DirectiveSectionWithRelocs, obj_path, lib_path, "directive section %S(%#x) has relocations", sect_name, (chunk_idx+1)); + } + lnk_parse_msvc_linker_directive(arena, obj_path, lib_path, &directive_info, sect_chunk->u.leaf); + } else { + lnk_error_with_loc(LNK_Warning_IllData, obj_path, lib_path, "unable to parse %S", sect_name); + } + } else { + Assert(!"linker directive section chunk must be of leaf type"); + } } - if (sect_chunk->u.leaf.size < 3) { - lnk_error(LNK_Warning_IllData, "%S: can't parse %S", obj_path, sect_name); - continue; - } - if (~sect_chunk->flags & COFF_SectionFlag_LNK_INFO) { - lnk_error(LNK_Warning_IllData, "%S: %S missing COFF_SectionFlag_LNK_INFO.", obj_path, sect_name); - } - - // TODO: warn if section has relocations - - lnk_parse_directives(arena, &directive_info, sect_chunk->u.leaf, obj_path); - int bad_vs = 0; (void)bad_vs; } return directive_info; } diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index ce316b0e..fa821460 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -25,6 +25,27 @@ typedef struct LNK_InputObjList //////////////////////////////// +typedef struct LNK_Directive +{ + struct LNK_Directive *next; + String8 id; + String8List value_list; +} LNK_Directive; + +typedef struct LNK_DirectiveList +{ + U64 count; + LNK_Directive *first; + LNK_Directive *last; +} LNK_DirectiveList; + +typedef struct LNK_DirectiveInfo +{ + LNK_DirectiveList v[LNK_CmdSwitch_Count]; +} LNK_DirectiveInfo; + +//////////////////////////////// + #define LNK_MakeChunkInputIdx(obj_idx, sect_idx) (((U64)(obj_idx) << 32) | (U64)((sect_idx) & max_U32)) typedef struct LNK_Obj @@ -181,10 +202,10 @@ internal LNK_ChunkList * lnk_collect_obj_chunks(TP_Context *tp, TP_Arena *arena internal LNK_ObjNodeArray lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *tp_arena, LNK_ObjList *obj_list, LNK_SectionTable *st, U64 *function_pad_min, U64 input_count, LNK_InputObj **inputs); internal LNK_Chunk * lnk_sect_chunk_array_from_coff(Arena *arena, U64 obj_id, String8 obj_path, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, String8 *sect_name_arr, String8 *sect_postfix_arr); -internal LNK_SymbolArray lnk_symbol_array_from_coff(Arena *arena, String8 coff_data, LNK_Obj *obj, String8 obj_path, B32 is_big_obj, U64 function_pad_min, U64 string_table_off, U64 sect_count, COFF_SectionHeader *coff_sect_arr, U64 coff_symbol_count, void *coff_symbols, LNK_ChunkPtr *chunk_ptr_arr, LNK_Chunk *master_common_block); +internal LNK_SymbolArray lnk_symbol_array_from_coff(Arena *arena, String8 coff_data, LNK_Obj *obj, String8 obj_path, String8 lib_path, B32 is_big_obj, U64 function_pad_min, U64 string_table_off, U64 sect_count, COFF_SectionHeader *coff_sect_arr, U64 coff_symbol_count, void *coff_symbols, LNK_ChunkPtr *chunk_ptr_arr, LNK_Chunk *master_common_block); internal LNK_RelocList lnk_reloc_list_from_coff_reloc_array(Arena *arena, COFF_MachineType machine, LNK_Chunk *chunk, LNK_SymbolArray symbol_array, COFF_Reloc *reloc_v, U64 reloc_count); internal LNK_RelocList * lnk_reloc_list_array_from_coff(Arena *arena, COFF_MachineType machine, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, LNK_ChunkPtr *chunk_ptr_arr, LNK_SymbolArray symbol_array); -internal LNK_DirectiveInfo lnk_init_directives(Arena *arena, String8 obj_path, U64 chunk_count, String8 *sect_name_arr, LNK_Chunk *chunk_arr); +internal LNK_DirectiveInfo lnk_directive_info_from_sections(Arena *arena, String8 obj_path, String8 lib_path, U64 chunk_count, LNK_RelocList *reloc_list_arr, String8 *sect_name_arr, LNK_Chunk *chunk_arr); internal U32 lnk_obj_get_features(LNK_Obj *obj); internal U32 lnk_obj_get_comp_id(LNK_Obj *obj);