diff --git a/build.bat b/build.bat index f3519851..eb0dd4d1 100644 --- a/build.bat +++ b/build.bat @@ -73,6 +73,8 @@ set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /pdbaltpath:%%%%_PDB%%% set cl_out= /out: set cl_linker= set clang_common= -I..\src\ -I..\local\ -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-unused-parameter -Wno-writable-strings -Wno-missing-field-initializers -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf -ferror-limit=10000 -mcx16 -msha +set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 %clang_common% %auto_compile_flags% +set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 %clang_common% %auto_compile_flags% set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 -D_DEBUG %clang_common% %auto_compile_flags% set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 -DNDEBUG %clang_common% %auto_compile_flags% set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /pdbaltpath:%%%%_PDB%%%% -Xlinker /NATVIS:"%~dp0\src\natvis\base.natvis" -Xlinker /opt:ref -Xlinker /opt:icf @@ -144,7 +146,7 @@ if "%strip_lib_debug%"=="1" set didbuild=1 && %compile% ..\src\strip_ if "%mule_main%"=="1" set didbuild=1 && del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1 if "%mule_module%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_module.cpp %compile_link% %link_dll% %out%mule_module.dll || exit /b 1 if "%mule_hotload%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_hotload_main.c %compile_link% %out%mule_hotload.exe & %compile% ..\src\mule\mule_hotload_module_main.c %compile_link% %link_dll% %out%mule_hotload_module.dll || exit /b 1 -if "%torture%"=="1" set didbuild=1 && %compile% ..\src\torture\torture.c %compile_link% %out%torture.exe || exit /b1 +if "%torture%"=="1" set didbuild=1 && %compile% ..\src\torture\torture_main.c %compile_link% %out%torture.exe || exit /b1 if "%dwarf_expr_test%"=="1" set didbuild=1 && %compile% ..\src\torture\dwarf_expr_test.c %compile_link% %out%dwarf_expr_test.exe || exit /b1 if "%mule_peb_trample%"=="1" ( set didbuild=1 diff --git a/src/coff/coff_parse.c b/src/coff/coff_parse.c index 3f9e81ca..1db0b582 100644 --- a/src/coff/coff_parse.c +++ b/src/coff/coff_parse.c @@ -127,12 +127,45 @@ coff_section_table_from_data(Arena *arena, String8 data, Rng1U64 section_table_r U64 section_count = dim_1u64(section_table_range) / sizeof(COFF_SectionHeader); COFF_SectionHeader **section_table = push_array_no_zero(arena, COFF_SectionHeader *, section_count+1); section_table[0] = push_array(arena, COFF_SectionHeader, 1); - for (U64 i = 0; i < section_count; ++i) { - section_table[i+1] = str8_deserial_get_raw_ptr(data, section_table_range.min + i*sizeof(COFF_SectionHeader), sizeof(COFF_SectionHeader)); + for EachIndex(sect_idx, section_count) { + section_table[sect_idx + 1] = str8_deserial_get_raw_ptr(data, section_table_range.min + sect_idx*sizeof(COFF_SectionHeader), sizeof(COFF_SectionHeader)); } return section_table; } +internal COFF_SectionHeader * +coff_section_header_from_name(String8 string_table, COFF_SectionHeader *section_table, U64 section_count, String8 name) +{ + for EachIndex(sect_idx, section_count) { + if (str8_match(coff_name_from_section_header(string_table, §ion_table[sect_idx]), name, 0)) { + return §ion_table[sect_idx]; + } + } + return 0; +} + +internal COFF_SectionHeaderArray +coff_section_header_array_from_name(Arena *arena, String8 string_table, COFF_SectionHeader *section_table, U64 section_count, String8 name) +{ + U64 match_count = 0; + for EachIndex(sect_idx, section_count) { + if (str8_match(coff_name_from_section_header(string_table, §ion_table[sect_idx]), name, 0)) { + match_count += 1; + } + } + + COFF_SectionHeader *matches = push_array(arena, COFF_SectionHeader, match_count); + for (U64 sect_idx = 0, match_idx = 0; sect_idx < section_count; sect_idx += 1) { + if (str8_match(coff_name_from_section_header(string_table, §ion_table[sect_idx]), name, 0)) { + matches[match_idx] = section_table[sect_idx]; + match_idx += 1; + } + } + + return (COFF_SectionHeaderArray){ .count = match_count, .v = matches }; +} + + internal COFF_ParsedSymbol coff_parse_symbol32(String8 string_table, COFF_Symbol32 *sym32) { diff --git a/src/coff/coff_parse.h b/src/coff/coff_parse.h index 4156b6c6..edcb2952 100644 --- a/src/coff/coff_parse.h +++ b/src/coff/coff_parse.h @@ -251,14 +251,17 @@ internal COFF_FileHeaderInfo coff_file_header_info_from_data(String8 raw_coff); //////////////////////////////// // Section -internal COFF_SectionHeader ** coff_section_table_from_data(Arena *arena, String8 data, Rng1U64 section_table_range); +internal COFF_SectionHeader ** coff_section_table_from_data (Arena *arena, String8 data, Rng1U64 section_table_range); +internal COFF_SectionHeader * coff_section_header_from_name (String8 string_table, COFF_SectionHeader *section_table, U64 section_count, String8 name); +internal COFF_SectionHeaderArray coff_section_header_array_from_name(Arena *arena, String8 string_table, COFF_SectionHeader *section_table, U64 section_count, String8 name); +internal String8 coff_name_from_section_header (String8 string_table, COFF_SectionHeader *header); //////////////////////////////// // Symbol internal COFF_ParsedSymbol coff_parse_symbol32(String8 string_table, COFF_Symbol32 *sym32); internal COFF_ParsedSymbol coff_parse_symbol16(String8 string_table, COFF_Symbol16 *sym16); -internal COFF_ParsedSymbol coff_parse_symbol(COFF_FileHeaderInfo header, String8 string_table, String8 symbol_table, U32 symbol_idx); +internal COFF_ParsedSymbol coff_parse_symbol (COFF_FileHeaderInfo header, String8 string_table, String8 symbol_table, U32 symbol_idx); internal COFF_Symbol32Array coff_symbol_array_from_data_16(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count); internal COFF_Symbol32Array coff_symbol_array_from_data_32(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count); diff --git a/src/torture/torture.c b/src/torture/torture.c index 1695d55a..a76d07cf 100644 --- a/src/torture/torture.c +++ b/src/torture/torture.c @@ -1,212 +1,30 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -//////////////////////////////// -// Build Options - -#define BUILD_CONSOLE_INTERFACE 1 -#define BUILD_TITLE "TORTURE" - -//////////////////////////////// - -#include "third_party/xxHash/xxhash.c" -#include "third_party/xxHash/xxhash.h" -#include "third_party/radsort/radsort.h" - -//////////////////////////////// - -#include "base/base_inc.h" -#include "os/os_inc.h" -#include "linker/base_ext/base_core.h" -#include "linker/base_ext/base_arena.h" -#include "linker/base_ext/base_arrays.h" -#include "linker/hash_table.h" -#include "coff/coff.h" -#include "coff/coff_parse.h" -#include "coff/coff_obj_writer.h" -#include "coff/coff_lib_writer.h" -#include "pe/pe.h" -#include "pe/pe_section_flags.h" -#include "elf/elf.h" -#include "elf/elf_parse.h" -#include "dwarf/dwarf_inc.h" -#include "regs/regs.h" -#include "regs/dwarf/regs_dwarf.h" - -#include "base/base_inc.c" -#include "os/os_inc.c" -#include "linker/hash_table.c" -#include "linker/base_ext/base_core.c" -#include "linker/base_ext/base_arena.c" -#include "linker/base_ext/base_arrays.c" -#include "coff/coff.c" -#include "coff/coff_parse.c" -#include "coff/coff_obj_writer.c" -#include "coff/coff_lib_writer.c" -#include "pe/pe.c" -#include "elf/elf.c" -#include "elf/elf_parse.c" -#include "dwarf/dwarf_inc.c" -#include "regs/regs.c" -#include "regs/dwarf/regs_dwarf.c" - -#include "linker/lnk_cmd_line.h" -#include "linker/lnk_cmd_line.c" -#include "linker/lnk_error.h" - -//////////////////////////////// - -typedef enum -{ - T_Result_Fail, - T_Result_Crash, - T_Result_Pass, -} T_Result; - -typedef T_Result (*T_Run)(void); - -internal char * -t_string_from_result(T_Result v) -{ - switch (v) { - case T_Result_Fail: return "FAIL"; - case T_Result_Crash: return "CRASH"; - case T_Result_Pass: return "PASS"; - } - return 0; -} - +// command line global String8 g_stdout_file_name = str8_lit_comp("torture.out"); -global U64 g_linker_time_out; -global String8 g_linker; global String8 g_wdir; global String8 g_out = str8_lit_comp("torture"); global B32 g_verbose; global B32 g_redirect_stdout = 1; +global String8 g_linker; -#define T_LINKER_TIME_OUT_EXIT_CODE 999999 +// tests +U64 g_torture_test_count; +T_Test g_torture_tests[0xffffff]; -typedef enum +// invoke +global int g_last_exit_code; + +internal char * +t_string_from_result(T_RunStatus v) { - T_Linker_Null, - T_Linker_RAD, - T_Linker_MSVC, - T_Linker_LLVM -} T_Linker; - -internal T_Linker -t_ident_linker(void) -{ - String8 name = g_linker; - name = str8_skip_last_slash(name); - name = str8_chop_last_dot(name); - if (str8_match(name, str8_lit("radlink"), StringMatchFlag_CaseInsensitive)) { - return T_Linker_RAD; + switch (v) { + case T_RunStatus_Fail: return "FAIL"; + case T_RunStatus_Crash: return "CRASH"; + case T_RunStatus_Pass: return "PASS"; } - if (str8_match(name, str8_lit("link"), StringMatchFlag_CaseInsensitive)) { - return T_Linker_MSVC; - } - if (str8_match(name, str8_lit("lld-link"), StringMatchFlag_CaseInsensitive)) { - return T_Linker_LLVM; - } - return T_Linker_Null; -} - -internal int -t_invoke_linker_with_time_out(U64 time_out, String8 cmdline) -{ - Temp scratch = scratch_begin(0,0); - - OS_Handle output_redirect = {0}; - if (g_redirect_stdout) { - output_redirect = os_file_open(OS_AccessFlag_Append|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite|OS_AccessFlag_Inherited, g_stdout_file_name); - } - - // - // Build Launch Options - // - OS_ProcessLaunchParams launch_opts = {0}; - launch_opts.path = g_wdir; - launch_opts.inherit_env = 1; - launch_opts.stdout_file = output_redirect; - launch_opts.stderr_file = output_redirect; - str8_list_push(scratch.arena, &launch_opts.cmd_line, g_linker); - str8_list_push(scratch.arena, &launch_opts.cmd_line, str8_lit("/nologo")); - { - String8List parsed_cmdline = lnk_arg_list_parse_windows_rules(scratch.arena, cmdline); - str8_list_concat_in_place(&launch_opts.cmd_line, &parsed_cmdline); - } - - // - // Invoke Linker - // - int exit_code = -1; - { - if (g_verbose) { - String8 full_cmd_line = str8_list_join(scratch.arena, &launch_opts.cmd_line, &(StringJoin){ .sep = str8_lit(" ") }); - fprintf(stdout, "Command Line: %.*s\n", str8_varg(full_cmd_line)); - fprintf(stdout, "Working Dir: %.*s\n", str8_varg(g_wdir)); - } - - OS_Handle linker_handle = os_process_launch(&launch_opts); - if (os_handle_match(linker_handle, os_handle_zero())) { - fprintf(stderr, "unable to start process: %.*s\n", str8_varg(g_linker)); - } else { - U64 exit_code_u64 = 0; - B32 was_joined = os_process_join(linker_handle, time_out, &exit_code_u64); - exit_code = (int)exit_code_u64; - if (!was_joined) { - os_process_kill(linker_handle); - exit_code = T_LINKER_TIME_OUT_EXIT_CODE; - } - os_process_detach(linker_handle); - } - } - - if (g_redirect_stdout) { - os_file_close(output_redirect); - } - - scratch_end(scratch); - return exit_code; -} - -internal int -t_invoke_linker(String8 cmdline) -{ - return t_invoke_linker_with_time_out(max_U64, cmdline); -} - -internal int -t_invoke_linkerf(char *fmt, ...) -{ - Temp scratch = scratch_begin(0,0); - va_list args; - va_start(args, fmt); - String8 cmdline = push_str8fv(scratch.arena, fmt, args); - int exit_code = t_invoke_linker(cmdline); - va_end(args); - scratch_end(scratch); - return exit_code; -} - -internal int -t_invoke_linker_with_time_outf(U64 time_out, char *fmt, ...) -{ - Temp scratch = scratch_begin(0,0); - va_list args; - va_start(args, fmt); - String8 cmdline = push_str8fv(scratch.arena, fmt, args); - int exit_code = t_invoke_linker_with_time_out(time_out, cmdline); - va_end(args); - scratch_end(scratch); - return exit_code; -} - -internal String8 -t_make_file_path(Arena *arena, String8 name) -{ - return push_str8f(arena, "%S\\%S", g_wdir, name); + return 0; } internal B32 @@ -241,16 +59,16 @@ t_read_file(Arena *arena, String8 name) return data; } -typedef struct +internal String8 +t_make_file_path(Arena *arena, String8 name) { - T_Run run; - T_Result result; -} T_RunCtx; + return push_str8f(arena, "%S\\%S", g_wdir, name); +} internal void t_run_caller(void *raw_ctx) { - T_RunCtx *ctx = raw_ctx; + T_RunCtx *ctx = raw_ctx; ctx->result = ctx->run(); } @@ -258,10 +76,10 @@ internal void t_run_fail_handler(void *raw_ctx) { T_RunCtx *ctx = raw_ctx; - ctx->result = T_Result_Crash; + ctx->result.status = T_RunStatus_Crash; } -internal T_Result +internal T_RunResult t_run(T_Run run) { T_RunCtx ctx = {0}; @@ -270,5085 +88,80 @@ t_run(T_Run run) return ctx.result; } -internal COFF_SectionHeader * -t_coff_section_header_from_name(String8 string_table, COFF_SectionHeader *section_table, U64 section_count, String8 name) +internal B32 +t_invoke(String8 exe_path, String8 cmdline, U64 timeout) { - for (U64 sect_idx = 0; sect_idx < section_count; sect_idx += 1) { - COFF_SectionHeader *section_header = §ion_table[sect_idx]; - String8 section_name = coff_name_from_section_header(string_table, section_header); - if (str8_match(section_name, name, 0)) { - return section_header; - } - } - return 0; -} + Temp scratch = scratch_begin(0,0); -internal COFF_SectionHeaderArray -t_coff_section_header_array_from_name(Arena *arena, String8 string_table, COFF_SectionHeader *section_table, U64 section_count, String8 name) -{ - U64 match_count = 0; - for (U64 sect_idx = 0; sect_idx < section_count; sect_idx += 1) { - COFF_SectionHeader *section_header = §ion_table[sect_idx]; - String8 section_name = coff_name_from_section_header(string_table, section_header); - if (str8_match(section_name, name, 0)) { - match_count += 1; + B32 is_ok = 0; + + OS_Handle output_redirect = {0}; + if (g_redirect_stdout) { + output_redirect = os_file_open(OS_AccessFlag_Append|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite|OS_AccessFlag_Inherited, g_stdout_file_name); + } + + // Build Launch Options + OS_ProcessLaunchParams launch_opts = { .path = g_wdir, .inherit_env = 1, .stdout_file = output_redirect, .stderr_file = output_redirect }; + str8_list_push(scratch.arena, &launch_opts.cmd_line, g_linker); + str8_list_push(scratch.arena, &launch_opts.cmd_line, str8_lit("/nologo")); + { + String8List parsed_cmdline = lnk_arg_list_parse_windows_rules(scratch.arena, cmdline); + str8_list_concat_in_place(&launch_opts.cmd_line, &parsed_cmdline); + } + + if (g_verbose) { + String8 full_cmd_line = str8_list_join(scratch.arena, &launch_opts.cmd_line, &(StringJoin){ .sep = str8_lit(" ") }); + fprintf(stdout, "Command Line: %.*s\n", str8_varg(full_cmd_line)); + fprintf(stdout, "Working Dir: %.*s\n", str8_varg(g_wdir)); + } + + // Invoke Exe + int exit_code = -1; + { + OS_Handle process_handle = os_process_launch(&launch_opts); + if (!os_handle_match(process_handle, os_handle_zero())) { + U64 exit_code_u64 = 0; + is_ok = os_process_join(process_handle, timeout, &exit_code_u64); + exit_code = (int)exit_code_u64; + if (!is_ok) { + os_process_kill(process_handle); + } + os_process_detach(process_handle); } } - COFF_SectionHeader *matches = push_array(arena, COFF_SectionHeader, match_count); - for (U64 sect_idx = 0, match_idx = 0; sect_idx < section_count; sect_idx += 1) { - COFF_SectionHeader *section_header = §ion_table[sect_idx]; - String8 section_name = coff_name_from_section_header(string_table, section_header); - if (str8_match(section_name, name, 0)) { - matches[match_idx++] = *section_header; - } + // close handles + if (g_redirect_stdout) { + os_file_close(output_redirect); } - COFF_SectionHeaderArray result = {0}; - result.count = match_count; - result.v = matches; + // update global exit code + if (is_ok) { + g_last_exit_code = exit_code; + } else { + g_last_exit_code = -1; + } - return result; + scratch_end(scratch); + return is_ok; } -//////////////////////////////////////////////////////////////// - -typedef enum +internal int +t_test_is_before(void *raw_a, void *raw_b) { - T_MsvcLinkExitCode_UnresolvedExternals = 1120, - T_MsvcLinkExitCode_CorruptOrInvalidSymbolTable = 1235, - T_MsvcLinkExitCode_SectionsFoundWithDifferentAttributes = 4078, -} T_MsvcLinkExitCode; - -internal COFF_ObjSection * -t_push_text_section(COFF_ObjWriter *obj_writer, String8 data) -{ - return coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_Align1Bytes, data); -} - -internal COFF_ObjSection * -t_push_data_section(COFF_ObjWriter *obj_writer, String8 data) -{ - return coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS, data); -} - -internal COFF_ObjSection * -t_push_rdata_section(COFF_ObjWriter *obj_writer, String8 data) -{ - return coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS, data); + T_Test *a = raw_a, *b = raw_b; + int cmp = str8_compar(str8_cstring(a->group), str8_cstring(b->group), 0); + if (cmp == 0) { + cmp = u64_compar(&a->decl_line, &b->decl_line); + } + return cmp < 0; } internal void -t_write_entry_obj(void) -{ - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { 0xc3 }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); - String8 obj = coff_obj_writer_serialize(obj_writer->arena, obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - AssertAlways(!"unable to write entry obj"); - } - coff_obj_writer_release(&obj_writer); -} - -//////////////////////////////// - -internal T_Result -t_machine_compat_check(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); - t_push_data_section(obj_writer, str8_lit("unknown")); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("unknown.obj"), obj)) { - goto exit; - } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - t_push_data_section(obj_writer, str8_lit("x64")); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("x64.obj"), obj)) { - goto exit; - } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Arm64); - t_push_data_section(obj_writer, str8_lit("arm64")); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("arm64.obj"), obj)) { - goto exit; - } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { 0xC3 }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int linker_exit_code; - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe entry.obj unknown.obj x64.obj"); - if (linker_exit_code != 0) { - goto exit; - } - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe entry.obj unknown.obj x64.obj arm64.obj"); - if (linker_exit_code == 0) { - goto exit; - } - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe /machine:amd64 arm64.obj entry.obj"); - if (linker_exit_code == 0) { - goto exit; - } - - result = T_Result_Pass; - - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_out_of_bounds_section_number(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *foo = coff_obj_writer_push_section(obj_writer, str8_lit(".foo"), PE_DATA_SECTION_FLAGS, str8_lit("foo")); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("foo"), 0, foo); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - { - COFF_FileHeaderInfo header = coff_file_header_info_from_data(obj); - String8 string_table = str8_substr(obj, header.string_table_range); - String8 symbol_table = str8_substr(obj, header.symbol_table_range); - COFF_ParsedSymbol symbol = coff_parse_symbol(header, string_table, symbol_table, 0); - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - symbol16->section_number = 123; - } - if (!t_write_file(str8_lit("bad.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj bad.obj"); - if (linker_exit_code == 0) { goto exit; } - if (t_ident_linker() == T_Linker_RAD && linker_exit_code != LNK_Error_IllData) { goto exit; } - - result = T_Result_Pass; - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_merge(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_section(obj_writer, str8_lit(".test"), PE_DATA_SECTION_FLAGS, str8_lit("hello, world")); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("test.obj"), obj)) { - goto exit; - } - } - - t_write_entry_obj(); - - int linker_exit_code; - - // circular merge - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.test entry.obj test.obj"); - if (linker_exit_code == 0) { - goto exit; - } - if (t_ident_linker() == T_Linker_RAD) { - if (linker_exit_code != LNK_Error_CircularMerge) { - goto exit; - } - } - - // circular merge with extra link - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.data /merge:.data=.test entry.obj test.obj"); - if (linker_exit_code == 0) { - goto exit; - } - if (t_ident_linker() == T_Linker_RAD) { - if (linker_exit_code != LNK_Error_CircularMerge) { - goto exit; - } - } - - // merge with non-defined section - { - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.qwe entry.obj test.obj"); - if (linker_exit_code != 0) { - goto exit; - } - - // make sure linker created .qwe and merged .test into it - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".qwe")); - if (sect == 0) { - goto exit; - } - if (sect->flags != PE_DATA_SECTION_FLAGS) { - goto exit; - } - String8 qwe = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(qwe, str8_lit("hello, world"),0)) { - goto exit; - } - } - - // illegal merge with .reloc - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.reloc entry.obj test.obj"); - if (linker_exit_code == 0) { - goto exit; - } - if (t_ident_linker() == T_Linker_RAD) { - if (linker_exit_code != LNK_Error_IllegalSectionMerge) { - goto exit; - } - } - - // illegal merge with .rsrc - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.rsrc entry.obj test.obj"); - if (linker_exit_code == 0) { - goto exit; - } - if (t_ident_linker() == T_Linker_RAD) { - if (linker_exit_code != LNK_Error_IllegalSectionMerge) { - goto exit; - } - } - - // merge non-defined section with defined section - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.qwe=.test entry.obj test.obj"); - if (linker_exit_code != 0) { - goto exit; - } - - // merge .test -> .qwe -> .data - { - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.qwe /merge:.qwe=.data entry.obj test.obj"); - if (linker_exit_code != 0) { - goto exit; - } - - // make sure linker merged .test into .data - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".data")); - if (sect == 0) { - goto exit; - } - if (sect->flags != PE_DATA_SECTION_FLAGS) { - goto exit; - } - String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(data, str8_lit("hello, world"),0)) { - goto exit; - } - } - - result = T_Result_Pass; - - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_simple_link_test(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - U8 text_payload[] = { 0xC3 }; - - String8 main_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text_payload)); - coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS, str8_lit("qwe")); - coff_obj_writer_push_section(obj_writer, str8_lit(".zero"), PE_BSS_SECTION_FLAGS, str8(0, 5)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 main_obj_name = str8_lit("main.obj"); - if (!t_write_file(main_obj_name, main_obj)) { - goto exit; - } - - int file_align = 512; - int virt_align = 4096; - String8 out_name = str8_lit("a.exe"); - int linker_exit_code = t_invoke_linkerf("/entry:my_entry /subsystem:console /fixed /filealign:%d /align:%d /out:%S %S", file_align, virt_align, out_name, main_obj_name); - if (linker_exit_code != 0) { - goto exit; - } - - String8 exe = t_read_file(scratch.arena, out_name); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - if (pe.is_pe32) { - goto exit; - } - if (pe.section_count != 3) { - goto exit; - } - if (pe.arch != Arch_x64) { - goto exit; - } - if (pe.subsystem != PE_WindowsSubsystem_WINDOWS_CUI) { - goto exit; - } - if (pe.virt_section_align != virt_align) { - goto exit; - } - if (pe.file_section_align != file_align) { - goto exit; - } - if (pe.symbol_count != 0) { - goto exit; - } - if (pe.data_dir_count != PE_DataDirectoryIndex_COUNT) { - goto exit; - } - - // check section alignment - for (U64 sect_idx = 0; sect_idx < pe.section_count; sect_idx += 1) { - COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - if (AlignPadPow2(sect_header->fsize, file_align) != 0) { - goto exit; - } - if (AlignPadPow2(sect_header->voff, virt_align) != 0) { - goto exit; - } - } - - COFF_SectionHeader *text_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (!text_section) { - goto exit; - } - if (text_section->foff != file_align) { - goto exit; - } - if (pe.entry_point != text_section->voff) { - goto exit; - } - - COFF_SectionHeader *data_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - if (data_section == 0) { - goto exit; - } - - COFF_SectionHeader *zero_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".zero")); - if (zero_section == 0) { - goto exit; - } - - String8 text_data = str8_substr(exe, rng_1u64(text_section->foff, text_section->foff + text_section->vsize)); - if (!str8_match(text_data, str8_array_fixed(text_payload), 0)) { - goto exit; - } - - PE_OptionalHeader32Plus *opt = str8_deserial_get_raw_ptr(exe, pe.optional_header_off, sizeof(*opt)); - if (opt->sizeof_code != text_section->fsize) { - goto exit; - } - if (opt->sizeof_inited_data != text_section->fsize + data_section->fsize) { - goto exit; - } - if (opt->sizeof_uninited_data != 0x200) { - goto exit; - } - if (opt->code_base != 0x1000) { - goto exit; - } - if (opt->image_base != 0x140000000) { - goto exit; - } - if (opt->major_os_ver != 6) { - goto exit; - } - if (opt->minor_os_ver != 0) { - goto exit; - } - if (opt->major_img_ver != 0) { - goto exit; - } - if (opt->minor_img_ver != 0) { - goto exit; - } - if (opt->major_subsystem_ver != 6) { - goto exit; - } - if (opt->minor_subsystem_ver != 0) { - goto exit; - } - if (opt->win32_version_value != 0) { - goto exit; - } - if (opt->sizeof_image != 0x4000) { - goto exit; - } - if (opt->sizeof_headers != 0x200) { - goto exit; - } - if (opt->dll_characteristics != 0x8120) { - goto exit; - } - if (opt->loader_flags != 0) { - goto exit; - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_link_undef(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 undef_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); - undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("undef.obj"), undef_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - // try linking unresolved symbol and see if linker picks up on that - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj undef.obj"); - if (linker_exit_code != LNK_Error_UnresolvedSymbol) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_link_unref_undef(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 undef_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); - undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { 0xc3 }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("undef.obj"), undef_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - // try linking unreferenced unresolved symbol, this must link - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj undef.obj"); - if (linker_exit_code == 0) { goto exit; } - - result = T_Result_Pass; - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_lib_vs_weak_lib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code; - - // linker must pick weak symbol from a.obj - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x111; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - // linker must pick weak symbol from entry.obj - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_lib_vs_weak_nolib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - // linker must pick weak symbol from entry.obj - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_lib_vs_weak_alias(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - // linker must pick weak symbol from entry.obj - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_lib_vs_weak_antidep(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - // linker must pick weak symbol from a.obj - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_alias_vs_weak_alias(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *qwe = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("qwe"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("sym"), COFF_WeakExt_SearchAlias, qwe); - a = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 b; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *ewq = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("ewq"), 0x222, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("sym"), COFF_WeakExt_SearchAlias, ewq); - b = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("sym")); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a)) { goto exit; } - if (!t_write_file(str8_lit("b.obj"), b)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); - if (linker_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_alias_vs_weak_lib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - // linker must pick weak symbol from entry.obj - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_alias_vs_weak_nolib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - // linker must pick weak symbol from entry.obj - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_alias_vs_weak_antidep(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - // linker must pick weak symbol from entry.obj - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_nolib_vs_weak_nolib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_nolib_vs_weak_lib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_nolib_vs_weak_alias(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_nolib_vs_weak_antidep(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_antidep_vs_weak_antidep(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code; - - // linker must pick weak symbol from a.obj - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x111; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - // linker must pick weak symbol from entry.obj - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_antidep_vs_weak_nolib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_antidep_vs_weak_lib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x222; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_antidep_vs_weak_alias(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); - coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); - if (linker_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - String8 imm = str8_substr(text_data, rng_1u64(3, 7)); - U32 expected = 0x111; - if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_vs_common(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 weak_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("a")); - COFF_ObjSymbol *sect_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("_a"), 0, sect); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, sect_symbol); - weak_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 common_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_common(obj_writer, str8_lit("w"), 2); - common_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("w")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("weak.obj"), weak_obj)) { goto exit; } - if (!t_write_file(str8_lit("common.obj"), common_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code; - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe common.obj weak.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe weak.obj common.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeader *bss = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".bss")); - if (!bss) { goto exit; } - if (bss->fsize != 0) { goto exit; } - if (bss->vsize != 2) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_abs_vs_weak(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - U32 abs_value = 0x123; - U8 text_code[] = { 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3 }; - - String8 abs_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("foo"), abs_value, COFF_SymStorageClass_External); - abs_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 text_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - - COFF_ObjSection *mydata = coff_obj_writer_push_section(obj_writer, str8_lit(".mydata"), COFF_SectionFlag_CntCode|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute|COFF_SectionFlag_Align1Bytes, str8_lit("mydata")); - COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("mydata"), 0, mydata); - COFF_ObjSymbol *foo = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("foo"), COFF_WeakExt_NoLibrary, tag); - - COFF_ObjSection *text = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), COFF_SectionFlag_CntCode|COFF_SectionFlag_MemExecute|COFF_SectionFlag_MemRead|COFF_SectionFlag_Align1Bytes, str8_array_fixed(text_code)); - coff_obj_writer_section_push_reloc(obj_writer, text, 2, foo, COFF_Reloc_X64_Addr64); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text); - - text_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("abs.obj"), abs_obj)) { goto exit; } - if (!t_write_file(str8_lit("text.obj"), text_obj)) { goto exit; } - - int abs_vs_weak_exit_code = t_invoke_linker(str8_lit("/subsystem:console /entry:my_entry /out:a.exe abs.obj text.obj")); - if (abs_vs_weak_exit_code != 0) { goto exit; } - - int weak_vs_abs_exit_code = t_invoke_linker(str8_lit("/subsystem:console /entry:my_entry /out:a.exe text.obj abs.obj")); - if (weak_vs_abs_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeader *text_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_section == 0) { goto exit; } - - String8 text_data = str8_substr(exe, rng_1u64(text_section->foff, text_section->foff + text_section->fsize)); - String8 inst = str8_prefix(text_data, 2); - if (!str8_match(inst, str8_array(text_code, 2), 0)) { goto exit; } - - String8 imm = str8_prefix(str8_skip(text_data, 2), 8); - U64 expected_imm = abs_value; - if (!str8_match(imm, str8_struct(&expected_imm), 0)) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_abs_vs_regular(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - String8 shared_symbol_name = str8_lit("foo"); - - U8 regular_payload[] = { 0xC0, 0xFF, 0xEE }; - String8 regular_obj_name = str8_lit("regular.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(regular_payload)); - coff_obj_writer_push_symbol_extern(obj_writer, shared_symbol_name, 0, data_sect); - String8 regular_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(regular_obj_name, regular_obj)) { - goto exit; - } - } - - String8 abs_obj_name = str8_lit("abs.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_abs(obj_writer, shared_symbol_name, 0x1234, COFF_SymStorageClass_External); - String8 abs_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(abs_obj_name, abs_obj)) { - goto exit; - } - } - - U8 entry_text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - String8 entry_obj_name = str8_lit("entry.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - COFF_ObjSymbol *shared_symbol = coff_obj_writer_push_symbol_undef(obj_writer, shared_symbol_name); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 3, shared_symbol, COFF_Reloc_X64_Addr32Nb); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } - } - - // TODO: validate that linker issues multiply defined symbol error - int abs_vs_regular_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe abs.obj regular.obj entry.obj"); - if (abs_vs_regular_exit_code == 0) { - // linker should complain about multiply defined symbol - goto exit; - } - - int regular_vs_abs_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe regular.obj abs.obj entry.obj"); - if (regular_vs_abs_exit_code == 0) { - // linker should complain even in case regular is before abs - goto exit; - } - - result = T_Result_Pass; - - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_abs_vs_common(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 shared_symbol_name = str8_lit("foo"); - - String8 common_obj_name = str8_lit("common.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_common(obj_writer, shared_symbol_name, 321); - String8 common_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(common_obj_name, common_obj)) { - goto exit; - } - } - - String8 abs_obj_name = str8_lit("abs.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_abs(obj_writer, shared_symbol_name, 0x1234, COFF_SymStorageClass_External); - String8 abs_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(abs_obj_name, abs_obj)) { - goto exit; - } - } - - U8 entry_text[] = { 0xC3 }; - String8 entry_obj_name = str8_lit("entry.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe abs.obj common.obj entry.obj"); - if (linker_exit_code == 0) { - // TODO: validate that linker issues multiply defined symbol error - int common_vs_abs = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe common.obj abs.obj entry.obj"); - if (t_ident_linker() == T_Linker_RAD) { - if (common_vs_abs == LNK_Error_MultiplyDefinedSymbol) { - result = T_Result_Pass; - } - } else { - if (common_vs_abs != 0) { - result = T_Result_Pass; - } - } - } - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_abs_vs_abs(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("foo"), 'a', COFF_SymStorageClass_External); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 b_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("foo"), 'b', COFF_SymStorageClass_External); - b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("b.obj"), b_obj)) { goto exit; } - t_write_entry_obj(); - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); - if (linker_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_undef_weak_lib(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 weak; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - COFF_ObjSymbol *b = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("b"), 0xc3000000, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("a"), COFF_WeakExt_SearchLibrary, b); - weak = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("a")); - U8 text[4] = {0}; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, sym); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - entry = coff_obj_writer_serialize(scratch.arena, obj_writer); - } - - if (!t_write_file(str8_lit("weak.obj"), weak)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry)) { goto exit; } - - // undefined symbol must always replace weak symbol with search library - int linker_exit_code = t_invoke_linkerf("/subsystem:console /out:a.exe /entry:entry entry.obj weak.obj"); - if (linker_exit_code != LNK_Error_UnresolvedSymbol) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_undef_weak_search_alias(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 weak_obj; - { - U8 weak_payload[] = { 0xDE, 0xAD, 0xBE, 0xEF }; - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *weak_sect = t_push_data_section(obj_writer, str8_array_fixed(weak_payload)); - COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("ptr")); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("foo"), COFF_WeakExt_SearchAlias, tag); - weak_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 ptr_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("entry")); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("ptr"), COFF_WeakExt_SearchAlias, tag); - ptr_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 undef_obj; - { - U8 undef_obj_payload[] = { 0x00, 0x00, 0x00, 0x00 }; - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *undef_sect = t_push_data_section(obj_writer, str8_array_fixed(undef_obj_payload)); - COFF_ObjSymbol *undef_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); - coff_obj_writer_section_push_reloc(obj_writer, undef_sect, 0, undef_symbol, COFF_Reloc_X64_Addr32Nb); - undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - U8 entry_payload[] = {0xC3}; - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_payload)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("weak.obj"), weak_obj)) { goto exit; } - if (!t_write_file(str8_lit("ptr.obj"), ptr_obj)) { goto exit; } - if (!t_write_file(str8_lit("undef.obj"), undef_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe weak.obj entry.obj ptr.obj undef.obj"); - if (linker_exit_code != 0) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_cycle(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - String8 ab_obj_name = str8_lit("ab.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *b = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("B")); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("A"), COFF_WeakExt_SearchAlias, b); - String8 ab_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(ab_obj_name, ab_obj)) { - goto exit; - } - } - - String8 ba_obj_name = str8_lit("ba.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *a = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("A")); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("B"), COFF_WeakExt_SearchAlias, a); - String8 ba_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(ba_obj_name, ba_obj)) { - goto exit; - } - } - - String8 entry_obj_name = str8_lit("entry.obj"); - U8 entry_payload[] = { 0xC3 }; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_payload)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } - } - - U64 time_out = os_now_microseconds() + 3 * 1000 * 1000; // give a generous 3 seconds - int linker_exit_code = t_invoke_linker_with_time_outf(time_out, "/subsystem:console /entry:my_entry %S %S %S", entry_obj_name, ab_obj_name, ba_obj_name); - if (linker_exit_code != T_LINKER_TIME_OUT_EXIT_CODE) { - if (t_ident_linker() == T_Linker_MSVC) { - if (linker_exit_code == T_MsvcLinkExitCode_UnresolvedExternals) { - result = T_Result_Pass; - } - } else { - result = T_Result_Pass; - } - } - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_weak_tag(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - U32 weak_tag_expected_value = 0x12345678; - String8 weak_tag_obj_name = str8_lit("weak_tag.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *tag_symbol = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("abs"), weak_tag_expected_value, COFF_SymStorageClass_Static); - COFF_ObjSymbol *weak_first = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("strong_first"), COFF_WeakExt_SearchAlias, tag_symbol); - COFF_ObjSymbol *weak_second = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("strong_second"), COFF_WeakExt_SearchAlias, weak_first); - - U8 sect_data[4] = {0}; - COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_array_fixed(sect_data)); - coff_obj_writer_section_push_reloc(obj_writer, sect, 0, weak_second, COFF_Reloc_X64_Addr32); - - String8 weak_tag_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(weak_tag_obj_name, weak_tag_obj)) { goto exit; } - } - - String8 entry_name = str8_lit("my_entry"); - U8 entry_text[] = { 0xC3 }; - String8 entry_obj_name = str8_lit("entry.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); - coff_obj_writer_push_symbol_extern(obj_writer, entry_name, 0, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe %S %S", weak_tag_obj_name, entry_obj_name); - if (linker_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *data_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - String8 data = str8_substr(exe, rng_1u64(data_section->foff, data_section->foff + data_section->vsize)); - if (!data_section) { goto exit; } - if (data_section->vsize != 4) { goto exit; } - if (!str8_match(data, str8_struct(&weak_tag_expected_value), 0)) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal String8 -t_make_sec_defn_obj(Arena *arena, String8 payload) -{ - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *mysect_section = coff_obj_writer_push_section(obj_writer, str8_lit(".mysect"), COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_Align1Bytes, payload); - coff_obj_writer_push_symbol_secdef(obj_writer, mysect_section, COFF_ComdatSelect_Null); - String8 obj = coff_obj_writer_serialize(arena, obj_writer); - coff_obj_writer_release(&obj_writer); - return obj; -} - -internal T_Result -t_undef_section(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 main_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - - U8 data[] = { 0, 0, 0, 0 }; - COFF_ObjSection *data_section = t_push_data_section(obj_writer, str8_array_fixed(data)); - COFF_ObjSymbol *foo = coff_obj_writer_push_symbol_undef_sect(obj_writer, str8_lit(".mysect"), COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead); - coff_obj_writer_section_push_reloc(obj_writer, data_section, 0, foo, COFF_Reloc_X64_Addr32Nb); - - U8 text[] = { 0xC3 }; - COFF_ObjSection *text_section = t_push_text_section(obj_writer, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_section); - - main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - - coff_obj_writer_release(&obj_writer); - } - - U8 payload[] = { 1, 2, 3 }; - String8 sec_defn_obj = t_make_sec_defn_obj(scratch.arena, str8_array_fixed(payload)); - - t_write_file(str8_lit("main.obj"), main_obj); - t_write_file(str8_lit("sec_defn.obj"), sec_defn_obj); - - int linker_exit_code = t_invoke_linker(str8_lit("/subsystem:console /entry:my_entry /out:a.exe main.obj sec_defn.obj")); - if (linker_exit_code == 0) { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeader *data_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - COFF_SectionHeader *mysect_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".mysect")); - if (data_section && mysect_section) { - if (data_section->vsize == 4 && mysect_section->vsize == 3) { - String8 addr32nb = str8_substr(exe, rng_1u64(data_section->foff, data_section->foff + data_section->vsize)); - String8 expected_voff = str8_struct(&mysect_section->voff); - if (str8_match(addr32nb, expected_voff, 0)) { - result = T_Result_Pass; - } - } - } - } - - scratch_end(scratch); - return result; -} - -internal T_Result -t_sect_symbol(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - String8 sect_payload = str8_lit("hello, world"); - String8 sect_obj_name = str8_lit("sect.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".mysect$1"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, sect_payload); - coff_obj_writer_push_directive(obj_writer, str8_lit("/merge:.mysect=.data")); - String8 sect_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(sect_obj_name, sect_obj)) { - goto exit; - } - } - - String8 main_obj_name = str8_lit("main.obj"); - { - U8 data[8] = {0}; - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_array_fixed(data)); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef_sect(obj_writer, str8_lit(".mysect$2222"), PE_DATA_SECTION_FLAGS); - coff_obj_writer_section_push_reloc_addr(obj_writer, sect, 0, symbol); - - U8 text[] = { 0xC3 }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - - String8 main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - - if (!t_write_file(main_obj_name, main_obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe main.obj sect.obj"); - - if (linker_exit_code != 0) { - goto exit; - } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - - if (!sect) { - goto exit; - } - - String8 sect_data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - - String8 addr_data = str8_substr(sect_data, rng_1u64(0, sizeof(U64))); - if (addr_data.size != sizeof(U64)) { - goto exit; - } - U64 addr = *(U64 *)addr_data.str; - if (addr - (pe.image_base + sect->voff) != 8) { - goto exit; - } - - String8 payload_got = str8_substr(sect_data, rng_1u64(8, sect_data.size)); - if (!str8_match(payload_got, sect_payload, 0)) { - goto exit; - } - - result = T_Result_Pass; - - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_undef_reloc_section(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 main_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - - U8 text[] = { 0xC3 }; - COFF_ObjSection *text_section = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - COFF_ObjSymbol *my_entry_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_section); - - U8 data[8] = { 0 }; - COFF_ObjSection *data_section = t_push_data_section(obj_writer, str8_array_fixed(data)); - COFF_ObjSymbol *foo = coff_obj_writer_push_symbol_undef_sect(obj_writer, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS); - coff_obj_writer_section_push_reloc(obj_writer, data_section, 0, foo, COFF_Reloc_X64_Addr64); - - main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - - coff_obj_writer_release(&obj_writer); - } - - U8 payload[] = { 1, 2, 3 }; - String8 sec_defn_obj = t_make_sec_defn_obj(scratch.arena, str8_array_fixed(payload)); - - t_write_file(str8_lit("main.obj"), main_obj); - t_write_file(str8_lit("sec_defn.obj"), sec_defn_obj); - - int linker_exit_code = t_invoke_linker(str8_lit("/subsystem:console /entry:my_entry /out:a.exe main.obj sec_defn.obj")); - if (t_ident_linker() == T_Linker_RAD) { - if (linker_exit_code != LNK_Error_SectRefsDiscardedMemory) { - goto exit; - } - } else if (linker_exit_code == 0) { - goto exit; - } - - result = T_Result_Pass; - - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_find_merged_pdata(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - U8 foobar_payload[] = { - 0x40, 0x57, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x33, 0xC4, 0x48, 0x89, 0x84, 0x24, 0xF0, 0x01, 0x00, 0x00, 0x48, 0x8D, 0x04, 0x24, 0x48, - 0x8B, 0xF8, 0x33, 0xC0, 0xB9, 0xEC, 0x01, 0x00, 0x00, 0xF3, 0xAA, 0xB8, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x6B, 0xC0, 0x02, 0x8B, 0x04, 0x04, 0x48, 0x8B, 0x8C, 0x24, 0xF0, 0x01, 0x00, 0x00, 0x48, - 0x33, 0xCC, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x81, 0xC4, 0x00, 0x02, 0x00, 0x00, 0x5F, 0xC3, - 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, - 0x48, 0x83, 0xEC, 0x28, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 0x28, 0xC3 - }; - U8 xdata_payload[] = { - 0x19, 0x1B, 0x03, 0x00, 0x09, 0x01, 0x40, 0x00, 0x02, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xF0, 0x01, 0x00, 0x00, 0x01, 0x04, 0x01, 0x00, 0x04, 0x42, 0x00, 0x00 - }; - PE_IntelPdata intel_pdata = {0}; - U8 text_payload[] = { 0xC3 }; - - String8 main_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *xdata = coff_obj_writer_push_section(obj_writer, str8_lit(".xdata"), COFF_SectionFlag_MemRead|COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_Align4Bytes, str8_array_fixed(xdata_payload)); - COFF_ObjSection *pdata = coff_obj_writer_push_section(obj_writer, str8_lit(".pdata"), COFF_SectionFlag_MemRead|COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_Align4Bytes, str8_struct(&intel_pdata)); - COFF_ObjSection *foobar = coff_obj_writer_push_section(obj_writer, str8_lit(".foobar"), COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute|COFF_SectionFlag_CntCode|COFF_SectionFlag_Align1Bytes, str8_array_fixed(foobar_payload)); - COFF_ObjSection *text = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute|COFF_SectionFlag_CntCode|COFF_SectionFlag_Align1Bytes, str8_array_fixed(text_payload)); - - COFF_ObjSymbol *foobar_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("foobar"), 0, foobar); - - coff_obj_writer_push_symbol_secdef(obj_writer, xdata, COFF_ComdatSelect_Null); - COFF_ObjSymbol *unwind_foobar = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("$unwind$foobar"), 0, xdata); - - coff_obj_writer_push_symbol_secdef(obj_writer, pdata, COFF_ComdatSelect_Null); - coff_obj_writer_push_symbol_static(obj_writer, str8_lit("$pdata$foobar"), 0, pdata); - - coff_obj_writer_section_push_reloc(obj_writer, pdata, OffsetOf(PE_IntelPdata, voff_unwind_info), unwind_foobar, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, pdata, OffsetOf(PE_IntelPdata, voff_first), foobar_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_section_push_reloc(obj_writer, pdata, OffsetOf(PE_IntelPdata, voff_one_past_last), foobar_symbol, COFF_Reloc_X64_Addr32Nb); - - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text); - main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - t_write_file(str8_lit("main.obj"), main_obj); - - int linker_exit_code = t_invoke_linker(str8_lit("/subsystem:console /entry:my_entry /out:a.exe main.obj /merge:.pdata=.rdata")); - if (linker_exit_code == 0) { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); - if (dim_1u64(pe.data_dir_franges[PE_DataDirectoryIndex_EXCEPTIONS]) == 0xC) { - result = T_Result_Pass; - } - } - - scratch_end(scratch); - return result; -} - -internal T_Result -t_section_sort(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - String8 data_obj_name = str8_lit("data.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - - COFF_SectionFlags data_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemRead|COFF_SectionFlag_Align1Bytes; - coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), data_flags, str8_lit("five")); - coff_obj_writer_push_section(obj_writer, str8_lit(".data$a"), data_flags, str8_lit("three")); - coff_obj_writer_push_section(obj_writer, str8_lit(".data$bbbbb"), data_flags, str8_lit("four")); - coff_obj_writer_push_section(obj_writer, str8_lit(".data$"), data_flags, str8_lit("two")); - coff_obj_writer_push_section(obj_writer, str8_lit(".data"), data_flags, str8_lit("one")); - - String8 data_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(data_obj_name, data_obj)) { - goto exit; - } - } - - String8 entry_obj_name = str8_lit("entry.obj"); - U8 entry_text[] = { 0xC3 }; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe data.obj entry.obj"); - if (linker_exit_code != 0) { - goto exit; - } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeader *data_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - if (!data_section) { - goto exit; - } - - String8 data = str8_substr(exe, rng_1u64(data_section->foff, data_section->foff + data_section->vsize)); - String8 expected_data = str8_lit("onetwothreefourfive"); - if (!str8_match(data, expected_data, 0)) { - goto exit; - } - - result = T_Result_Pass; - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_flag_conf(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - COFF_SectionFlags my_sect0_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute; - COFF_SectionFlags my_sect1_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite; - String8 conf_obj_name = str8_lit("conf.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *a_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".mysect"), my_sect0_flags, str8_lit("one")); - COFF_ObjSection *b_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".mysect"), my_sect1_flags, str8_lit("two")); - String8 conf_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(conf_obj_name, conf_obj)) { - goto exit; - } - } - - U8 entry_text[] = { 0xC3 }; - String8 entry_obj_name = str8_lit("entry.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe conf.obj entry.obj"); - if (linker_exit_code != 0) { - goto exit; - } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeaderArray my_sects = t_coff_section_header_array_from_name(scratch.arena, string_table, section_table, pe.section_count, str8_lit(".mysect")); - - if (my_sects.count != 2) { - goto exit; - } - - COFF_SectionHeader *my_sect0 = &my_sects.v[0]; - COFF_SectionHeader *my_sect1 = &my_sects.v[1]; - if (my_sect0->flags != my_sect0_flags) { - goto exit; - } - if (my_sect1->flags != my_sect1_flags) { - goto exit; - } - - result = T_Result_Pass; - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_invalid_bss(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - COFF_SectionFlags bss_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead; - String8 bss_obj_name = str8_lit("bss.obj"); - String8 bss_data = str8_lit("Hello, World"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_section(obj_writer, str8_lit(".bss"), bss_flags, bss_data); - String8 bss_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(bss_obj_name, bss_obj)) { - goto exit; - } - } - - String8 entry_obj_name = str8_lit("entry.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { 0xC3 }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe bss.obj entry.obj"); - if (linker_exit_code != 0) { - goto exit; - } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeader *bss_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".bss")); - if (bss_sect == 0) { - goto exit; - } - if (bss_sect->vsize != 0xC) { - goto exit; - } - if (bss_sect->flags != bss_flags) { - goto exit; - } - String8 data = str8_substr(exe, rng_1u64(bss_sect->foff, bss_sect->foff + bss_sect->vsize)); - if (!str8_match(data, bss_data, 0)) { - goto exit; - } - - result = T_Result_Pass; - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_common_block(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - String8 a_obj_name = str8_lit("a.obj"); - U8 a_data[6] = {0}; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_common(obj_writer, str8_lit("A"), 3); - COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(a_data)); - data_sect->flags |= COFF_SectionFlag_Align1Bytes; - coff_obj_writer_push_section(obj_writer, str8_lit(".bss"), PE_BSS_SECTION_FLAGS, str8(0, 1)); // shift common block's initial position - coff_obj_writer_section_push_reloc(obj_writer, data_sect, 0, symbol, COFF_Reloc_X64_Addr32); - String8 a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(a_obj_name, a_obj)) { goto exit; } - } - - String8 b_obj_name = str8_lit("b.obj"); - U8 b_data[9] = { 0 }; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(b_data)); - data_sect->flags |= COFF_SectionFlag_Align1Bytes; - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_common(obj_writer, str8_lit("B"), 6); - coff_obj_writer_section_push_reloc(obj_writer, data_sect, 0, symbol, COFF_Reloc_X64_Addr64); - String8 b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(b_obj_name, b_obj)) { goto exit; } - } - - String8 entry_obj_name = str8_lit("entry.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { 0xC3 }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe /fixed /largeaddressaware:no /merge:.bss=.comm a.obj b.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); - String8 string_table = str8_substr(exe, pe.string_table_range); - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; - COFF_SectionHeader *comm_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".comm")); - COFF_SectionHeader *data_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - if (comm_sect == 0) { goto exit; } - if (data_sect == 0) { goto exit; } - - // blocks must be sorted in descending order to reduce alignment padding - if (comm_sect->vsize != 0x13) { goto exit; } - - // ensure linker correctly patched addresses for symbols pointing into common block - String8 data = str8_substr(exe, rng_1u64(data_sect->foff, data_sect->foff + data_sect->fsize)); - U32 *a_addr = (U32 *)data.str; - U64 *b_addr = (U64 *)(data.str + sizeof(a_data)); - if (*a_addr != (pe.image_base + comm_sect->voff + 0x10)) { goto exit; } - if (*b_addr != (pe.image_base + comm_sect->voff + 0x8)) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_base_relocs(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - // main.obj - String8 entry_name = str8_lit("my_entry"); - U64 mov_func_name64 = 2; - U64 mov_func_name32 = 16; - U8 main_text[] = { - 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, func_name - 0xff, 0xd0, // call rax - 0x48, 0x31, 0xc0, // xor rax, rax - 0xb8, 0x00, 0x00, 0x00, 0x00, // mov eax, func_name - 0xff, 0xd0, // call rax - 0xc3 // ret - }; - - // func.obj - String8 func_name = str8_lit("foo"); - U8 func_text[] = { 0xc3 }; - - String8 main_obj_name = str8_lit("main.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(main_text)); - COFF_ObjSymbol *func_undef = coff_obj_writer_push_symbol_undef(obj_writer, func_name); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, mov_func_name64, func_undef, COFF_Reloc_X64_Addr64); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, mov_func_name32, func_undef, COFF_Reloc_X64_Addr32); - - // linker must not produce base relocations for absolute symbol - U8 data[4] = {0}; - COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(data)); - COFF_ObjSymbol *abs_symbol = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("abs"), 0x12345678, COFF_SymStorageClass_Static); - coff_obj_writer_section_push_reloc(obj_writer, data_sect, 0, abs_symbol, COFF_Reloc_X64_Addr32); - - coff_obj_writer_push_symbol_extern(obj_writer, entry_name, 0, text_sect); - String8 main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(main_obj_name, main_obj)) { - goto exit; - } - } - - String8 func_obj_name = str8_lit("func.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(func_text)); - coff_obj_writer_push_symbol_extern(obj_writer, func_name, 0, text_sect); - String8 func_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(func_obj_name, func_obj)) { - goto exit; - } - } - - String8 out_name = str8_lit("a.exe"); - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe main.obj func.obj"); - if (linker_exit_code != 0) { - goto exit; - } - - // it is illegal to merge .reloc with other sections - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.reloc=.rdata main.obj func.obj"); - if (t_ident_linker() == T_Linker_RAD) { - if (linker_exit_code != LNK_Error_IllegalSectionMerge) { - goto exit; - } - } else { - if (linker_exit_code == 0) { - goto exit; - } - } - - // the other way around is illegal too - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.rdata=.reloc main.obj func.obj"); - if (t_ident_linker() == T_Linker_RAD) { - if (linker_exit_code != LNK_Error_IllegalSectionMerge) { - goto exit; - } - } else { - if (linker_exit_code == 0) { - goto exit; - } - } - - result = T_Result_Pass; - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_simple_lib_test(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 test_payload = str8_lit("The quick brown fox jumps over the lazy dog"); - String8 test_obj = {0}; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_Unknown); - COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8(test_payload.str, test_payload.size+1)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("test"), 0, data_sect); - test_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 test_lib_name = str8_lit("test.lib"); - { - COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); - coff_lib_writer_push_obj(lib_writer, str8_lit("test.obj"), test_obj); - String8 test_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); - coff_lib_writer_release(&lib_writer); - if (!t_write_file(test_lib_name, test_lib)) { - goto exit; - } - } - - U8 entry_text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, - 0xC3 - }; - String8 entry_obj_name = str8_lit("entry.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); - COFF_ObjSymbol *test_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("test")); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 3, test_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 7, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe entry.obj test.lib"); - if (linker_exit_code != 0) { - goto exit; - } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - COFF_SectionHeader *data_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->fsize)); - String8 data_data = str8_substr(exe, rng_1u64(data_sect->foff, data_sect->foff + data_sect->fsize)); - - // was test payload linked? - String8 data_string = str8_cstring_capped(data_data.str, data_data.str + data_data.size); - if (!str8_match(data_string, test_payload, 0)) { - goto exit; - } - - // do we have enough bytes to read text? - if (text_data.size < sizeof(entry_text)) { - goto exit; - } - - // linker must pull-in test.obj and patch relocation for "test" symbol - U32 *data_addr32nb = (U32 *)(text_data.str+3); - if (*data_addr32nb != data_sect->voff) { - goto exit; - } - - result = T_Result_Pass; - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_import_export(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - { - String8 export_obj_name = str8_lit("export.obj"); - String8 export_obj_payload = str8_lit("test"); - U8 export_text[] = { 0xC3 }; - - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *data_sect = t_push_data_section(obj_writer, export_obj_payload); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("foo"), 0, data_sect); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("ord"), 0, data_sect); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("ord2"), 0, data_sect); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("ord3"), 0, data_sect); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("ord4"), 0, data_sect); - //coff_obj_writer_push_directive(obj_writer, str8_lit("/export:foo=foo")); - //coff_obj_writer_push_directive(obj_writer, str8_lit("/export:bar=foo")); - //coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord,@5")); - //coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord2,@6,DATA")); - //coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord3,@7,NONAME,PRIVATE")); - //coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord4,@8,NONAME,DATA")); - //coff_obj_writer_push_directive(obj_writer, str8_lit("/export:baz=baz.qwe")); - //coff_obj_writer_push_directive(obj_writer, str8_lit("/export:baf=baz.#1")); - String8 export_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(export_obj_name, export_obj)) { - goto exit; - } - } - - { - String8 import_obj_name = str8_lit("import.obj"); - U8 import_payload[1024] = {0}; - - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(import_payload)); - - char *import_symbols[] = { - "__imp_foo", - "__imp_bar", - "__imp_baz", - "__imp_baf", - "__imp_ord", - //"__imp_ord2", - //"__imp_ord4", - "bar", - //"baf", - //"baz", - "foo", - "ord", - }; - - for (U64 i = 0; i < ArrayCount(import_symbols); ++i) { - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_cstring(import_symbols[i])); - coff_obj_writer_section_push_reloc_voff(obj_writer, data_sect, i * 4, symbol); - } - - String8 import_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(import_obj_name, import_obj)) { - goto exit; - } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *s1 = coff_obj_writer_push_section(obj_writer, str8_lit(".s1"), PE_DATA_SECTION_FLAGS, str8_lit("s1")); - COFF_ObjSection *s2 = coff_obj_writer_push_section(obj_writer, str8_lit(".s2"), PE_DATA_SECTION_FLAGS, str8_lit("s2")); - COFF_ObjSection *text_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed((U8[]){ 0xC3 })); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("_DllMainCRTStartup"), 0, text_sect); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("s1"), 0, s1); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("s2"), 0, s2); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("baz.obj"), obj)) { - goto exit; - } - } - - t_write_entry_obj(); - - // - // link export.dll - // - int lib_link_exit_code = t_invoke_linkerf("/dll /out:export.dll libcmt.lib export.obj"); - if (lib_link_exit_code != 0) { - goto exit; - } - - // - // link baz.dll - // - int baz_link_exit_code = t_invoke_linkerf("/dll /out:baz.dll /export:s1,@1,NONAME /export:qwe=s2 baz.obj"); - if (baz_link_exit_code != 0) { - goto exit; - } - - // - // check export.dll export table - // - if (0) { - String8 dll = t_read_file(scratch.arena, str8_lit("export.dll")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, dll); - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(dll, pe.section_table_range).str; - String8 string_table = str8_substr(dll, pe.string_table_range); - PE_ParsedExportTable export_table = pe_exports_from_data(scratch.arena, pe.section_count, section_table, dll, pe.data_dir_franges[PE_DataDirectoryIndex_EXPORT], pe.data_dir_vranges[PE_DataDirectoryIndex_EXPORT]); - - if (export_table.export_count != 6) { - goto exit; - } - - { - String8 expected_symbols[] = { - str8_lit_comp("foo"), - str8_lit_comp("bar"), - //str8_lit_comp("baz"), - //str8_lit_comp("baf"), - str8_lit_comp("ord"), - str8_lit_comp("ord2") - }; - U64 match_count = 0; - for (U64 i = 0; i < export_table.export_count; i += 1) { - for (U64 k = 0; k < ArrayCount(expected_symbols); k += 1) { - if (str8_match(export_table.exports[i].name, expected_symbols[k], 0)) { - match_count += 1; - } - } - } - if (match_count != ArrayCount(expected_symbols)) { - goto exit; - } - } - - if (0) { - String8 expected_forwarders[] = { - str8_lit_comp("baz.qwe"), - str8_lit_comp("baz.#1"), - }; - U64 match_count = 0; - for (U64 i = 0; i < export_table.export_count; i += 1) { - for (U64 k = 0; k < ArrayCount(expected_forwarders); k += 1) { - if (str8_match(export_table.exports[i].forwarder, expected_forwarders[k], 0)) { - match_count += 1; - } - } - } - if (match_count != ArrayCount(expected_forwarders)) { - goto exit; - } - } - } - - #if OS_WINDOWS - BOOL is_dir_set = SetDllDirectoryA((LPCSTR)g_wdir.str); - AssertAlways(is_dir_set); - - HANDLE export_dll = LoadLibrary("export.dll"); - DWORD last_error = GetLastError(); - if (export_dll == 0) { - goto exit; - } - - char *export_dll_procs[] = { "foo", "bar", "baz", "baf" }; - for (U64 i = 0; i < ArrayCount(export_dll_procs); ++i) { - void *proc = GetProcAddress(export_dll, export_dll_procs[i]); - if (proc == 0) { - goto exit; - } - } - - U16 export_dll_ordinals[] = { 5 }; - for (U64 i = 0; i < ArrayCount(export_dll_ordinals); ++i) { - void *proc = GetProcAddress(export_dll, MAKEINTRESOURCE(export_dll_ordinals[i])); - if (proc == 0) { - goto exit; - } - } - #endif - - int import_link_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /delayload:export.dll /export:entry kernel32.Lib delayimp.lib libcmt.lib export.lib import.obj entry.obj"); - if (import_link_exit_code != 0) { - goto exit; - } - - // TODO: check import table - - result = T_Result_Pass; - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_image_base(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - String8 obj_name = str8_lit("image_base.obj"); - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00, // lea rcx, [__ImageBase] - 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, __ImageBase - 0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, __ImageBase - 0xC3 // ret - }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - COFF_ObjSymbol *image_base_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__ImageBase")); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 3, image_base_symbol, COFF_Reloc_X64_Rel32); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 9, image_base_symbol, COFF_Reloc_X64_Addr64); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 18, image_base_symbol, COFF_Reloc_X64_Addr32Nb); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(obj_name, obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /base:0x2000000140000000 /out:a.exe image_base.obj"); - - if (linker_exit_code != 0) { - goto exit; - } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - - if (!text_section) { - goto exit; - } - - U8 expected_text[] = { - 0x48, 0x8D, 0x0D, 0xF9, 0xEF, 0xFF, 0xFF, - 0x48, 0xB8, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x20, - 0xB8, 0x00, 0x00, 0x00, 0x00, - 0xC3 - }; - String8 text_data = str8_substr(exe, rng_1u64(text_section->foff, text_section->foff + sizeof(expected_text))); - if (!str8_match(text_data, str8_array_fixed(expected_text), 0)) { - goto exit; - } - - result = T_Result_Pass; - - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_any(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".test$mn"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("1")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Any); - COFF_ObjSymbol *test = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - test->type.u.msb = COFF_SymDType_Func; - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("1.obj"), obj)) { - goto exit; - } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".test$mn"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("2")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Any); - COFF_ObjSymbol *test = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("2.obj"), obj)) { - goto exit; - } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - { - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:1.exe 1.obj 2.obj entry.obj"); - if (linker_exit_code != 0) { - goto exit; - } - String8 exe = t_read_file(scratch.arena, str8_lit("1.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".test")); - String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(data, str8_lit("1"), 0)) { - goto exit; - } - } - - { - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:2.exe 2.obj 1.obj entry.obj"); - if (linker_exit_code != 0) { - goto exit; - } - String8 exe = t_read_file(scratch.arena, str8_lit("2.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".test")); - String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(data, str8_lit("2"), 0)) { - goto exit; - } - } - - result = T_Result_Pass; - - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_no_duplicates(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 test_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *test_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".test"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("a")); - coff_obj_writer_push_symbol_secdef(obj_writer, test_sect, COFF_ComdatSelect_NoDuplicates); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("a"), 0, test_sect); - test_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("a")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - if (!t_write_file(str8_lit("a.obj"), test_obj)) { goto exit; } - if (!t_write_file(str8_lit("b.obj"), test_obj)) { goto exit; } - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - - int duplicates_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); - if (duplicates_exit_code == 0) { goto exit; } - if (t_ident_linker() == T_Linker_RAD && duplicates_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } - - int good_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj entry.obj"); - if (good_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("b.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".test")); - if (!sect) { goto exit; } - String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(data, str8_lit("a"), 0)) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_same_size(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("a")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_SameSize); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("a.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("b")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_SameSize); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("b.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".c"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("cc")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_SameSize); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("c.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int same_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); - if (same_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".a")); - if (sect == 0) { goto exit; } - String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(data, str8_lit("a"), 0)) { goto exit; } - } - - int not_same_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj b.obj c.obj entry.obj"); - if (not_same_exit_code == 0) { goto exit; } - if (t_ident_linker() == T_Linker_RAD && not_same_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_exact_match(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_ExactMatch); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("a.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a2"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_ExactMatch); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("a2.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("b")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_ExactMatch); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("b.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int not_exact_match = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj b.obj"); - if (not_exact_match == 0) { goto exit; } - - int exact_match = t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe entry.obj a2.obj a.obj"); - if (exact_match != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("b.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".a2")); - if (sect == 0) { goto exit; } - String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(data, str8_lit("a"), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_largest(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("a.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("bb")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("b.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".c"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("c")); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("c.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int largest_exit_code = t_invoke_linkerf("/subsystem:console /out:a.exe /entry:entry entry.obj a.obj b.obj"); - if (largest_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *discard_sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".a")); - if (discard_sect != 0) { goto exit; } - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".b")); - if (sect == 0) { goto exit; } - String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(data, str8_lit("bb"), 0)) { goto exit; } - } - - int same_size_exit_code = t_invoke_linkerf("/subsystem:console /out:b.exe /entry:entry entry.obj c.obj a.obj"); - if (same_size_exit_code != 0) { goto exit; } - - { - String8 exe = t_read_file(scratch.arena, str8_lit("b.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".c")); - if (sect == 0) { goto exit; } - String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - if (!str8_match(data, str8_lit("c"), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_associative(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - COFF_ObjSection *a = coff_obj_writer_push_section(obj_writer, str8_lit("a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); - COFF_ObjSection *aa = coff_obj_writer_push_section(obj_writer, str8_lit("aa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("aa")); - coff_obj_writer_push_symbol_secdef(obj_writer, a, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, a); - coff_obj_writer_push_symbol_associative(obj_writer, aa, a); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("a.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - COFF_ObjSection *bb = coff_obj_writer_push_section(obj_writer, str8_lit("bb"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("bb")); - COFF_ObjSection *b = coff_obj_writer_push_section(obj_writer, str8_lit("b"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("b")); - COFF_ObjSection *bbb = coff_obj_writer_push_section(obj_writer, str8_lit("bbb"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("bbb")); - coff_obj_writer_push_symbol_secdef(obj_writer, bb, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_associative(obj_writer, b, bb); - coff_obj_writer_push_symbol_associative(obj_writer, bbb, bb); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, bb); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("b.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj b.obj"); - if (linker_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeader *a = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("a")); - COFF_SectionHeader *aa = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("aa")); - COFF_SectionHeader *b = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("b")); - COFF_SectionHeader *bb = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("bb")); - COFF_SectionHeader *bbb = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("bbb")); - if (a != 0) { goto exit; } - if (aa != 0) { goto exit; } - if (b == 0) { goto exit; } - if (bb == 0) { goto exit; } - if (bbb == 0) { goto exit; } - String8 b_data = str8_substr(exe, rng_1u64(b->foff, b->foff + b->vsize)); - String8 bb_data = str8_substr(exe, rng_1u64(bb->foff, bb->foff + bb->vsize)); - String8 bbb_data = str8_substr(exe, rng_1u64(bbb->foff, bbb->foff + bbb->vsize)); - if (!str8_match(b_data, str8_lit("b"), 0)) { goto exit; } - if (!str8_match(bb_data, str8_lit("bb"), 0)) { goto exit; } - if (!str8_match(bbb_data, str8_lit("bbb"), 0)) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_associative_loop(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *aaaa = coff_obj_writer_push_section(obj_writer, str8_lit(".aaaa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("aaaa")); - COFF_ObjSection *aa = coff_obj_writer_push_section(obj_writer, str8_lit(".aa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("aa")); - COFF_ObjSection *a = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("a")); - COFF_ObjSection *aaa = coff_obj_writer_push_section(obj_writer, str8_lit(".aaa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("aaa")); - coff_obj_writer_push_symbol_associative(obj_writer, aaa, aa); - coff_obj_writer_push_symbol_associative(obj_writer, aaaa, aaa); - coff_obj_writer_push_symbol_associative(obj_writer, a, aa); - coff_obj_writer_push_symbol_associative(obj_writer, aa, a); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("loop.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe loop.obj entry.obj"); - if (exit_code == 0) { goto exit; } - if (t_ident_linker() == T_Linker_RAD && exit_code != LNK_Error_AssociativeLoop) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_associative_non_comdat(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *a = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("a")); - COFF_ObjSection *b = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS, str8_lit("b")); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, a); - coff_obj_writer_push_symbol_associative(obj_writer, b, a); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("test.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj test.obj"); - if (exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *a = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".a")); - COFF_SectionHeader *b = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".b")); - if (a == 0) { goto exit; } - if (b == 0) { goto exit; } - String8 a_data = str8_substr(exe, rng_1u64(a->foff, a->foff + a->vsize)); - String8 b_data = str8_substr(exe, rng_1u64(b->foff, b->foff + b->vsize)); - if (!str8_match(a_data, str8_lit("a"), 0)) { goto exit; } - if (!str8_match(b_data, str8_lit("b"), 0)) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_associative_out_of_bounds(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - COFF_ObjSection *a = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); - COFF_ObjSection *aa = coff_obj_writer_push_section(obj_writer, str8_lit(".aa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("aa")); - coff_obj_writer_push_symbol_secdef(obj_writer, a, COFF_ComdatSelect_Any); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, a); - coff_obj_writer_push_symbol_associative(obj_writer, aa, a); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - { - COFF_FileHeaderInfo header = coff_file_header_info_from_data(obj); - String8 string_table = str8_substr(obj, header.string_table_range); - String8 symbol_table = str8_substr(obj, header.symbol_table_range); - COFF_ParsedSymbol symbol = coff_parse_symbol(header, string_table, symbol_table, 3); - AssertAlways(str8_match(symbol.name, str8_lit(".aa"), 0)); - AssertAlways(symbol.aux_symbol_count == 1); - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - COFF_SymbolSecDef *secdef = (COFF_SymbolSecDef *)(symbol16 + 1); - secdef->number_lo = 321; - } - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("bad.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj bad.obj"); - if (linker_exit_code == 0) { goto exit; } - if (t_ident_linker() == T_Linker_RAD && linker_exit_code != LNK_Error_IllData) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_comdat_with_offset(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 a[] = "1Hello, World!"; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(a)); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 1, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("a.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 a[] = "Hello, World!"; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(a)); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 1, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("b.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 3, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_reloc_against_removed_comdat(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 a[] = "1Hello, World!"; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(a)); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 1, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("a.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 a[] = "H"; - COFF_ObjSection *comdat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(a)); - coff_obj_writer_push_symbol_secdef(obj_writer, comdat_sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 1, comdat_sect); - COFF_ObjSymbol *static_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("STATIC"), 2, comdat_sect); - - U8 rdata[4] = {0}; - COFF_ObjSection *regular_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS, str8_array_fixed(rdata)); - coff_obj_writer_section_push_reloc_voff(obj_writer, regular_sect, 0, static_symbol); - - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("b.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 3, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj c.obj entry.obj"); - if (linker_exit_code != LNK_Error_RelocationAgainstRemovedSection) { goto exit; } - -exit:; - result = T_Result_Pass; - scratch_end(scratch); - return result; -} - -internal T_Result -t_sect_align(void) -{ - Temp scratch = scratch_begin(0,0); - - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - - COFF_ObjSection *sect_align_shift = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("q")); - COFF_ObjSection *sect_align_none = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("abc")); - COFF_ObjSection *sect_align_1 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_lit("wr")); - COFF_ObjSection *sect_align_2 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_lit("e")); - COFF_ObjSection *sect_align_4 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_lit("ttttt")); - COFF_ObjSection *sect_align_8 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align8Bytes, str8_lit("g")); - COFF_ObjSection *sect_align_16 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align16Bytes, str8_lit("o")); - COFF_ObjSection *sect_align_32 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align32Bytes, str8_lit("p")); - COFF_ObjSection *sect_align_64 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align64Bytes, str8_lit("f")); - COFF_ObjSection *sect_align_128 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align128Bytes, str8_lit("x")); - COFF_ObjSection *sect_align_256 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align256Bytes, str8_lit("c")); - COFF_ObjSection *sect_align_512 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align512Bytes, str8_lit("v")); - COFF_ObjSection *sect_align_1024 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align1024Bytes, str8_lit("b")); - COFF_ObjSection *sect_align_2048 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align2048Bytes, str8_lit("n")); - COFF_ObjSection *sect_align_4096 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align4096Bytes, str8_lit("m")); - COFF_ObjSection *sect_align_8192 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align8192Bytes, str8_lit("z")); - - U8 text[] = { 0xC3 }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); - - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("test.obj"), obj)) { - goto exit; - } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe /align:8192 test.obj"); - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - - COFF_SectionHeader *sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".a")); - if (!sect) { - goto exit; - } - String8 sect_data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); - - String8 shift = str8_substr(sect_data, rng_1u64(0, 1)); - if (!str8_match(shift, str8_lit("q"), 0)) { - goto exit; - } - String8 a_none = str8_substr(sect_data, rng_1u64(16, 16 + 3)); - if (!str8_match(a_none, str8_lit("abc"), 0)) { - goto exit; - } - String8 a_1 = str8_substr(sect_data, rng_1u64(19, 21)); - if (!str8_match(a_1, str8_lit("wr"), 0)) { - goto exit; - } - String8 a_2 = str8_substr(sect_data, rng_1u64(22, 23)); - if (!str8_match(a_2, str8_lit("e"), 0)) { - goto exit; - } - String8 a_4 = str8_substr(sect_data, rng_1u64(24, 29)); - if (!str8_match(a_4, str8_lit("ttttt"), 0)) { - goto exit; - } - String8 a_8 = str8_substr(sect_data, rng_1u64(32, 33)); - if (!str8_match(a_8, str8_lit("g"), 0)) { - goto exit; - } - String8 a_16 = str8_substr(sect_data, rng_1u64(48, 49)); - if (!str8_match(a_16, str8_lit("o"), 0)) { - goto exit; - } - String8 a_32 = str8_substr(sect_data, rng_1u64(64, 65)); - if (!str8_match(a_32, str8_lit("p"), 0)) { - goto exit; - } - String8 a_64 = str8_substr(sect_data, rng_1u64(128, 129)); - if (!str8_match(a_64, str8_lit("f"), 0)) { - goto exit; - } - String8 a_128 = str8_substr(sect_data, rng_1u64(256, 257)); - if (!str8_match(a_128, str8_lit("x"), 0)) { - goto exit; - } - String8 a_256 = str8_substr(sect_data, rng_1u64(512, 513)); - if (!str8_match(a_256, str8_lit("c"), 0)) { - goto exit; - } - String8 a_512 = str8_substr(sect_data, rng_1u64(1024, 1025)); - if (!str8_match(a_512, str8_lit("v"), 0)) { - goto exit; - } - String8 a_1024 = str8_substr(sect_data, rng_1u64(2048, 2049)); - if (!str8_match(a_1024, str8_lit("b"), 0)) { - goto exit; - } - String8 a_2048 = str8_substr(sect_data, rng_1u64(4096, 4097)); - if (!str8_match(a_2048, str8_lit("n"), 0)) { - goto exit; - } - String8 a_4096 = str8_substr(sect_data, rng_1u64(8192, 8193)); - if (!str8_match(a_4096, str8_lit("m"), 0)) { - goto exit; - } - String8 a_8192 = str8_substr(sect_data, rng_1u64(16384, 16385)); - if (!str8_match(a_8192, str8_lit("z"), 0)) { - goto exit; - } - - result = T_Result_Pass; - -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_alt_name(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("test")); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("test"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("test.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("foo")); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("foo"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("foo.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { goto exit; } - } - - int linker_exit_code; - - // basic alternate name test - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename:foo=test test.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - // linker should not chase alt name links - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe /alternatename:foo=bar /alternatename:bar=test test.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - // alt name conflict - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:c.exe /alternatename:foo=test /alternatename:foo=qwe test.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - // syntax error - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:d.exe /alternatename:foo foo.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - // syntax error - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:e.exe /alternatename:foo-oof foo.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - // syntax error - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename:foo=test=bar foo.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - // syntax error - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename:foo= foo.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - // syntax error - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename:= foo.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - // syntax error - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename: foo.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - // TODO: check that RAD Linker prints these warnings - - // warn about alt name to self alt name? - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:f.exe /alternatename:foo=foo foo.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - // warn about alt name to unknown symbol? - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:g.exe /alternatename:qwe=ewq foo.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_include(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("foo")); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("foo"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("include.obj"), obj)) { goto exit; } - - COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); - coff_lib_writer_push_obj(lib_writer, str8_lit("include.obj"), obj); - String8 lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); - coff_lib_writer_release(&lib_writer); - if (!t_write_file(str8_lit("include.lib"), lib)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { goto exit; } - } - - int linker_exit_code; - - // simple include test - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /include:foo entry.obj include.lib"); - if (linker_exit_code != 0) { goto exit; } - - // validate that linker pulled-in include.obj - { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *foo_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - if (foo_sect == 0) { goto exit; } - String8 foo_data = str8_substr(exe, rng_1u64(foo_sect->foff, foo_sect->foff + foo_sect->vsize)); - if (!str8_match(foo_data, str8_lit("foo"), 0)) { goto exit; } - } - - // test unresolved include - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /include:ewq entry.obj"); - if (linker_exit_code == 0) { goto exit; } - if (t_ident_linker() == T_Linker_RAD && linker_exit_code != LNK_Error_UnresolvedSymbol) { goto exit; } - - result = T_Result_Pass; - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_communal_var_vs_regular(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_common(obj_writer, str8_lit("TEST"), 1); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("communal.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("test")); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("defn.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - // linker should replace communal TEST with .data TEST - int linker_exit_code; - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe communal.obj defn.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe defn.obj communal.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - char *exes[] = { "a.exe", "b.exe" }; - for (U64 i = 0; i < ArrayCount(exes); i += 1) { - String8 exe = t_read_file(scratch.arena, str8_cstring(exes[i])); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *data_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - if (!data_sect) { goto exit; } - String8 data = str8_substr(exe, rng_1u64(data_sect->foff, data_sect->foff + data_sect->vsize)); - if (!str8_match(data, str8_lit("test"), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_communal_var_vs_regular_comdat(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_common(obj_writer, str8_lit("TEST"), 1); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("communal.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("test")); - sect->flags |= COFF_SectionFlag_LnkCOMDAT; - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("large.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { - goto exit; - } - } - - // linker should replace communal TEST with .data TEST - int linker_exit_code; - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe communal.obj large.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe large.obj communal.obj entry.obj"); - if (linker_exit_code != 0) { goto exit; } - - char *exes[] = { "a.exe", "b.exe" }; - for (U64 i = 0; i < ArrayCount(exes); i += 1) { - String8 exe = t_read_file(scratch.arena, str8_cstring(exes[i])); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *data_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - if (!data_sect) { goto exit; } - String8 data = str8_substr(exe, rng_1u64(data_sect->foff, data_sect->foff + data_sect->vsize)); - if (!str8_match(data, str8_lit("test"), 0)) { goto exit; } - } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_import_kernel32(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 data[] = "test"; - U8 text[] = { - 0x48, 0x83, 0xec, 0x68, // sub rsp,68h ; alloc space on stack - 0xc7, 0x44, 0x24, 0x48, 0x18, 0x00, 0x00, 0x00, // mov dword ptr [rsp+48h],18h ; SECURITY_ATTRIBUTES.nLength - 0x48, 0xc7, 0x44, 0x24, 0x50, 0x00, 0x00, 0x00, 0x00, // mov qword ptr [rsp+50h],0 ; SECURITY_ATTRIBUTES.lpSecurityDescriptor - 0xc7, 0x44, 0x24, 0x58, 0x00, 0x00, 0x00, 0x00, // mov dword ptr [rsp+58h],0 ; SECURITY_ATTRIBUTES.bInheritHandle - 0x48, 0xc7, 0x44, 0x24, 0x30, 0x00, 0x00, 0x00, 0x00, // mov qword ptr [rsp+30h],0 ; hTemplateFile - 0xc7, 0x44, 0x24, 0x28, 0x80, 0x00, 0x00, 0x00, // mov dword ptr [rsp+28h],80h ; dwFlagsAndAttributes - 0xc7, 0x44, 0x24, 0x20, 0x02, 0x00, 0x00, 0x00, // mov dword ptr [rsp+20h],2 ; dwCreationDisposition - 0x4c, 0x8d, 0x4c, 0x24, 0x48, // lea r9,[rsp+48h] ; lpSecurityAttributes - 0x45, 0x33, 0xc0, // xor r8d,r8d ; dwShareMode - 0xba, 0x00, 0x00, 0x00, 0x40, // mov edx,40000000h ; dwDesiredAccess - 0x48, 0x8d, 0x0d, 0x00, 0x00, 0x00, 0x00, // lea rcx,[test] ; lpFileName - 0xff, 0x15, 0x00, 0x00, 0x00, 0x00, // call qword ptr [__imp_CreateFileA] ; call CreateFileA - 0x48, 0x89, 0xc1, // mov rcx,rax ; hObject - 0xff, 0x15, 0x00, 0x00, 0x00, 0x00, // call qword ptr [__imp_CloseHandle] ; call CloseHandle - 0x33, 0xc0, // xor eax,eax ; clear result - 0x48, 0x83, 0xc4, 0x68, // add rsp,68h ; dealloc stack - 0xc3 // ret ; return - }; - COFF_ObjSection *data_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS, str8_array_fixed(data)); - COFF_ObjSection *text_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - COFF_ObjSymbol *data_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("test"), 0, data_sect); - COFF_ObjSymbol *entry_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); - COFF_ObjSymbol *create_file_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__imp_CreateFileA")); - COFF_ObjSymbol *close_handle_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__imp_CloseHandle")); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 70, data_symbol, COFF_Reloc_X64_Rel32); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 76, create_file_symbol, COFF_Reloc_X64_Rel32); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 85, close_handle_symbol, COFF_Reloc_X64_Rel32); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("import.obj"), obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /fixed import.obj kernel32.lib"); - if (linker_exit_code != 0) { goto exit; } - -#if OS_WINDOWS - { - String8 test_file_path = push_str8f(scratch.arena, "%S/test", g_wdir); - os_delete_file_at_path(test_file_path); - - OS_ProcessLaunchParams launch_opts = {0}; - launch_opts.inherit_env = 0; - launch_opts.path = g_wdir; - str8_list_pushf(scratch.arena, &launch_opts.cmd_line, "%S/a.exe", g_wdir); - OS_Handle handle = os_process_launch(&launch_opts); - AssertAlways(!os_handle_match(handle, os_handle_zero())); - U64 exit_code = max_U64; - os_process_join(handle, max_U64, &exit_code); - os_process_detach(handle); - if (exit_code != 0) { goto exit; } - - if (!os_file_path_exists(test_file_path)) { goto exit; } - } -#endif - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_delay_import(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 return_0[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, 0 - 0xc3 // ret - }; - U8 return_1[] = { - 0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00, // mov rax, 1 - 0xc3 // ret - }; - U8 return_2[] = { - 0x48, 0xC7, 0xC0, 0x02, 0x00, 0x00, 0x00, // mov rax, 2 - 0xc3 // ret - }; - COFF_ObjSection *return_0_sect = t_push_text_section(obj_writer, str8_array_fixed(return_0)); - COFF_ObjSection *return_1_sect = t_push_text_section(obj_writer, str8_array_fixed(return_1)); - COFF_ObjSection *return_2_sect = t_push_text_section(obj_writer, str8_array_fixed(return_2)); - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("return_1"), 0, return_1_sect); - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("return_2"), 0, return_2_sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("a.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 return_0[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, 0 - 0xc3 // ret - }; - U8 return_123[] = { - 0x48, 0xC7, 0xC0, 0x7B, 0x00, 0x00, 0x00, // mov rax, 123 - 0xc3 // ret - }; - U8 return_321[] = { - 0x48, 0xC7, 0xC0, 0x41, 0x01, 0x00, 0x00, // mov rax, 321 - 0xc3 // ret - }; - COFF_ObjSection *return_0_sect = t_push_text_section(obj_writer, str8_array_fixed(return_0)); - COFF_ObjSection *return_123_sect = t_push_text_section(obj_writer, str8_array_fixed(return_123)); - COFF_ObjSection *return_321_sect = t_push_text_section(obj_writer, str8_array_fixed(return_321)); - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("return_123"), 0, return_123_sect); - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("return_321"), 0, return_321_sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("b.obj"), obj)) { goto exit; } - } - - { - U8 text[] = { - 0x56, // push rsi - 0x57, // push rdi - 0x48, 0x83, 0xEC, 0x28, // sub rsp,28h - 0xE8, 0x00, 0x00, 0x00, 0x00, // call return_1 - 0x89, 0xC6, // mov esi,eax - 0xE8, 0x00, 0x00, 0x00, 0x00, // call return_2 - 0x89, 0xC7, // mov edi,eax - 0x01, 0xF7, // add edi,esi - 0xE8, 0x00, 0x00, 0x00, 0x00, // call return_123 - 0x89, 0xC6, // mov esi,eax - 0xE8, 0x00, 0x00, 0x00, 0x00, // call return_321 - 0x01, 0xF0, // add eax,esi - 0x01, 0xF8, // add eax,edi - 0x48, 0x83, 0xC4, 0x28, // add rsp,28h - 0x5F, // pop rdi - 0x5E, // pop rsi - 0xC3, // ret - }; - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); - COFF_ObjSymbol *return_1_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("return_1")); - COFF_ObjSymbol *return_2_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("return_2")); - COFF_ObjSymbol *return_123_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("return_123")); - COFF_ObjSymbol *return_321_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("return_321")); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 7, return_1_symbol, COFF_Reloc_X64_Rel32); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 14, return_2_symbol, COFF_Reloc_X64_Rel32); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 23, return_123_symbol, COFF_Reloc_X64_Rel32); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 30, return_321_symbol, COFF_Reloc_X64_Rel32); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("main.obj"), obj)) { goto exit; } - } - - int linker_exit_code; - - linker_exit_code = t_invoke_linkerf("/dll /implib:a.lib /export:return_1 /export:return_2 a.obj libcmt.lib"); - if (linker_exit_code != 0) { goto exit; } - - linker_exit_code = t_invoke_linkerf("/dll /implib:b.lib /export:return_123 /export:return_321 b.obj libcmt.lib"); - if (linker_exit_code != 0) { goto exit; } - - linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /fixed /debug:full main.obj a.lib b.lib kernel32.lib delayimp.lib libcmt.lib /delayload:a.dll /delayload:b.dll"); - if (linker_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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_ParsedDelayImportTable delay_import_table = pe_delay_imports_from_data(scratch.arena, pe.is_pe32, pe.section_count, section_table, exe, pe.data_dir_franges[PE_DataDirectoryIndex_DELAY_IMPORT]); - - PE_ParsedDelayDLLImport *a_import = &delay_import_table.v[0]; - if (a_import->attributes != 1) { goto exit; } - if (!str8_match(a_import->name, str8_lit("a.dll"), 0)) { goto exit; } - if (a_import->module_handle_voff == 0) { goto exit; } - if (a_import->name_table_voff == 0) { goto exit; } - if (a_import->bound_table_voff == 0) { goto exit; } - if (a_import->unload_table_voff == 0) { goto exit; } - if (a_import->time_stamp != 0) { goto exit; } - if (a_import->bound_table_count != 2) { goto exit; } - if (a_import->unload_table_count != 2) { goto exit; } - if (a_import->import_count != 2) { goto exit; } - - PE_ParsedImport *return_1 = &a_import->imports[0]; - if (return_1->type != PE_ParsedImport_Name) { goto exit; } - if (!str8_match(return_1->u.name.string, str8_lit("return_1"), 0)) { goto exit; } - if (return_1->u.name.hint != 0) { goto exit; } - - PE_ParsedImport *return_2 = &a_import->imports[1]; - if (return_2->type != PE_ParsedImport_Name) { goto exit; } - if (!str8_match(return_2->u.name.string, str8_lit("return_2"), 0)) { goto exit; } - if (return_2->u.name.hint != 1) { goto exit; } - - PE_ParsedDelayDLLImport *b_import = &delay_import_table.v[1]; - if (b_import->attributes != 1) { goto exit; } - if (!str8_match(b_import->name, str8_lit("b.dll"), 0)) { goto exit; } - if (b_import->module_handle_voff == 0) { goto exit; } - if (b_import->name_table_voff == 0) { goto exit; } - if (b_import->bound_table_voff == 0) { goto exit; } - if (b_import->unload_table_voff == 0) { goto exit; } - if (b_import->time_stamp != 0) { goto exit; } - if (b_import->bound_table_count != 2) { goto exit; } - if (b_import->unload_table_count != 2) { goto exit; } - if (b_import->import_count != 2) { goto exit; } - - PE_ParsedImport *return_123 = &b_import->imports[0]; - if (return_123->type != PE_ParsedImport_Name) { goto exit; } - if (!str8_match(return_123->u.name.string, str8_lit("return_123"), 0)) { goto exit; } - if (return_123->u.name.hint != 0) { goto exit; } - - PE_ParsedImport *return_321 = &b_import->imports[1]; - if (return_321->type != PE_ParsedImport_Name) { goto exit; } - if (!str8_match(return_321->u.name.string, str8_lit("return_321"), 0)) { goto exit; } - if (return_321->u.name.hint != 1) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_delay_import_user32(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *data_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".str"), PE_DATA_SECTION_FLAGS, str8_zero()); - U64 msg_off = data_sect->data.total_size; - str8_list_pushf(obj_writer->arena, &data_sect->data, "test\0"); - U64 caption_off = data_sect->data.total_size; - str8_list_pushf(obj_writer->arena, &data_sect->data, "foo\0"); - COFF_ObjSymbol *msg_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("msg"), msg_off, data_sect); - COFF_ObjSymbol *caption_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("caption"), caption_off, data_sect); - - U8 text[] = { - 0x48, 0x83, 0xEC, 0x28, // sub rsp,28h - 0x45, 0x33, 0xC9, // xor r9d,r9d - 0x4C, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00, // lea r8,[msg] - 0x48, 0x8D, 0x15, 0x00, 0x00, 0x00, 0x00, // lea rdx,[caption] - 0x33, 0xC9, // xor ecx,ecx - 0xFF, 0x15, 0x00, 0x00, 0x00, 0x00, // call qword ptr [__imp_MessageBoxA] - 0x33, 0xC0, // xor eax,eax - 0x48, 0x83, 0xC4, 0x28, // add rsp,28h - 0xC3, // ret - }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - COFF_ObjSymbol *text_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); - COFF_ObjSymbol *message_box_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__imp_MessageBoxA")); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 10, msg_symbol, COFF_Reloc_X64_Rel32); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 17, caption_symbol, COFF_Reloc_X64_Rel32); - coff_obj_writer_section_push_reloc(obj_writer, text_sect, 25, message_box_symbol, COFF_Reloc_X64_Rel32); - - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("delay_import.obj"), obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /out:a.exe /entry:entry /fixed /delayload:user32.dll kernel32.lib user32.lib libcmt.lib delayimp.lib delay_import.obj /debug:full"); - if (linker_exit_code != 0) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_empty_section(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *test = coff_obj_writer_push_section(obj_writer, str8_lit(".test"), PE_TEXT_SECTION_FLAGS, str8(0,0)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, test); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("empty_section.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - COFF_ObjSymbol *test_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, text_sect, 3, test_symbol); // relocation against removed section - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe empty_section.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_removed_section(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 test_text[] = { 0xc3 }; - COFF_ObjSection *test_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".test"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkRemove, str8_array_fixed(test_text)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, test_sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("test.obj"), obj)) { goto exit; } - } - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); - COFF_ObjSymbol *test_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); - coff_obj_writer_section_push_reloc_voff(obj_writer, text_sect, 3, test_symbol); // relocation against removed section - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("entry.obj"), obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe test.obj entry.obj"); - if (linker_exit_code == 0) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_function_pad_min(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 ret[] = { 0xc3 }; - COFF_ObjSection *text_sect_0 = t_push_text_section(obj_writer, str8_array_fixed(ret)); - COFF_ObjSection *text_sect_1 = t_push_text_section(obj_writer, str8_array_fixed(ret)); - COFF_ObjSection *text_sect_2 = t_push_text_section(obj_writer, str8_array_fixed(ret)); - text_sect_0->flags |= COFF_SectionFlag_Align4Bytes; - text_sect_1->flags |= COFF_SectionFlag_Align2Bytes; - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("A"), 0, text_sect_0); - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("B"), 0, text_sect_1); - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("C"), 0, text_sect_2); - String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - if (!t_write_file(str8_lit("funcs.obj"), obj)) { goto exit; } - } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:A /functionpadmin:1 /out:a.exe funcs.obj"); - if (linker_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_sect == 0) { goto exit; } - String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); - - U8 expected_text[] = { - 0xcc, 0xcc, 0xcc, 0xcc, 0xc3, - 0xcc, 0xcc, 0xcc, 0xc3, - 0xcc, 0xc3, - }; - if (!str8_match(text_data, str8_array_fixed(expected_text), 0)) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_first_member_header(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("8"), 0x8, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("1"), 0x1, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("9"), 0x9, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("7"), 0x7, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("4"), 0x4, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("5"), 0x5, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("2"), 0x2, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("3"), 0x3, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("6"), 0x6, COFF_SymStorageClass_External); - obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 lib; - { - COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); - coff_lib_writer_push_obj(lib_writer, str8_lit("obj.obj"), obj); - lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 0); - coff_lib_writer_release(&lib_writer); - } - - t_write_file(str8_lit("test.lib"), lib); - t_write_entry_obj(); - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe test.lib entry.obj /include:1 /include:2 /include:3 /include:4 /include:5 /include:6 /include:7 /include:8 /include:9"); - if (linker_exit_code != 0) goto exit; - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_second_member_header(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("8"), 0x8, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("1"), 0x1, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("9"), 0x9, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("7"), 0x7, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("4"), 0x4, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("5"), 0x5, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("2"), 0x2, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("3"), 0x3, COFF_SymStorageClass_External); - coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("6"), 0x6, COFF_SymStorageClass_External); - obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 lib; - { - COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); - coff_lib_writer_push_obj(lib_writer, str8_lit("obj.obj"), obj); - lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); - coff_lib_writer_release(&lib_writer); - } - - t_write_file(str8_lit("test.lib"), lib); - t_write_entry_obj(); - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe test.lib entry.obj /include:1 /include:2 /include:3 /include:4 /include:5 /include:6 /include:7 /include:8 /include:9"); - if (linker_exit_code != 0) goto exit; - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_defer_impl_link_to_second_search_pass(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 imp_ref_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { 0xc3 }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("entry"), 0, sect); - coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__imp_foo")); - coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("func")); // force linker to pull obj from the lib - imp_ref_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 impl_ref_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { 0xc3 }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("func"), 0, sect); - coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); // on a second search pass force linker to look up same import - impl_ref_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 impl_ref_lib; - { - COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); - coff_lib_writer_push_obj(lib_writer, str8_lit("impl_ref.obj"), impl_ref_obj); - impl_ref_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); - coff_lib_writer_release(&lib_writer); - } - - String8 foo_lib; - { - COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); - coff_lib_writer_push_import(lib_writer, COFF_MachineType_X64, ~0u, str8_lit("foo.dll"), COFF_ImportBy_Name, str8_lit("foo"), 0, COFF_ImportHeader_Code); - foo_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); - coff_lib_writer_release(&lib_writer); - } - - if (!t_write_file(str8_lit("imp_ref.obj"), imp_ref_obj)) { goto exit; } - if (!t_write_file(str8_lit("impl_ref.lib"), impl_ref_lib)) { goto exit; } - if (!t_write_file(str8_lit("foo.lib"), foo_lib)) { goto exit; } - if (!t_write_file(str8_lit("foo2.lib"), foo_lib)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe foo.lib foo2.lib imp_ref.obj impl_ref.lib"); - if (linker_exit_code != 0) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_opt_ref_dangling_section(void) -{ - Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - - String8 entry_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - U8 text[] = { - 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm - 0xC3 // ret - }; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("f")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 a_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - - U8 data[] = "A0000"; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(data)); - - COFF_ObjSymbol *q = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("q")); - coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, q); - - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("f"), 0, sect); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 b_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - - U8 q[] = { 1,2,3,4}; - COFF_ObjSection *q_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".q"), PE_DATA_SECTION_FLAGS, str8_array_fixed(q)); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("q"), 0, q_sect); - - U8 data[] = "BBBBBBBBBBBBBBB"; - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(data)); - coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); - coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("f"), 0, sect); - - b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - - String8 b_lib; - { - COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); - coff_lib_writer_push_obj(lib_writer, str8_lit("b.obj"), b_obj); - b_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); - coff_lib_writer_release(&lib_writer); - } - - if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("b.lib"), b_lib)) { goto exit; } - - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj b.lib"); - if (linker_exit_code != 0) { goto exit; } - - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.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); - COFF_SectionHeader *sect = t_coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".q")); - if (sect != 0) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_value_in_register(void) -{ - Temp scratch = scratch_begin(0, 0); - T_Result result = T_Result_Fail; - - // setup register context - REGS_RegBlockX64 regs = {0}; - REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_Reg3 - DW_ExprOp_Reg0); - Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); - U64 value = 0xc0ffee; - MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); - - // compile a simple program which reads the value from register 3 - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Reg3) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); - - // evaluate the program - DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); - - // validate eval result - if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } - if (expr_value.type != DW_ExprValueType_U64) { goto exit; } - if (expr_value.u64 != value) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_value_in_x_register(void) -{ - Temp scratch = scratch_begin(0, 0); - T_Result result = T_Result_Fail; - - // setup register context - REGS_RegBlockX64 regs = {0}; - REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_RegX64_FsBase); - Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); - U64 value = 0xc0ffee; - MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); - - // compile a simple program which reads the value from register 3 - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(RegX), DW_ExprEnc_ULEB128(DW_RegX64_FsBase) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); - - // evaluate the program - DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); - - // validate eval result - if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } - if (expr_value.type != DW_ExprValueType_U64) { goto exit; } - if (expr_value.u64 != value) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_address_of_value(void) -{ - Temp scratch = scratch_begin(0, 0); - T_Result result = T_Result_Fail; - - // compile a simple program which reads address - U64 addr = 0xdeadbeef; - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Addr), DW_ExprEnc_U64(addr) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); - - // evaluate the program - DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, 0, 0, 0, 0, &expr_value); - - // validate eval result - if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } - if (expr_value.type != DW_ExprValueType_Addr) { goto exit; } - if (expr_value.addr != addr) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_register_relative_variable(void) -{ - Temp scratch = scratch_begin(0, 0); - T_Result result = T_Result_Fail; - - // setup register context - REGS_RegBlockX64 regs = {0}; - REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_BReg11 - DW_ExprOp_BReg0); - Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); - U64 value = 1; - MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); - - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(BReg11), DW_ExprEnc_SLEB128(44) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); - - DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); - - // validate eval result - if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } - if (expr_value.type != DW_ExprValueType_Addr) { goto exit; } - if (expr_value.addr != (1 + 44)) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_frame_relative_variable(void) -{ - Temp scratch = scratch_begin(0, 0); - T_Result result = T_Result_Fail; - - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(FBReg), DW_ExprEnc_SLEB128(-50) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); - - U64 frame_base = 123; - DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, frame_base, 0, 0, max_U64, expr, 0, 0, 0, 0, &expr_value); - - if (expr_eval != DW_ExprEvalResult_Ok) { goto exit; } - if (expr_value.type != DW_ExprValueType_Addr) { goto exit; } - if (expr_value.addr != frame_base -50) { goto exit; } - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -internal -MACHINE_OP_MEM_READ(t_machine_op_mem_read) -{ - MemoryCopy(buffer, PtrFromInt(addr), buffer_size); - return MachineOpResult_Ok; -} - -internal T_Result -t_call_by_reference(void) -{ - Temp scratch = scratch_begin(0, 0); - T_Result result = T_Result_Fail; - - U8 *memory = push_array(scratch.arena, U8, 128); - U64 value = 0xc0ffee; - MemoryCopy(memory + 32, &value, sizeof(value)); - - REGS_RegBlockX64 regs = {0}; - REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, 58); // fsbase - Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); - U64 memory_ptr = IntFromPtr(memory); - MemoryCopy((U8 *)®s + reg_range.min, &memory_ptr, sizeof(memory_ptr)); - - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(BRegX), DW_ExprEnc_ULEB128(58), DW_ExprEnc_SLEB128(32), DW_ExprEnc_Op(Deref) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); - - DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, t_machine_op_mem_read, 0, &expr_value); - - if (expr_value.type != DW_ExprValueType_Generic) { goto exit; } - if (expr_value.generic.size != sizeof(U64)) { goto exit; } - if (*(U64 *)expr_value.generic.str != value) { goto exit; } - - result = T_Result_Pass; - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_plus_uconst(void) -{ - Temp scratch = scratch_begin(0, 0); - T_Result result = T_Result_Fail; - - U64 struct_addr = 0x123; - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Addr), DW_ExprEnc_Addr(struct_addr), DW_ExprEnc_Op(PlusUConst), DW_ExprEnc_ULEB128(4) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); - - DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, 0, 0, t_machine_op_mem_read, 0, &expr_value); - - if (expr_value.type != DW_ExprValueType_Addr) { goto exit; } - if (expr_value.addr != 0x123 + 4) { goto exit; } - - result = T_Result_Pass; - exit:; - scratch_end(scratch); - return result; -} - -internal T_Result -t_reg_split_spill(void) -{ - Temp scratch = scratch_begin(0, 0); - T_Result result = T_Result_Fail; - - // setup register context - REGS_RegBlockX64 regs = {0}; - { - REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_BReg3 - DW_ExprOp_BReg0); - Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); - U64 value = 0xaaaa; - MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); - } - { - REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_BReg10 - DW_ExprOp_BReg0); - Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); - U64 value = 0xbbbb; - MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); - } - - DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Reg3), DW_ExprEnc_Op(Piece), DW_ExprEnc_ULEB128(4), DW_ExprEnc_Op(Reg10), DW_ExprEnc_Op(Piece), DW_ExprEnc_ULEB128(2) }; - String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); - DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); - - DW_ExprValue expr_value; - DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); - - result = T_Result_Pass; -exit:; - scratch_end(scratch); - return result; -} - -//////////////////////////////////////////////////////////////// - -internal void -entry_point(CmdLine *cmdline) +t_entry_point(CmdLine *cmdline) { Temp scratch = scratch_begin(0,0); - // - // Test Targets - // - static struct { char *label; T_Result (*r)(void); } target_array[] = { -#define T(x) { Stringify(x), t_##x } - // Linker Tests - T(simple_link_test), - T(machine_compat_check), - T(out_of_bounds_section_number), - T(merge), - T(undef_section), - T(undef_reloc_section), - T(link_undef), - T(link_unref_undef), - T(weak_lib_vs_weak_lib), - T(weak_lib_vs_weak_nolib), - T(weak_lib_vs_weak_alias), - T(weak_lib_vs_weak_antidep), - T(weak_alias_vs_weak_alias), - T(weak_alias_vs_weak_lib), - T(weak_alias_vs_weak_nolib), - T(weak_alias_vs_weak_antidep), - T(weak_nolib_vs_weak_nolib), - T(weak_nolib_vs_weak_lib), - T(weak_nolib_vs_weak_alias), - T(weak_nolib_vs_weak_antidep), - T(weak_antidep_vs_weak_antidep), - T(weak_antidep_vs_weak_nolib), - T(weak_antidep_vs_weak_lib), - T(weak_antidep_vs_weak_alias), - T(weak_vs_common), - T(abs_vs_weak), - T(abs_vs_regular), - T(abs_vs_common), - T(abs_vs_abs), - T(undef_weak_lib), - T(undef_weak_search_alias), - T(sect_symbol), - T(weak_cycle), - T(weak_tag), - T(find_merged_pdata), - T(section_sort), - T(flag_conf), - T(invalid_bss), - T(common_block), - T(base_relocs), - T(simple_lib_test), - T(sect_align), - T(image_base), - T(comdat_any), - T(comdat_no_duplicates), - T(comdat_same_size), - T(comdat_exact_match), - T(comdat_largest), - T(comdat_associative), - T(comdat_associative_loop), - T(comdat_associative_non_comdat), - T(comdat_associative_out_of_bounds), - T(comdat_with_offset), - T(reloc_against_removed_comdat), - T(alt_name), - T(include), - T(communal_var_vs_regular_comdat), - T(communal_var_vs_regular), - T(import_kernel32), - T(delay_import_user32), - T(delay_import), - T(empty_section), - T(removed_section), - T(function_pad_min), - T(first_member_header), - T(second_member_header), - T(defer_impl_link_to_second_search_pass), - - // DWARF Expression Tests - T(value_in_register), - T(value_in_x_register), - T(address_of_value), - T(register_relative_variable), - T(frame_relative_variable), - T(call_by_reference), - T(plus_uconst), - //T(reg_split_spill), -#undef T - }; - // // Handle -help // @@ -5376,9 +189,9 @@ entry_point(CmdLine *cmdline) // { if (cmd_line_has_flag(cmdline, str8_lit("list"))) { - fprintf(stdout, "--- Targets --------------------------------------------------------------------\n"); - for (U64 i = 0; i < ArrayCount(target_array); i += 1) { - fprintf(stdout, " %s\n", target_array[i].label); + fprintf(stdout, "--- Tests --------------------------------------------------------------------\n"); + for EachIndex(i, g_torture_test_count) { + fprintf(stdout, " %s\n", g_torture_tests[i].label); } os_abort(0); } @@ -5467,11 +280,13 @@ entry_point(CmdLine *cmdline) os_delete_file_at_path(g_stdout_file_name); // - // Run Test Targets + // Run tests // { + radsort(g_torture_tests, g_torture_test_count, t_test_is_before); + U64 max_label_size = 0; - for (U64 i = 0; i < ArrayCount(target_array); i += 1) { max_label_size = Max(max_label_size, cstring8_length((U8*)target_array[i].label)); } + for EachIndex(i, g_torture_test_count) { max_label_size = Max(max_label_size, cstring8_length((U8*)g_torture_tests[i].label)); } U64 dots_min = 10; U64 dots_size = max_label_size+dots_min; @@ -5481,17 +296,17 @@ entry_point(CmdLine *cmdline) U64 target_indices_count; U64 *target_indices; if (target.node_count == 0) { - target_indices_count = ArrayCount(target_array); - target_indices = push_array(scratch.arena, U64, ArrayCount(target_array)); - for (U64 i = 0; i < target_indices_count; i += 1) { target_indices[i] = i; } + target_indices_count = g_torture_test_count; + target_indices = push_array(scratch.arena, U64, g_torture_test_count); + for EachIndex(i, target_indices_count) { target_indices[i] = i; } } else { target_indices_count = 0; target_indices = push_array(scratch.arena, U64, target.node_count); - for (String8Node *target_n = target.first; target_n != 0; target_n = target_n->next) { + for EachNode(target_n, String8Node, target.first) { B32 is_target_unknown = 1; - for (U64 i = 0; i < ArrayCount(target_array); i += 1) { - if (str8_match(str8_cstring(target_array[i].label), target_n->string, 0)) { + for EachIndex(i, g_torture_test_count) { + if (str8_match(str8_cstring(g_torture_tests[i].label), target_n->string, 0)) { target_indices[target_indices_count++] = i; is_target_unknown = 0; break; @@ -5506,10 +321,17 @@ entry_point(CmdLine *cmdline) U64 pass_count = 0; U64 fail_count = 0; U64 crash_count = 0; - for (U64 i = 0; i < target_indices_count; i += 1) { + for EachIndex(i, target_indices_count) { U64 target_idx = target_indices[i]; - g_wdir = push_str8f(scratch.arena, "%S\\%s", g_out, target_array[target_idx].label); + // print run progress + U64 dots_count = (max_label_size - cstring8_length((U8*)g_torture_tests[target_idx].label)) + dots_min; + fprintf(stdout, "[%2I64u/%2I64u] ", i+1, target_indices_count); + fprintf(stdout, "(%s) %s", g_torture_tests[target_idx].group, g_torture_tests[target_idx].label); + fprintf(stdout, "%.*s", (int)dots_count, dots); + + // setup output directory + g_wdir = push_str8f(scratch.arena, "%S\\%s", g_out, g_torture_tests[target_idx].label); g_wdir = os_full_path_from_path(scratch.arena, g_wdir); os_make_directory(g_wdir); if (!os_folder_path_exists(g_out)) { @@ -5517,30 +339,27 @@ entry_point(CmdLine *cmdline) continue; } - T_Result result = t_run(target_array[target_idx].r); + // run test + T_RunResult result = t_run(g_torture_tests[target_idx].r); - U64 dots_count = (max_label_size - cstring8_length((U8*)target_array[target_idx].label)) + dots_min; - String8 msg = push_str8f(scratch.arena, "%s%.*s%s", target_array[target_idx].label, dots_count, dots, t_string_from_result(result)); - - // run progress - fprintf(stdout, "[%2llu/%2llu] ", i+1, target_indices_count); - - if (result == T_Result_Pass) { - fprintf(stdout, "\x1b[32m" "%.*s" "\x1b[0m" "\n", str8_varg(msg)); + // print result + if (result.status == T_RunStatus_Pass) { + fprintf(stdout, "\x1b[32m" "%s" "\x1b[0m" "\n", t_string_from_result(result.status)); pass_count += 1; - } else if (result == T_Result_Fail) { - fprintf(stdout, "\x1b[31m" "%.*s" "\x1b[0m" "\n", str8_varg(msg)); + } else if (result.status == T_RunStatus_Fail) { + fprintf(stdout, "\x1b[31m" "%s" "\x1b[0m" "\n", t_string_from_result(result.status)); fail_count += 1; - } else if (result == T_Result_Crash) { - fprintf(stdout, "\x1b[33m" "%.*s" "\x1b[0m" "\n", str8_varg(msg)); + } else if (result.status == T_RunStatus_Crash) { + fprintf(stdout, "\x1b[33m" "%s" "\x1b[0m" "\n", t_string_from_result(result.status)); crash_count += 1; } + + if (result.status != T_RunStatus_Pass) { + fprintf(stdout, " ERROR: %s:%d: condition: \"%s\"\n", result.fail_file, result.fail_line, result.fail_cond); + } } - fprintf(stdout, "--- Report ---------------------------------------------------------------------\n"); - fprintf(stdout, " Passed: %llu\n", pass_count); - fprintf(stdout, " Failed: %llu\n", fail_count); - fprintf(stdout, " Crashed: %llu\n", crash_count); + fprintf(stdout, "---- Passed: %I64u, Failed: %I64u, Crashed: %I64u ----\n", pass_count, fail_count, crash_count); if (fail_count + crash_count != 0) { fflush(stdout); diff --git a/src/torture/torture.h b/src/torture/torture.h new file mode 100644 index 00000000..74f27eb4 --- /dev/null +++ b/src/torture/torture.h @@ -0,0 +1,94 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#pragma once + +//////////////////////////////// + +typedef enum +{ + T_RunStatus_Fail, + T_RunStatus_Crash, + T_RunStatus_Pass, +} T_RunStatus; + +typedef struct +{ + T_RunStatus status; + char *fail_file; + int fail_line; + char *fail_cond; +} T_RunResult; + +typedef T_RunResult (*T_Run)(void); + +typedef struct +{ + T_Run run; + T_RunResult result; +} T_RunCtx; + +typedef struct +{ + char *group; + char *label; + int decl_line; + T_Run r; +} T_Test; + +extern U64 g_torture_test_count; +extern T_Test g_torture_tests[0xffffff]; + +#define T_AddTest(name, l, ...) \ + T_RunResult t_##name(void); \ + __VA_ARGS__ void t_add_test_##name(void) \ + { \ + g_torture_tests[g_torture_test_count].group = T_Group; \ + g_torture_tests[g_torture_test_count].label = Stringify(name); \ + g_torture_tests[g_torture_test_count].r = &t_##name; \ + g_torture_tests[g_torture_test_count].decl_line = l; \ + g_torture_test_count += 1; \ + } + +#if COMPILER_MSVC +#pragma section(".CRT$XCU", read) +# define T_BeginTest_(name) \ + T_AddTest(name, __LINE__) \ + __declspec(allocate(".CRT$XCU")) void(*r_##name)(void) = t_add_test_##name; \ + __pragma(comment(linker, "/include:" Stringify(r_##name))) +#else +# define T_BeginTest_(name) \ + T_AddTest(name, __LINE__, __attribute__((constructor))) +#endif + +#define T_BeginTest(name) \ + T_BeginTest_(name) \ + T_RunResult t_##name(void) { \ + Temp scratch = scratch_begin(0,0); \ + T_RunResult result = { .status = T_RunStatus_Fail }; + +#define T_EndTest \ + result.status = T_RunStatus_Pass; \ + exit__:; \ + scratch_end(scratch); \ + return result; \ + } + +#define T_Ok(c) do { if (!(c)) { result.fail_file = __FILE__; result.fail_line = __LINE__; result.fail_cond = Stringify(c); goto exit__; } } while(0) + +//////////////////////////////// + +// output directory +internal B32 t_write_file_list(String8 name, String8List data); +internal B32 t_write_file(String8 name, String8 data); +internal String8 t_read_file(Arena *arena, String8 name); +internal String8 t_make_file_path(Arena *arena, String8 name); + +// test runner +internal void t_run_caller(void *raw_ctx); +internal void t_run_fail_handler(void *raw_ctx); +internal T_RunResult t_run(T_Run run); + +internal B32 t_invoke(String8 exe, String8 cmdline, U64 timeout); + + diff --git a/src/torture/torture_dwarf.c b/src/torture/torture_dwarf.c new file mode 100644 index 00000000..40088cbd --- /dev/null +++ b/src/torture/torture_dwarf.c @@ -0,0 +1,190 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#define T_Group "Dwarf" + +T_BeginTest(value_in_register) +{ + // setup register context + REGS_RegBlockX64 regs = {0}; + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_Reg3 - DW_ExprOp_Reg0); + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 value = 0xc0ffee; + MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); + + // compile a simple program which reads the value from register 3 + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Reg3) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + // evaluate the program + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); + + // validate eval result + T_Ok(expr_eval == DW_ExprEvalResult_Ok); + T_Ok(expr_value.type == DW_ExprValueType_U64); + T_Ok(expr_value.u64 == value); +} +T_EndTest; + +T_BeginTest(value_in_x_register) +{ + // setup register context + REGS_RegBlockX64 regs = {0}; + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_RegX64_FsBase); + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 value = 0xc0ffee; + MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); + + // compile a simple program which reads the value from register 3 + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(RegX), DW_ExprEnc_ULEB128(DW_RegX64_FsBase) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + // evaluate the program + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); + + // validate eval result + T_Ok(expr_eval == DW_ExprEvalResult_Ok); + T_Ok(expr_value.type == DW_ExprValueType_U64); + T_Ok(expr_value.u64 == value); +} +T_EndTest; + +T_BeginTest(address_of_value) +{ + // compile a simple program which reads address + U64 addr = 0xdeadbeef; + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Addr), DW_ExprEnc_U64(addr) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + // evaluate the program + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, 0, 0, 0, 0, &expr_value); + + // validate eval result + T_Ok(expr_eval == DW_ExprEvalResult_Ok); + T_Ok(expr_value.type == DW_ExprValueType_Addr); + T_Ok(expr_value.addr == addr); +} +T_EndTest; + +T_BeginTest(register_relative_variable) +{ + // setup register context + REGS_RegBlockX64 regs = {0}; + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_BReg11 - DW_ExprOp_BReg0); + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 value = 1; + MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); + + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(BReg11), DW_ExprEnc_SLEB128(44) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); + + // validate eval result + T_Ok(expr_eval == DW_ExprEvalResult_Ok); + T_Ok(expr_value.type == DW_ExprValueType_Addr); + T_Ok(expr_value.addr == (1 + 44)); +} +T_EndTest; + +T_BeginTest(frame_relative_variable) +{ + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(FBReg), DW_ExprEnc_SLEB128(-50) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + U64 frame_base = 123; + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, frame_base, 0, 0, max_U64, expr, 0, 0, 0, 0, &expr_value); + + T_Ok(expr_eval == DW_ExprEvalResult_Ok); + T_Ok(expr_value.type == DW_ExprValueType_Addr); + T_Ok(expr_value.addr == frame_base -50); +} +T_EndTest; + +internal +MACHINE_OP_MEM_READ(t_machine_op_mem_read) +{ + MemoryCopy(buffer, PtrFromInt(addr), buffer_size); + return MachineOpResult_Ok; +} + +T_BeginTest(call_by_reference) +{ + U8 *memory = push_array(scratch.arena, U8, 128); + U64 value = 0xc0ffee; + MemoryCopy(memory + 32, &value, sizeof(value)); + + REGS_RegBlockX64 regs = {0}; + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, 58); // fsbase + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 memory_ptr = IntFromPtr(memory); + MemoryCopy((U8 *)®s + reg_range.min, &memory_ptr, sizeof(memory_ptr)); + + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(BRegX), DW_ExprEnc_ULEB128(58), DW_ExprEnc_SLEB128(32), DW_ExprEnc_Op(Deref) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + DW_ExprValue expr_value = { 0 }; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, t_machine_op_mem_read, 0, &expr_value); + + T_Ok(expr_value.type == DW_ExprValueType_Generic); + T_Ok(expr_value.generic.size == sizeof(U64)); + T_Ok(*(U64 *)expr_value.generic.str == value); +} +T_EndTest; + +T_BeginTest(plus_uconst) +{ + U64 struct_addr = 0x123; + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Addr), DW_ExprEnc_Addr(struct_addr), DW_ExprEnc_Op(PlusUConst), DW_ExprEnc_ULEB128(4) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, 0, 0, t_machine_op_mem_read, 0, &expr_value); + + T_Ok(expr_value.type == DW_ExprValueType_Addr); + T_Ok(expr_value.addr == 0x123 + 4); +} +T_EndTest; + +#if 0 +T_BeginTest(reg_split_spill) +{ + // setup register context + REGS_RegBlockX64 regs = {0}; + { + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_BReg3 - DW_ExprOp_BReg0); + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 value = 0xaaaa; + MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); + } + { + REGS_RegCode reg_code = reg_code_from_dw_reg(Arch_x64, DW_ExprOp_BReg10 - DW_ExprOp_BReg0); + Rng1U64 reg_range = regs_range_from_code(Arch_x64, 0, reg_code); + U64 value = 0xbbbb; + MemoryCopy((U8 *)®s + reg_range.min, &value, sizeof(value)); + } + + DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Reg3), DW_ExprEnc_Op(Piece), DW_ExprEnc_ULEB128(4), DW_ExprEnc_Op(Reg10), DW_ExprEnc_Op(Piece), DW_ExprEnc_ULEB128(2) }; + String8 expr_data = dw_encode_expr(scratch.arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); + DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_64Bit, byte_size_from_arch(Arch_x64), expr_data); + + DW_ExprValue expr_value; + DW_ExprEvalResult expr_eval = dw_eval_expr(scratch.arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); +} +T_EndTest; +#endif + +#undef T_Group + diff --git a/src/torture/torture_main.c b/src/torture/torture_main.c new file mode 100644 index 00000000..75ecb3ba --- /dev/null +++ b/src/torture/torture_main.c @@ -0,0 +1,62 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +// Build Options + +#define BUILD_CONSOLE_INTERFACE 1 +#define BUILD_TITLE "TORTURE" + +//////////////////////////////// + +#include "base/base_inc.h" +#include "os/os_inc.h" +#include "linker/base_ext/base_core.h" +#include "linker/base_ext/base_arena.h" +#include "linker/base_ext/base_arrays.h" +#include "linker/hash_table.h" +#include "coff/coff.h" +#include "coff/coff_parse.h" +#include "coff/coff_obj_writer.h" +#include "coff/coff_lib_writer.h" +#include "pe/pe.h" +#include "pe/pe_section_flags.h" +#include "elf/elf.h" +#include "elf/elf_parse.h" +#include "dwarf/dwarf_inc.h" +#include "regs/regs.h" +#include "regs/dwarf/regs_dwarf.h" +#include "linker/lnk_cmd_line.h" +#include "linker/lnk_cmd_line.c" +#include "linker/lnk_error.h" +#include "torture.h" +#include "torture_radlink.h" + +#include "base/base_inc.c" +#include "os/os_inc.c" +#include "linker/hash_table.c" +#include "linker/base_ext/base_core.c" +#include "linker/base_ext/base_arena.c" +#include "linker/base_ext/base_arrays.c" +#include "coff/coff.c" +#include "coff/coff_parse.c" +#include "coff/coff_obj_writer.c" +#include "coff/coff_lib_writer.c" +#include "pe/pe.c" +#include "elf/elf.c" +#include "elf/elf_parse.c" +#include "dwarf/dwarf_inc.c" +#include "regs/regs.c" +#include "regs/dwarf/regs_dwarf.c" +#include "torture.c" +#include "torture_radlink.c" +#include "torture_dwarf.c" + +//////////////////////////////// + +internal void +entry_point(CmdLine *cmdline) +{ + t_entry_point(cmdline); +} + diff --git a/src/torture/torture_radlink.c b/src/torture/torture_radlink.c new file mode 100644 index 00000000..500e2255 --- /dev/null +++ b/src/torture/torture_radlink.c @@ -0,0 +1,3860 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal T_Linker +t_id_linker(void) +{ + String8 name = str8_chop_last_dot(str8_skip_last_slash(g_linker)); + if (str8_match(name, str8_lit("radlink"), StringMatchFlag_CaseInsensitive)) { return T_Linker_RAD; } + if (str8_match(name, str8_lit("link"), StringMatchFlag_CaseInsensitive)) { return T_Linker_MSVC; } + if (str8_match(name, str8_lit("lld-link"), StringMatchFlag_CaseInsensitive)) { return T_Linker_LLVM; } + return T_Linker_Null; +} + +internal COFF_ObjSection * +t_push_text_section(COFF_ObjWriter *obj_writer, String8 data) +{ + return coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_Align1Bytes, data); +} + +internal COFF_ObjSection * +t_push_data_section(COFF_ObjWriter *obj_writer, String8 data) +{ + return coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS, data); +} + +internal COFF_ObjSection * +t_push_rdata_section(COFF_ObjWriter *obj_writer, String8 data) +{ + return coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS, data); +} + +internal String8 +t_make_sec_defn_obj(Arena *arena, String8 payload) +{ + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *mysect_section = coff_obj_writer_push_section(obj_writer, str8_lit(".mysect"), COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_Align1Bytes, payload); + coff_obj_writer_push_symbol_secdef(obj_writer, mysect_section, COFF_ComdatSelect_Null); + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); + return obj; +} + +internal String8 +t_make_entry_obj(Arena *arena) +{ + /* + "machine": "x64", + "sectab": [ ".text": [ "alias: "entry", "data": [ 0xc3 ] ] ] + "symtab" : [ { "name": "entry", "section": "entry", "offset": "100", "type": "extern" } ] + */ + + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { 0xc3 }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); + return obj; +} + +internal B32 +t_write_entry_obj(void) +{ + Temp scratch = scratch_begin(0,0); + String8 obj = t_make_entry_obj(scratch.arena); + B32 is_ok = t_write_file(str8_lit("entry.obj"), obj); + scratch_end(scratch); + return is_ok; +} + +//////////////////////////////// + +#define T_Group "Linker" + +T_BeginTest(machine_compat_check) +{ + // unknown.obj + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); + t_push_data_section(obj_writer, str8_lit("unknown")); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("unknown.obj"), obj)); + } + + // x64.obj + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + t_push_data_section(obj_writer, str8_lit("x64")); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("x64.obj"), obj)); + } + + // entry.obj + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { 0xC3 }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + // arm64.obj + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Arm64); + t_push_data_section(obj_writer, str8_lit("arm64")); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("arm64.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe entry.obj unknown.obj x64.obj"); + T_Ok(g_last_exit_code == 0); + + // test objs with conflicting machines + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe entry.obj unknown.obj x64.obj arm64.obj"); + T_Ok(g_last_exit_code != 0); + + // check /MACHINE switch + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe /machine:amd64 arm64.obj entry.obj"); + T_Ok(g_last_exit_code != 0); +} +T_EndTest; + +T_BeginTest(simple_link_test) +{ + U8 text_payload[] = { 0xC3 }; + + String8 main_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text_payload)); + coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS, str8_lit("qwe")); + coff_obj_writer_push_section(obj_writer, str8_lit(".zero"), PE_BSS_SECTION_FLAGS, str8(0, 5)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 main_obj_name = str8_lit("main.obj"); + T_Ok(t_write_file(main_obj_name, main_obj)); + + int file_align = 512; + int virt_align = 4096; + String8 out_name = str8_lit("a.exe"); + t_invoke_linkerf("/entry:my_entry /subsystem:console /fixed /filealign:%d /align:%d /out:%S %S", file_align, virt_align, out_name, main_obj_name); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, out_name); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + T_Ok(!pe.is_pe32); + T_Ok(pe.section_count == 3); + T_Ok(pe.arch == Arch_x64); + T_Ok(pe.subsystem == PE_WindowsSubsystem_WINDOWS_CUI); + T_Ok(pe.virt_section_align == virt_align); + T_Ok(pe.file_section_align == file_align); + T_Ok(pe.symbol_count == 0); + T_Ok(pe.data_dir_count == PE_DataDirectoryIndex_COUNT); + + // check section alignment + for EachIndex(sect_idx, pe.section_count) { + COFF_SectionHeader *sect_header = §ion_table[sect_idx]; + T_Ok(AlignPadPow2(sect_header->fsize, file_align) == 0); + T_Ok(AlignPadPow2(sect_header->voff, virt_align) == 0); + } + + COFF_SectionHeader *text_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_section != 0); + T_Ok(text_section->foff == file_align); + T_Ok(pe.entry_point == text_section->voff); + + COFF_SectionHeader *data_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + T_Ok(data_section != 0); + + COFF_SectionHeader *zero_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".zero")); + T_Ok(zero_section != 0); + + String8 text_data = str8_substr(exe, rng_1u64(text_section->foff, text_section->foff + text_section->vsize)); + T_Ok(str8_match(text_data, str8_array_fixed(text_payload), 0)); + + PE_OptionalHeader32Plus *opt = str8_deserial_get_raw_ptr(exe, pe.optional_header_off, sizeof(*opt)); + T_Ok(opt->sizeof_code == text_section->fsize); + T_Ok(opt->sizeof_inited_data == (text_section->fsize + data_section->fsize)); + T_Ok(opt->sizeof_uninited_data == 0x200); + T_Ok(opt->code_base == 0x1000); + T_Ok(opt->image_base == 0x140000000); + T_Ok(opt->major_os_ver == 6); + T_Ok(opt->minor_os_ver == 0); + T_Ok(opt->major_img_ver == 0); + T_Ok(opt->minor_img_ver == 0); + T_Ok(opt->major_subsystem_ver == 6); + T_Ok(opt->minor_subsystem_ver == 0); + T_Ok(opt->win32_version_value == 0); + T_Ok(opt->sizeof_image == 0x4000); + T_Ok(opt->sizeof_headers == 0x200); + T_Ok(opt->dll_characteristics == 0x8120); + T_Ok(opt->loader_flags == 0); +} +T_EndTest; + +T_BeginTest(out_of_bounds_section_number) +{ + // bad.obj + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *foo = coff_obj_writer_push_section(obj_writer, str8_lit(".foo"), PE_DATA_SECTION_FLAGS, str8_lit("foo")); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("foo"), 0, foo); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + { + COFF_FileHeaderInfo header = coff_file_header_info_from_data(obj); + String8 string_table = str8_substr(obj, header.string_table_range); + String8 symbol_table = str8_substr(obj, header.symbol_table_range); + COFF_ParsedSymbol symbol = coff_parse_symbol(header, string_table, symbol_table, 0); + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = 123; + } + T_Ok(t_write_file(str8_lit("bad.obj"), obj)); + } + + // entry.obj + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj bad.obj"); + T_Ok(g_last_exit_code != 0); +} +T_EndTest; + +T_BeginTest(merge) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_section(obj_writer, str8_lit(".test"), PE_DATA_SECTION_FLAGS, str8_lit("hello, world")); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("test.obj"), obj)); + } + + T_Ok(t_write_entry_obj()); + + // circular merge + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.test entry.obj test.obj"); + T_Ok(g_last_exit_code != 0); + + if (t_id_linker() == T_Linker_RAD) { + T_Ok(g_last_exit_code == LNK_Error_CircularMerge); + } + + // circular merge with extra link + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.data /merge:.data=.test entry.obj test.obj"); + T_Ok(g_last_exit_code != 0); + if (t_id_linker() == T_Linker_RAD) { + T_Ok(g_last_exit_code == LNK_Error_CircularMerge); + } + + // merge with non-defined section + { + g_last_exit_code; + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.qwe entry.obj test.obj"); + T_Ok(g_last_exit_code == 0); + + // make sure linker created .qwe and merged .test into it + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".qwe")); + T_Ok(sect != 0); + T_Ok(sect->flags == PE_DATA_SECTION_FLAGS); + String8 qwe = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(qwe, str8_lit("hello, world"),0)); + } + + // illegal merge with .reloc + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.reloc entry.obj test.obj"); + T_Ok(g_last_exit_code != 0); + if (t_id_linker() == T_Linker_RAD) { + T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge); + } + + // illegal merge with .rsrc + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.rsrc entry.obj test.obj"); + T_Ok(g_last_exit_code != 0); + if (t_id_linker() == T_Linker_RAD) { + T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge); + } + + // merge non-defined section with defined section + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.qwe=.test entry.obj test.obj"); + T_Ok(g_last_exit_code == 0); + + // merge .test -> .qwe -> .data + { + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.qwe /merge:.qwe=.data entry.obj test.obj"); + T_Ok(g_last_exit_code == 0); + + // make sure linker merged .test into .data + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".data")); + T_Ok(sect != 0); + T_Ok(sect->flags == PE_DATA_SECTION_FLAGS); + String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(data, str8_lit("hello, world"),0)); + } +} +T_EndTest; + +T_BeginTest(link_undef) +{ + String8 undef_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); + undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("undef.obj"), undef_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // try linking unresolved symbol and see if linker picks up on that + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj undef.obj"); + T_Ok(g_last_exit_code == LNK_Error_UnresolvedSymbol); +} +T_EndTest; + +T_BeginTest(link_unref_undef) +{ + String8 undef_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); + undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { 0xc3 }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("undef.obj"), undef_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // try linking unreferenced unresolved symbol, this must link + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj undef.obj"); + T_Ok(g_last_exit_code != 0); +} +T_EndTest; + +T_BeginTest(weak_lib_vs_weak_lib) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // linker must pick weak symbol from a.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x111; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } + + // linker must pick weak symbol from entry.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_lib_vs_weak_nolib) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // linker must pick weak symbol from entry.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_lib_vs_weak_alias) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // linker must pick weak symbol from entry.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); +} +T_EndTest; + +T_BeginTest(weak_lib_vs_weak_antidep) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // linker must pick weak symbol from a.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_alias_vs_weak_alias) +{ + String8 a; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *qwe = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("qwe"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("sym"), COFF_WeakExt_SearchAlias, qwe); + a = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 b; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *ewq = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("ewq"), 0x222, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("sym"), COFF_WeakExt_SearchAlias, ewq); + b = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("sym")); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a)); + T_Ok(t_write_file(str8_lit("b.obj"), b)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); + T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); +} +T_EndTest; + +T_BeginTest(weak_alias_vs_weak_lib) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // linker must pick weak symbol from entry.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_alias_vs_weak_nolib) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // linker must pick weak symbol from entry.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_alias_vs_weak_antidep) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // linker must pick weak symbol from entry.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_nolib_vs_weak_nolib) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_nolib_vs_weak_lib) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_nolib_vs_weak_alias) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); +} +T_EndTest; + +T_BeginTest(weak_nolib_vs_weak_antidep) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_antidep_vs_weak_antidep) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + // linker must pick weak symbol from a.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x111; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } + + // linker must pick weak symbol from entry.obj + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_antidep_vs_weak_nolib) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_antidep_vs_weak_lib) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_antidep_vs_weak_alias) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x111; + T_Ok(str8_match(imm, str8_struct(&expected), 0)); + } +} +T_EndTest; + +T_BeginTest(weak_vs_common) +{ + String8 weak_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("a")); + COFF_ObjSymbol *sect_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("_a"), 0, sect); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, sect_symbol); + weak_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 common_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_common(obj_writer, str8_lit("w"), 2); + common_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("w")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("weak.obj"), weak_obj)); + T_Ok(t_write_file(str8_lit("common.obj"), common_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe common.obj weak.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe weak.obj common.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeader *bss = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".bss")); + T_Ok(bss); + T_Ok(bss->fsize == 0); + T_Ok(bss->vsize == 2); +} +T_EndTest; + +T_BeginTest(abs_vs_weak) +{ + U32 abs_value = 0x123; + U8 text_code[] = { 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3 }; + + String8 abs_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("foo"), abs_value, COFF_SymStorageClass_External); + abs_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 text_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + COFF_ObjSection *mydata = coff_obj_writer_push_section(obj_writer, str8_lit(".mydata"), COFF_SectionFlag_CntCode|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute|COFF_SectionFlag_Align1Bytes, str8_lit("mydata")); + COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("mydata"), 0, mydata); + COFF_ObjSymbol *foo = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("foo"), COFF_WeakExt_NoLibrary, tag); + + COFF_ObjSection *text = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), COFF_SectionFlag_CntCode|COFF_SectionFlag_MemExecute|COFF_SectionFlag_MemRead|COFF_SectionFlag_Align1Bytes, str8_array_fixed(text_code)); + coff_obj_writer_section_push_reloc(obj_writer, text, 2, foo, COFF_Reloc_X64_Addr64); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text); + + text_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("abs.obj"), abs_obj)); + T_Ok(t_write_file(str8_lit("text.obj"), text_obj)); + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe abs.obj text.obj"); + T_Ok(g_last_exit_code == 0); + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe text.obj abs.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeader *text_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_section != 0); + + String8 text_data = str8_substr(exe, rng_1u64(text_section->foff, text_section->foff + text_section->fsize)); + String8 inst = str8_prefix(text_data, 2); + T_Ok(str8_match(inst, str8_array(text_code, 2), 0)); + + String8 imm = str8_prefix(str8_skip(text_data, 2), 8); + U64 expected_imm = abs_value; + T_Ok(str8_match(imm, str8_struct(&expected_imm), 0)); +} +T_EndTest; + +T_BeginTest(abs_vs_regular) +{ + String8 shared_symbol_name = str8_lit("foo"); + + U8 regular_payload[] = { 0xC0, 0xFF, 0xEE }; + String8 regular_obj_name = str8_lit("regular.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(regular_payload)); + coff_obj_writer_push_symbol_extern(obj_writer, shared_symbol_name, 0, data_sect); + String8 regular_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(regular_obj_name, regular_obj)); + } + + String8 abs_obj_name = str8_lit("abs.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, shared_symbol_name, 0x1234, COFF_SymStorageClass_External); + String8 abs_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(abs_obj_name, abs_obj)); + } + + U8 entry_text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + String8 entry_obj_name = str8_lit("entry.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + COFF_ObjSymbol *shared_symbol = coff_obj_writer_push_symbol_undef(obj_writer, shared_symbol_name); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 3, shared_symbol, COFF_Reloc_X64_Addr32Nb); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + // TODO: validate that linker issues multiply defined symbol error + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe abs.obj regular.obj entry.obj"); + // linker should complain about multiply defined symbol + T_Ok(g_last_exit_code != 0); + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe regular.obj abs.obj entry.obj"); + // linker should complain even in case regular is before abs + T_Ok(g_last_exit_code != 0); +} +T_EndTest; + +T_BeginTest(abs_vs_common) +{ + String8 shared_symbol_name = str8_lit("foo"); + + String8 common_obj_name = str8_lit("common.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_common(obj_writer, shared_symbol_name, 321); + String8 common_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(common_obj_name, common_obj)); + } + + String8 abs_obj_name = str8_lit("abs.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, shared_symbol_name, 0x1234, COFF_SymStorageClass_External); + String8 abs_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(abs_obj_name, abs_obj)); + } + + U8 entry_text[] = { 0xC3 }; + String8 entry_obj_name = str8_lit("entry.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe abs.obj common.obj entry.obj"); + if (g_last_exit_code == 0) { + // TODO: validate that linker issues multiply defined symbol error + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe common.obj abs.obj entry.obj"); + if (t_id_linker() == T_Linker_RAD) { + T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); + } else { + T_Ok(g_last_exit_code != 0); + } + } +} +T_EndTest; + +T_BeginTest(abs_vs_abs) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("foo"), 'a', COFF_SymStorageClass_External); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 b_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("foo"), 'b', COFF_SymStorageClass_External); + b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("b.obj"), b_obj)); + T_Ok(t_write_entry_obj()); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); + T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); +} +T_EndTest; + +T_BeginTest(undef_weak_lib) +{ + String8 weak; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSymbol *b = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("b"), 0xc3000000, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("a"), COFF_WeakExt_SearchLibrary, b); + weak = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("a")); + U8 text[4] = {0}; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, sym); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + entry = coff_obj_writer_serialize(scratch.arena, obj_writer); + } + + T_Ok(t_write_file(str8_lit("weak.obj"), weak)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry)); + + // undefined symbol must always replace weak symbol with search library + t_invoke_linkerf("/subsystem:console /out:a.exe /entry:entry entry.obj weak.obj"); + T_Ok(g_last_exit_code == LNK_Error_UnresolvedSymbol); +} +T_EndTest; + +T_BeginTest(undef_weak_search_alias) +{ + Temp scratch = scratch_begin(0,0); + + String8 weak_obj; + { + U8 weak_payload[] = { 0xDE, 0xAD, 0xBE, 0xEF }; + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *weak_sect = t_push_data_section(obj_writer, str8_array_fixed(weak_payload)); + COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("ptr")); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("foo"), COFF_WeakExt_SearchAlias, tag); + weak_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 ptr_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("entry")); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("ptr"), COFF_WeakExt_SearchAlias, tag); + ptr_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 undef_obj; + { + U8 undef_obj_payload[] = { 0x00, 0x00, 0x00, 0x00 }; + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *undef_sect = t_push_data_section(obj_writer, str8_array_fixed(undef_obj_payload)); + COFF_ObjSymbol *undef_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); + coff_obj_writer_section_push_reloc(obj_writer, undef_sect, 0, undef_symbol, COFF_Reloc_X64_Addr32Nb); + undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + U8 entry_payload[] = {0xC3}; + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_payload)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("weak.obj"), weak_obj)); + T_Ok(t_write_file(str8_lit("ptr.obj"), ptr_obj)); + T_Ok(t_write_file(str8_lit("undef.obj"), undef_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe weak.obj entry.obj ptr.obj undef.obj"); + T_Ok(g_last_exit_code == 0); +} +T_EndTest; + +T_BeginTest(weak_cycle) +{ + String8 ab_obj_name = str8_lit("ab.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *b = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("B")); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("A"), COFF_WeakExt_SearchAlias, b); + String8 ab_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(ab_obj_name, ab_obj)); + } + + String8 ba_obj_name = str8_lit("ba.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *a = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("A")); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("B"), COFF_WeakExt_SearchAlias, a); + String8 ba_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(ba_obj_name, ba_obj)); + } + + String8 entry_obj_name = str8_lit("entry.obj"); + U8 entry_payload[] = { 0xC3 }; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_payload)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + U64 timeout = os_now_microseconds() + 3 * 1000 * 1000; // give a generous 3 seconds + t_invoke_linker_timeoutf(timeout, "/subsystem:console /entry:my_entry %S %S %S", entry_obj_name, ab_obj_name, ba_obj_name); +} +T_EndTest; + +T_BeginTest(weak_tag) +{ + U32 weak_tag_expected_value = 0x12345678; + String8 weak_tag_obj_name = str8_lit("weak_tag.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *tag_symbol = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("abs"), weak_tag_expected_value, COFF_SymStorageClass_Static); + COFF_ObjSymbol *weak_first = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("strong_first"), COFF_WeakExt_SearchAlias, tag_symbol); + COFF_ObjSymbol *weak_second = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("strong_second"), COFF_WeakExt_SearchAlias, weak_first); + + U8 sect_data[4] = {0}; + COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_array_fixed(sect_data)); + coff_obj_writer_section_push_reloc(obj_writer, sect, 0, weak_second, COFF_Reloc_X64_Addr32); + + String8 weak_tag_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(weak_tag_obj_name, weak_tag_obj)); + } + + String8 entry_name = str8_lit("my_entry"); + U8 entry_text[] = { 0xC3 }; + String8 entry_obj_name = str8_lit("entry.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); + coff_obj_writer_push_symbol_extern(obj_writer, entry_name, 0, text_sect); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe %S %S", weak_tag_obj_name, entry_obj_name); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *data_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + String8 data = str8_substr(exe, rng_1u64(data_section->foff, data_section->foff + data_section->vsize)); + T_Ok(data_section); + T_Ok(data_section->vsize == 4); + T_Ok(str8_match(data, str8_struct(&weak_tag_expected_value), 0)); +} +T_EndTest; + +T_BeginTest(undef_section) +{ + String8 main_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + U8 data[] = { 0, 0, 0, 0 }; + COFF_ObjSection *data_section = t_push_data_section(obj_writer, str8_array_fixed(data)); + COFF_ObjSymbol *foo = coff_obj_writer_push_symbol_undef_sect(obj_writer, str8_lit(".mysect"), COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead); + coff_obj_writer_section_push_reloc(obj_writer, data_section, 0, foo, COFF_Reloc_X64_Addr32Nb); + + U8 text[] = { 0xC3 }; + COFF_ObjSection *text_section = t_push_text_section(obj_writer, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_section); + + main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + U8 payload[] = { 1, 2, 3 }; + String8 sec_defn_obj = t_make_sec_defn_obj(scratch.arena, str8_array_fixed(payload)); + + t_write_file(str8_lit("main.obj"), main_obj); + t_write_file(str8_lit("sec_defn.obj"), sec_defn_obj); + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe main.obj sec_defn.obj"); + if (g_last_exit_code == 0) { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeader *data_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + COFF_SectionHeader *mysect_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".mysect")); + if (data_section && mysect_section) { + if (data_section->vsize == 4 && mysect_section->vsize == 3) { + String8 addr32nb = str8_substr(exe, rng_1u64(data_section->foff, data_section->foff + data_section->vsize)); + String8 expected_voff = str8_struct(&mysect_section->voff); + T_Ok(str8_match(addr32nb, expected_voff, 0)); + } + } + } +} +T_EndTest; + +T_BeginTest(sect_symbol) +{ + String8 sect_payload = str8_lit("hello, world"); + String8 sect_obj_name = str8_lit("sect.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".mysect$1"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, sect_payload); + coff_obj_writer_push_directive(obj_writer, str8_lit("/merge:.mysect=.data")); + String8 sect_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(sect_obj_name, sect_obj)); + } + + String8 main_obj_name = str8_lit("main.obj"); + { + U8 data[8] = {0}; + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_array_fixed(data)); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef_sect(obj_writer, str8_lit(".mysect$2222"), PE_DATA_SECTION_FLAGS); + coff_obj_writer_section_push_reloc_addr(obj_writer, sect, 0, symbol); + + U8 text[] = { 0xC3 }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + + String8 main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + + T_Ok(t_write_file(main_obj_name, main_obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe main.obj sect.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + + T_Ok(sect != 0); + + String8 sect_data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + + String8 addr_data = str8_substr(sect_data, rng_1u64(0, sizeof(U64))); + T_Ok(addr_data.size == sizeof(U64)); + + U64 addr = *(U64 *)addr_data.str; + T_Ok(addr - (pe.image_base + sect->voff) == 8); + + String8 payload_got = str8_substr(sect_data, rng_1u64(8, sect_data.size)); + T_Ok(str8_match(payload_got, sect_payload, 0)); +} +T_EndTest; + +T_BeginTest(undef_reloc_section) +{ + String8 main_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + U8 text[] = { 0xC3 }; + COFF_ObjSection *text_section = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + COFF_ObjSymbol *my_entry_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_section); + + U8 data[8] = { 0 }; + COFF_ObjSection *data_section = t_push_data_section(obj_writer, str8_array_fixed(data)); + COFF_ObjSymbol *foo = coff_obj_writer_push_symbol_undef_sect(obj_writer, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS); + coff_obj_writer_section_push_reloc(obj_writer, data_section, 0, foo, COFF_Reloc_X64_Addr64); + + main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + + coff_obj_writer_release(&obj_writer); + } + + U8 payload[] = { 1, 2, 3 }; + String8 sec_defn_obj = t_make_sec_defn_obj(scratch.arena, str8_array_fixed(payload)); + + T_Ok(t_write_file(str8_lit("main.obj"), main_obj)); + T_Ok(t_write_file(str8_lit("sec_defn.obj"), sec_defn_obj)); + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe main.obj sec_defn.obj"); + if (t_id_linker() == T_Linker_RAD) { + T_Ok(g_last_exit_code == LNK_Error_SectRefsDiscardedMemory); + } else { + T_Ok(g_last_exit_code != 0); + } +} +T_EndTest; + +T_BeginTest(find_merged_pdata) +{ + U8 foobar_payload[] = { + 0x40, 0x57, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x33, 0xC4, 0x48, 0x89, 0x84, 0x24, 0xF0, 0x01, 0x00, 0x00, 0x48, 0x8D, 0x04, 0x24, 0x48, + 0x8B, 0xF8, 0x33, 0xC0, 0xB9, 0xEC, 0x01, 0x00, 0x00, 0xF3, 0xAA, 0xB8, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x6B, 0xC0, 0x02, 0x8B, 0x04, 0x04, 0x48, 0x8B, 0x8C, 0x24, 0xF0, 0x01, 0x00, 0x00, 0x48, + 0x33, 0xCC, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x81, 0xC4, 0x00, 0x02, 0x00, 0x00, 0x5F, 0xC3, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0x48, 0x83, 0xEC, 0x28, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 0x28, 0xC3 + }; + U8 xdata_payload[] = { + 0x19, 0x1B, 0x03, 0x00, 0x09, 0x01, 0x40, 0x00, 0x02, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF0, 0x01, 0x00, 0x00, 0x01, 0x04, 0x01, 0x00, 0x04, 0x42, 0x00, 0x00 + }; + PE_IntelPdata intel_pdata = {0}; + U8 text_payload[] = { 0xC3 }; + + String8 main_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *xdata = coff_obj_writer_push_section(obj_writer, str8_lit(".xdata"), COFF_SectionFlag_MemRead|COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_Align4Bytes, str8_array_fixed(xdata_payload)); + COFF_ObjSection *pdata = coff_obj_writer_push_section(obj_writer, str8_lit(".pdata"), COFF_SectionFlag_MemRead|COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_Align4Bytes, str8_struct(&intel_pdata)); + COFF_ObjSection *foobar = coff_obj_writer_push_section(obj_writer, str8_lit(".foobar"), COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute|COFF_SectionFlag_CntCode|COFF_SectionFlag_Align1Bytes, str8_array_fixed(foobar_payload)); + COFF_ObjSection *text = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute|COFF_SectionFlag_CntCode|COFF_SectionFlag_Align1Bytes, str8_array_fixed(text_payload)); + + COFF_ObjSymbol *foobar_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("foobar"), 0, foobar); + + coff_obj_writer_push_symbol_secdef(obj_writer, xdata, COFF_ComdatSelect_Null); + COFF_ObjSymbol *unwind_foobar = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("$unwind$foobar"), 0, xdata); + + coff_obj_writer_push_symbol_secdef(obj_writer, pdata, COFF_ComdatSelect_Null); + coff_obj_writer_push_symbol_static(obj_writer, str8_lit("$pdata$foobar"), 0, pdata); + + coff_obj_writer_section_push_reloc(obj_writer, pdata, OffsetOf(PE_IntelPdata, voff_unwind_info), unwind_foobar, COFF_Reloc_X64_Addr32Nb); + coff_obj_writer_section_push_reloc(obj_writer, pdata, OffsetOf(PE_IntelPdata, voff_first), foobar_symbol, COFF_Reloc_X64_Addr32Nb); + coff_obj_writer_section_push_reloc(obj_writer, pdata, OffsetOf(PE_IntelPdata, voff_one_past_last), foobar_symbol, COFF_Reloc_X64_Addr32Nb); + + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text); + main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("main.obj"), main_obj)); + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe main.obj /merge:.pdata=.rdata"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + T_Ok(dim_1u64(pe.data_dir_franges[PE_DataDirectoryIndex_EXCEPTIONS]) == 0xC); +} +T_EndTest; + +T_BeginTest(section_sort) +{ + String8 data_obj_name = str8_lit("data.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + COFF_SectionFlags data_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemRead|COFF_SectionFlag_Align1Bytes; + coff_obj_writer_push_section(obj_writer, str8_lit(".data$z"), data_flags, str8_lit("five")); + coff_obj_writer_push_section(obj_writer, str8_lit(".data$a"), data_flags, str8_lit("three")); + coff_obj_writer_push_section(obj_writer, str8_lit(".data$bbbbb"), data_flags, str8_lit("four")); + coff_obj_writer_push_section(obj_writer, str8_lit(".data$"), data_flags, str8_lit("two")); + coff_obj_writer_push_section(obj_writer, str8_lit(".data"), data_flags, str8_lit("one")); + + String8 data_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(data_obj_name, data_obj)); + } + + String8 entry_obj_name = str8_lit("entry.obj"); + U8 entry_text[] = { 0xC3 }; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe data.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeader *data_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + T_Ok(data_section); + + String8 data = str8_substr(exe, rng_1u64(data_section->foff, data_section->foff + data_section->vsize)); + String8 expected_data = str8_lit("onetwothreefourfive"); + T_Ok(str8_match(data, expected_data, 0)); +} +T_EndTest; + +T_BeginTest(flag_conf) +{ + COFF_SectionFlags my_sect0_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute; + COFF_SectionFlags my_sect1_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite; + String8 conf_obj_name = str8_lit("conf.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *a_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".mysect"), my_sect0_flags, str8_lit("one")); + COFF_ObjSection *b_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".mysect"), my_sect1_flags, str8_lit("two")); + String8 conf_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(conf_obj_name, conf_obj)); + } + + U8 entry_text[] = { 0xC3 }; + String8 entry_obj_name = str8_lit("entry.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe conf.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeaderArray my_sects = coff_section_header_array_from_name(scratch.arena, string_table, section_table, pe.section_count, str8_lit(".mysect")); + T_Ok(my_sects.count == 2); + + COFF_SectionHeader *my_sect0 = &my_sects.v[0]; + COFF_SectionHeader *my_sect1 = &my_sects.v[1]; + T_Ok(my_sect0->flags == my_sect0_flags); + T_Ok(my_sect1->flags == my_sect1_flags); +} +T_EndTest; + +T_BeginTest(invalid_bss) +{ + COFF_SectionFlags bss_flags = COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead; + String8 bss_obj_name = str8_lit("bss.obj"); + String8 bss_data = str8_lit("Hello, World"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_section(obj_writer, str8_lit(".bss"), bss_flags, bss_data); + String8 bss_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(bss_obj_name, bss_obj)); + } + + String8 entry_obj_name = str8_lit("entry.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { 0xC3 }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe bss.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeader *bss_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".bss")); + T_Ok(bss_sect != 0); + T_Ok(bss_sect->vsize == 0xC); + T_Ok(bss_sect->flags == bss_flags); + String8 data = str8_substr(exe, rng_1u64(bss_sect->foff, bss_sect->foff + bss_sect->vsize)); + T_Ok(str8_match(data, bss_data, 0)); +} +T_EndTest; + +T_BeginTest(common_block) +{ + String8 a_obj_name = str8_lit("a.obj"); + U8 a_data[6] = {0}; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_common(obj_writer, str8_lit("A"), 3); + COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(a_data)); + data_sect->flags |= COFF_SectionFlag_Align1Bytes; + coff_obj_writer_push_section(obj_writer, str8_lit(".bss"), PE_BSS_SECTION_FLAGS, str8(0, 1)); // shift common block's initial position + coff_obj_writer_section_push_reloc(obj_writer, data_sect, 0, symbol, COFF_Reloc_X64_Addr32); + String8 a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(a_obj_name, a_obj)); + } + + String8 b_obj_name = str8_lit("b.obj"); + U8 b_data[9] = { 0 }; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(b_data)); + data_sect->flags |= COFF_SectionFlag_Align1Bytes; + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_common(obj_writer, str8_lit("B"), 6); + coff_obj_writer_section_push_reloc(obj_writer, data_sect, 0, symbol, COFF_Reloc_X64_Addr64); + String8 b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(b_obj_name, b_obj)); + } + + String8 entry_obj_name = str8_lit("entry.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { 0xC3 }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe /fixed /largeaddressaware:no /merge:.bss=.comm a.obj b.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + COFF_SectionHeader *comm_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".comm")); + COFF_SectionHeader *data_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + T_Ok(comm_sect != 0); + T_Ok(data_sect != 0); + + // blocks must be sorted in descending order to reduce alignment padding + T_Ok(comm_sect->vsize == 0x13); + + // ensure linker correctly patched addresses for symbols pointing into common block + String8 data = str8_substr(exe, rng_1u64(data_sect->foff, data_sect->foff + data_sect->fsize)); + U32 *a_addr = (U32 *)data.str; + U64 *b_addr = (U64 *)(data.str + sizeof(a_data)); + T_Ok(*a_addr == (pe.image_base + comm_sect->voff + 0x10)); + T_Ok(*b_addr == (pe.image_base + comm_sect->voff + 0x8)); +} +T_EndTest; + +T_BeginTest(base_relocs) +{ + // main.obj + String8 entry_name = str8_lit("my_entry"); + U64 mov_func_name64 = 2; + U64 mov_func_name32 = 16; + U8 main_text[] = { + 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, func_name + 0xff, 0xd0, // call rax + 0x48, 0x31, 0xc0, // xor rax, rax + 0xb8, 0x00, 0x00, 0x00, 0x00, // mov eax, func_name + 0xff, 0xd0, // call rax + 0xc3 // ret + }; + + // func.obj + String8 func_name = str8_lit("foo"); + U8 func_text[] = { 0xc3 }; + + String8 main_obj_name = str8_lit("main.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(main_text)); + COFF_ObjSymbol *func_undef = coff_obj_writer_push_symbol_undef(obj_writer, func_name); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, mov_func_name64, func_undef, COFF_Reloc_X64_Addr64); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, mov_func_name32, func_undef, COFF_Reloc_X64_Addr32); + + // linker must not produce base relocations for absolute symbol + U8 data[4] = {0}; + COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(data)); + COFF_ObjSymbol *abs_symbol = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("abs"), 0x12345678, COFF_SymStorageClass_Static); + coff_obj_writer_section_push_reloc(obj_writer, data_sect, 0, abs_symbol, COFF_Reloc_X64_Addr32); + + coff_obj_writer_push_symbol_extern(obj_writer, entry_name, 0, text_sect); + String8 main_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(main_obj_name, main_obj)); + } + + String8 func_obj_name = str8_lit("func.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(func_text)); + coff_obj_writer_push_symbol_extern(obj_writer, func_name, 0, text_sect); + String8 func_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(func_obj_name, func_obj)); + } + + String8 out_name = str8_lit("a.exe"); + t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe main.obj func.obj"); + T_Ok(g_last_exit_code == 0); + + // it is illegal to merge .reloc with other sections + t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.reloc=.rdata main.obj func.obj"); + if (t_id_linker() == T_Linker_RAD) { + T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge); + } else { + T_Ok(g_last_exit_code != 0); + } + + // the other way around is illegal too + t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.rdata=.reloc main.obj func.obj"); + if (t_id_linker() == T_Linker_RAD) { + T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge); + } else { + T_Ok(g_last_exit_code != 0); + } +} +T_EndTest; + +T_BeginTest(simple_lib_test) +{ + String8 test_payload = str8_lit("The quick brown fox jumps over the lazy dog"); + String8 test_obj = {0}; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_Unknown); + COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8(test_payload.str, test_payload.size+1)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("test"), 0, data_sect); + test_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 test_lib_name = str8_lit("test.lib"); + { + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_obj(lib_writer, str8_lit("test.obj"), test_obj); + String8 test_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + coff_lib_writer_release(&lib_writer); + T_Ok(t_write_file(test_lib_name, test_lib)); + } + + U8 entry_text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0xC3 + }; + String8 entry_obj_name = str8_lit("entry.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_text)); + COFF_ObjSymbol *test_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("test")); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 3, test_symbol, COFF_Reloc_X64_Addr32Nb); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 7, text_sect); + String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(entry_obj_name, entry_obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe entry.obj test.lib"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + COFF_SectionHeader *data_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->fsize)); + String8 data_data = str8_substr(exe, rng_1u64(data_sect->foff, data_sect->foff + data_sect->fsize)); + + // was test payload linked? + String8 data_string = str8_cstring_capped(data_data.str, data_data.str + data_data.size); + T_Ok(str8_match(data_string, test_payload, 0)); + + // do we have enough bytes to read text? + T_Ok(text_data.size >= sizeof(entry_text)); + + // linker must pull-in test.obj and patch relocation for "test" symbol + U32 *data_addr32nb = (U32 *)(text_data.str+3); + T_Ok(*data_addr32nb == data_sect->voff); +} +T_EndTest; + +T_BeginTest(import_export) +{ + String8 export_obj; + { + String8 export_obj_name = str8_lit("export.obj"); + String8 export_obj_payload = str8_lit("test"); + U8 export_text[] = { + 0x90, // 0: nop + 0x54, // 1: push rsp + 0x48, 0xc7, 0xc0, 0x21, 0x03, 0x00, 0x00, // 2: mov rax, 0x321 + 0x5c, // 9: pop rsp + 0xc3 // 10: ret + }; + + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *data_sect = t_push_data_section(obj_writer, export_obj_payload); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("foo"), 0, data_sect); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("ord"), 1, data_sect); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("ord2"), 2, data_sect); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("ord3"), 9, data_sect); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("ord4"), 10, data_sect); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:foo=foo")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:bar=foo")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord,@5")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord2,@6,DATA")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord3,@7,NONAME,PRIVATE")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:ord4,@8,NONAME,DATA")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:baz=BAZ.qwe")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:baf=BAZ.#1")); + export_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 import_obj; + { + String8 import_obj_name = str8_lit("import.obj"); + U8 import_payload[1024] = {0}; + + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *data_sect = t_push_data_section(obj_writer, str8_array_fixed(import_payload)); + + char *import_symbols[] = { + "__imp_foo", + "__imp_bar", + "__imp_baz", + "__imp_baf", + "__imp_ord", + //"__imp_ord2", + //"__imp_ord4", + "bar", + //"baf", + //"baz", + "foo", + "ord", + }; + + for EachElement(i, import_symbols) { + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_cstring(import_symbols[i])); + coff_obj_writer_section_push_reloc_voff(obj_writer, data_sect, i * 4, symbol); + } + + import_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 baz_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *s1 = coff_obj_writer_push_section(obj_writer, str8_lit(".s1"), PE_DATA_SECTION_FLAGS, str8_lit("s1")); + COFF_ObjSection *s2 = coff_obj_writer_push_section(obj_writer, str8_lit(".s2"), PE_DATA_SECTION_FLAGS, str8_lit("s2")); + COFF_ObjSection *text_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed((U8[]){ 0xC3 })); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("_DllMainCRTStartup"), 0, text_sect); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("s1"), 0, s1); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("s2"), 0, s2); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:baf=s1")); + coff_obj_writer_push_directive(obj_writer, str8_lit("/export:baz=s2")); + baz_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + // write objs + T_Ok(t_write_file(str8_lit("export.obj"), export_obj)); + T_Ok(t_write_file(str8_lit("import.obj"), import_obj)); + T_Ok(t_write_file(str8_lit("baz.obj"), baz_obj)); + T_Ok(t_write_entry_obj()); + + // link dlls + t_invoke_linkerf("/dll /out:export.dll libcmt.lib export.obj"); // export.dll + T_Ok(g_last_exit_code == 0); + t_invoke_linkerf("/dll /out:baz.dll /export:s1,@1,NONAME /export:qwe=s2 baz.obj"); // baz.dll + T_Ok(g_last_exit_code == 0); + + // validate export table in export.dll + if (t_id_linker() == T_Linker_RAD) { + // validate export table in export.dll + { + String8 dll = t_read_file(scratch.arena, str8_lit("export.dll")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, dll); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(dll, pe.section_table_range).str; + PE_ParsedExportTable export_table = pe_exports_from_data(scratch.arena, pe.section_count, section_table, dll, pe.data_dir_franges[PE_DataDirectoryIndex_EXPORT], pe.data_dir_vranges[PE_DataDirectoryIndex_EXPORT]); + + // validate header + T_Ok(export_table.flags == 0); + T_Ok(export_table.time_stamp == COFF_TimeStamp_Max); + T_Ok(export_table.major_ver == 0); + T_Ok(export_table.minor_ver == 0); + T_Ok(export_table.ordinal_base == 5); + T_Ok(export_table.export_count == 8); + + // validate names + T_Ok(str8_match(export_table.exports[0].name, str8_lit("baf"), 0)); + T_Ok(str8_match(export_table.exports[1].name, str8_lit("bar"), 0)); + T_Ok(str8_match(export_table.exports[2].name, str8_lit("baz"), 0)); + T_Ok(str8_match(export_table.exports[3].name, str8_lit("foo"), 0)); + T_Ok(str8_match(export_table.exports[4].name, str8_lit("ord"), 0)); + T_Ok(str8_match(export_table.exports[5].name, str8_lit("ord2"), 0)); + T_Ok(export_table.exports[6].name.size == 0); + T_Ok(export_table.exports[7].name.size == 0); + + // validate forwarders + T_Ok(str8_match(export_table.exports[0].forwarder, str8_lit("BAZ.#1"), 0)); + T_Ok(export_table.exports[1].forwarder.size == 0); + T_Ok(str8_match(export_table.exports[2].forwarder, str8_lit("BAZ.qwe"), 0)); + T_Ok(export_table.exports[3].forwarder.size == 0); + T_Ok(export_table.exports[4].forwarder.size == 0); + T_Ok(export_table.exports[5].forwarder.size == 0); + T_Ok(export_table.exports[6].forwarder.size == 0); + T_Ok(export_table.exports[7].forwarder.size == 0); + + // validate voffs + T_Ok(export_table.exports[0].voff == 0x20070); + T_Ok(export_table.exports[1].voff == 0x19000); + T_Ok(export_table.exports[2].voff == 0x20077); + T_Ok(export_table.exports[3].voff == 0x19000); + T_Ok(export_table.exports[4].voff == 0x19001); + T_Ok(export_table.exports[5].voff == 0x19002); + T_Ok(export_table.exports[6].voff == 0x19009); + T_Ok(export_table.exports[7].voff == 0x1900a); + + // validate ordinals + T_Ok(export_table.exports[0].ordinal == 9); + T_Ok(export_table.exports[1].ordinal == 10); + T_Ok(export_table.exports[2].ordinal == 11); + T_Ok(export_table.exports[3].ordinal == 12); + T_Ok(export_table.exports[4].ordinal == 5); + T_Ok(export_table.exports[5].ordinal == 6); + T_Ok(export_table.exports[6].ordinal == 7); + T_Ok(export_table.exports[7].ordinal == 8); + } + + // validate export table in baz.dll + { + String8 dll = t_read_file(scratch.arena, str8_lit("baz.dll")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, dll); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(dll, pe.section_table_range).str; + PE_ParsedExportTable export_table = pe_exports_from_data(scratch.arena, pe.section_count, section_table, dll, pe.data_dir_franges[PE_DataDirectoryIndex_EXPORT], pe.data_dir_vranges[PE_DataDirectoryIndex_EXPORT]); + + // validate header + T_Ok(export_table.flags == 0); + T_Ok(export_table.time_stamp == COFF_TimeStamp_Max); + T_Ok(export_table.major_ver == 0); + T_Ok(export_table.minor_ver == 0); + T_Ok(export_table.ordinal_base == 1); + T_Ok(export_table.export_count == 4); + + // validate names + T_Ok(str8_match(export_table.exports[0].name, str8_lit("baf"), 0)); + T_Ok(str8_match(export_table.exports[1].name, str8_lit("baz"), 0)); + T_Ok(str8_match(export_table.exports[2].name, str8_lit("qwe"), 0)); + T_Ok(str8_match(export_table.exports[3].name, str8_zero(), 0)); + + // validate forwarders + T_Ok(str8_match(export_table.exports[0].forwarder, str8_zero(), 0)); + T_Ok(str8_match(export_table.exports[1].forwarder, str8_zero(), 0)); + T_Ok(str8_match(export_table.exports[2].forwarder, str8_zero(), 0)); + T_Ok(str8_match(export_table.exports[3].forwarder, str8_zero(), 0)); + + // validate voffs + T_Ok(export_table.exports[0].voff == 0x2000); + T_Ok(export_table.exports[1].voff == 0x3000); + T_Ok(export_table.exports[2].voff == 0x3000); + T_Ok(export_table.exports[3].voff == 0x2000); + + // validate ordinals + T_Ok(export_table.exports[0].ordinal == 2); + T_Ok(export_table.exports[1].ordinal == 3); + T_Ok(export_table.exports[2].ordinal == 4); + T_Ok(export_table.exports[3].ordinal == 1); + } + + } + + #if OS_WINDOWS + { + T_Ok(SetDllDirectoryA((LPCSTR)g_wdir.str)); + HANDLE export_dll = LoadLibrary("export.dll"); + T_Ok(export_dll); + + // test query by function name + //T_Ok(GetProcAddress(export_dll, "baf")); + T_Ok(GetProcAddress(export_dll, "bar")); + //T_Ok(GetProcAddress(export_dll, "baz")); + T_Ok(GetProcAddress(export_dll, "foo")); + T_Ok(GetProcAddress(export_dll, "ord")); + T_Ok(GetProcAddress(export_dll, "ord2")); + + // test query by ordinal + //T_Ok(GetProcAddress(export_dll, MAKEINTRESOURCE(9))); + T_Ok(GetProcAddress(export_dll, MAKEINTRESOURCE(10))); + //T_Ok(GetProcAddress(export_dll, MAKEINTRESOURCE(11))); + T_Ok(GetProcAddress(export_dll, MAKEINTRESOURCE(12))); + T_Ok(GetProcAddress(export_dll, MAKEINTRESOURCE(5))); + T_Ok(GetProcAddress(export_dll, MAKEINTRESOURCE(6))); + T_Ok(GetProcAddress(export_dll, MAKEINTRESOURCE(7))); + T_Ok(GetProcAddress(export_dll, MAKEINTRESOURCE(8))); + } + #endif + + //T_Ok(t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /delayload:export.dll /export:entry kernel32.Lib delayimp.lib libcmt.lib export.lib import.obj entry.obj") == 0); + // TODO: check import table +} +T_EndTest; + +T_BeginTest(image_base) +{ + String8 obj_name = str8_lit("image_base.obj"); + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00, // lea rcx, [__ImageBase] + 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, __ImageBase + 0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, __ImageBase + 0xC3 // ret + }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + COFF_ObjSymbol *image_base_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__ImageBase")); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 3, image_base_symbol, COFF_Reloc_X64_Rel32); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 9, image_base_symbol, COFF_Reloc_X64_Addr64); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 18, image_base_symbol, COFF_Reloc_X64_Addr32Nb); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(obj_name, obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /base:0x2000000140000000 /out:a.exe image_base.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_section = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_section); + + U8 expected_text[] = { + 0x48, 0x8D, 0x0D, 0xF9, 0xEF, 0xFF, 0xFF, + 0x48, 0xB8, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x20, + 0xB8, 0x00, 0x00, 0x00, 0x00, + 0xC3 + }; + String8 text_data = str8_substr(exe, rng_1u64(text_section->foff, text_section->foff + sizeof(expected_text))); + T_Ok(str8_match(text_data, str8_array_fixed(expected_text), 0)); +} +T_EndTest; + +T_BeginTest(comdat_any) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".test$mn"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("1")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Any); + COFF_ObjSymbol *test = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + test->type.u.msb = COFF_SymDType_Func; + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("1.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".test$mn"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("2")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Any); + COFF_ObjSymbol *test = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("2.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + { + t_invoke_linkerf("/subsystem:console /entry:entry /out:1.exe 1.obj 2.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + String8 exe = t_read_file(scratch.arena, str8_lit("1.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".test")); + String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(data, str8_lit("1"), 0)); + } + + { + t_invoke_linkerf("/subsystem:console /entry:entry /out:2.exe 2.obj 1.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + String8 exe = t_read_file(scratch.arena, str8_lit("2.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".test")); + String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(data, str8_lit("2"), 0)); + } +} +T_EndTest; + +T_BeginTest(comdat_no_duplicates) +{ + String8 test_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *test_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".test"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("a")); + coff_obj_writer_push_symbol_secdef(obj_writer, test_sect, COFF_ComdatSelect_NoDuplicates); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("a"), 0, test_sect); + test_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("a")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), test_obj)); + T_Ok(t_write_file(str8_lit("b.obj"), test_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("b.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".test")); + T_Ok(sect); + String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(data, str8_lit("a"), 0)); +} +T_EndTest; + +T_BeginTest(comdat_same_size) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("a")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_SameSize); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("a.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("b")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_SameSize); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("b.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".c"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("cc")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_SameSize); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("c.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".a")); + T_Ok(sect != 0); + String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(data, str8_lit("a"), 0)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj b.obj c.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); } +} +T_EndTest; + +T_BeginTest(comdat_exact_match) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_ExactMatch); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("a.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a2"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_ExactMatch); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("a2.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("b")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_ExactMatch); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("b.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj b.obj"); + T_Ok(g_last_exit_code != 0); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe entry.obj a2.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("b.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".a2")); + T_Ok(sect != 0); + String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(data, str8_lit("a"), 0)); + } +} +T_EndTest; + +T_BeginTest(comdat_largest) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("a.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("bb")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("b.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".c"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("c")); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("c.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /out:a.exe /entry:entry entry.obj a.obj b.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *discard_sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".a")); + T_Ok(discard_sect == 0); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".b")); + T_Ok(sect != 0); + String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(data, str8_lit("bb"), 0)); + } + + t_invoke_linkerf("/subsystem:console /out:b.exe /entry:entry entry.obj c.obj a.obj"); + T_Ok(g_last_exit_code == 0); + + { + String8 exe = t_read_file(scratch.arena, str8_lit("b.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".c")); + T_Ok(sect != 0); + String8 data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + T_Ok(str8_match(data, str8_lit("c"), 0)); + } +} +T_EndTest; + +T_BeginTest(comdat_associative) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSection *a = coff_obj_writer_push_section(obj_writer, str8_lit("a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); + COFF_ObjSection *aa = coff_obj_writer_push_section(obj_writer, str8_lit("aa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("aa")); + coff_obj_writer_push_symbol_secdef(obj_writer, a, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, a); + coff_obj_writer_push_symbol_associative(obj_writer, aa, a); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("a.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSection *bb = coff_obj_writer_push_section(obj_writer, str8_lit("bb"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("bb")); + COFF_ObjSection *b = coff_obj_writer_push_section(obj_writer, str8_lit("b"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("b")); + COFF_ObjSection *bbb = coff_obj_writer_push_section(obj_writer, str8_lit("bbb"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("bbb")); + coff_obj_writer_push_symbol_secdef(obj_writer, bb, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_associative(obj_writer, b, bb); + coff_obj_writer_push_symbol_associative(obj_writer, bbb, bb); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, bb); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("b.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj b.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeader *a = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("a")); + COFF_SectionHeader *aa = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("aa")); + COFF_SectionHeader *b = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("b")); + COFF_SectionHeader *bb = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("bb")); + COFF_SectionHeader *bbb = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit("bbb")); + T_Ok(a == 0); + T_Ok(aa == 0); + T_Ok(b != 0); + T_Ok(bb != 0); + T_Ok(bbb != 0); + String8 b_data = str8_substr(exe, rng_1u64(b->foff, b->foff + b->vsize)); + String8 bb_data = str8_substr(exe, rng_1u64(bb->foff, bb->foff + bb->vsize)); + String8 bbb_data = str8_substr(exe, rng_1u64(bbb->foff, bbb->foff + bbb->vsize)); + T_Ok(str8_match(b_data, str8_lit("b"), 0)); + T_Ok(str8_match(bb_data, str8_lit("bb"), 0)); + T_Ok(str8_match(bbb_data, str8_lit("bbb"), 0)); +} +T_EndTest; + +T_BeginTest(comdat_associative_loop) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *aaaa = coff_obj_writer_push_section(obj_writer, str8_lit(".aaaa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("aaaa")); + COFF_ObjSection *aa = coff_obj_writer_push_section(obj_writer, str8_lit(".aa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("aa")); + COFF_ObjSection *a = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("a")); + COFF_ObjSection *aaa = coff_obj_writer_push_section(obj_writer, str8_lit(".aaa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT|COFF_SectionFlag_Align1Bytes, str8_lit("aaa")); + coff_obj_writer_push_symbol_associative(obj_writer, aaa, aa); + coff_obj_writer_push_symbol_associative(obj_writer, aaaa, aaa); + coff_obj_writer_push_symbol_associative(obj_writer, a, aa); + coff_obj_writer_push_symbol_associative(obj_writer, aa, a); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("loop.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe loop.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_AssociativeLoop); } +} +T_EndTest; + +T_BeginTest(comdat_associative_non_comdat) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *a = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("a")); + COFF_ObjSection *b = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS, str8_lit("b")); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, a); + coff_obj_writer_push_symbol_associative(obj_writer, b, a); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("test.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj test.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *a = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".a")); + COFF_SectionHeader *b = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".b")); + T_Ok(a != 0); + T_Ok(b != 0); + String8 a_data = str8_substr(exe, rng_1u64(a->foff, a->foff + a->vsize)); + String8 b_data = str8_substr(exe, rng_1u64(b->foff, b->foff + b->vsize)); + T_Ok(str8_match(a_data, str8_lit("a"), 0)); + T_Ok(str8_match(b_data, str8_lit("b"), 0)); +} +T_EndTest; + +T_BeginTest(comdat_associative_out_of_bounds) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSection *a = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("a")); + COFF_ObjSection *aa = coff_obj_writer_push_section(obj_writer, str8_lit(".aa"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_lit("aa")); + coff_obj_writer_push_symbol_secdef(obj_writer, a, COFF_ComdatSelect_Any); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, a); + coff_obj_writer_push_symbol_associative(obj_writer, aa, a); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + { + COFF_FileHeaderInfo header = coff_file_header_info_from_data(obj); + String8 string_table = str8_substr(obj, header.string_table_range); + String8 symbol_table = str8_substr(obj, header.symbol_table_range); + COFF_ParsedSymbol symbol = coff_parse_symbol(header, string_table, symbol_table, 3); + AssertAlways(str8_match(symbol.name, str8_lit(".aa"), 0)); + AssertAlways(symbol.aux_symbol_count == 1); + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + COFF_SymbolSecDef *secdef = (COFF_SymbolSecDef *)(symbol16 + 1); + secdef->number_lo = 321; + } + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("bad.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj bad.obj"); + T_Ok(g_last_exit_code != 0); + if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_IllData); } +} +T_EndTest; + +T_BeginTest(comdat_with_offset) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 a[] = "1Hello, World!"; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(a)); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 1, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("a.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 a[] = "Hello, World!"; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(a)); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 1, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("b.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 3, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); + T_Ok(g_last_exit_code == 0); +} +T_EndTest; + +T_BeginTest(reloc_against_removed_comdat) +{ + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 a[] = "1Hello, World!"; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(a)); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 1, sect); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 b_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 a[] = "H"; + COFF_ObjSection *comdat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(a)); + coff_obj_writer_push_symbol_secdef(obj_writer, comdat_sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 1, comdat_sect); + COFF_ObjSymbol *static_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("STATIC"), 2, comdat_sect); + + U8 rdata[4] = {0}; + COFF_ObjSection *regular_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS, str8_array_fixed(rdata)); + coff_obj_writer_section_push_reloc_voff(obj_writer, regular_sect, 0, static_symbol); + + b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 3, symbol); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("b.obj"), b_obj)); + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); + T_Ok(g_last_exit_code == LNK_Error_RelocationAgainstRemovedSection); +} +T_EndTest; + +T_BeginTest(sect_align) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + COFF_ObjSection *sect_align_shift = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("q")); + COFF_ObjSection *sect_align_none = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("abc")); + COFF_ObjSection *sect_align_1 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_lit("wr")); + COFF_ObjSection *sect_align_2 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_lit("e")); + COFF_ObjSection *sect_align_4 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_lit("ttttt")); + COFF_ObjSection *sect_align_8 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align8Bytes, str8_lit("g")); + COFF_ObjSection *sect_align_16 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align16Bytes, str8_lit("o")); + COFF_ObjSection *sect_align_32 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align32Bytes, str8_lit("p")); + COFF_ObjSection *sect_align_64 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align64Bytes, str8_lit("f")); + COFF_ObjSection *sect_align_128 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align128Bytes, str8_lit("x")); + COFF_ObjSection *sect_align_256 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align256Bytes, str8_lit("c")); + COFF_ObjSection *sect_align_512 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align512Bytes, str8_lit("v")); + COFF_ObjSection *sect_align_1024 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align1024Bytes, str8_lit("b")); + COFF_ObjSection *sect_align_2048 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align2048Bytes, str8_lit("n")); + COFF_ObjSection *sect_align_4096 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align4096Bytes, str8_lit("m")); + COFF_ObjSection *sect_align_8192 = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align8192Bytes, str8_lit("z")); + + U8 text[] = { 0xC3 }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("my_entry"), 0, text_sect); + + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("test.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe /align:8192 test.obj"); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + + COFF_SectionHeader *sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".a")); + T_Ok(sect); + String8 sect_data = str8_substr(exe, rng_1u64(sect->foff, sect->foff + sect->vsize)); + + String8 shift = str8_substr(sect_data, rng_1u64(0, 1)); + T_Ok(str8_match(shift, str8_lit("q"), 0)); + String8 a_none = str8_substr(sect_data, rng_1u64(16, 16 + 3)); + T_Ok(str8_match(a_none, str8_lit("abc"), 0)); + String8 a_1 = str8_substr(sect_data, rng_1u64(19, 21)); + T_Ok(str8_match(a_1, str8_lit("wr"), 0)); + String8 a_2 = str8_substr(sect_data, rng_1u64(22, 23)); + T_Ok(str8_match(a_2, str8_lit("e"), 0)); + String8 a_4 = str8_substr(sect_data, rng_1u64(24, 29)); + T_Ok(str8_match(a_4, str8_lit("ttttt"), 0)); + String8 a_8 = str8_substr(sect_data, rng_1u64(32, 33)); + T_Ok(str8_match(a_8, str8_lit("g"), 0)); + String8 a_16 = str8_substr(sect_data, rng_1u64(48, 49)); + T_Ok(str8_match(a_16, str8_lit("o"), 0)); + String8 a_32 = str8_substr(sect_data, rng_1u64(64, 65)); + T_Ok(str8_match(a_32, str8_lit("p"), 0)); + String8 a_64 = str8_substr(sect_data, rng_1u64(128, 129)); + T_Ok(str8_match(a_64, str8_lit("f"), 0)); + String8 a_128 = str8_substr(sect_data, rng_1u64(256, 257)); + T_Ok(str8_match(a_128, str8_lit("x"), 0)); + String8 a_256 = str8_substr(sect_data, rng_1u64(512, 513)); + T_Ok(str8_match(a_256, str8_lit("c"), 0)); + String8 a_512 = str8_substr(sect_data, rng_1u64(1024, 1025)); + T_Ok(str8_match(a_512, str8_lit("v"), 0)); + String8 a_1024 = str8_substr(sect_data, rng_1u64(2048, 2049)); + T_Ok(str8_match(a_1024, str8_lit("b"), 0)); + String8 a_2048 = str8_substr(sect_data, rng_1u64(4096, 4097)); + T_Ok(str8_match(a_2048, str8_lit("n"), 0)); + String8 a_4096 = str8_substr(sect_data, rng_1u64(8192, 8193)); + T_Ok(str8_match(a_4096, str8_lit("m"), 0)); + String8 a_8192 = str8_substr(sect_data, rng_1u64(16384, 16385)); + T_Ok(str8_match(a_8192, str8_lit("z"), 0)); +} +T_EndTest; + +T_BeginTest(alt_name) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("test")); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("test"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("test.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("foo")); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("foo"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("foo.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + // basic alternate name test + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename:foo=test test.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + // linker should not chase alt name links + t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe /alternatename:foo=bar /alternatename:bar=test test.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + + // alt name conflict + t_invoke_linkerf("/subsystem:console /entry:entry /out:c.exe /alternatename:foo=test /alternatename:foo=qwe test.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + + // syntax error + t_invoke_linkerf("/subsystem:console /entry:entry /out:d.exe /alternatename:foo foo.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + + // syntax error + t_invoke_linkerf("/subsystem:console /entry:entry /out:e.exe /alternatename:foo-oof foo.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + + // syntax error + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename:foo=test=bar foo.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + + // syntax error + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename:foo= foo.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + + // syntax error + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename:= foo.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + + // syntax error + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /alternatename: foo.obj entry.obj"); + T_Ok(g_last_exit_code != 0); + + // TODO: check that RAD Linker prints these warnings + + // warn about alt name to self alt name? + t_invoke_linkerf("/subsystem:console /entry:entry /out:f.exe /alternatename:foo=foo foo.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + // warn about alt name to unknown symbol? + t_invoke_linkerf("/subsystem:console /entry:entry /out:g.exe /alternatename:qwe=ewq foo.obj entry.obj"); + T_Ok(g_last_exit_code == 0); +} +T_EndTest; + +T_BeginTest(include) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("foo")); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("foo"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("include.obj"), obj)); + + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_obj(lib_writer, str8_lit("include.obj"), obj); + String8 lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + coff_lib_writer_release(&lib_writer); + T_Ok(t_write_file(str8_lit("include.lib"), lib)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + // simple include test + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /include:foo entry.obj include.lib"); + T_Ok(g_last_exit_code == 0); + + // validate that linker pulled-in include.obj + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *foo_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + T_Ok(foo_sect != 0); + String8 foo_data = str8_substr(exe, rng_1u64(foo_sect->foff, foo_sect->foff + foo_sect->vsize)); + T_Ok(str8_match(foo_data, str8_lit("foo"), 0)); + } + + // test unresolved include + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /include:ewq entry.obj"); + T_Ok(g_last_exit_code != 0); + if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_UnresolvedSymbol); } +} +T_EndTest; + +T_BeginTest(communal_var_vs_regular) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_common(obj_writer, str8_lit("TEST"), 1); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("communal.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("test")); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("defn.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + // linker should replace communal TEST with .data TEST + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe communal.obj defn.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe defn.obj communal.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + char *exes[] = { "a.exe", "b.exe" }; + for EachElement(i, exes) { + String8 exe = t_read_file(scratch.arena, str8_cstring(exes[i])); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *data_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + T_Ok(data_sect); + String8 data = str8_substr(exe, rng_1u64(data_sect->foff, data_sect->foff + data_sect->vsize)); + T_Ok(str8_match(data, str8_lit("test"), 0)); + } +} +T_EndTest; + +T_BeginTest(communal_var_vs_regular_comdat) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_common(obj_writer, str8_lit("TEST"), 1); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("communal.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_lit("test")); + sect->flags |= COFF_SectionFlag_LnkCOMDAT; + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("large.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + // linker should replace communal TEST with .data TEST + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe communal.obj large.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe large.obj communal.obj entry.obj"); + T_Ok(g_last_exit_code == 0); + + char *exes[] = { "a.exe", "b.exe" }; + for EachElement(i, exes) { + String8 exe = t_read_file(scratch.arena, str8_cstring(exes[i])); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *data_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + T_Ok(data_sect); + String8 data = str8_substr(exe, rng_1u64(data_sect->foff, data_sect->foff + data_sect->vsize)); + T_Ok(str8_match(data, str8_lit("test"), 0)); + } +} +T_EndTest; + +T_BeginTest(import_kernel32) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 data[] = "test"; + U8 text[] = { + 0x48, 0x83, 0xec, 0x68, // sub rsp,68h ; alloc space on stack + 0xc7, 0x44, 0x24, 0x48, 0x18, 0x00, 0x00, 0x00, // mov dword ptr [rsp+48h],18h ; SECURITY_ATTRIBUTES.nLength + 0x48, 0xc7, 0x44, 0x24, 0x50, 0x00, 0x00, 0x00, 0x00, // mov qword ptr [rsp+50h],0 ; SECURITY_ATTRIBUTES.lpSecurityDescriptor + 0xc7, 0x44, 0x24, 0x58, 0x00, 0x00, 0x00, 0x00, // mov dword ptr [rsp+58h],0 ; SECURITY_ATTRIBUTES.bInheritHandle + 0x48, 0xc7, 0x44, 0x24, 0x30, 0x00, 0x00, 0x00, 0x00, // mov qword ptr [rsp+30h],0 ; hTemplateFile + 0xc7, 0x44, 0x24, 0x28, 0x80, 0x00, 0x00, 0x00, // mov dword ptr [rsp+28h],80h ; dwFlagsAndAttributes + 0xc7, 0x44, 0x24, 0x20, 0x02, 0x00, 0x00, 0x00, // mov dword ptr [rsp+20h],2 ; dwCreationDisposition + 0x4c, 0x8d, 0x4c, 0x24, 0x48, // lea r9,[rsp+48h] ; lpSecurityAttributes + 0x45, 0x33, 0xc0, // xor r8d,r8d ; dwShareMode + 0xba, 0x00, 0x00, 0x00, 0x40, // mov edx,40000000h ; dwDesiredAccess + 0x48, 0x8d, 0x0d, 0x00, 0x00, 0x00, 0x00, // lea rcx,[test] ; lpFileName + 0xff, 0x15, 0x00, 0x00, 0x00, 0x00, // call qword ptr [__imp_CreateFileA] ; call CreateFileA + 0x48, 0x89, 0xc1, // mov rcx,rax ; hObject + 0xff, 0x15, 0x00, 0x00, 0x00, 0x00, // call qword ptr [__imp_CloseHandle] ; call CloseHandle + 0x33, 0xc0, // xor eax,eax ; clear result + 0x48, 0x83, 0xc4, 0x68, // add rsp,68h ; dealloc stack + 0xc3 // ret ; return + }; + COFF_ObjSection *data_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS, str8_array_fixed(data)); + COFF_ObjSection *text_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + COFF_ObjSymbol *data_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("test"), 0, data_sect); + COFF_ObjSymbol *entry_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); + COFF_ObjSymbol *create_file_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__imp_CreateFileA")); + COFF_ObjSymbol *close_handle_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__imp_CloseHandle")); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 70, data_symbol, COFF_Reloc_X64_Rel32); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 76, create_file_symbol, COFF_Reloc_X64_Rel32); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 85, close_handle_symbol, COFF_Reloc_X64_Rel32); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("import.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /fixed import.obj kernel32.lib"); + T_Ok(g_last_exit_code == 0); + +#if OS_WINDOWS + { + String8 test_file_path = push_str8f(scratch.arena, "%S/test", g_wdir); + os_delete_file_at_path(test_file_path); + + OS_ProcessLaunchParams launch_opts = {0}; + launch_opts.inherit_env = 0; + launch_opts.path = g_wdir; + str8_list_pushf(scratch.arena, &launch_opts.cmd_line, "%S/a.exe", g_wdir); + OS_Handle handle = os_process_launch(&launch_opts); + AssertAlways(!os_handle_match(handle, os_handle_zero())); + U64 exit_code = max_U64; + os_process_join(handle, max_U64, &exit_code); + os_process_detach(handle); + T_Ok(exit_code == 0); + T_Ok(os_file_path_exists(test_file_path)); + } +#endif +} +T_EndTest; + +T_BeginTest(delay_import) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 return_0[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, 0 + 0xc3 // ret + }; + U8 return_1[] = { + 0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00, // mov rax, 1 + 0xc3 // ret + }; + U8 return_2[] = { + 0x48, 0xC7, 0xC0, 0x02, 0x00, 0x00, 0x00, // mov rax, 2 + 0xc3 // ret + }; + COFF_ObjSection *return_0_sect = t_push_text_section(obj_writer, str8_array_fixed(return_0)); + COFF_ObjSection *return_1_sect = t_push_text_section(obj_writer, str8_array_fixed(return_1)); + COFF_ObjSection *return_2_sect = t_push_text_section(obj_writer, str8_array_fixed(return_2)); + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("return_1"), 0, return_1_sect); + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("return_2"), 0, return_2_sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("a.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 return_0[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, 0 + 0xc3 // ret + }; + U8 return_123[] = { + 0x48, 0xC7, 0xC0, 0x7B, 0x00, 0x00, 0x00, // mov rax, 123 + 0xc3 // ret + }; + U8 return_321[] = { + 0x48, 0xC7, 0xC0, 0x41, 0x01, 0x00, 0x00, // mov rax, 321 + 0xc3 // ret + }; + COFF_ObjSection *return_0_sect = t_push_text_section(obj_writer, str8_array_fixed(return_0)); + COFF_ObjSection *return_123_sect = t_push_text_section(obj_writer, str8_array_fixed(return_123)); + COFF_ObjSection *return_321_sect = t_push_text_section(obj_writer, str8_array_fixed(return_321)); + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("return_123"), 0, return_123_sect); + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("return_321"), 0, return_321_sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("b.obj"), obj)); + } + + { + U8 text[] = { + 0x56, // push rsi + 0x57, // push rdi + 0x48, 0x83, 0xEC, 0x28, // sub rsp,28h + 0xE8, 0x00, 0x00, 0x00, 0x00, // call return_1 + 0x89, 0xC6, // mov esi,eax + 0xE8, 0x00, 0x00, 0x00, 0x00, // call return_2 + 0x89, 0xC7, // mov edi,eax + 0x01, 0xF7, // add edi,esi + 0xE8, 0x00, 0x00, 0x00, 0x00, // call return_123 + 0x89, 0xC6, // mov esi,eax + 0xE8, 0x00, 0x00, 0x00, 0x00, // call return_321 + 0x01, 0xF0, // add eax,esi + 0x01, 0xF8, // add eax,edi + 0x48, 0x83, 0xC4, 0x28, // add rsp,28h + 0x5F, // pop rdi + 0x5E, // pop rsi + 0xC3, // ret + }; + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); + COFF_ObjSymbol *return_1_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("return_1")); + COFF_ObjSymbol *return_2_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("return_2")); + COFF_ObjSymbol *return_123_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("return_123")); + COFF_ObjSymbol *return_321_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("return_321")); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 7, return_1_symbol, COFF_Reloc_X64_Rel32); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 14, return_2_symbol, COFF_Reloc_X64_Rel32); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 23, return_123_symbol, COFF_Reloc_X64_Rel32); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 30, return_321_symbol, COFF_Reloc_X64_Rel32); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("main.obj"), obj)); + } + + t_invoke_linkerf("/dll /implib:a.lib /export:return_1 /export:return_2 a.obj libcmt.lib"); + T_Ok(g_last_exit_code == 0); + + t_invoke_linkerf("/dll /implib:b.lib /export:return_123 /export:return_321 b.obj libcmt.lib"); + T_Ok(g_last_exit_code == 0); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /fixed /debug:full main.obj a.lib b.lib kernel32.lib delayimp.lib libcmt.lib /delayload:a.dll /delayload:b.dll"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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_ParsedDelayImportTable delay_import_table = pe_delay_imports_from_data(scratch.arena, pe.is_pe32, pe.section_count, section_table, exe, pe.data_dir_franges[PE_DataDirectoryIndex_DELAY_IMPORT]); + + PE_ParsedDelayDLLImport *a_import = &delay_import_table.v[0]; + T_Ok(a_import->attributes == 1); + T_Ok(str8_match(a_import->name, str8_lit("a.dll"), 0)); + T_Ok(a_import->module_handle_voff != 0); + T_Ok(a_import->name_table_voff != 0); + T_Ok(a_import->bound_table_voff != 0); + T_Ok(a_import->unload_table_voff != 0); + T_Ok(a_import->time_stamp == 0); + T_Ok(a_import->bound_table_count == 2); + T_Ok(a_import->unload_table_count == 2); + T_Ok(a_import->import_count == 2); + + PE_ParsedImport *return_1 = &a_import->imports[0]; + T_Ok(return_1->type == PE_ParsedImport_Name); + T_Ok(str8_match(return_1->u.name.string, str8_lit("return_1"), 0)); + T_Ok(return_1->u.name.hint == 0); + + PE_ParsedImport *return_2 = &a_import->imports[1]; + T_Ok(return_2->type == PE_ParsedImport_Name); + T_Ok(str8_match(return_2->u.name.string, str8_lit("return_2"), 0)); + T_Ok(return_2->u.name.hint == 1); + + PE_ParsedDelayDLLImport *b_import = &delay_import_table.v[1]; + T_Ok(b_import->attributes == 1); + T_Ok(str8_match(b_import->name, str8_lit("b.dll"), 0)); + T_Ok(b_import->module_handle_voff != 0); + T_Ok(b_import->name_table_voff != 0); + T_Ok(b_import->bound_table_voff != 0); + T_Ok(b_import->unload_table_voff != 0); + T_Ok(b_import->time_stamp == 0); + T_Ok(b_import->bound_table_count == 2); + T_Ok(b_import->unload_table_count == 2); + T_Ok(b_import->import_count == 2); + + PE_ParsedImport *return_123 = &b_import->imports[0]; + T_Ok(return_123->type == PE_ParsedImport_Name); + T_Ok(str8_match(return_123->u.name.string, str8_lit("return_123"), 0)); + T_Ok(return_123->u.name.hint == 0); + + PE_ParsedImport *return_321 = &b_import->imports[1]; + T_Ok(return_321->type == PE_ParsedImport_Name); + T_Ok(str8_match(return_321->u.name.string, str8_lit("return_321"), 0)); + T_Ok(return_321->u.name.hint == 1); +} +T_EndTest; + +T_BeginTest(delay_import_user32) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *data_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".str"), PE_DATA_SECTION_FLAGS, str8_zero()); + U64 msg_off = data_sect->data.total_size; + str8_list_pushf(obj_writer->arena, &data_sect->data, "test\0"); + U64 caption_off = data_sect->data.total_size; + str8_list_pushf(obj_writer->arena, &data_sect->data, "foo\0"); + COFF_ObjSymbol *msg_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("msg"), msg_off, data_sect); + COFF_ObjSymbol *caption_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("caption"), caption_off, data_sect); + + U8 text[] = { + 0x48, 0x83, 0xEC, 0x28, // sub rsp,28h + 0x45, 0x33, 0xC9, // xor r9d,r9d + 0x4C, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00, // lea r8,[msg] + 0x48, 0x8D, 0x15, 0x00, 0x00, 0x00, 0x00, // lea rdx,[caption] + 0x33, 0xC9, // xor ecx,ecx + 0xFF, 0x15, 0x00, 0x00, 0x00, 0x00, // call qword ptr [__imp_MessageBoxA] + 0x33, 0xC0, // xor eax,eax + 0x48, 0x83, 0xC4, 0x28, // add rsp,28h + 0xC3, // ret + }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + COFF_ObjSymbol *text_symbol = coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); + COFF_ObjSymbol *message_box_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__imp_MessageBoxA")); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 10, msg_symbol, COFF_Reloc_X64_Rel32); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 17, caption_symbol, COFF_Reloc_X64_Rel32); + coff_obj_writer_section_push_reloc(obj_writer, text_sect, 25, message_box_symbol, COFF_Reloc_X64_Rel32); + + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("delay_import.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /out:a.exe /entry:entry /fixed /delayload:user32.dll kernel32.lib user32.lib libcmt.lib delayimp.lib delay_import.obj /debug:full"); + T_Ok(g_last_exit_code == 0); +} +T_EndTest; + +T_BeginTest(empty_section) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *test = coff_obj_writer_push_section(obj_writer, str8_lit(".test"), PE_TEXT_SECTION_FLAGS, str8(0,0)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, test); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("empty_section.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + COFF_ObjSymbol *test_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, text_sect, 3, test_symbol); // relocation against removed section + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe empty_section.obj entry.obj"); + T_Ok(g_last_exit_code != 0); +} +T_EndTest; + +T_BeginTest(removed_section) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 test_text[] = { 0xc3 }; + COFF_ObjSection *test_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".test"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkRemove, str8_array_fixed(test_text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("TEST"), 0, test_sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("test.obj"), obj)); + } + + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(text)); + COFF_ObjSymbol *test_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("TEST")); + coff_obj_writer_section_push_reloc_voff(obj_writer, text_sect, 3, test_symbol); // relocation against removed section + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("entry.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe test.obj entry.obj"); + T_Ok(g_last_exit_code != 0); +} +T_EndTest; + +T_BeginTest(function_pad_min) +{ + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 ret[] = { 0xc3 }; + COFF_ObjSection *text_sect_0 = t_push_text_section(obj_writer, str8_array_fixed(ret)); + COFF_ObjSection *text_sect_1 = t_push_text_section(obj_writer, str8_array_fixed(ret)); + COFF_ObjSection *text_sect_2 = t_push_text_section(obj_writer, str8_array_fixed(ret)); + text_sect_0->flags |= COFF_SectionFlag_Align4Bytes; + text_sect_1->flags |= COFF_SectionFlag_Align2Bytes; + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("A"), 0, text_sect_0); + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("B"), 0, text_sect_1); + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("C"), 0, text_sect_2); + String8 obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + T_Ok(t_write_file(str8_lit("funcs.obj"), obj)); + } + + t_invoke_linkerf("/subsystem:console /entry:A /functionpadmin:1 /out:a.exe funcs.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *text_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + T_Ok(text_sect != 0); + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + + U8 expected_text[] = { + 0xcc, 0xcc, 0xcc, 0xcc, 0xc3, + 0xcc, 0xcc, 0xcc, 0xc3, + 0xcc, 0xc3, + }; + T_Ok(str8_match(text_data, str8_array_fixed(expected_text), 0)); +} +T_EndTest; + +T_BeginTest(first_member_header) +{ + String8 obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("8"), 0x8, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("1"), 0x1, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("9"), 0x9, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("7"), 0x7, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("4"), 0x4, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("5"), 0x5, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("2"), 0x2, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("3"), 0x3, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("6"), 0x6, COFF_SymStorageClass_External); + obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 lib; + { + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_obj(lib_writer, str8_lit("obj.obj"), obj); + lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 0); + coff_lib_writer_release(&lib_writer); + } + + T_Ok(t_write_file(str8_lit("test.lib"), lib)); + T_Ok(t_write_entry_obj()); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe test.lib entry.obj /include:1 /include:2 /include:3 /include:4 /include:5 /include:6 /include:7 /include:8 /include:9"); + T_Ok(g_last_exit_code == 0); +} +T_EndTest; + +T_BeginTest(second_member_header) +{ + String8 obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("8"), 0x8, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("1"), 0x1, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("9"), 0x9, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("7"), 0x7, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("4"), 0x4, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("5"), 0x5, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("2"), 0x2, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("3"), 0x3, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("6"), 0x6, COFF_SymStorageClass_External); + obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 lib; + { + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_obj(lib_writer, str8_lit("obj.obj"), obj); + lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + coff_lib_writer_release(&lib_writer); + } + + T_Ok(t_write_file(str8_lit("test.lib"), lib)); + T_Ok(t_write_entry_obj()); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe test.lib entry.obj /include:1 /include:2 /include:3 /include:4 /include:5 /include:6 /include:7 /include:8 /include:9"); + T_Ok(g_last_exit_code == 0); +} +T_EndTest; + +T_BeginTest(defer_impl_link_to_second_search_pass) +{ + String8 imp_ref_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { 0xc3 }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("entry"), 0, sect); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("__imp_foo")); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("func")); // force linker to pull obj from the lib + imp_ref_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 impl_ref_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { 0xc3 }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern_func(obj_writer, str8_lit("func"), 0, sect); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); // on a second search pass force linker to look up same import + impl_ref_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 impl_ref_lib; + { + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_obj(lib_writer, str8_lit("impl_ref.obj"), impl_ref_obj); + impl_ref_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + coff_lib_writer_release(&lib_writer); + } + + String8 foo_lib; + { + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_import(lib_writer, COFF_MachineType_X64, ~0u, str8_lit("foo.dll"), COFF_ImportBy_Name, str8_lit("foo"), 0, COFF_ImportHeader_Code); + foo_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + coff_lib_writer_release(&lib_writer); + } + + T_Ok(t_write_file(str8_lit("imp_ref.obj"), imp_ref_obj)); + T_Ok(t_write_file(str8_lit("impl_ref.lib"), impl_ref_lib)); + T_Ok(t_write_file(str8_lit("foo.lib"), foo_lib)); + T_Ok(t_write_file(str8_lit("foo2.lib"), foo_lib)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe foo.lib foo2.lib imp_ref.obj impl_ref.lib"); + T_Ok(g_last_exit_code == 0); +} +T_EndTest; + +T_BeginTest(opt_ref_dangling_section) +{ + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("f")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + U8 data[] = "A0000"; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(data)); + + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("q")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, q); + + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("f"), 0, sect); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 b_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + U8 q[] = { 1,2,3,4}; + COFF_ObjSection *q_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".q"), PE_DATA_SECTION_FLAGS, str8_array_fixed(q)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("q"), 0, q_sect); + + U8 data[] = "BBBBBBBBBBBBBBB"; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data"), PE_DATA_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(data)); + coff_obj_writer_push_symbol_secdef(obj_writer, sect, COFF_ComdatSelect_Largest); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("f"), 0, sect); + + b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 b_lib; + { + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_obj(lib_writer, str8_lit("b.obj"), b_obj); + b_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + coff_lib_writer_release(&lib_writer); + } + + T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + T_Ok(t_write_file(str8_lit("b.lib"), b_lib)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj b.lib"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.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); + COFF_SectionHeader *sect = coff_section_header_from_name(exe, section_table, pe.section_count, str8_lit(".q")); + T_Ok(sect != 0); +} +T_EndTest; + +#undef T_Group + diff --git a/src/torture/torture_radlink.h b/src/torture/torture_radlink.h new file mode 100644 index 00000000..0aee7555 --- /dev/null +++ b/src/torture/torture_radlink.h @@ -0,0 +1,40 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#pragma once + +//////////////////////////////// + +typedef enum +{ + T_Linker_Null, + T_Linker_RAD, + T_Linker_MSVC, + T_Linker_LLVM +} T_Linker; + +typedef enum +{ + T_MsvcLinkExitCode_UnresolvedExternals = 1120, + T_MsvcLinkExitCode_CorruptOrInvalidSymbolTable = 1235, + T_MsvcLinkExitCode_SectionsFoundWithDifferentAttributes = 4078, +} T_MsvcLinkExitCode; + +//////////////////////////////// + +#define t_invoke_linker_timeout(c, t) T_Ok(t_invoke(g_linker, c, t)) +#define t_invoke_linker_timeoutf(t, f, ...) t_invoke_linker_timeout(push_str8f(scratch.arena, f, ##__VA_ARGS__), t) +#define t_invoke_linker(c) t_invoke_linker_timeout(c, max_U64) +#define t_invoke_linkerf(f, ...) t_invoke_linker(push_str8f(scratch.arena, f, ##__VA_ARGS__)) + +internal T_Linker t_id_linker(void); + +internal COFF_ObjSection * t_push_text_section(COFF_ObjWriter *obj_writer, String8 data); +internal COFF_ObjSection * t_push_data_section(COFF_ObjWriter *obj_writer, String8 data); +internal COFF_ObjSection * t_push_rdata_section(COFF_ObjWriter *obj_writer, String8 data); + +internal String8 t_make_entry_obj(Arena *arena); +internal String8 t_make_sec_defn_obj(Arena *arena, String8 payload); + +internal B32 t_write_entry_obj(void); +