diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 2aec5160..465414e9 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -42,8 +42,8 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_NotImplemented, 0, "EXPORTADMIN", "", "" }, { LNK_CmdSwitch_Experimental, 0, "EXPERIMENTAL", "Not supported." }, { LNK_CmdSwitch_FastFail, 0, "FASTFAIL", "", "Not used." }, + { LNK_CmdSwitch_FailIfMismatch, 1, "FAILIFMISMATCH", "{id=value}", "" }, { LNK_CmdSwitch_NotImplemented, 0, "FASTGENPROFILE", "", "" }, - { LNK_CmdSwitch_FailIfMismatch, 1, "FAILIFMISMATCH", "", "" }, { LNK_CmdSwitch_FileAlign, 0, "FILEALIGN", ":#", "" }, { LNK_CmdSwitch_Fixed, 0, "FIXED", "[:NO]", "" }, { 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"); } } 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:; @@ -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); } } 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; } @@ -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"); } } 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; } @@ -1183,7 +1183,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List } } } 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; @@ -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); } } 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; @@ -1255,7 +1255,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List case LNK_CmdSwitch_Delay: { 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 { String8 value = value_strings.first->string; 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 } 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: { lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->file_align, LNK_ParseU64Flag_CheckPow2); } 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); } } 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; @@ -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); } } 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; @@ -1717,7 +1747,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List if (value_strings.node_count == 0) { config->flags |= LNK_ConfigFlag_WriteImageChecksum; } 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; @@ -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); } } 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; @@ -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); } } 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; @@ -1919,7 +1949,7 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List } } } 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; @@ -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); } } 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; @@ -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->delay_load_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 for (LNK_CmdOption *cmd = cmd_line.first_option; cmd != 0; cmd = cmd->next) { diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index e05a6ef2..304ade0b 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -50,6 +50,7 @@ typedef enum LNK_CmdSwitch_ErrorReport, LNK_CmdSwitch_Experimental, LNK_CmdSwitch_Export, + LNK_CmdSwitch_FailIfMismatch, LNK_CmdSwitch_FastFail, LNK_CmdSwitch_FileAlign, LNK_CmdSwitch_Fixed, @@ -107,7 +108,6 @@ typedef enum LNK_CmdSwitch_EmitVolatileMetadata, LNK_CmdSwitch_ExportAdmin, LNK_CmdSwitch_FastGenProfile, - LNK_CmdSwitch_FailIfMismatch, LNK_CmdSwitch_Force, LNK_CmdSwitch_Guard, LNK_CmdSwitch_GuardSym, @@ -416,6 +416,7 @@ typedef struct LNK_Config HashTable *include_symbol_ht; HashTable *delay_load_ht; HashTable *disallow_lib_ht; + HashTable *fail_if_mismatch_ht; U64 unresolved_symbol_limit; U64 unresolved_symbol_ref_limit; LNK_SwitchState map_lines_for_unresolved_symbols; diff --git a/src/linker/lnk_error.h b/src/linker/lnk_error.h index 7a7e4a28..ced3a142 100644 --- a/src/linker/lnk_error.h +++ b/src/linker/lnk_error.h @@ -40,6 +40,7 @@ typedef enum LNK_Error_AssociativeLoop, LNK_Error_AlternateNameConflict, LNK_Error_RelocationAgainstRemovedSection, + LNK_Error_FailIfMismatch, LNK_Error_StopLast, LNK_Error_First, diff --git a/src/torture/torture_radlink.c b/src/torture/torture_radlink.c index 500e2255..35a84a21 100644 --- a/src/torture/torture_radlink.c +++ b/src/torture/torture_radlink.c @@ -40,6 +40,16 @@ t_make_sec_defn_obj(Arena *arena, String8 payload) 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 t_make_entry_obj(Arena *arena) { @@ -3856,5 +3866,48 @@ T_BeginTest(opt_ref_dangling_section) } 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 diff --git a/src/torture/torture_radlink.h b/src/torture/torture_radlink.h index 0aee7555..57cd8b32 100644 --- a/src/torture/torture_radlink.h +++ b/src/torture/torture_radlink.h @@ -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_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);