diff --git a/src/base/base_strings.c b/src/base/base_strings.c index 3aa6019b..450b804a 100644 --- a/src/base/base_strings.c +++ b/src/base/base_strings.c @@ -1197,6 +1197,16 @@ str8_array_from_list(Arena *arena, String8List *list) return array; } +internal String8Array * +str8_array_from_list_arr(Arena *arena, String8List **lists, U64 count) +{ + String8Array *result = push_array(arena, String8Array, count); + for (U64 idx = 0; idx < count; idx += 1) { + result[idx] = str8_array_from_list(arena, lists[idx]); + } + return result; +} + internal String8Array str8_array_reserve(Arena *arena, U64 count) { diff --git a/src/coff/coff_obj_writer.c b/src/coff/coff_obj_writer.c index d804e2cd..d9380f94 100644 --- a/src/coff/coff_obj_writer.c +++ b/src/coff/coff_obj_writer.c @@ -29,7 +29,6 @@ coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value, s->loc = loc; s->type = type; s->storage_class = storage_class; - s->idx = obj_writer->symbol_count-1; return s; } @@ -48,9 +47,41 @@ internal COFF_ObjSymbol * coff_obj_writer_push_symbol_static(COFF_ObjWriter *obj_writer, String8 name, U32 off, COFF_ObjSection *section) { COFF_SymbolLocation loc = {0}; - loc.type = COFF_SymbolLocation_Section; - loc.u.section = section; - COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, off, loc, (COFF_SymbolType){0}, COFF_SymStorageClass_Static); + loc.type = COFF_SymbolLocation_Section; + loc.u.section = section; + + COFF_SymbolType symtype = {0}; + + COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, off, loc, symtype, COFF_SymStorageClass_Static); + return s; +} + +internal COFF_ObjSymbol * +coff_obj_writer_push_symbol_secdef(COFF_ObjWriter *obj_writer, COFF_ObjSection *section, COFF_ComdatSelectType selection) +{ + COFF_ObjSymbol *s = coff_obj_writer_push_symbol_static(obj_writer, section->name, 0, section); + + COFF_ObjSymbolSecDef *sd = push_array(obj_writer->arena, COFF_ObjSymbolSecDef, 1); + sd->selection = selection; + + str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(sd)); + + return s; +} + +internal COFF_ObjSymbol * +coff_obj_writer_push_symbol_weak(COFF_ObjWriter *obj_writer, String8 name, COFF_WeakExtType characteristics, COFF_ObjSymbol *tag) +{ + COFF_SymbolLocation loc = {0}; + COFF_SymbolType symtype = {0}; + COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, COFF_Symbol_UndefinedSection, loc, symtype, COFF_SymStorageClass_WeakExternal); + + COFF_ObjSymbolWeak *weak_ext = push_array(obj_writer->arena, COFF_ObjSymbolWeak, 1); + weak_ext->tag = tag; + weak_ext->characteristics = characteristics; + + str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(weak_ext)); + return s; } @@ -73,6 +104,15 @@ coff_obj_writer_push_symbol_undef(COFF_ObjWriter *obj_writer, String8 name) return s; } +internal COFF_ObjSymbol * +coff_obj_writer_push_symbol_undef_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags) +{ + COFF_SymbolType type = {0}; + COFF_SymbolLocation loc = { COFF_SymbolLocation_Undef }; + COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, flags, loc, type, COFF_SymStorageClass_Section); + return s; +} + internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_func(COFF_ObjWriter *obj_writer, String8 name) { @@ -161,7 +201,7 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer) str8_list_push(scratch.arena, &string_table, str8_struct(string_table_size)); // - // + // assing section numbers // U64 obj_sections_count; COFF_ObjSection **obj_sections; @@ -178,6 +218,90 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer) } AssertAlways(obj_sections_count <= max_U16); + // + // serialize symbol table + // + String8List symbol_table = {0}; + { + U64 symbol_idx = 0; + for (COFF_ObjSymbolNode *symbol_n = obj_writer->symbol_first; symbol_n != 0; symbol_n = symbol_n->next) { + COFF_ObjSymbol *s = &symbol_n->v; + + // assign symbol index + s->idx = symbol_idx++; + + COFF_Symbol16 *d = push_array(scratch.arena, COFF_Symbol16, 1); + str8_list_push(scratch.arena, &symbol_table, str8_struct(d)); + + COFF_SymbolName name = {0}; + // long name + if (s->name.size > sizeof(name.short_name)) { + U64 string_table_offset = string_table.total_size; + str8_list_push_cstr(scratch.arena, &string_table, s->name); + + name.long_name.zeroes = 0; + name.long_name.string_table_offset = safe_cast_u32(string_table_offset); + } + // short name + else { + MemoryCopyStr8(name.short_name, s->name); + MemoryZeroTyped(name.short_name + s->name.size, sizeof(name.short_name) - s->name.size); + } + + // symbol header + AssertAlways(s->aux_symbols.node_count <= max_U8); + d->name = name; + d->value = s->value; + switch (s->loc.type) { + case COFF_SymbolLocation_Null: break; + case COFF_SymbolLocation_Section: d->section_number = safe_cast_u16(s->loc.u.section->section_number); break; + case COFF_SymbolLocation_Abs: d->section_number = COFF_Symbol_AbsSection16; break; + case COFF_SymbolLocation_Undef: d->section_number = COFF_Symbol_UndefinedSection; break; + } + d->type = s->type; + d->storage_class = s->storage_class; + d->aux_symbol_count = 0; + + U64 start_symbol_idx = symbol_idx; + if (s->storage_class == COFF_SymStorageClass_WeakExternal) { + Assert(s->aux_symbols.node_count <= 1); + + if (s->aux_symbols.node_count == 1) { + COFF_ObjSymbolWeak *s_weak = (COFF_ObjSymbolWeak *)s->aux_symbols.first->string.str; + COFF_SymbolWeakExt *d_weak = push_array(scratch.arena, COFF_SymbolWeakExt, 1); + d_weak->tag_index = s_weak->tag->idx; + d_weak->characteristics = s_weak->characteristics; + + str8_list_push(scratch.arena, &symbol_table, str8_struct(d_weak)); + symbol_idx += 1; + } + } else if (s->storage_class == COFF_SymStorageClass_Static) { + Assert(s->aux_symbols.node_count <= 1); + + if (s->aux_symbols.node_count == 1) { + Assert(s->loc.type == COFF_SymbolLocation_Section); + COFF_ObjSection *sect = s->loc.u.section; + + COFF_ObjSymbolSecDef *s_sd = (COFF_ObjSymbolSecDef *)s->aux_symbols.first->string.str; + COFF_SymbolSecDef *d_sd = push_array(scratch.arena, COFF_SymbolSecDef, 1); + + d_sd->length = safe_cast_u32(sect->data.total_size); + d_sd->number_of_relocations = (U16)sect->reloc_count; + d_sd->check_sum = 0; + d_sd->number_lo = (U16)sect->section_number; + d_sd->selection = s_sd->selection; + + str8_list_push(scratch.arena, &symbol_table, str8_struct(d_sd)); + symbol_idx += 1; + } + } else { + Assert(s->aux_symbols.node_count == 0); + } + + d->aux_symbol_count = (U8)(symbol_idx - start_symbol_idx); + } + } + // // file header // @@ -186,7 +310,7 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer) file_header->section_count = obj_sections_count; file_header->time_stamp = obj_writer->time_stamp; file_header->symbol_table_foff = 0; - file_header->symbol_count = safe_cast_u32(obj_writer->symbol_count); + file_header->symbol_count = safe_cast_u32(symbol_table.node_count); file_header->optional_header_size = 0; file_header->flags = 0; str8_list_push(scratch.arena, &srl, str8_struct(file_header)); @@ -256,55 +380,9 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer) // // symbol table // - if (obj_writer->symbol_count) { + if (symbol_table.total_size) { file_header->symbol_table_foff = srl.total_size; - COFF_Symbol16 *symtab = push_array(scratch.arena, COFF_Symbol16, obj_writer->symbol_count); - str8_list_push(scratch.arena, &srl, str8_array(symtab, obj_writer->symbol_count)); - { - U64 symbol_idx = 0; - for (COFF_ObjSymbolNode *symbol_n = obj_writer->symbol_first; symbol_n != 0; symbol_n = symbol_n->next) { - COFF_ObjSymbol *s = &symbol_n->v; - COFF_Symbol16 *d = &symtab[symbol_idx]; - - COFF_SymbolName name = {0}; - // long name - if (s->name.size > sizeof(name.short_name)) { - U64 string_table_offset = string_table.total_size; - str8_list_push_cstr(scratch.arena, &string_table, s->name); - - name.long_name.zeroes = 0; - name.long_name.string_table_offset = safe_cast_u32(string_table_offset); - } - // short name - else { - MemoryCopyStr8(name.short_name, s->name); - MemoryZeroTyped(name.short_name + s->name.size, sizeof(name.short_name) - s->name.size); - } - - // symbol header - AssertAlways(s->aux_symbols.node_count <= max_U8); - d->name = name; - d->value = s->value; - switch (s->loc.type) { - case COFF_SymbolLocation_Null: break; - case COFF_SymbolLocation_Section: d->section_number = safe_cast_u16(s->loc.u.section->section_number); break; - case COFF_SymbolLocation_Abs: d->section_number = COFF_Symbol_AbsSection16; break; - case COFF_SymbolLocation_Undef: d->section_number = COFF_Symbol_UndefinedSection; break; - } - d->type = s->type; - d->storage_class = s->storage_class; - d->aux_symbol_count = (U8)s->aux_symbols.node_count; - - // aux symbols - symbol_idx += 1; - for (String8Node *aux_n = s->aux_symbols.first; aux_n != 0; aux_n = aux_n->next, symbol_idx += 1) { - AssertAlways(aux_n->string.size <= sizeof(COFF_Symbol16)); - COFF_Symbol16 *a = &symtab[symbol_idx]; - MemoryZeroStruct(a); - MemoryCopyStr8(a, aux_n->string); - } - } - } + str8_list_concat_in_place(&srl, &symbol_table); } // diff --git a/src/coff/coff_obj_writer.h b/src/coff/coff_obj_writer.h index 6a203d60..f7c217d5 100644 --- a/src/coff/coff_obj_writer.h +++ b/src/coff/coff_obj_writer.h @@ -17,6 +17,17 @@ typedef struct COFF_SymbolLocation } u; } COFF_SymbolLocation; +typedef struct COFF_ObjSymbolWeak +{ + struct COFF_ObjSymbol *tag; + COFF_WeakExtType characteristics; +} COFF_ObjSymbolWeak; + +typedef struct COFF_ObjSymbolSecDef +{ + COFF_ComdatSelectType selection; +} COFF_ObjSymbolSecDef; + typedef struct COFF_ObjSymbol { String8 name; diff --git a/src/os/core/linux/os_core_linux.c b/src/os/core/linux/os_core_linux.c index 83921594..6d8ceb99 100644 --- a/src/os/core/linux/os_core_linux.c +++ b/src/os/core/linux/os_core_linux.c @@ -774,6 +774,12 @@ os_process_join(OS_Handle handle, U64 endt_us) NotImplemented; } +internal B32 +os_process_join_exit_code(OS_Handle handle, U64 endt_us, int *exit_code_out) +{ + NotImplemented; +} + internal void os_process_detach(OS_Handle handle) { diff --git a/src/os/core/os_core.h b/src/os/core/os_core.h index 922f793c..84ce63d5 100644 --- a/src/os/core/os_core.h +++ b/src/os/core/os_core.h @@ -255,6 +255,7 @@ internal void os_sleep_milliseconds(U32 msec); internal OS_Handle os_process_launch(OS_ProcessLaunchParams *params); internal B32 os_process_join(OS_Handle handle, U64 endt_us); +internal B32 os_process_join_exit_code(OS_Handle handle, U64 endt_us, int *exit_code_out); internal void os_process_detach(OS_Handle handle); //////////////////////////////// diff --git a/src/os/core/win32/os_core_win32.c b/src/os/core/win32/os_core_win32.c index cdb2c2e6..650ece89 100644 --- a/src/os/core/win32/os_core_win32.c +++ b/src/os/core/win32/os_core_win32.c @@ -1070,6 +1070,22 @@ os_process_join(OS_Handle handle, U64 endt_us) return (result == WAIT_OBJECT_0); } +internal B32 +os_process_join_exit_code(OS_Handle handle, U64 endt_us, int *exit_code_out) +{ + B32 result = 0; + if(os_process_join(handle, endt_us)) + { + DWORD exit_code; + if(GetExitCodeProcess((HANDLE)handle.u64[0], &exit_code)) + { + *exit_code_out = exit_code; + result = 1; + } + } + return result; +} + internal void os_process_detach(OS_Handle handle) { diff --git a/src/pe/pe.c b/src/pe/pe.c index cf7cedfb..eaffd5f5 100644 --- a/src/pe/pe.c +++ b/src/pe/pe.c @@ -1742,3 +1742,19 @@ pe_compute_checksum(U8 *buffer, U64 buffer_size) return hash; } +//////////////////////////////// + +internal B32 +pe_has_plus_header(COFF_MachineType machine) +{ + B32 has_plus_header = 0; + switch (machine) { + case COFF_MachineType_X86: { + has_plus_header = 0; + } break; + case COFF_MachineType_X64: { + has_plus_header = 1; + } break; + } + return has_plus_header; +} diff --git a/src/torture/torture.c b/src/torture/torture.c new file mode 100644 index 00000000..af4fb94e --- /dev/null +++ b/src/torture/torture.c @@ -0,0 +1,496 @@ +// Copyright (c) 2025 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 "coff/coff.h" +#include "coff/coff_enum.h" +#include "coff/coff_parse.h" +#include "coff/coff_obj_writer.h" +#include "pe/pe.h" + +#include "base/base_inc.c" +#include "os/os_inc.c" +#include "coff/coff.c" +#include "coff/coff_enum.c" +#include "coff/coff_parse.c" +#include "coff/coff_obj_writer.c" +#include "pe/pe.c" + +#include "linker/lnk_cmd_line.h" +#include "linker/lnk_cmd_line.c" + +//////////////////////////////// + +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; +} + +global String8 g_linker; +global String8 g_wdir; +global String8 g_out = str8_lit_comp("torture_out"); +global B32 g_verbose; + +internal int +t_invoke_linker(String8 cmdline) +{ + Temp scratch = scratch_begin(0,0); + + // + // Build Launch Options + // + OS_ProcessLaunchParams launch_opts = {0}; + launch_opts.path = g_wdir; + launch_opts.inherit_env = 1; + 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 { + B32 was_joined = os_process_join_exit_code(linker_handle, max_U64, &exit_code); + Assert(was_joined); + os_process_detach(linker_handle); + } + } + + 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); +} + +internal B32 +t_write_file(String8 name, String8 data) +{ + Temp scratch = scratch_begin(0,0); + String8 path = t_make_file_path(scratch.arena, name); + B32 is_written = os_write_data_to_file_path(path, data); + scratch_end(scratch); + return is_written; +} + +internal String8 +t_read_file(Arena *arena, String8 name) +{ + Temp scratch = scratch_begin(0,0); + String8 path = t_make_file_path(scratch.arena, name); + String8 data = os_data_from_file_path(scratch.arena, path); + scratch_end(scratch); + return data; +} + +typedef struct +{ + T_Run run; + T_Result result; +} T_RunCtx; + +internal void +t_run_caller(void *raw_ctx) +{ + T_RunCtx *ctx = raw_ctx; + ctx->result = ctx->run(); +} + +internal void +t_run_fail_handler(void *raw_ctx) +{ + T_RunCtx *ctx = raw_ctx; + ctx->result = T_Result_Crash; +} + +internal T_Result +t_run(T_Run run) +{ + T_RunCtx ctx = {0}; + ctx.run = run; + os_safe_call(t_run_caller, t_run_fail_handler, &ctx); + return ctx.result; +} + +internal COFF_SectionHeader * +t_coff_section_header_from_name(String8 string_table, COFF_SectionHeader *section_table, U64 section_count, String8 name) +{ + 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; +} + +//////////////////////////////////////////////////////////////// + +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); + } + + { + B32 was_file_written = 0; + was_file_written = t_write_file(str8_lit("abs.obj"), abs_obj); + if (!was_file_written) { + goto exit; + } + was_file_written = t_write_file(str8_lit("text.obj"), text_obj); + if (!was_file_written) { + goto exit; + } + } + + int linker_exit_code = t_invoke_linker(str8_lit("/subsystem:console /entry:my_entry /out:a.exe abs.obj text.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 *text_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_section) { + 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)) { + String8 imm = str8_prefix(str8_skip(text_data, 2), 8); + U64 expected_imm = abs_value; + if (str8_match(imm, str8_struct(&expected_imm), 0)) { + 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 = coff_obj_writer_push_section(obj_writer, str8_lit(".data"), COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemWrite, str8_array_fixed(data)); + COFF_ObjSymbol *foo = coff_obj_writer_push_symbol_undef_section(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 = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), COFF_SectionFlag_CntCode|COFF_SectionFlag_MemRead|COFF_SectionFlag_MemExecute, 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 void +entry_point(CmdLine *cmdline) +{ + Temp scratch = scratch_begin(0,0); + + // + // Targets + // + static struct { + char *label; + T_Result (*r)(void); + } target_array[] = { + { "undef_section", t_undef_section }, + { "abs_vs_weak", t_abs_vs_weak }, + }; + + // + // Handle -help + // + { + B32 print_help = cmd_line_has_flag(cmdline, str8_lit("help")) || + cmd_line_has_flag(cmdline, str8_lit("h")) || + cmdline->argc == 1; + if (print_help) { + fprintf(stderr, "--- Help -----------------------------------------------------------------------\n"); + fprintf(stderr, " %s\n\n", BUILD_TITLE_STRING_LITERAL); + fprintf(stderr, " Usage: torture [Options] [Files]\n\n"); + fprintf(stderr, " Options:\n"); + fprintf(stderr, " -linker:{path} Path to PE/COFF linker\n"); + fprintf(stderr, " -target:{name[,name]} Selects targets to test\n"); + fprintf(stderr, " -list Print available test targets and exit\n"); + fprintf(stderr, " -out:{path} Directory path for test outputs (default \"%.*s\")\n", str8_varg(g_out)); + fprintf(stderr, " -verbose Enable verbose mode\n"); + fprintf(stderr, " -help Print help menu and exit\n"); + os_abort(0); + } + } + + // + // Handle -list + // + { + 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); + } + os_abort(0); + } + } + + + // + // Handle -linker + // + { + CmdLineOpt *linker_opt = cmd_line_opt_from_string(cmdline, str8_lit("linker")); + if (linker_opt == 0) { + linker_opt = cmd_line_opt_from_string(cmdline, str8_lit("l")); + } + if (linker_opt) { + if (linker_opt->value_strings.node_count == 1) { + g_linker = linker_opt->value_string; + } else { + fprintf(stderr, "ERROR: -linker has invalid number of arguments\n"); + os_abort(1); + } + } else { + fprintf(stderr, "ERROR: missing -linker option\n"); + os_abort(1); + } + } + + // + // Handle optional -target + // + String8List target = cmdline->inputs; + { + CmdLineOpt *target_opt = cmd_line_opt_from_string(cmdline, str8_lit("target")); + if (target_opt == 0) { + target_opt = cmd_line_opt_from_string(cmdline, str8_lit("t")); + } + if (target_opt) { + if (target_opt->value_strings.node_count > 0) { + str8_list_concat_in_place(&target, &target_opt->value_strings); + } else { + fprintf(stderr, "ERROR: -target has invalid number of arguments\n"); + } + } + } + + // + // Handle -out + // + { + CmdLineOpt *out_opt = cmd_line_opt_from_string(cmdline, str8_lit("out")); + if (out_opt) { + if (out_opt->value_strings.node_count == 1) { + g_out = out_opt->value_string; + } else { + fprintf(stderr, "ERROR: -out invalid number of arguments"); + } + } + } + + // + // Handle -verbose + // + { + g_verbose = cmd_line_has_flag(cmdline, str8_lit("verbose")); + } + + // + // Make Output Directory + // + os_make_directory(g_out); + if (!os_folder_path_exists(g_out)) { + fprintf(stderr, "ERROR: unable to create output directory \"%.*s\"\n", str8_varg(g_out)); + os_abort(1); + } + + // + // Run Test Targets + // + { + U64 max_label_size = 0; + for (U64 i = 0; i < ArrayCount(target_array); i += 1) { max_label_size = Max(max_label_size, cstring8_length(target_array[i].label)); } + + U64 dots_min = 10; + U64 dots_size = max_label_size+dots_min; + U8 *dots = push_array(scratch.arena, U8, dots_size); + MemorySet(dots, '.', dots_size); + + 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; } + } 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) { + 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)) { + target_indices[target_indices_count++] = i; + is_target_unknown = 0; + break; + } + } + if (is_target_unknown) { + fprintf(stderr, "ERROR: unknown target \"%.*s\"\n", str8_varg(target_n->string)); + } + } + } + + for (U64 i = 0; i < target_indices_count; i += 1) { + U64 target_idx = target_indices[i]; + + g_wdir = push_str8f(scratch.arena, "%S\\%s", g_out, target_array[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)) { + fprintf(stderr, "ERROR: unable to create output directory for test run %.*s\n", str8_varg(g_wdir)); + continue; + } + + T_Result result = t_run(target_array[target_idx].r); + + U64 dots_count = (max_label_size - cstring8_length(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)); + + if (result == T_Result_Pass) { + fprintf(stdout, "\x1b[32m" "%.*s" "\x1b[0m" "\n", str8_varg(msg)); + } else if (result == T_Result_Fail) { + fprintf(stdout, "\x1b[31m" "%.*s" "\x1b[0m" "\n", str8_varg(msg)); + } else if (result == T_Result_Crash) { + fprintf(stdout, "\x1b[33m" "%.*s" "\x1b[0m" "\n", str8_varg(msg)); + } + } + } + + scratch_end(scratch); +} +