add /FAILIFMISMATCH

This commit is contained in:
Nikita Smith
2026-02-05 19:45:34 -08:00
parent 8b729d4588
commit 0015e52d3c
5 changed files with 103 additions and 16 deletions
+45 -14
View File
@@ -42,8 +42,8 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
{ LNK_CmdSwitch_NotImplemented, 0, "EXPORTADMIN", "", "" }, { LNK_CmdSwitch_NotImplemented, 0, "EXPORTADMIN", "", "" },
{ LNK_CmdSwitch_Experimental, 0, "EXPERIMENTAL", "Not supported." }, { LNK_CmdSwitch_Experimental, 0, "EXPERIMENTAL", "Not supported." },
{ LNK_CmdSwitch_FastFail, 0, "FASTFAIL", "", "Not used." }, { LNK_CmdSwitch_FastFail, 0, "FASTFAIL", "", "Not used." },
{ LNK_CmdSwitch_FailIfMismatch, 1, "FAILIFMISMATCH", "{id=value}", "" },
{ LNK_CmdSwitch_NotImplemented, 0, "FASTGENPROFILE", "", "" }, { LNK_CmdSwitch_NotImplemented, 0, "FASTGENPROFILE", "", "" },
{ LNK_CmdSwitch_FailIfMismatch, 1, "FAILIFMISMATCH", "", "" },
{ LNK_CmdSwitch_FileAlign, 0, "FILEALIGN", ":#", "" }, { LNK_CmdSwitch_FileAlign, 0, "FILEALIGN", ":#", "" },
{ LNK_CmdSwitch_Fixed, 0, "FIXED", "[:NO]", "" }, { LNK_CmdSwitch_Fixed, 0, "FIXED", "[:NO]", "" },
{ LNK_CmdSwitch_NotImplemented, 0, "FORCE", "", "" }, { LNK_CmdSwitch_NotImplemented, 0, "FORCE", "", "" },
@@ -398,7 +398,7 @@ lnk_cmd_switch_parse_version(LNK_Obj *obj, LNK_CmdSwitchType cmd_switch, String8
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse major version"); lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse major version");
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
exit:; exit:;
@@ -431,7 +431,7 @@ lnk_cmd_switch_parse_tuple(LNK_Obj *obj, LNK_CmdSwitchType cmd_switch, String8Li
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse first parameter \"%S\"", value_strings.first->string); lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse first parameter \"%S\"", value_strings.first->string);
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
return 0; return 0;
} }
@@ -595,7 +595,7 @@ lnk_cmd_switch_parse_string(LNK_Obj *obj, LNK_CmdSwitchType cmd_switch, String8L
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "empty string is not permitted"); lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "empty string is not permitted");
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
return 0; return 0;
} }
@@ -1183,7 +1183,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
} }
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -1240,7 +1240,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid parameter \"%S\"", value_strings.first->string); lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid parameter \"%S\"", value_strings.first->string);
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -1255,7 +1255,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
case LNK_CmdSwitch_Delay: { case LNK_CmdSwitch_Delay: {
if (value_strings.node_count == 0 || value_strings.node_count > 1) { if (value_strings.node_count == 0 || value_strings.node_count > 1) {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} else { } else {
String8 value = value_strings.first->string; String8 value = value_strings.first->string;
if (str8_match_lit("unload", value, StringMatchFlag_CaseInsensitive)) { if (str8_match_lit("unload", value, StringMatchFlag_CaseInsensitive)) {
@@ -1359,6 +1359,36 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
// do nothing // do nothing
} break; } break;
case LNK_CmdSwitch_FailIfMismatch: {
if (value_strings.node_count != 1) {
lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
break;
}
LNK_AltName dir;
if ( ! lnk_parse_alt_name_directive(value_strings.first->string, obj, &dir)) {
break;
}
LNK_AltName *current = hash_table_search_string_raw(config->fail_if_mismatch_ht, dir.from);
if (current) {
if ( ! str8_match(current->to, dir.to, 0)) {
lnk_error_cmd_switch(LNK_Error_FailIfMismatch, obj, cmd_switch,
"'%S=%S' mismatch in:\n"
" %S: /FAILIFMISMATCH:%S=%S\n",
dir.from, dir.to,
lnk_loc_from_obj(scratch.arena, current->obj), current->from, current->to);
break;
}
} else {
LNK_AltName *n = push_array(config->arena, LNK_AltName, 1);
n->from = push_str8_copy(config->arena, dir.from);
n->to = push_str8_copy(config->arena, dir.to);
n->obj = obj;
hash_table_push_string_raw(config->arena, config->fail_if_mismatch_ht, n->from, n);
}
} break;
case LNK_CmdSwitch_FileAlign: { case LNK_CmdSwitch_FileAlign: {
lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->file_align, LNK_ParseU64Flag_CheckPow2); lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->file_align, LNK_ParseU64Flag_CheckPow2);
} break; } break;
@@ -1472,7 +1502,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unknown parameter \"%S\"", value_strings.first->string); lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unknown parameter \"%S\"", value_strings.first->string);
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -1603,7 +1633,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
lnk_merge_directive_list_push(config->arena, &config->merge_list, merge); lnk_merge_directive_list_push(config->arena, &config->merge_list, merge);
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters %d", value_strings.node_count); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -1717,7 +1747,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
if (value_strings.node_count == 0) { if (value_strings.node_count == 0) {
config->flags |= LNK_ConfigFlag_WriteImageChecksum; config->flags |= LNK_ConfigFlag_WriteImageChecksum;
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -1766,7 +1796,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid subsystem \"%S\"", value_strings.first->string); lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid subsystem \"%S\"", value_strings.first->string);
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -1893,7 +1923,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid parameter: \"%S\", expected NO or QUIET", value_strings.first->string); lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid parameter: \"%S\", expected NO or QUIET", value_strings.first->string);
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -1919,7 +1949,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
} }
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters, expected 1"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -1944,7 +1974,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse parameter \"%S\"", value_strings.first->string); lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse parameter \"%S\"", value_strings.first->string);
} }
} else { } else {
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); lnk_error_cmd_switch_invalid_param_count(LNK_Error_Cmdl, obj, cmd_switch);
} }
} break; } break;
@@ -2095,6 +2125,7 @@ lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line)
config->include_symbol_ht = hash_table_init(arena, 0x100); config->include_symbol_ht = hash_table_init(arena, 0x100);
config->delay_load_ht = hash_table_init(arena, 0x100); config->delay_load_ht = hash_table_init(arena, 0x100);
config->disallow_lib_ht = hash_table_init(arena, 0x100); config->disallow_lib_ht = hash_table_init(arena, 0x100);
config->fail_if_mismatch_ht = hash_table_init(arena, 0x100);
// process command line switches // process command line switches
for (LNK_CmdOption *cmd = cmd_line.first_option; cmd != 0; cmd = cmd->next) { for (LNK_CmdOption *cmd = cmd_line.first_option; cmd != 0; cmd = cmd->next) {
+2 -1
View File
@@ -50,6 +50,7 @@ typedef enum
LNK_CmdSwitch_ErrorReport, LNK_CmdSwitch_ErrorReport,
LNK_CmdSwitch_Experimental, LNK_CmdSwitch_Experimental,
LNK_CmdSwitch_Export, LNK_CmdSwitch_Export,
LNK_CmdSwitch_FailIfMismatch,
LNK_CmdSwitch_FastFail, LNK_CmdSwitch_FastFail,
LNK_CmdSwitch_FileAlign, LNK_CmdSwitch_FileAlign,
LNK_CmdSwitch_Fixed, LNK_CmdSwitch_Fixed,
@@ -107,7 +108,6 @@ typedef enum
LNK_CmdSwitch_EmitVolatileMetadata, LNK_CmdSwitch_EmitVolatileMetadata,
LNK_CmdSwitch_ExportAdmin, LNK_CmdSwitch_ExportAdmin,
LNK_CmdSwitch_FastGenProfile, LNK_CmdSwitch_FastGenProfile,
LNK_CmdSwitch_FailIfMismatch,
LNK_CmdSwitch_Force, LNK_CmdSwitch_Force,
LNK_CmdSwitch_Guard, LNK_CmdSwitch_Guard,
LNK_CmdSwitch_GuardSym, LNK_CmdSwitch_GuardSym,
@@ -416,6 +416,7 @@ typedef struct LNK_Config
HashTable *include_symbol_ht; HashTable *include_symbol_ht;
HashTable *delay_load_ht; HashTable *delay_load_ht;
HashTable *disallow_lib_ht; HashTable *disallow_lib_ht;
HashTable *fail_if_mismatch_ht;
U64 unresolved_symbol_limit; U64 unresolved_symbol_limit;
U64 unresolved_symbol_ref_limit; U64 unresolved_symbol_ref_limit;
LNK_SwitchState map_lines_for_unresolved_symbols; LNK_SwitchState map_lines_for_unresolved_symbols;
+1
View File
@@ -40,6 +40,7 @@ typedef enum
LNK_Error_AssociativeLoop, LNK_Error_AssociativeLoop,
LNK_Error_AlternateNameConflict, LNK_Error_AlternateNameConflict,
LNK_Error_RelocationAgainstRemovedSection, LNK_Error_RelocationAgainstRemovedSection,
LNK_Error_FailIfMismatch,
LNK_Error_StopLast, LNK_Error_StopLast,
LNK_Error_First, LNK_Error_First,
+53
View File
@@ -40,6 +40,16 @@ t_make_sec_defn_obj(Arena *arena, String8 payload)
return obj; return obj;
} }
internal String8
t_make_obj_with_directive(Arena *arena, String8 directive)
{
COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64);
coff_obj_writer_push_directive(cow, directive);
String8 obj = coff_obj_writer_serialize(arena, cow);
coff_obj_writer_release(&cow);
return obj;
}
internal String8 internal String8
t_make_entry_obj(Arena *arena) t_make_entry_obj(Arena *arena)
{ {
@@ -3856,5 +3866,48 @@ T_BeginTest(opt_ref_dangling_section)
} }
T_EndTest; T_EndTest;
T_BeginTest(fail_if_mismatch)
{
T_Ok(t_write_entry_obj());
// ------------------------------------------------------------
// try linking two objs with mismatching directives
String8 a1 = t_make_obj_with_directive(scratch.arena, str8_lit("/FAILIFMISMATCH:a=1"));
String8 a2 = t_make_obj_with_directive(scratch.arena, str8_lit("/FAILIFMISMATCH:a=2"));
T_Ok(t_write_file(str8_lit("a1.obj"), a1));
T_Ok(t_write_file(str8_lit("a2.obj"), a2));
t_invoke_linkerf("entry.obj a1.obj a2.obj /entry:entry /subsystem:console /out:a2.exe");
if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
else T_Ok(g_last_exit_code != 0);
// ------------------------------------------------------------
// happy case
T_Ok(t_write_file(str8_lit("a1_copy.obj"), a1));
t_invoke_linkerf("entry.obj a1.obj a1_copy.obj /entry:entry /subsystem:console /out:a1.exe");
T_Ok(g_last_exit_code == 0);
// ------------------------------------------------------------
// test conflicting directives in obj
String8 conf_dirs = t_make_obj_with_directive(scratch.arena, str8_lit("/FAILIFMISMATCH:a=1 /FAILIFMISMATCH:a=2"));
T_Ok(t_write_file(str8_lit("conf_dirs.obj"), conf_dirs));
t_invoke_linkerf("entry.obj conf_dirs.obj /entry:entry /subsystem:console /out:conf_dirs.exe");
if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
else T_Ok(g_last_exit_code != 0);
// ------------------------------------------------------------
// passing switch on command line
t_invoke_linkerf("entry.obj a1.obj /FAILIFMISMATCH:a=2 /out:cmddir.exe");
if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
else T_Ok(g_last_exit_code != 0);
}
T_EndTest;
#undef T_Group #undef T_Group
+2 -1
View File
@@ -35,6 +35,7 @@ internal COFF_ObjSection * t_push_rdata_section(COFF_ObjWriter *obj_writer, Stri
internal String8 t_make_entry_obj(Arena *arena); internal String8 t_make_entry_obj(Arena *arena);
internal String8 t_make_sec_defn_obj(Arena *arena, String8 payload); internal String8 t_make_sec_defn_obj(Arena *arena, String8 payload);
internal String8 t_make_obj_with_directive(Arena *arena, String8 directive);
internal B32 t_write_entry_obj(void); internal B32 t_write_entry_obj(void);