infer subsystem when only entry point is specified

This commit is contained in:
Nikita Smith
2025-08-27 13:18:09 -07:00
committed by Ryan Fleury
parent b0db2b19f8
commit d9eb26a830
2 changed files with 126 additions and 156 deletions
+124 -153
View File
@@ -875,6 +875,49 @@ lnk_make_linker_coff_obj(Arena *arena,
return obj;
}
internal String8
lnk_make_linker_obj(Arena *arena, LNK_Config *config)
{
ProfBeginFunction();
COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, config->machine);
// Emit __ImageBase symbol.
//
// This symbol is used with REL32 to compute delta from current IP
// to the image base. CRT uses this trick to get to HINSTANCE * without
// passing it around as a function argument.
//
// 100h: lea rax, [rip + ffffff00h] ; -100h
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("__ImageBase"), 0, COFF_SymStorageClass_External);
{ // load config symbols
if (config->machine == COFF_MachineType_X86) {
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_SAFE_SE_HANDLER_TABLE_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_SAFE_SE_HANDLER_COUNT_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
}
// TODO: investigate IMAGE_ENCLAVE_CONFIG 32/64
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_ENCLAVE_CONFIG_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_FLAGS_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_FIDS_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_FIDS_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_IAT_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_IAT_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_LONGJMP_TABLE_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_LONGJMP_COUNT_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_EHCONT_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_EHCONT_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
}
String8 obj = coff_obj_writer_serialize(arena, obj_writer);
coff_obj_writer_release(&obj_writer);
ProfEnd();
return obj;
}
internal String8
lnk_get_lib_name(String8 path)
{
@@ -922,49 +965,6 @@ lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path)
}
}
internal String8
lnk_make_linker_obj(Arena *arena, LNK_Config *config)
{
ProfBeginFunction();
COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(COFF_TimeStamp_Max, config->machine);
// Emit __ImageBase symbol.
//
// This symbol is used with REL32 to compute delta from current IP
// to the image base. CRT uses this trick to get to HINSTANCE * without
// passing it around as a function argument.
//
// 100h: lea rax, [rip + ffffff00h] ; -100h
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("__ImageBase"), 0, COFF_SymStorageClass_External);
{ // load config symbols
if (config->machine == COFF_MachineType_X86) {
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_SAFE_SE_HANDLER_TABLE_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_SAFE_SE_HANDLER_COUNT_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
}
// TODO: investigate IMAGE_ENCLAVE_CONFIG 32/64
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_ENCLAVE_CONFIG_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_FLAGS_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_FIDS_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_FIDS_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_IAT_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_IAT_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_LONGJMP_TABLE_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_LONGJMP_COUNT_SYMBOL_NAME), 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_EHCONT_TABLE_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(MSCRT_GUARD_EHCONT_COUNT_SYMBOL_NAME) , 0, COFF_SymStorageClass_External);
}
String8 obj = coff_obj_writer_serialize(arena, obj_writer);
coff_obj_writer_release(&obj_writer);
ProfEnd();
return obj;
}
internal void
lnk_queue_lib_member_for_input(Arena *arena,
LNK_Config *config,
@@ -1761,129 +1761,95 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
} break;
case State_SearchEntryPoint: {
ProfBegin("Search Entry Point");
LNK_Symbol *entry_point_symbol = 0;
B32 is_entry_point_unspecified = config->entry_point_name.size == 0;
if (is_entry_point_unspecified) {
if (config->subsystem == PE_WindowsSubsystem_UNKNOWN) {
// we don't have a subsystem and entry point name,
// so we loop over every subsystem and search potential entry
// points in the symbol table
for EachIndex(subsys_idx, PE_WindowsSubsystem_COUNT) {
String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics);
for EachIndex(entry_idx, name_arr.count) {
entry_point_symbol = lnk_symbol_table_search(symtab, name_arr.v[entry_idx]);
if (entry_point_symbol) {
config->subsystem = (PE_WindowsSubsystem)subsys_idx;
config->entry_point_name = name_arr.v[entry_idx];
goto dbl_break;
}
}
}
// search for potential entry points in libs
if (config->entry_point_name.size) {
for EachIndex(subsys_idx, PE_WindowsSubsystem_COUNT) {
String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics);
for EachIndex(entry_idx, name_arr.count) {
for EachIndex(i, ArrayCount(lib_index)) {
for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) {
if (lnk_search_lib(&lib_n->data, name_arr.v[entry_idx], 0)) {
config->subsystem = (PE_WindowsSubsystem)subsys_idx;
config->entry_point_name = name_arr.v[entry_idx];
goto dbl_break;
}
}
}
}
}
}
dbl_break:;
} else {
// we have subsystem but no entry point name, get potential entry point names
// and see which is in the symbol table
String8Array name_arr = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics);
for EachIndex(entry_idx, name_arr.count) {
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, name_arr.v[entry_idx]);
// loop over all possible subsystems and entry point names and pick
// subsystem that has a defined entry point symbol
if (config->entry_point_name.size == 0) {
PE_WindowsSubsystem subsys_first = config->subsystem;
PE_WindowsSubsystem subsys_last = config->subsystem == PE_WindowsSubsystem_UNKNOWN ? PE_WindowsSubsystem_COUNT : config->subsystem+1;
LNK_Symbol *entry_point_symbol = 0;
for (U64 subsys_idx = subsys_first; subsys_idx < subsys_last; subsys_idx += 1) {
String8Array entry_points = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics);
for EachIndex(i, entry_points.count) {
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, entry_points.v[i]);
if (symbol) {
if (config->entry_point_name.size) {
lnk_error(LNK_Error_EntryPoint,
"multiple entry point symbols found: %S(%S) and %S(%S)",
entry_point_symbol->name, entry_point_symbol->defined.obj->path,
symbol->name, symbol->defined.obj->path);
} else {
config->entry_point_name = name_arr.v[entry_idx];
}
config->subsystem = subsys_idx;
config->entry_point_name = entry_points.v[i];
goto found_entry_and_subsystem;
}
}
// search for entry point in libs
if (!config->entry_point_name.size) {
for EachIndex(entry_idx, name_arr.count) {
for EachIndex(i, ArrayCount(lib_index)) {
for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) {
if (lnk_search_lib(&lib_n->data, name_arr.v[entry_idx], 0)) {
config->entry_point_name = name_arr.v[entry_idx];
goto dbl_break2;
}
}
}
}
dbl_break2:;
}
}
// redirect user entry to appropriate CRT entry
if (str8_match_lit("wmain", config->entry_point_name, 0)) {
config->entry_point_name = str8_lit("wmainCRTStartup");
} else if (str8_match_lit("main", config->entry_point_name, 0)) {
config->entry_point_name = str8_lit("mainCRTStartup");
} else if (str8_match_lit("WinMain", config->entry_point_name, 0)) {
config->entry_point_name = str8_lit("WinMainCRTStartup");
} else if (str8_match_lit("wWinMain", config->entry_point_name, 0)) {
config->entry_point_name = str8_lit("wWinMainCRTStartup");
}
found_entry_and_subsystem:;
}
// generate undefined symbol so in case obj is in lib it will be linked
// search for entry point in libs
if (config->entry_point_name.size == 0 && config->subsystem != PE_WindowsSubsystem_UNKNOWN) {
String8Array entry_points = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics);
for EachIndex(entry_idx, entry_points.count) {
for EachIndex(i, ArrayCount(lib_index)) {
for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) {
if (lnk_search_lib(&lib_n->data, entry_points.v[entry_idx], 0)) {
config->entry_point_name = entry_points.v[entry_idx];
goto found_entry_in_libs;
}
}
}
}
found_entry_in_libs:;
}
// infer subsystem from entry point name
if (config->entry_point_name.size != 0 && config->subsystem == PE_WindowsSubsystem_UNKNOWN) {
for EachIndex(subsys_idx, PE_WindowsSubsystem_COUNT) {
String8Array entry_points = pe_get_entry_point_names(config->machine, subsys_idx, config->file_characteristics);
for EachIndex(i, entry_points.count) {
if (str8_match(entry_points.v[i], config->entry_point_name, 0)) {
config->subsystem = subsys_idx;
goto subsystem_inferred_from_entry;
}
}
}
subsystem_inferred_from_entry:;
}
// do we have an entry point name?
if (config->entry_point_name.size) {
// redirect user entry to appropriate CRT entry
String8 crt_entry_point_name = msvcrt_ctr_entry_from_user_entry(config->entry_point_name);
config->entry_point_name = crt_entry_point_name.size ? crt_entry_point_name : config->entry_point_name;
// generate undefined symbol for entry point
//
// TODO: config_refactor
String8List value_strings = {0};
str8_list_push(scratch.arena, &value_strings, config->entry_point_name);
lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0);
}
// no entry point, print out error and exit
else {
lnk_error(LNK_Error_EntryPoint, "unable to find entry point symbol");
}
// by default terminal server is enabled for windows and console applications
if (~config->flags & LNK_ConfigFlag_NoTsAware &&
~config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) {
if (config->subsystem == PE_WindowsSubsystem_WINDOWS_GUI || config->subsystem == PE_WindowsSubsystem_WINDOWS_CUI) {
config->dll_characteristics |= PE_DllCharacteristic_TERMINAL_SERVER_AWARE;
// do we have a subsystem?
if (config->subsystem != PE_WindowsSubsystem_UNKNOWN) {
// if subsystem version not specified set default values
if (config->subsystem_ver.major == 0 && config->subsystem_ver.minor == 0) {
config->subsystem_ver = lnk_get_default_subsystem_version(config->subsystem, config->machine);
}
// check subsystem version against allowed min version
Version min_subsystem_ver = lnk_get_min_subsystem_version(config->subsystem, config->machine);
if (version_compar(config->subsystem_ver, min_subsystem_ver) < 0) {
lnk_error(LNK_Error_Cmdl, "subsystem version %I64u.%I64u can't be lower than %I64u.%I64u",
config->subsystem_ver.major, config->subsystem_ver.minor, min_subsystem_ver.major, min_subsystem_ver.minor);
}
// by default terminal server is enabled for windows and console applications
if (~config->flags & LNK_ConfigFlag_NoTsAware && ~config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) {
if (config->subsystem == PE_WindowsSubsystem_WINDOWS_GUI || config->subsystem == PE_WindowsSubsystem_WINDOWS_CUI) {
config->dll_characteristics |= PE_DllCharacteristic_TERMINAL_SERVER_AWARE;
}
}
} else {
lnk_error(LNK_Error_NoSubsystem, "unknown subsystem, please use /SUBSYSTEM to set subsytem type you need");
}
}
// do we have a subsystem?
if (config->subsystem == PE_WindowsSubsystem_UNKNOWN) {
lnk_error(LNK_Error_NoSubsystem, "unknown subsystem, please use /SUBSYSTEM to set subsytem type you need");
}
if (config->subsystem_ver.major == 0 && config->subsystem_ver.minor == 0) {
// subsystem version not specified, set default values
config->subsystem_ver = lnk_get_default_subsystem_version(config->subsystem, config->machine);
}
// check subsystem version against allowed min version
Version min_subsystem_ver = lnk_get_min_subsystem_version(config->subsystem, config->machine);
int ver_cmp = version_compar(config->subsystem_ver, min_subsystem_ver);
if (ver_cmp < 0) {
lnk_error(LNK_Error_Cmdl, "subsystem version %I64u.%I64u can't be lower than %I64u.%I64u",
config->subsystem_ver.major, config->subsystem_ver.minor, min_subsystem_ver.major, min_subsystem_ver.minor);
}
ProfEnd();
} break;
case State_InputLinkerObjs: {
@@ -2215,6 +2181,11 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
}
ProfEnd();
if (config->entry_point_name.size == 0) {
lnk_error(LNK_Error_EntryPoint, "unable to find entry point symbol");
}
ProfBegin("Report Unresolved Symbols");
{
U64 count = 0;
+2 -3
View File
@@ -201,7 +201,7 @@ internal String8 lnk_manifest_from_inputs(Arena *arena, LNK_IO_Flags io_flags, S
internal String8 lnk_make_null_obj(Arena *arena);
internal String8 lnk_make_res_obj(Arena *arena, String8List res_file_list, String8List res_path_list, COFF_MachineType machine, U32 time_stamp, String8 work_dir, PathStyle system_path_style, String8 obj_name);
internal String8 lnk_make_linker_coff_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 cwd_path, String8 exe_path, String8 pdb_path, String8 cmd_line, String8 obj_name);
internal String8 lnk_make_linker_obj(Arena *arena, LNK_Config *config);
// --- Link Context ------------------------------------------------------------
@@ -211,8 +211,7 @@ internal B32 lnk_is_lib_loaded(HashTable *loaded_lib_ht, String8 lib_path);
internal void lnk_push_disallow_lib(Arena *arena, HashTable *disallow_lib_ht, String8 path);
internal void lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path);
internal LNK_InputObjList lnk_push_linker_symbols(Arena *arena, LNK_Config *config);
internal void lnk_queue_lib_member_for_input(Arena *arena, LNK_Config *config, LNK_Symbol *pull_in_ref, LNK_Lib *lib, U32 member_idx, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list);
internal void lnk_queue_lib_member_for_input(Arena *arena, LNK_Config *config, LNK_Symbol *pull_in_ref, LNK_Lib *lib, U32 member_idx, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list);
internal LNK_LinkContext lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config);