mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-07 22:38:41 +00:00
add DEF, GUARD, and SECTION config support
Parse /DEF, /GUARD, and /SECTION switches, including DEF handling for NAME/LIBRARY, BASE, VERSION, HEAPSIZE, STACKSIZE, EXPORTS, and SECTIONS. Store section directives in linker config and apply them consistently when gathering object sections when pushing/searching image sections. Pull load-config symbols for /GUARD without setting GUARD_CF until guard tables are emitted. Add coverage for DEF file parity, section directives, and guard load-config handling.
This commit is contained in:
committed by
Ryan Fleury
parent
35722f2a01
commit
b77c958b5f
+46
-23
@@ -2008,12 +2008,13 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer
|
||||
lnk_inputer_push_lib_thin(inputer, config, LNK_InputSource_CmdLine, (*link->last_cmd_lib)->string);
|
||||
}
|
||||
|
||||
if (config->guard_flags != LNK_Guard_None) {
|
||||
lnk_include_symbol(config, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME), 0);
|
||||
}
|
||||
|
||||
// link inputer
|
||||
lnk_link_inputs(tp, arena, config, inputer, symtab, link);
|
||||
|
||||
// TODO: need to figure out under what condition to include load config
|
||||
//lnk_include_symbol(config, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME), 0);
|
||||
|
||||
{
|
||||
ProfBegin("Push Linker Symbols");
|
||||
String8 linker_symbols_obj = lnk_make_linker_obj(arena->v[0], config);
|
||||
@@ -3424,8 +3425,10 @@ THREAD_POOL_TASK_FUNC(lnk_gather_section_definitions_task)
|
||||
Temp temp = temp_begin(scratch.arena);
|
||||
|
||||
// was section defined?
|
||||
COFF_SectionFlags image_sect_flags = sect_flags & ~(COFF_SectionFlags_LnkFlags | COFF_SectionFlags_Reserved);
|
||||
String8 sect_name = coff_name_from_section_header(string_table, sect_header);
|
||||
String8 sect_name_with_flags = lnk_make_name_with_flags(temp.arena, sect_name, sect_flags & ~COFF_SectionFlags_LnkFlags);
|
||||
image_sect_flags = lnk_apply_section_directives_to_flags(task->config, sect_name, image_sect_flags);
|
||||
String8 sect_name_with_flags = lnk_make_name_with_flags(temp.arena, sect_name, image_sect_flags);
|
||||
LNK_SectionDefinition *sect_defn = hash_table_search_string_raw(sect_defn_ht, sect_name_with_flags);
|
||||
|
||||
// push new section definition
|
||||
@@ -3434,7 +3437,7 @@ THREAD_POOL_TASK_FUNC(lnk_gather_section_definitions_task)
|
||||
sect_defn->name = sect_name;
|
||||
sect_defn->obj = obj;
|
||||
sect_defn->obj_sect_idx = sect_idx;
|
||||
sect_defn->flags = sect_flags & ~COFF_SectionFlags_LnkFlags;
|
||||
sect_defn->flags = image_sect_flags;
|
||||
|
||||
sect_name_with_flags = push_str8_copy(arena, sect_name_with_flags);
|
||||
hash_table_push_string_raw(arena, sect_defn_ht, sect_name_with_flags, sect_defn);
|
||||
@@ -3471,9 +3474,11 @@ THREAD_POOL_TASK_FUNC(lnk_gather_section_contribs_task)
|
||||
LNK_SectionContribChunk *sc_chunk = 0;
|
||||
{
|
||||
Temp temp = temp_begin(scratch.arena);
|
||||
String8 sect_name = coff_name_from_section_header(string_table, sect_header);
|
||||
String8 sect_name_with_flags = lnk_make_name_with_flags(temp.arena, sect_name, sect_flags & ~COFF_SectionFlags_LnkFlags);
|
||||
sc_chunk = hash_table_search_string_raw(task->contribs_ht, sect_name_with_flags);
|
||||
COFF_SectionFlags sect_flags_clean = sect_flags & ~(COFF_SectionFlags_LnkFlags | COFF_SectionFlags_Reserved);
|
||||
String8 sect_name = coff_name_from_section_header(string_table, sect_header);
|
||||
sect_flags_clean = lnk_apply_section_directives_to_flags(task->config, sect_name, sect_flags_clean);
|
||||
String8 sect_key = lnk_make_name_with_flags(temp.arena, sect_name, sect_flags_clean);
|
||||
sc_chunk = hash_table_search_string_raw(task->contribs_ht, sect_key);
|
||||
temp_end(temp);
|
||||
}
|
||||
|
||||
@@ -5005,6 +5010,22 @@ lnk_build_win32_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config
|
||||
return result;
|
||||
}
|
||||
|
||||
internal LNK_Section *
|
||||
lnk_image_section_table_push(LNK_Config *config, LNK_SectionTable *sectab, String8 name, COFF_SectionFlags flags)
|
||||
{
|
||||
flags = lnk_apply_section_directives_to_flags(config, name, flags);
|
||||
LNK_Section *sect = lnk_section_table_push(sectab, name, flags);
|
||||
return sect;
|
||||
}
|
||||
|
||||
internal LNK_Section *
|
||||
lnk_image_section_table_search(LNK_Config *config, LNK_SectionTable *sectab, String8 name, COFF_SectionFlags flags)
|
||||
{
|
||||
flags = lnk_apply_section_directives_to_flags(config, name, flags);
|
||||
LNK_Section *sect = lnk_section_table_search(sectab, name, flags);
|
||||
return sect;
|
||||
}
|
||||
|
||||
internal LNK_ImageContext
|
||||
lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs)
|
||||
{
|
||||
@@ -5017,14 +5038,15 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
// init section table
|
||||
//
|
||||
LNK_SectionTable *sectab = lnk_section_table_alloc();
|
||||
lnk_section_table_push(sectab, str8_lit(".text" ), PE_TEXT_SECTION_FLAGS );
|
||||
lnk_section_table_push(sectab, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS);
|
||||
lnk_section_table_push(sectab, str8_lit(".data" ), PE_DATA_SECTION_FLAGS );
|
||||
lnk_section_table_push(sectab, str8_lit(".bss" ), PE_BSS_SECTION_FLAGS );
|
||||
lnk_section_table_push(sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS);
|
||||
LNK_Section *common_block_sect = lnk_section_table_search(sectab, str8_lit(".bss"), PE_BSS_SECTION_FLAGS);
|
||||
lnk_image_section_table_push(config, sectab, str8_lit(".text" ), PE_TEXT_SECTION_FLAGS );
|
||||
lnk_image_section_table_push(config, sectab, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS);
|
||||
lnk_image_section_table_push(config, sectab, str8_lit(".data" ), PE_DATA_SECTION_FLAGS );
|
||||
lnk_image_section_table_push(config, sectab, str8_lit(".bss" ), PE_BSS_SECTION_FLAGS );
|
||||
lnk_image_section_table_push(config, sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS);
|
||||
LNK_Section *common_block_sect = lnk_image_section_table_search(config, sectab, str8_lit(".bss"), PE_BSS_SECTION_FLAGS);
|
||||
|
||||
LNK_BuildImageTask task = {
|
||||
.config = config,
|
||||
.symtab = symtab,
|
||||
.sectab = sectab,
|
||||
.objs_count = objs_count,
|
||||
@@ -5322,7 +5344,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
if (~config->flags & LNK_ConfigFlag_Fixed) {
|
||||
String8 base_relocs_data = lnk_build_base_relocs(tp, arena, config, objs_count, objs);
|
||||
if (base_relocs_data.size) {
|
||||
LNK_Section *reloc = lnk_section_table_push(sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS);
|
||||
LNK_Section *reloc = lnk_image_section_table_push(config, sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS);
|
||||
LNK_SectionContribChunk *first_sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &reloc->contribs, 1, str8_zero());
|
||||
LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(first_sc_chunk, 1);
|
||||
sc->first_data_node.string = base_relocs_data;
|
||||
@@ -5454,6 +5476,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
U32 load_config_size = 0;
|
||||
if (sizeof(load_config_size) <= load_config_data.size) {
|
||||
MemoryCopyStruct(&load_config_size, load_config_data.str); // TODO: load config
|
||||
PE_DataDirectory *load_config_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_LOAD_CONFIG);
|
||||
load_config_dir->virt_off = lnk_voff_from_symbol(image_section_table, load_config_symbol);
|
||||
load_config_dir->virt_size = load_config_size;
|
||||
@@ -5465,7 +5488,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
// patch exceptions
|
||||
{
|
||||
LNK_Section *pdata_sect = lnk_section_table_search(sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS);
|
||||
LNK_Section *pdata_sect = lnk_image_section_table_search(config, sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS);
|
||||
if (pdata_sect) {
|
||||
String8 raw_pdata = str8_substr(image_data, rng_1u64(pdata_sect->foff, pdata_sect->foff + pdata_sect->vsize));
|
||||
pe_pdata_sort(config->machine, raw_pdata);
|
||||
@@ -5478,7 +5501,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
// patch export
|
||||
{
|
||||
LNK_Section *edata_sect = lnk_section_table_search(sectab, str8_lit(".edata"), PE_EDATA_SECTION_FLAGS);
|
||||
LNK_Section *edata_sect = lnk_image_section_table_search(config, sectab, str8_lit(".edata"), PE_EDATA_SECTION_FLAGS);
|
||||
if (edata_sect) {
|
||||
PE_DataDirectory *export_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_EXPORT);
|
||||
LNK_SectionContrib *edata_first_contrib = lnk_get_first_section_contrib(edata_sect);
|
||||
@@ -5490,7 +5513,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
// patch base relocs
|
||||
{
|
||||
LNK_Section *reloc_sect = lnk_section_table_search(sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS);
|
||||
LNK_Section *reloc_sect = lnk_image_section_table_search(config, sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS);
|
||||
if (reloc_sect) {
|
||||
PE_DataDirectory *reloc_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_BASE_RELOC);
|
||||
reloc_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, reloc_sect);
|
||||
@@ -5500,7 +5523,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
// patch import and import addr
|
||||
{
|
||||
LNK_Section *idata_sect = lnk_section_table_search(sectab, str8_lit(".idata"), PE_IDATA_SECTION_FLAGS);
|
||||
LNK_Section *idata_sect = lnk_image_section_table_search(config, sectab, str8_lit(".idata"), PE_IDATA_SECTION_FLAGS);
|
||||
LNK_Symbol *null_import_desc = lnk_symbol_table_searchf(symtab, "__NULL_IMPORT_DESCRIPTOR");
|
||||
LNK_Symbol *null_thunk_data = lnk_symbol_table_searchf(symtab, "\x7f%S_NULL_THUNK_DATA", lnk_get_image_name(config));
|
||||
if (idata_sect && null_import_desc && null_thunk_data) {
|
||||
@@ -5522,7 +5545,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
// patch delay imports
|
||||
{
|
||||
LNK_Section *didat_sect = lnk_section_table_search(sectab, str8_lit(".didat"), PE_IDATA_SECTION_FLAGS);
|
||||
LNK_Section *didat_sect = lnk_image_section_table_search(config, sectab, str8_lit(".didat"), PE_IDATA_SECTION_FLAGS);
|
||||
LNK_Symbol *null_import_desc = lnk_symbol_table_search(symtab, str8_lit("__NULL_DELAY_IMPORT_DESCRIPTOR"));
|
||||
LNK_Symbol *last_null_thunk = lnk_symbol_table_searchf(symtab,"\x7f%S_NULL_THUNK_DATA_DLA", lnk_get_image_name(config));
|
||||
if (didat_sect && null_import_desc && last_null_thunk) {
|
||||
@@ -5542,7 +5565,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
// find max align in .tls
|
||||
U64 tls_align = 0;
|
||||
LNK_Section *tls_sect = lnk_section_table_search(sectab, str8_lit(".tls"), PE_TLS_SECTION_FLAGS);
|
||||
LNK_Section *tls_sect = lnk_image_section_table_search(config, sectab, str8_lit(".tls"), PE_TLS_SECTION_FLAGS);
|
||||
for (LNK_SectionContribChunk *sc_chunk = tls_sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) {
|
||||
for EachIndex (sc_idx, sc_chunk->count) {
|
||||
Assert(IsPow2(sc_chunk->v[sc_idx]->align));
|
||||
@@ -5572,7 +5595,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
// patch debug
|
||||
{
|
||||
LNK_Section *debug_dir_sect = lnk_section_table_search(sectab, str8_lit(".RAD_LINK_PE_DEBUG_DIR"), PE_RDATA_SECTION_FLAGS);
|
||||
LNK_Section *debug_dir_sect = lnk_image_section_table_search(config, sectab, str8_lit(".RAD_LINK_PE_DEBUG_DIR"), PE_RDATA_SECTION_FLAGS);
|
||||
if (debug_dir_sect) {
|
||||
// patch directory
|
||||
PE_DataDirectory *debug_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_DEBUG);
|
||||
@@ -5599,7 +5622,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
|
||||
// patch resources
|
||||
{
|
||||
LNK_Section *rsrc_sect = lnk_section_table_search(sectab, str8_lit(".rsrc"), PE_RSRC_SECTION_FLAGS);
|
||||
LNK_Section *rsrc_sect = lnk_image_section_table_search(config, sectab, str8_lit(".rsrc"), PE_RSRC_SECTION_FLAGS);
|
||||
if (rsrc_sect) {
|
||||
PE_DataDirectory *rsrc_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_RESOURCES);
|
||||
rsrc_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, rsrc_sect);
|
||||
|
||||
@@ -261,6 +261,7 @@ typedef struct LNK_ImageFillNode
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LNK_Config *config;
|
||||
LNK_SymbolTable *symtab;
|
||||
LNK_SectionTable *sectab;
|
||||
U64 objs_count;
|
||||
|
||||
+550
-39
@@ -14,6 +14,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
|
||||
{ LNK_CmdSwitch_Brepro, 0, "BREPRO", "", "No support." },
|
||||
{ LNK_CmdSwitch_Debug, 0, "DEBUG", "[:{FULL|NONE}]", "Controls debug info level." },
|
||||
{ LNK_CmdSwitch_DefaultLib, 1, "DEFAULTLIB", ":LIBNAME", "Set default library." },
|
||||
{ LNK_CmdSwitch_Def, 1, "DEF", ":FILENAME", "Read exports from a module-definition file." },
|
||||
{ LNK_CmdSwitch_Delay, 0, "DELAY", ":{NOBIND|UNLOAD}", "Controls emission of unload and bind tables." },
|
||||
{ LNK_CmdSwitch_DelayLoad, 0, "DELAYLOAD", ":DLL", "Delay load DLL." },
|
||||
{ LNK_CmdSwitch_Dll, 0, "DLL", "", "Link to a DLL." },
|
||||
@@ -26,6 +27,8 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
|
||||
{ LNK_CmdSwitch_Fixed, 0, "FIXED", "[:NO]", "Load the image at the default base address." },
|
||||
{ LNK_CmdSwitch_Force, 0, "FORCE", "", "Force image output despite errors." },
|
||||
{ LNK_CmdSwitch_FunctionPadMin, 0, "FUNCTIONPADMIN", ":#", "Minimum function byte size." },
|
||||
{ LNK_CmdSwitch_Guard, 0, "GUARD", ":{CF|NO|LONGJMP|EHCONT}", "Controls Control Flow Guard metadata." },
|
||||
{ LNK_CmdSwitch_GuardSym, 1, "GUARDSYM", ":SYMBOL,S", "MSVC guard symbol directive." },
|
||||
{ LNK_CmdSwitch_Heap, 0, "HEAP", "RESERVE[,COMMIT]", "Set reserve and commit size for the heap." },
|
||||
{ LNK_CmdSwitch_HighEntropyVa, 0, "HIGHENTROPYVA", "[:NO]", "Indicate that image supports full 64-bit address space ASLR." },
|
||||
{ LNK_CmdSwitch_Ignore, 0, "IGNORE", ":#", "Ignore a warning." },
|
||||
@@ -56,6 +59,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
|
||||
{ LNK_CmdSwitch_PdbPageSize, 0, "PDBPAGESIZE", ":#", "Page size must be power of two." },
|
||||
{ LNK_CmdSwitch_PdbStripped, 0, "PDBSTRIPPED", ":FILENAME", "Create a stripped PDB containing public symbols, a section map, and a list of object files." },
|
||||
{ LNK_CmdSwitch_Release, 1, "RELEASE", "", "Write image checksum." },
|
||||
{ LNK_CmdSwitch_Section, 1, "SECTION", ":NAME,ATTRS", "Set output section attributes." },
|
||||
{ LNK_CmdSwitch_Stack, 1, "STACK", ":RESERVE[,COMMIT]", "Set reserve and commit size for the stack." },
|
||||
{ LNK_CmdSwitch_SubSystem, 1, "SUBSYSTEM", ":{CONSOLE|NATIVE|WINDOWS}[,#[.##]]", "Set subsystem for the image." },
|
||||
{ LNK_CmdSwitch_TsAware, 0, "TSAWARE", "[:NO]", "Image is terminal server aware." },
|
||||
@@ -715,14 +719,51 @@ internal String8
|
||||
lnk_text_file_string_from_data(Arena *arena, String8 data)
|
||||
{
|
||||
String8 result = data;
|
||||
|
||||
if (data.size >= 2 && data.str[0] == 0xff && data.str[1] == 0xfe) {
|
||||
// decode UTF-16LE BOM
|
||||
result = str8_from_16(arena, str16((U16 *)(data.str + 2), (data.size - 2) / sizeof(U16)));
|
||||
} else if (data.size >= 3 && data.str[0] == 0xef && data.str[1] == 0xbb && data.str[2] == 0xbf) {
|
||||
// strip UTF-8 BOM
|
||||
result = str8_skip(data, 3);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
lnk_push_export_to_config(LNK_Config *config, LNK_Obj *obj, PE_ExportParse export_parse)
|
||||
{
|
||||
// lookup existing export
|
||||
String8 export_name = pe_name_from_export_parse(&export_parse);
|
||||
PE_ExportParseNode *exp_n = hash_map_search_string_raw(&config->export_ht, export_name);
|
||||
|
||||
if (exp_n == 0) {
|
||||
// push new export
|
||||
if (!export_parse.is_forwarder) {
|
||||
lnk_include_symbol(config, export_parse.name, 0);
|
||||
}
|
||||
|
||||
exp_n = pe_export_parse_list_push(config->arena, &config->export_symbol_list, export_parse);
|
||||
hash_map_push_string_raw(config->arena, &config->export_ht, export_name, exp_n);
|
||||
} else {
|
||||
// merge duplicate export
|
||||
PE_ExportParse *extant_export = &exp_n->data;
|
||||
B32 alias_conflict = extant_export->alias.size &&
|
||||
export_parse.alias.size &&
|
||||
!str8_match(extant_export->alias, export_parse.alias, 0);
|
||||
B32 ordinal_conflict = extant_export->ordinal != export_parse.ordinal;
|
||||
|
||||
if (alias_conflict || ordinal_conflict) {
|
||||
lnk_error_obj(LNK_Error_IllExport, obj, "ambiguous symbol export %S", export_parse.name);
|
||||
}
|
||||
|
||||
if (!alias_conflict && !ordinal_conflict && extant_export->alias.size == 0 && export_parse.alias.size != 0) {
|
||||
extant_export->alias = export_parse.alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal B32
|
||||
lnk_parse_merge_directive(String8 string, LNK_Obj *obj, LNK_MergeDirective *out)
|
||||
{
|
||||
@@ -740,6 +781,43 @@ lnk_parse_merge_directive(String8 string, LNK_Obj *obj, LNK_MergeDirective *out)
|
||||
return is_parse_ok;
|
||||
}
|
||||
|
||||
typedef struct LNK_SectionDirectiveAttr
|
||||
{
|
||||
U8 code;
|
||||
COFF_SectionFlags flag;
|
||||
B32 is_mem_attr;
|
||||
B32 negated_sets;
|
||||
} LNK_SectionDirectiveAttr;
|
||||
|
||||
global read_only LNK_SectionDirectiveAttr g_section_directive_attr_map[] =
|
||||
{
|
||||
{ 'D', COFF_SectionFlag_MemDiscardable, 0, 0 },
|
||||
{ 'E', COFF_SectionFlag_MemExecute, 1, 0 },
|
||||
{ 'K', COFF_SectionFlag_MemNotCached, 0, 1 },
|
||||
{ 'P', COFF_SectionFlag_MemNotPaged, 0, 1 },
|
||||
{ 'R', COFF_SectionFlag_MemRead, 1, 0 },
|
||||
{ 'S', COFF_SectionFlag_MemShared, 0, 0 },
|
||||
{ 'W', COFF_SectionFlag_MemWrite, 1, 0 },
|
||||
};
|
||||
|
||||
typedef struct LNK_GuardOption
|
||||
{
|
||||
String8 name;
|
||||
LNK_GuardFlags set_flags;
|
||||
LNK_GuardFlags clear_flags;
|
||||
} LNK_GuardOption;
|
||||
|
||||
global read_only LNK_GuardOption g_guard_option_table[] =
|
||||
{
|
||||
{ str8_lit_comp("cf"), LNK_Guard_Cf, 0 },
|
||||
{ str8_lit_comp("nocf"), 0, LNK_Guard_Cf },
|
||||
{ str8_lit_comp("longjmp"), LNK_Guard_LongJmp, 0 },
|
||||
{ str8_lit_comp("nolongjmp"), 0, LNK_Guard_LongJmp },
|
||||
{ str8_lit_comp("ehcont"), LNK_Guard_EhCont, 0 },
|
||||
{ str8_lit_comp("noehcont"), 0, LNK_Guard_EhCont },
|
||||
{ str8_lit_comp("no"), 0, LNK_Guard_All },
|
||||
};
|
||||
|
||||
internal LNK_AltNameNode *
|
||||
lnk_alt_name_list_push(Arena *arena, LNK_AltNameList *list, LNK_AltName v)
|
||||
{
|
||||
@@ -760,6 +838,24 @@ lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_Me
|
||||
return node;
|
||||
}
|
||||
|
||||
internal COFF_SectionFlags
|
||||
lnk_apply_section_directives_to_flags(LNK_Config *config, String8 full_section_name, COFF_SectionFlags flags)
|
||||
{
|
||||
String8 section_name = {0};
|
||||
String8 sort_idx = {0};
|
||||
coff_parse_section_name(full_section_name, §ion_name, &sort_idx);
|
||||
|
||||
for EachNode(dir_n, LNK_SectionDirectiveNode, config->section_list.first) {
|
||||
LNK_SectionDirective *dir = &dir_n->v;
|
||||
if (str8_match(dir->name, section_name, 0)) {
|
||||
flags &= ~dir->clear_flags;
|
||||
flags |= dir->set_flags;
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
internal String8
|
||||
lnk_get_image_name(LNK_Config *config)
|
||||
{
|
||||
@@ -1148,6 +1244,8 @@ lnk_apply_write_temp_files(Arena *arena, LNK_Config *config)
|
||||
}
|
||||
}
|
||||
|
||||
internal void lnk_apply_def_file_to_config(LNK_Config *config, String8 path, LNK_Obj *obj);
|
||||
|
||||
internal void
|
||||
lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List value_strings, LNK_Obj *obj)
|
||||
{
|
||||
@@ -1266,6 +1364,13 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Def: {
|
||||
String8 path = {0};
|
||||
if (lnk_cmd_switch_parse_string(obj, cmd_switch, value_strings, &path)) {
|
||||
lnk_apply_def_file_to_config(config, path, obj);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Delay: {
|
||||
if (value_strings.node_count == 0 || value_strings.node_count > 1) {
|
||||
lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
|
||||
@@ -1313,42 +1418,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
|
||||
case LNK_CmdSwitch_Export: {
|
||||
PE_ExportParse export_parse = {0};
|
||||
if (lnk_parse_export_directive_ex(config->arena, value_strings, obj, &export_parse)) {
|
||||
String8 export_name = pe_name_from_export_parse(&export_parse);
|
||||
PE_ExportParseNode *exp_n = hash_map_search_string_raw(&config->export_ht, export_name);
|
||||
|
||||
if (exp_n == 0) {
|
||||
// make sure export is defined
|
||||
if (!export_parse.is_forwarder) {
|
||||
lnk_include_symbol(config, export_parse.name, 0);
|
||||
}
|
||||
|
||||
// push new export
|
||||
exp_n = pe_export_parse_list_push(config->arena, &config->export_symbol_list, export_parse);
|
||||
|
||||
hash_map_push_string_raw(config->arena, &config->export_ht, export_name, exp_n);
|
||||
} else {
|
||||
B32 is_ambiguous = 1;
|
||||
PE_ExportParse *extant_export = &exp_n->data;
|
||||
|
||||
if (extant_export->alias.size && export_parse.alias.size && !str8_match(extant_export->alias, export_parse.alias, 0)) {
|
||||
goto report;
|
||||
}
|
||||
|
||||
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_obj(LNK_Error_IllExport, obj, "ambiguous symbol export %S", export_parse.name);
|
||||
}
|
||||
}
|
||||
lnk_push_export_to_config(config, obj, export_parse);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1400,6 +1470,25 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
|
||||
config->do_function_pad_min = LNK_SwitchState_Yes;
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Guard: {
|
||||
for EachNode(n, String8Node, value_strings.first) {
|
||||
LNK_GuardOption *option = 0;
|
||||
for EachElement(i, g_guard_option_table) {
|
||||
if (str8_matchi(g_guard_option_table[i].name, n->string)) {
|
||||
option = &g_guard_option_table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (option != 0) {
|
||||
config->guard_flags &= ~option->clear_flags;
|
||||
config->guard_flags |= option->set_flags;
|
||||
} else {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unknown option \"%S\"", n->string);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Heap: {
|
||||
Rng1U64 reserve_commit;
|
||||
reserve_commit.v[0] = config->heap_reserve;
|
||||
@@ -1744,6 +1833,101 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Section: {
|
||||
LNK_SectionDirective section_dir = {0};
|
||||
B32 is_parse_ok = 1;
|
||||
|
||||
if (value_strings.node_count < 2) {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, LNK_CmdSwitch_Section, "expected section name and attributes");
|
||||
is_parse_ok = 0;
|
||||
} else {
|
||||
section_dir.name = value_strings.first->string;
|
||||
|
||||
B32 has_attr = 0;
|
||||
B32 has_mem_attr = 0;
|
||||
for (String8Node *param_n = value_strings.first->next; param_n != 0; param_n = param_n->next) {
|
||||
String8 param = param_n->string;
|
||||
|
||||
if (str8_match_lit("ALIGN=", param, StringMatchFlag_CaseInsensitive|StringMatchFlag_RightSideSloppy)) {
|
||||
String8 align_string = str8_skip(param, sizeof("ALIGN=") - 1);
|
||||
U64 align = 0;
|
||||
if (try_u64_from_str8_c_rules(align_string, &align)) {
|
||||
COFF_SectionFlags align_flag = coff_section_flag_from_align_size(align);
|
||||
if (align_flag) {
|
||||
COFF_SectionFlags align_mask = (COFF_SectionFlag_AlignMask << COFF_SectionFlag_AlignShift);
|
||||
section_dir.clear_flags |= align_mask;
|
||||
section_dir.set_flags &= ~align_mask;
|
||||
section_dir.set_flags |= align_flag;
|
||||
section_dir.clear_flags &= ~align_flag;
|
||||
} else {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, LNK_CmdSwitch_Section, "invalid alignment \"%S\"", align_string);
|
||||
is_parse_ok = 0;
|
||||
}
|
||||
} else {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, LNK_CmdSwitch_Section, "unable to parse alignment \"%S\"", align_string);
|
||||
is_parse_ok = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
B32 negate = 0;
|
||||
for EachIndex(i, param.size) {
|
||||
U8 c = upper_from_char(param.str[i]);
|
||||
if (c == '!') {
|
||||
negate = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
LNK_SectionDirectiveAttr *attr = 0;
|
||||
for EachElement(attr_idx, g_section_directive_attr_map) {
|
||||
if (g_section_directive_attr_map[attr_idx].code == c) {
|
||||
attr = &g_section_directive_attr_map[attr_idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (attr == 0) {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, LNK_CmdSwitch_Section, "unknown section attribute '%c' in \"%S\"", c, param);
|
||||
is_parse_ok = 0;
|
||||
} else {
|
||||
has_attr = 1;
|
||||
has_mem_attr = has_mem_attr || attr->is_mem_attr;
|
||||
|
||||
COFF_SectionFlags flags = (negate == attr->negated_sets) ? attr->flag : 0;
|
||||
section_dir.clear_flags |= attr->flag;
|
||||
section_dir.set_flags &= ~attr->flag;
|
||||
section_dir.set_flags |= flags;
|
||||
section_dir.clear_flags &= ~flags;
|
||||
}
|
||||
|
||||
negate = 0;
|
||||
}
|
||||
|
||||
if (negate) {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, LNK_CmdSwitch_Section, "dangling '!' in \"%S\"", param);
|
||||
is_parse_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_attr) {
|
||||
section_dir.clear_flags |= COFF_SectionFlag_MemDiscardable|COFF_SectionFlag_MemNotCached|COFF_SectionFlag_MemNotPaged|COFF_SectionFlag_MemShared;
|
||||
if (has_mem_attr) {
|
||||
section_dir.clear_flags |= COFF_SectionFlag_MemExecute|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite;
|
||||
}
|
||||
section_dir.clear_flags &= ~section_dir.set_flags;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_parse_ok) {
|
||||
section_dir.name = push_str8_copy(config->arena, section_dir.name);
|
||||
|
||||
LNK_SectionDirectiveNode *node = push_array_no_zero(config->arena, LNK_SectionDirectiveNode, 1);
|
||||
node->v = section_dir;
|
||||
SLLQueuePush(config->section_list.first, config->section_list.last, node);
|
||||
config->section_list.count += 1;
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Stack: {
|
||||
Rng1U64 reserve_commit;
|
||||
reserve_commit.v[0] = config->stack_reserve;
|
||||
@@ -2204,6 +2388,331 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
typedef enum LNK_DefFileStmt
|
||||
{
|
||||
LNK_DefFileStmt_Null,
|
||||
LNK_DefFileStmt_Description,
|
||||
LNK_DefFileStmt_Exports,
|
||||
LNK_DefFileStmt_HeapSize,
|
||||
LNK_DefFileStmt_Imports,
|
||||
LNK_DefFileStmt_Library,
|
||||
LNK_DefFileStmt_Name,
|
||||
LNK_DefFileStmt_Sections,
|
||||
LNK_DefFileStmt_Segments,
|
||||
LNK_DefFileStmt_StackSize,
|
||||
LNK_DefFileStmt_Stub,
|
||||
LNK_DefFileStmt_Version,
|
||||
} LNK_DefFileStmt;
|
||||
|
||||
global read_only struct
|
||||
{
|
||||
String8 name;
|
||||
LNK_DefFileStmt stmt;
|
||||
} g_def_file_stmt_map[] =
|
||||
{
|
||||
{ str8_lit_comp("DESCRIPTION"), LNK_DefFileStmt_Description },
|
||||
{ str8_lit_comp("EXPORTS"), LNK_DefFileStmt_Exports },
|
||||
{ str8_lit_comp("HEAPSIZE"), LNK_DefFileStmt_HeapSize },
|
||||
{ str8_lit_comp("IMPORTS"), LNK_DefFileStmt_Imports },
|
||||
{ str8_lit_comp("LIBRARY"), LNK_DefFileStmt_Library },
|
||||
{ str8_lit_comp("NAME"), LNK_DefFileStmt_Name },
|
||||
{ str8_lit_comp("SECTIONS"), LNK_DefFileStmt_Sections },
|
||||
{ str8_lit_comp("SEGMENTS"), LNK_DefFileStmt_Segments },
|
||||
{ str8_lit_comp("STACKSIZE"), LNK_DefFileStmt_StackSize },
|
||||
{ str8_lit_comp("STUB"), LNK_DefFileStmt_Stub },
|
||||
{ str8_lit_comp("VERSION"), LNK_DefFileStmt_Version },
|
||||
};
|
||||
|
||||
typedef struct LNK_DefFileLineNode LNK_DefFileLineNode;
|
||||
struct LNK_DefFileLineNode
|
||||
{
|
||||
LNK_DefFileStmt stmt;
|
||||
String8 line;
|
||||
B32 is_keyword;
|
||||
LNK_DefFileLineNode *next;
|
||||
};
|
||||
|
||||
typedef struct LNK_DefFileLineList LNK_DefFileLineList;
|
||||
struct LNK_DefFileLineList
|
||||
{
|
||||
U64 count;
|
||||
LNK_DefFileLineNode *first;
|
||||
LNK_DefFileLineNode *last;
|
||||
};
|
||||
|
||||
internal String8List
|
||||
lnk_def_file_tokenize(Arena *arena, String8 line)
|
||||
{
|
||||
// tokenize with windows quoting rules
|
||||
line = push_str8_copy(arena, line);
|
||||
for EachIndex(i, line.size) {
|
||||
if (line.str[i] == '\t') {
|
||||
line.str[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
String8List tokens = lnk_arg_list_parse_windows_rules(arena, line);
|
||||
|
||||
String8List result = {0};
|
||||
for (String8Node *token_n = tokens.first; token_n != 0; token_n = token_n->next) {
|
||||
String8 token = token_n->string;
|
||||
String8Node *next_n = token_n->next;
|
||||
if (token.size == 1 && token.str[0] == '@' && next_n != 0) {
|
||||
str8_list_push(arena, &result, str8_cat(arena, token, next_n->string));
|
||||
token_n = next_n;
|
||||
} else {
|
||||
str8_list_push(arena, &result, token);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
lnk_apply_def_file_to_config(LNK_Config *config, String8 path, LNK_Obj *obj)
|
||||
{
|
||||
Temp scratch = scratch_begin(&config->arena, 1);
|
||||
|
||||
// load DEF file
|
||||
String8 raw_file = lnk_read_data_from_file_path(scratch.arena, config->io_flags, path);
|
||||
String8 file = lnk_text_file_string_from_data(scratch.arena, raw_file);
|
||||
|
||||
// parse & collect normalized DEF lines
|
||||
LNK_DefFileLineList def_lines = {0};
|
||||
LNK_DefFileStmt active_stmt = LNK_DefFileStmt_Null;
|
||||
String8 rest = file;
|
||||
while (rest.size != 0) {
|
||||
String8 line = str8_chop_line(&rest);
|
||||
|
||||
// strip comments outside quotes
|
||||
B32 in_quote = 0;
|
||||
for EachIndex(i, line.size) {
|
||||
U8 c = line.str[i];
|
||||
if (in_quote && c == '\\') {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (c == '"') {
|
||||
in_quote = !in_quote;
|
||||
} else if (c == ';' && !in_quote) {
|
||||
line = str8_prefix(line, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
line = str8_skip_chop_whitespace(line);
|
||||
if (line.size == 0) { continue; }
|
||||
|
||||
// parse statement keyword
|
||||
String8 tail = line;
|
||||
LNK_DefFileStmt stmt = LNK_DefFileStmt_Null;
|
||||
B32 is_keyword = 0;
|
||||
if (line.size != 0 && line.str[0] != '"') {
|
||||
U64 keyword_opl = 0;
|
||||
for (; keyword_opl < line.size; keyword_opl += 1) {
|
||||
U8 c = line.str[keyword_opl];
|
||||
if (char_is_space(c) || c == ':' || c == '=') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String8 keyword = str8_prefix(line, keyword_opl);
|
||||
for EachElement(i, g_def_file_stmt_map) {
|
||||
if (str8_matchi(g_def_file_stmt_map[i].name, keyword)) {
|
||||
stmt = g_def_file_stmt_map[i].stmt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (stmt != LNK_DefFileStmt_Null) {
|
||||
U64 tail_pos = keyword_opl;
|
||||
while (tail_pos < line.size && char_is_space(line.str[tail_pos])) {
|
||||
tail_pos += 1;
|
||||
}
|
||||
if (tail_pos < line.size && (line.str[tail_pos] == ':' || line.str[tail_pos] == '=')) {
|
||||
tail_pos += 1;
|
||||
}
|
||||
tail = str8_skip_chop_whitespace(str8_skip(line, tail_pos));
|
||||
}
|
||||
}
|
||||
|
||||
if (stmt != LNK_DefFileStmt_Null) {
|
||||
B32 is_multiline = stmt == LNK_DefFileStmt_Exports ||
|
||||
stmt == LNK_DefFileStmt_Sections ||
|
||||
stmt == LNK_DefFileStmt_Imports ||
|
||||
stmt == LNK_DefFileStmt_Segments;
|
||||
active_stmt = is_multiline ? stmt : LNK_DefFileStmt_Null;
|
||||
is_keyword = 1;
|
||||
line = tail;
|
||||
} else {
|
||||
stmt = active_stmt;
|
||||
}
|
||||
|
||||
LNK_DefFileLineNode *node = push_array(scratch.arena, LNK_DefFileLineNode, 1);
|
||||
node->stmt = stmt;
|
||||
node->line = line;
|
||||
node->is_keyword = is_keyword;
|
||||
SLLQueuePush(def_lines.first, def_lines.last, node);
|
||||
def_lines.count += 1;
|
||||
}
|
||||
|
||||
// apply collected DEF lines to config
|
||||
for EachNode(def_line, LNK_DefFileLineNode, def_lines.first) {
|
||||
String8 line = def_line->line;
|
||||
LNK_DefFileStmt stmt = def_line->stmt;
|
||||
|
||||
switch (stmt) {
|
||||
case LNK_DefFileStmt_Description: {
|
||||
lnk_error_cmd_switch(LNK_Warning_Cmdl, obj, LNK_CmdSwitch_Def, "DESCRIPTION is ignored in DEF files");
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_Imports: {
|
||||
if (def_line->is_keyword) {
|
||||
lnk_error_cmd_switch(LNK_Warning_Cmdl, obj, LNK_CmdSwitch_Def, "IMPORTS is ignored in DEF files");
|
||||
} else {
|
||||
lnk_log(LNK_Log_Debug, "%S: unsupported old-style import specifications", path);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_Stub: {
|
||||
lnk_error_cmd_switch(LNK_Warning_Cmdl, obj, LNK_CmdSwitch_Def, "STUB is ignored in DEF files");
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_Segments: {
|
||||
lnk_error_cmd_switch(LNK_Warning_Cmdl, obj, LNK_CmdSwitch_Def, "SEGMENTS is ignored in DEF files");
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_Exports: {
|
||||
String8List tokens = lnk_def_file_tokenize(scratch.arena, line);
|
||||
if (tokens.node_count != 0) {
|
||||
PE_ExportParse export_parse = {0};
|
||||
if (lnk_parse_export_directive_ex(config->arena, tokens, obj, &export_parse)) {
|
||||
export_parse.obj_path = path;
|
||||
lnk_push_export_to_config(config, obj, export_parse);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_Library:
|
||||
case LNK_DefFileStmt_Name: {
|
||||
String8List tokens = lnk_def_file_tokenize(scratch.arena, line);
|
||||
String8 name = {0};
|
||||
|
||||
for EachNode(token_n, String8Node, tokens.first) {
|
||||
String8 token = token_n->string;
|
||||
String8 base_value = {0};
|
||||
B32 has_base = 0;
|
||||
|
||||
U64 sep_pos = str8_find_needle(token, 0, str8_lit("="), 0);
|
||||
if (sep_pos < token.size) {
|
||||
String8 key = str8_prefix(token, sep_pos);
|
||||
if (str8_matchi(key, str8_lit("BASE"))) {
|
||||
base_value = str8_skip(token, sep_pos + 1);
|
||||
has_base = 1;
|
||||
}
|
||||
} else if (str8_matchi(token, str8_lit("BASE")) ||
|
||||
str8_match_lit("BASE:", token, StringMatchFlag_CaseInsensitive|StringMatchFlag_RightSideSloppy)) {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, LNK_CmdSwitch_Def, "syntax error in DEF file BASE specification");
|
||||
}
|
||||
|
||||
if (has_base) {
|
||||
String8List values = str8_split_by_string_chars(scratch.arena, base_value, str8_lit(","), 0);
|
||||
lnk_apply_cmd_option_to_config(config, str8_lit("base"), values, obj);
|
||||
} else if (name.size == 0) {
|
||||
name = token;
|
||||
} else {
|
||||
lnk_error_cmd_switch(LNK_Warning_Cmdl, obj, LNK_CmdSwitch_Def, "ignoring unexpected DEF token \"%S\"", token);
|
||||
}
|
||||
}
|
||||
|
||||
if (stmt == LNK_DefFileStmt_Library) {
|
||||
config->file_characteristics |= PE_ImageFileCharacteristic_FILE_DLL;
|
||||
}
|
||||
|
||||
if (name.size != 0) {
|
||||
String8 image_name = name;
|
||||
if (stmt == LNK_DefFileStmt_Library) {
|
||||
// set DLL import name
|
||||
String8 file_name = str8_skip_last_slash(name);
|
||||
B32 has_ext = (str8_skip_last_dot(file_name).size != file_name.size);
|
||||
if (!has_ext) {
|
||||
image_name = path_replace_file_extension(scratch.arena, name, str8_lit("dll"));
|
||||
}
|
||||
if (config->image_alt_path.size == 0) {
|
||||
config->image_alt_path = push_str8_copy(config->arena, image_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (config->out_path.size == 0) {
|
||||
config->out_path = push_str8_copy(config->arena, image_name);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_Sections: {
|
||||
String8List tokens = lnk_def_file_tokenize(scratch.arena, line);
|
||||
if (tokens.node_count != 0) {
|
||||
B32 is_parse_ok = 1;
|
||||
String8List section_values = {0};
|
||||
str8_list_push(scratch.arena, §ion_values, tokens.first->string);
|
||||
|
||||
for (String8Node *token_n = tokens.first->next; token_n != 0; token_n = token_n->next) {
|
||||
String8 token = token_n->string;
|
||||
if (str8_matchi(token, str8_lit("CLASS"))) {
|
||||
if (token_n->next != 0) {
|
||||
token_n = token_n->next;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
String8 attr = {0};
|
||||
if (str8_matchi(token, str8_lit("EXECUTE"))) {
|
||||
attr = str8_lit("E");
|
||||
} else if (str8_matchi(token, str8_lit("READ"))) {
|
||||
attr = str8_lit("R");
|
||||
} else if (str8_matchi(token, str8_lit("SHARED"))) {
|
||||
attr = str8_lit("S");
|
||||
} else if (str8_matchi(token, str8_lit("WRITE"))) {
|
||||
attr = str8_lit("W");
|
||||
} else {
|
||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, LNK_CmdSwitch_Def, "unknown DEF SECTIONS specifier \"%S\"", token);
|
||||
is_parse_ok = 0;
|
||||
}
|
||||
|
||||
if (attr.size != 0) {
|
||||
str8_list_push(scratch.arena, §ion_values, attr);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_parse_ok) {
|
||||
lnk_apply_cmd_option_to_config(config, str8_lit("section"), section_values, obj);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_StackSize: {
|
||||
String8List values = str8_split_by_string_chars(scratch.arena, line, str8_lit(" \t,"), 0);
|
||||
lnk_apply_cmd_option_to_config(config, str8_lit("stack"), values, obj);
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_Version: {
|
||||
String8List values = str8_split_by_string_chars(scratch.arena, line, str8_lit(" \t"), 0);
|
||||
lnk_apply_cmd_option_to_config(config, str8_lit("version"), values, obj);
|
||||
} break;
|
||||
|
||||
case LNK_DefFileStmt_HeapSize: {
|
||||
String8List values = str8_split_by_string_chars(scratch.arena, line, str8_lit(" \t,"), 0);
|
||||
lnk_apply_cmd_option_to_config(config, str8_lit("heap"), values, obj);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
lnk_error_cmd_switch(LNK_Warning_Cmdl, obj, LNK_CmdSwitch_Def, "ignoring unrecognized DEF statement \"%S\"", line);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
lnk_config_pushf(LNK_Config *config, char *fmt, ...)
|
||||
{
|
||||
@@ -2354,13 +2863,15 @@ lnk_config_init(LNK_CmdLine cmd_line)
|
||||
config->dll_characteristics |= PE_DllCharacteristic_DYNAMIC_BASE;
|
||||
}
|
||||
|
||||
// set flag for /guard
|
||||
// TODO: Set GUARD_CF only after emitting the Guard CF function tables.
|
||||
// Marking the image without valid load-config tables makes CFG-enabled
|
||||
// executables crash on indirect calls.
|
||||
if (config->guard_flags != LNK_Guard_None) {
|
||||
config->dll_characteristics |= PE_DllCharacteristic_GUARD_CF;
|
||||
// config->dll_characteristics |= PE_DllCharacteristic_GUARD_CF;
|
||||
}
|
||||
|
||||
// handle empty /OUT
|
||||
if (!lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Out)) {
|
||||
if (!config->out_path.size) {
|
||||
String8 name = str8_list_first(&config->input_list[LNK_Input_Obj]);
|
||||
String8 ext = (config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) ? str8_lit("dll") : str8_lit("exe");
|
||||
config->out_path = path_replace_file_extension(scratch.arena, name, ext);
|
||||
|
||||
@@ -47,6 +47,7 @@ typedef enum
|
||||
LNK_CmdSwitch_Brepro,
|
||||
LNK_CmdSwitch_Debug,
|
||||
LNK_CmdSwitch_DefaultLib,
|
||||
LNK_CmdSwitch_Def,
|
||||
LNK_CmdSwitch_Delay,
|
||||
LNK_CmdSwitch_DelayLoad,
|
||||
LNK_CmdSwitch_DisallowLib,
|
||||
@@ -59,6 +60,8 @@ typedef enum
|
||||
LNK_CmdSwitch_Fixed,
|
||||
LNK_CmdSwitch_Force,
|
||||
LNK_CmdSwitch_FunctionPadMin,
|
||||
LNK_CmdSwitch_Guard,
|
||||
LNK_CmdSwitch_GuardSym,
|
||||
LNK_CmdSwitch_Heap,
|
||||
LNK_CmdSwitch_HighEntropyVa,
|
||||
LNK_CmdSwitch_Ignore,
|
||||
@@ -88,6 +91,7 @@ typedef enum
|
||||
LNK_CmdSwitch_PdbPageSize,
|
||||
LNK_CmdSwitch_PdbStripped,
|
||||
LNK_CmdSwitch_Release,
|
||||
LNK_CmdSwitch_Section,
|
||||
LNK_CmdSwitch_Stack,
|
||||
LNK_CmdSwitch_SubSystem,
|
||||
LNK_CmdSwitch_Time,
|
||||
@@ -267,6 +271,26 @@ typedef struct LNK_MergeDirectiveList
|
||||
LNK_MergeDirectiveNode *last;
|
||||
} LNK_MergeDirectiveList;
|
||||
|
||||
typedef struct LNK_SectionDirective
|
||||
{
|
||||
String8 name;
|
||||
COFF_SectionFlags set_flags;
|
||||
COFF_SectionFlags clear_flags;
|
||||
} LNK_SectionDirective;
|
||||
|
||||
typedef struct LNK_SectionDirectiveNode
|
||||
{
|
||||
struct LNK_SectionDirectiveNode *next;
|
||||
LNK_SectionDirective v;
|
||||
} LNK_SectionDirectiveNode;
|
||||
|
||||
typedef struct LNK_SectionDirectiveList
|
||||
{
|
||||
U64 count;
|
||||
LNK_SectionDirectiveNode *first;
|
||||
LNK_SectionDirectiveNode *last;
|
||||
} LNK_SectionDirectiveList;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LNK_DebugInfoGuid_Null,
|
||||
@@ -363,6 +387,7 @@ typedef struct LNK_Config
|
||||
LNK_IncludeSymbolList include_symbol_list;
|
||||
LNK_AltNameList alt_name_list;
|
||||
LNK_MergeDirectiveList merge_list;
|
||||
LNK_SectionDirectiveList section_list;
|
||||
U64 data_dir_count;
|
||||
B32 build_imp_lib;
|
||||
B32 build_exp;
|
||||
@@ -575,6 +600,8 @@ internal B32 lnk_is_thread_pool_shared(LNK_Config *config);
|
||||
internal B32 lnk_is_section_removed (LNK_Config *config, String8 section_name);
|
||||
internal B32 lnk_is_dll_delay_load (LNK_Config *config, String8 dll_name);
|
||||
|
||||
internal COFF_SectionFlags lnk_apply_section_directives_to_flags(LNK_Config *config, String8 full_section_name, COFF_SectionFlags flags);
|
||||
|
||||
internal String8 lnk_get_lib_name (String8 path);
|
||||
internal void lnk_push_disallow_lib(LNK_Config *config, String8 path);
|
||||
internal B32 lnk_is_lib_disallowed(LNK_Config *config, String8 path);
|
||||
|
||||
@@ -710,6 +710,58 @@ TEST(merge)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(section_directive_read_only_grouped_section)
|
||||
{
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
T_Ok(t_write_def_obj("prot.obj", (T_COFF_DefObj){
|
||||
.machine = T_COFF_DefSetMachine(X64),
|
||||
.sections = (T_COFF_DefSection[]){
|
||||
{ "prot_a", "prot$a", str8_lit_comp("A"), .flags = "rw:data@1" },
|
||||
{ "prot_mem", "prot$mem", str8_lit_comp("mem"), .flags = "rw:data@1" },
|
||||
{ "prot_z", "prot$z", str8_lit_comp("Z"), .flags = "rw:data@1" },
|
||||
{0}
|
||||
},
|
||||
.directives = (char *[]){ "/SECTION:prot,R", 0 },
|
||||
}));
|
||||
|
||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj prot.obj");
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
String8 exe = t_read_file(arena, str8_lit("a.exe"));
|
||||
PE_BinInfo pe = pe_bin_info_from_data(arena, exe);
|
||||
COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str;
|
||||
COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("prot"));
|
||||
T_Ok(sect != 0);
|
||||
T_Ok(sect->flags == (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead));
|
||||
}
|
||||
|
||||
TEST(section_directive_align_grouped_section)
|
||||
{
|
||||
if (t_id_linker() != Linker_radlink) { return; }
|
||||
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
T_Ok(t_write_def_obj("prot.obj", (T_COFF_DefObj){
|
||||
.machine = T_COFF_DefSetMachine(X64),
|
||||
.sections = (T_COFF_DefSection[]){
|
||||
{ "prot_mem", "prot$mem", str8_lit_comp("mem"), .flags = "rw:data@1" },
|
||||
{0}
|
||||
},
|
||||
.directives = (char *[]){ "/SECTION:prot,R,ALIGN=8192", 0 },
|
||||
}));
|
||||
|
||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj prot.obj");
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
String8 exe = t_read_file(arena, str8_lit("a.exe"));
|
||||
PE_BinInfo pe = pe_bin_info_from_data(arena, exe);
|
||||
COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str;
|
||||
COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("prot"));
|
||||
T_Ok(sect != 0);
|
||||
T_Ok(sect->flags == (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_Align8192Bytes));
|
||||
}
|
||||
|
||||
TEST(link_undef)
|
||||
{
|
||||
T_Ok(t_write_def_obj("undef.obj", (T_COFF_DefObj){
|
||||
@@ -2153,6 +2205,48 @@ TEST(find_merged_pdata)
|
||||
T_Ok(dim_1u64(pe.data_dir_franges[PE_DataDirectoryIndex_EXCEPTIONS]) == 0xC);
|
||||
}
|
||||
|
||||
TEST(guard_cf_pulls_load_config)
|
||||
{
|
||||
U8 load_config_data[0x40] = {0};
|
||||
U32 load_config_size = sizeof(load_config_data);
|
||||
MemoryCopy(load_config_data, &load_config_size, sizeof(load_config_size));
|
||||
|
||||
T_Ok(t_write_entry_obj());
|
||||
T_Ok(t_write_def_lib("loadcfg.lib", (T_COFF_DefLib){
|
||||
.emit_second_member = 1,
|
||||
.members = (T_COFF_DefLibMember[]){
|
||||
{
|
||||
.type = T_COFF_DefLibMember_Obj,
|
||||
.obj = {
|
||||
.path = str8_lit("loadcfg.obj"),
|
||||
.machine = T_COFF_DefSetMachine(X64),
|
||||
.sections = (T_COFF_DefSection[]){
|
||||
{ "loadcfg", ".rdata", str8_array_fixed(load_config_data), .flags = "r:data@8" },
|
||||
{0}
|
||||
},
|
||||
.symbols = (T_COFF_DefSymbol[]){
|
||||
T_COFF_DefSymbol_Extern("_load_config_used", "loadcfg", 0),
|
||||
{0}
|
||||
}
|
||||
}
|
||||
},
|
||||
{0}
|
||||
}
|
||||
}));
|
||||
|
||||
t_invoke_linkerf("/nodefaultlib /subsystem:console /entry:entry /out:a.exe /guard:cf entry.obj loadcfg.lib");
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
String8 exe = t_read_file(arena, str8_lit("a.exe"));
|
||||
PE_BinInfo pe = pe_bin_info_from_data(arena, exe);
|
||||
T_Ok(dim_1u64(pe.data_dir_franges[PE_DataDirectoryIndex_LOAD_CONFIG]) == load_config_size);
|
||||
|
||||
PE_OptionalHeader32Plus *opt = str8_deserial_get_raw_ptr(exe, pe.optional_header_off, sizeof(*opt));
|
||||
if (t_id_linker() == Linker_radlink) {
|
||||
T_Ok(!(opt->dll_characteristics & PE_DllCharacteristic_GUARD_CF));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(section_sort)
|
||||
{
|
||||
COFF_SectionFlags data_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemRead|COFF_SectionFlag_Align1Bytes;
|
||||
@@ -2792,6 +2886,112 @@ TEST(import_export)
|
||||
// TODO: check import table
|
||||
}
|
||||
|
||||
TEST(def_file_full)
|
||||
{
|
||||
if (t_id_linker() == Linker_lld) { return; }
|
||||
|
||||
T_Ok(t_write_def_obj("def_full.obj", (T_COFF_DefObj){
|
||||
.machine = T_COFF_DefSetMachine(X64),
|
||||
.sections = (T_COFF_DefSection[]){
|
||||
{ "data", ".rdata", str8_lit("test"), .flags = "rw:data" },
|
||||
{ "text", ".text", str8_lit_comp("\xc3"), .flags = "rx:code" },
|
||||
{0}
|
||||
},
|
||||
.symbols = (T_COFF_DefSymbol[]){
|
||||
T_COFF_DefSymbol_Extern("entry", "text", 0),
|
||||
T_COFF_DefSymbol_Extern("foo", "data", 0),
|
||||
{0}
|
||||
},
|
||||
}));
|
||||
|
||||
T_Ok(t_write_file(str8_lit("full.def"), str8_lit(
|
||||
"; leading comment\n"
|
||||
"NAME \"def full.exe\" BASE=0x140020000\n"
|
||||
"VERSION 7.8\n"
|
||||
"HEAPSIZE 0x30000, 0x4000\n"
|
||||
"STACKSIZE 0x50000,0x6000\n"
|
||||
"SECTIONS .rdata READ\n"
|
||||
"EXPORTS foo @ 2 DATA\n")));
|
||||
|
||||
t_invoke_linkerf("/subsystem:console /entry:entry /def:full.def def_full.obj");
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
String8 exe = t_read_file(arena, str8_lit("def full.exe"));
|
||||
PE_BinInfo pe = pe_bin_info_from_data(arena, exe);
|
||||
COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str;
|
||||
String8 string_table = str8_substr(exe, pe.string_table_range);
|
||||
PE_OptionalHeader32Plus *opt = str8_deserial_get_raw_ptr(exe, pe.optional_header_off, sizeof(*opt));
|
||||
|
||||
T_Ok(opt->image_base == 0x140020000);
|
||||
T_Ok(opt->major_img_ver == 7);
|
||||
T_Ok(opt->minor_img_ver == 8);
|
||||
T_Ok(opt->sizeof_heap_reserve == 0x30000);
|
||||
T_Ok(opt->sizeof_heap_commit == 0x4000);
|
||||
T_Ok(opt->sizeof_stack_reserve == 0x50000);
|
||||
T_Ok(opt->sizeof_stack_commit == 0x6000);
|
||||
|
||||
COFF_SectionHeader *rdata = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".rdata"));
|
||||
T_Ok(rdata != 0);
|
||||
T_Ok(rdata->flags == (COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead));
|
||||
|
||||
PE_ParsedExportTable export_table = pe_exports_from_data(arena, pe.section_count, section_table, exe, pe.data_dir_franges[PE_DataDirectoryIndex_EXPORT], pe.data_dir_vranges[PE_DataDirectoryIndex_EXPORT]);
|
||||
T_Ok(export_table.export_count == 1);
|
||||
T_Ok(str8_match(export_table.exports[0].name, str8_lit("foo"), 0));
|
||||
T_Ok(export_table.exports[0].ordinal == 2);
|
||||
|
||||
T_Ok(t_write_file(str8_lit("bad_base_space.def"), str8_lit(
|
||||
"NAME bad_base_space BASE = 0x140020000\n"
|
||||
"EXPORTS foo @2 DATA\n")));
|
||||
T_Ok(t_write_file(str8_lit("bad_base_colon.def"), str8_lit(
|
||||
"NAME bad_base_colon BASE:0x140020000\n"
|
||||
"EXPORTS foo @2 DATA\n")));
|
||||
T_Ok(t_write_file(str8_lit("bad_section_align.def"), str8_lit(
|
||||
"NAME bad_section_align\n"
|
||||
"SECTIONS .rdata READ ALIGN=8192\n"
|
||||
"EXPORTS foo @2 DATA\n")));
|
||||
|
||||
t_invoke_linkerf("/subsystem:console /entry:entry /def:bad_base_space.def /out:bad_base_space.exe def_full.obj");
|
||||
T_Ok(g_last_exit_code != 0);
|
||||
|
||||
t_invoke_linkerf("/subsystem:console /entry:entry /def:bad_base_colon.def /out:bad_base_colon.exe def_full.obj");
|
||||
T_Ok(g_last_exit_code != 0);
|
||||
|
||||
t_invoke_linkerf("/subsystem:console /entry:entry /def:bad_section_align.def /out:bad_section_align.exe def_full.obj");
|
||||
T_Ok(g_last_exit_code != 0);
|
||||
|
||||
T_Ok(t_write_def_obj("def_full_dll.obj", (T_COFF_DefObj){
|
||||
.machine = T_COFF_DefSetMachine(X64),
|
||||
.sections = (T_COFF_DefSection[]){
|
||||
{ "data", ".data", str8_lit("test"), .flags = "rw:data" },
|
||||
{ "text", ".text", str8_lit_comp("\xc3"), .flags = "rx:code" },
|
||||
{0}
|
||||
},
|
||||
.symbols = (T_COFF_DefSymbol[]){
|
||||
T_COFF_DefSymbol_Extern("_DllMainCRTStartup", "text", 0),
|
||||
T_COFF_DefSymbol_Extern("dll_foo", "data", 0),
|
||||
{0}
|
||||
},
|
||||
}));
|
||||
|
||||
T_Ok(t_write_file(str8_lit("full_dll.def"), str8_lit(
|
||||
"LIBRARY folded BASE=0x180020000\n"
|
||||
"EXPORTS\n"
|
||||
" dll_foo DATA\n")));
|
||||
|
||||
t_invoke_linkerf("/dll /subsystem:console /def:full_dll.def def_full_dll.obj");
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
String8 dll = t_read_file(arena, str8_lit("folded.dll"));
|
||||
PE_BinInfo dll_pe = pe_bin_info_from_data(arena, dll);
|
||||
COFF_SectionHeader *dll_section_table = (COFF_SectionHeader *)str8_substr(dll, dll_pe.section_table_range).str;
|
||||
PE_OptionalHeader32Plus *dll_opt = str8_deserial_get_raw_ptr(dll, dll_pe.optional_header_off, sizeof(*dll_opt));
|
||||
PE_ParsedExportTable dll_export_table = pe_exports_from_data(arena, dll_pe.section_count, dll_section_table, dll, dll_pe.data_dir_franges[PE_DataDirectoryIndex_EXPORT], dll_pe.data_dir_vranges[PE_DataDirectoryIndex_EXPORT]);
|
||||
|
||||
T_Ok(dll_opt->image_base == 0x180020000);
|
||||
T_Ok(dll_export_table.export_count == 1);
|
||||
T_Ok(str8_match(dll_export_table.exports[0].name, str8_lit("dll_foo"), 0));
|
||||
}
|
||||
|
||||
TEST(utf16_rsp)
|
||||
{
|
||||
T_Ok(t_write_entry_obj());
|
||||
|
||||
Reference in New Issue
Block a user