From 0c02e21284f8969a5becdb17f7c90cbad1b5df4a Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Sun, 1 Feb 2026 23:23:37 -0800 Subject: [PATCH] WIP tests for ICF --- src/coff/coff_obj_writer.c | 12 ++ src/coff/coff_obj_writer.h | 33 ++-- src/torture/torture_radlink.c | 319 ++++++++++++++++++++++++++++++++++ 3 files changed, 348 insertions(+), 16 deletions(-) diff --git a/src/coff/coff_obj_writer.c b/src/coff/coff_obj_writer.c index ccfc4f47..f6a421f1 100644 --- a/src/coff/coff_obj_writer.c +++ b/src/coff/coff_obj_writer.c @@ -440,6 +440,18 @@ coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection * return reloc; } +internal COFF_ObjReloc * +coff_obj_writer_section_push_reloc_rel32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol) +{ + COFF_RelocType reloc_type = 0; + switch (obj_writer->machine) { + case COFF_MachineType_Unknown: break; + case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Rel32; break; + default: { NotImplemented; } break; + } + return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type); +} + internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol) { diff --git a/src/coff/coff_obj_writer.h b/src/coff/coff_obj_writer.h index 1419396a..0f68aabc 100644 --- a/src/coff/coff_obj_writer.h +++ b/src/coff/coff_obj_writer.h @@ -95,32 +95,33 @@ typedef struct COFF_ObjWriter //////////////////////////////// -internal COFF_ObjWriter * coff_obj_writer_alloc(COFF_TimeStamp time_stamp, COFF_MachineType machine); -internal void coff_obj_writer_release(COFF_ObjWriter **obj_writer); +internal COFF_ObjWriter * coff_obj_writer_alloc (COFF_TimeStamp time_stamp, COFF_MachineType machine); +internal void coff_obj_writer_release (COFF_ObjWriter **obj_writer); internal String8 coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer); internal COFF_ObjSection * coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data); -internal COFF_ObjSymbol* coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymbolLocation loc, COFF_SymbolType type, COFF_SymStorageClass storage_class); +internal COFF_ObjSymbol* coff_obj_writer_push_symbol (COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymbolLocation loc, COFF_SymbolType type, COFF_SymStorageClass storage_class); internal COFF_ObjSymbol * coff_obj_writer_push_symbol_extern_func(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_extern(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_static(COFF_ObjWriter *obj_writer, String8 name, U32 off, COFF_ObjSection *section); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_secdef(COFF_ObjWriter *obj_writer, COFF_ObjSection *section, COFF_ComdatSelectType selection); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_extern (COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_static (COFF_ObjWriter *obj_writer, String8 name, U32 off, COFF_ObjSection *section); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_secdef (COFF_ObjWriter *obj_writer, COFF_ObjSection *section, COFF_ComdatSelectType selection); internal COFF_ObjSymbol * coff_obj_writer_push_symbol_associative(COFF_ObjWriter *obj_writer, COFF_ObjSection *head, COFF_ObjSection *associate); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_weak(COFF_ObjWriter *obj_writer, String8 name, COFF_WeakExtType characteristics, COFF_ObjSymbol *tag); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_abs(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymStorageClass storage_class); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef(COFF_ObjWriter *obj_writer, String8 name); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_func(COFF_ObjWriter *obj_writer, String8 name); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_sect(COFF_ObjWriter *obj_writer, String8 name, U32 value); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_sect(COFF_ObjWriter *obj_writer, String8 name, COFF_ObjSection *sect); -internal COFF_ObjSymbol * coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32 size); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_weak (COFF_ObjWriter *obj_writer, String8 name, COFF_WeakExtType characteristics, COFF_ObjSymbol *tag); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_abs (COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymStorageClass storage_class); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef (COFF_ObjWriter *obj_writer, String8 name); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_func (COFF_ObjWriter *obj_writer, String8 name); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_sect (COFF_ObjWriter *obj_writer, String8 name, U32 value); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_sect (COFF_ObjWriter *obj_writer, String8 name, COFF_ObjSection *sect); +internal COFF_ObjSymbol * coff_obj_writer_push_symbol_common (COFF_ObjWriter *obj_writer, String8 name, U32 size); internal void coff_obj_writer_set_default_symbol(COFF_ObjSymbol *weak_symbol, COFF_ObjSymbol *default_symbol); -internal COFF_ObjReloc * coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType reloc_type); +internal COFF_ObjReloc * coff_obj_writer_section_push_reloc (COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType reloc_type); +internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_rel32 (COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol); internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol); -internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol); -internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol); +internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr (COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol); +internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_voff (COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol); internal void coff_obj_writer_push_directive(COFF_ObjWriter *obj_writer, String8 directive); diff --git a/src/torture/torture_radlink.c b/src/torture/torture_radlink.c index 70a6bb3e..35e84f65 100644 --- a/src/torture/torture_radlink.c +++ b/src/torture/torture_radlink.c @@ -3909,5 +3909,324 @@ T_BeginTest(fail_if_mismatch) } T_EndTest; +#if 0 + +T_BeginTest(fold_two_funcs) +{ + String8 ident_funcs_obj; + { + U8 same_text[] = { + 0x48, 0x31, 0xc0, // xor rax, rax + 0xc3 // ret + }; + COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + COFF_ObjSection *a_sect = coff_obj_writer_push_section(cow, str8_lit(".text$mn"), PE_TEXT_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(same_text)); + COFF_ObjSection *b_sect = coff_obj_writer_push_section(cow, str8_lit(".text$mb"), PE_TEXT_SECTION_FLAGS|COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(same_text)); + coff_obj_writer_push_symbol_secdef(cow, a_sect, COFF_ComdatSelect_NoDuplicates); + coff_obj_writer_push_symbol_secdef(cow, b_sect, COFF_ComdatSelect_NoDuplicates); + COFF_ObjSymbol *a_symbol = coff_obj_writer_push_symbol_extern_func(cow, str8_lit("a"), 0, a_sect); + COFF_ObjSymbol *b_symbol = coff_obj_writer_push_symbol_extern_func(cow, str8_lit("b"), 0, b_sect); + + U8 entry_text[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, // call a + 0xe8, 0x00, 0x00, 0x00, 0x00, // call b + 0xc3, // ret + }; + COFF_ObjSection *entry_sect = t_push_text_section(cow, str8_array_fixed(entry_text)); + coff_obj_writer_push_symbol_extern_func(cow, str8_lit("entry"), 0, entry_sect); + coff_obj_writer_section_push_reloc_rel32(cow, entry_sect, 1, a_symbol); + coff_obj_writer_section_push_reloc_rel32(cow, entry_sect, 6, b_symbol); + + ident_funcs_obj = coff_obj_writer_serialize(scratch.arena, cow); + coff_obj_writer_release(&cow); + } + + T_Ok(t_write_file(str8_lit("ident_funcs.obj"), ident_funcs_obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /opt:icf ident_funcs.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(scratch.arena, str8_lit("ident_funcs.exe")); + T_Ok(exe.size); + + 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(exe, section_table, pe.section_count, str8_lit(".text")); + + // validate .text header + T_Ok(text_sect->voff == 0x1000); + T_Ok(text_sect->vsize >= 0x14); + T_Ok(text_sect->fsize == 0x200); + + T_Ok(text_sect->foff + text_sect->vsize <= exe.size); + String8 text_data = str8_substr(exe, r1u64(text_sect->foff, text_sect->foff + 0x14)); + + U8 expected_text[] = { + // entry + 0xe8, 0x0b, 0x00, 0x00, 0x00, + 0xe8, 0x06, 0x00, 0x00, 0x00, + 0xc3, + + // pad + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + + // a and b folded + 0x48, 0x31, 0xc0, + 0xc3, + }; + T_Ok(str8_match(text_data, str8_array_fixed(expected_text), 0)); +} +T_EndTest; + +T_BeginTest(same_but_different) +{ + String8 obj; + { + U8 text[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, // call $ + 0xc3 + }; + + 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 + }; + + U8 call_a_and_b[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, + 0xe8, 0x00, 0x00, 0x00, 0x00, + 0xc3 + }; + + COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + COFF_ObjSection *entry_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(call_a_and_b)); + COFF_ObjSection *a_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(text)); + COFF_ObjSection *b_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(text)); + COFF_ObjSection *c_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(return_1)); + COFF_ObjSection *d_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(return_2)); + + coff_obj_writer_push_symbol_secdef(cow, a_sect, COFF_ComdatSelect_NoDuplicates); + coff_obj_writer_push_symbol_secdef(cow, b_sect, COFF_ComdatSelect_NoDuplicates); + coff_obj_writer_push_symbol_secdef(cow, c_sect, COFF_ComdatSelect_NoDuplicates); + coff_obj_writer_push_symbol_secdef(cow, d_sect, COFF_ComdatSelect_NoDuplicates); + + coff_obj_writer_push_symbol_extern(cow, str8_lit("entry"), 0, entry_sect); + + COFF_ObjSymbol *a_symbol = coff_obj_writer_push_symbol_extern(cow, str8_lit("a"), 0, a_sect); + COFF_ObjSymbol *b_symbol = coff_obj_writer_push_symbol_extern(cow, str8_lit("b"), 0, b_sect); + COFF_ObjSymbol *c_symbol = coff_obj_writer_push_symbol_extern(cow, str8_lit("c"), 0, c_sect); + COFF_ObjSymbol *d_symbol = coff_obj_writer_push_symbol_extern(cow, str8_lit("d"), 0, d_sect); + + // a -> c + coff_obj_writer_section_push_reloc_rel32(cow, a_sect, 1, c_symbol); + + // b -> d + coff_obj_writer_section_push_reloc_rel32(cow, b_sect, 1, d_symbol); + + // entry -> { a | b } + coff_obj_writer_section_push_reloc_rel32(cow, entry_sect, 1, a_symbol); + coff_obj_writer_section_push_reloc_rel32(cow, entry_sect, 6, b_symbol); + + obj = coff_obj_writer_serialize(scratch.arena, cow); + coff_obj_writer_release(&cow); + } + + T_Ok(t_write_file(str8_lit("a.obj"), obj)); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /opt:icf a.obj"); + T_Ok(g_last_exit_code == 0); + + // validate output + { + U8 expected_text[] = { + 0xE8, 0x0B, 0x00, 0x00, 0x00, // call a + 0xE8, 0x16, 0x00, 0x00, 0x00, // call b + 0xC3, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xE8, 0x1B, 0x00, 0x00, 0x00, // call c + 0xC3, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xE8, 0x1B, 0x00, 0x00, 0x00, // call d + 0xC3, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00, // mov rax, 1 + 0xC3, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0x48, 0xC7, 0xC0, 0x02, 0x00, 0x00, 0x00, // mov rax, 2 + 0xC3, + }; + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + T_Ok(exe.size); + + 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); + T_Ok(text_section->foff + sizeof(expected_text) <= exe.size); + + String8 text = str8_substr(exe, r1u64(text_section->foff, text_section->foff + text_section->vsize)); + T_Ok(str8_match(text, str8_array_fixed(expected_text), 0)); + } +} +T_EndTest; + +T_BeginTest(fold_diamond) +{ + String8 a_obj; + { + COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64); + + U8 call_b_and_c[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, + 0xe8, 0x00, 0x00, 0x00, 0x00, + 0xc3 + }; + + U8 call_and_return[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, + 0xc3 + }; + + U8 clear_and_return[] = { + 0x48, 0x31, 0xc0, + 0xc3 + }; + + COFF_ObjSection *a_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(call_b_and_c)); + COFF_ObjSection *b_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(call_and_return)); + COFF_ObjSection *c_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(call_and_return)); + COFF_ObjSection *d_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_LnkCOMDAT, str8_array_fixed(clear_and_return)); + + coff_obj_writer_push_symbol_secdef(cow, a_sect, COFF_ComdatSelect_NoDuplicates); + coff_obj_writer_push_symbol_secdef(cow, b_sect, COFF_ComdatSelect_NoDuplicates); + coff_obj_writer_push_symbol_secdef(cow, c_sect, COFF_ComdatSelect_NoDuplicates); + coff_obj_writer_push_symbol_secdef(cow, d_sect, COFF_ComdatSelect_NoDuplicates); + + coff_obj_writer_push_symbol_extern(cow, str8_lit("a"), 0, a_sect); + COFF_ObjSymbol *b_symbol = coff_obj_writer_push_symbol_extern(cow, str8_lit("b"), 0, b_sect); + COFF_ObjSymbol *c_symbol = coff_obj_writer_push_symbol_extern(cow, str8_lit("c"), 0, c_sect); + COFF_ObjSymbol *d_symbol = coff_obj_writer_push_symbol_extern(cow, str8_lit("d"), 0, d_sect); + + // a -> { b | c } + coff_obj_writer_section_push_reloc_rel32(cow, a_sect, 1, b_symbol); + coff_obj_writer_section_push_reloc_rel32(cow, a_sect, 6, c_symbol); + + // b -> d + coff_obj_writer_section_push_reloc_rel32(cow, b_sect, 1, d_symbol); + + // c -> d + coff_obj_writer_section_push_reloc_rel32(cow, c_sect, 1, d_symbol); + + a_obj = coff_obj_writer_serialize(scratch.arena, cow); + coff_obj_writer_release(&cow); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + t_invoke_linkerf("/subsystem:console /entry:a /out:a.exe /opt:icf a.obj"); + T_Ok(g_last_exit_code == 0); + + // validate output + { + U8 expected_text[] = { + 0xe8, 0x0b, 0x00, 0x00, 0x00, + 0xe8, 0x06, 0x00, 0x00, 0x00, + 0xc3, + + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + + 0xe8, 0x0b, 0x00, 0x00, 0x00, + 0xc3, + + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + + 0x48, 0x31, 0xc0, + 0xc3 + }; + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + T_Ok(exe.size); + + 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); + T_Ok(text_section->foff + sizeof(expected_text) <= exe.size); + + String8 text = str8_substr(exe, r1u64(text_section->foff, text_section->foff + text_section->vsize)); + T_Ok(str8_match(text, str8_array_fixed(expected_text), 0)); + } +} +T_EndTest; + +T_BeginTest(cyclic_icf) +{ + String8 a_obj; + { + U8 text[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, + 0xc3 + }; + COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *a_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + COFF_ObjSection *b_sect = coff_obj_writer_push_section(cow, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + COFF_ObjSymbol *a_symbol = coff_obj_writer_push_symbol_extern(cow, str8_lit("a"), 0, a_sect); + COFF_ObjSymbol *b_symbol = coff_obj_writer_push_symbol_static(cow, str8_lit("b"), 0, b_sect); + + // a <-> b + coff_obj_writer_section_push_reloc_rel32(cow, a_sect, 1, b_symbol); + coff_obj_writer_section_push_reloc_rel32(cow, b_sect, 1, a_symbol); + + a_obj = coff_obj_writer_serialize(scratch.arena, cow); + + coff_obj_writer_release(&cow); + } + + T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); + + t_invoke_linkerf("/subsystem:console /out:a.exe /entry:a /opt:icf a.obj"); + T_Ok(g_last_exit_code == 0); + + // validate output + { + U8 expected_text[] = { + 0xe8, 0x0b, 0x00, 0x00, 0x00, // a + 0xc3, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xe8, 0xeb, 0xff, 0xff, 0xff, // b + 0xc3, + }; + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + T_Ok(exe.size); + + 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); + T_Ok(text_section->foff + sizeof(expected_text) <= exe.size); + + String8 text = str8_substr(exe, r1u64(text_section->foff, text_section->foff + text_section->vsize)); + T_Ok(str8_match(text, str8_array_fixed(expected_text), 0)); + } +} +T_EndTest; + +#endif + #undef T_Group