mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-25 05:04:58 -07:00
tester for linkers
This commit is contained in:
committed by
Ryan Fleury
parent
19a7ada1dc
commit
b9768be4ed
@@ -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)
|
||||
{
|
||||
|
||||
+132
-54
@@ -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);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
+16
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user