// Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #define BUILD_CONSOLE_INTERFACE 1 #define BUILD_TITLE "Epic Games Tools (R) COFF/PE Dumper" //////////////////////////////// #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 "path/path.h" #include "coff/coff.h" #include "pe/pe.h" #include "msvc_crt/msvc_crt.h" #include "codeview/codeview.h" #include "msf/msf.h" #include "msf/msf_parse.h" #include "pdb/pdb.h" #include "base/base_inc.c" #include "os/os_inc.c" #include "path/path.c" #include "coff/coff.c" #include "pe/pe.c" #include "msvc_crt/msvc_crt.c" #include "codeview/codeview.c" #include "msf/msf_parse.c" #include "pdb/pdb.c" #include "linker/base_ext/base_core.h" #include "linker/base_ext/base_core.c" #include "linker/path_ext/path.h" #include "linker/path_ext/path.c" #include "linker/hash_table.h" #include "linker/hash_table.c" //////////////////////////////// #define COFF_INDENT_WIDTH 4 #define COFF_INDENT_MAX 4096 #define coff_printf(f, ...) str8_list_pushf(arena, out, "%S" f, indent, __VA_ARGS__) #define coff_newline() str8_list_pushf(arena, out, ""); #define coff_errorf(f, ...) coff_printf("ERROR: "f, __VA_ARGS__) #define coff_indent() do { if (indent.size + COFF_INDENT_WIDTH <= COFF_INDENT_MAX) { indent.size += COFF_INDENT_WIDTH; } else { Assert(!"indent overflow"); } } while (0) #define coff_unindent() do { if (indent.size >= COFF_INDENT_WIDTH) { indent.size -= COFF_INDENT_WIDTH; } else { Assert(!"unbalanced indent"); } } while (0) //////////////////////////////// typedef U64 Coffdump_Option; enum Coffdump_OptionEnum { Coffdump_Option_Help = (1 << 0), Coffdump_Option_Version = (1 << 1), Coffdump_Option_Headers = (1 << 2), Coffdump_Option_Sections = (1 << 3), Coffdump_Option_Debug = (1 << 4), Coffdump_Option_Imports = (1 << 5), Coffdump_Option_Exports = (1 << 6), Coffdump_Option_Disasm = (1 << 7), Coffdump_Option_Rawdata = (1 << 8), Coffdump_Option_Tls = (1 << 9), Coffdump_Option_Codeview = (1 << 10), Coffdump_Option_Symbols = (1 << 11), Coffdump_Option_Relocs = (1 << 12), Coffdump_Option_Exceptions = (1 << 13), Coffdump_Option_LoadConfig = (1 << 14), Coffdump_Option_Resources = (1 << 15), Coffdump_Option_LongNames = (1 << 16), }; global read_only struct { Coffdump_Option opt; char *name; char *help; } g_coffdump_option_map[] = { { Coffdump_Option_Help, "help", "Print help and exit" }, { Coffdump_Option_Version, "v", "Print version and exit" }, { Coffdump_Option_Headers, "headers", "Dump DOS header, file header, optional header, and/or archive header" }, { Coffdump_Option_Sections, "sections", "Dump section headers as table" }, { Coffdump_Option_Rawdata, "rawdata", "Dump raw section data" }, { Coffdump_Option_Codeview, "cv", "Dump relocations" }, { Coffdump_Option_Disasm, "disasm", "Disassemble code sections" }, { Coffdump_Option_Symbols, "symbols", "Dump COFF symbol table" }, { Coffdump_Option_Relocs, "relocs", "Dump relocations" }, { Coffdump_Option_Exceptions, "exceptions", "Dump exceptions" }, { Coffdump_Option_Tls, "tls", "Dump Thread Local Storage directory" }, { Coffdump_Option_Debug, "debug", "Dump debug directory" }, { Coffdump_Option_Imports, "imports", "Dump import table" }, { Coffdump_Option_Exports, "exports", "Dump export table" }, { Coffdump_Option_LoadConfig, "loadconfig", "Dump load config" }, { Coffdump_Option_Resources, "resources", "Dump resource directory" }, { Coffdump_Option_LongNames, "longnames", "Dump archive long names" }, }; //////////////////////////////// //~ COFF internal String8 coff_format_time_stamp(Arena *arena, COFF_TimeStamp time_stamp) { String8 result; if (time_stamp == 0) { result = str8_lit("0"); } else if (time_stamp >= max_U32) { result = str8_lit("-1"); } else { DateTime dt = date_time_from_unix_time(time_stamp); result = push_date_time_string(arena, &dt); } return result; } internal void coff_format_archive_member_header(Arena *arena, String8List *out, String8 indent, COFF_ArchiveMemberHeader header, String8 long_names) { Temp scratch = scratch_begin(&arena, 1); String8 time_stamp = coff_format_time_stamp(scratch.arena, header.time_stamp); coff_printf("Name: %S" , header.name ); coff_printf("Time Stamp: %S" , time_stamp ); coff_printf("User ID: %u" , header.user_id ); coff_printf("Group ID: %u" , header.group_id); coff_printf("Mode: %S" , header.mode ); coff_printf("Data: [%#llx-%#llx)", header.data_range.min, header.data_range.max); scratch_end(scratch); } internal void coff_format_section_table(Arena *arena, String8List *out, String8 indent, String8 raw_data, U64 string_table_off, COFF_Symbol32Array symbols, U64 sect_count, COFF_SectionHeader *sect_headers) { Temp scratch = scratch_begin(&arena, 1); String8 *symlinks = push_array(scratch.arena, String8, sect_count); for (U64 i = 0; i < symbols.count; ++i) { COFF_Symbol32 *symbol = symbols.v+i; COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol->section_number, symbol->value, symbol->storage_class); if (interp == COFF_SymbolValueInterp_REGULAR && symbol->aux_symbol_count == 0 && (symbol->storage_class == COFF_SymStorageClass_EXTERNAL || symbol->storage_class == COFF_SymStorageClass_STATIC)) { if (symbol->section_number > 0 && symbol->section_number <= symbols.count) { COFF_SectionHeader *header = sect_headers+(symbol->section_number-1); if (header->flags & COFF_SectionFlag_LNK_COMDAT) { symlinks[symbol->section_number-1] = coff_read_symbol_name(raw_data, string_table_off, &symbol->name); } } } i += symbol->aux_symbol_count; } if (sect_count) { coff_printf("# Section Table"); coff_indent(); coff_printf("%-4s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-5s %-10s %s", "No.", "Name", "VirtSize", "VirtOff", "FileSize", "FileOff", "RelocOff", "LinesOff", "RelocCnt", "LineCnt", "Align", "Flags", "Symlink"); for (U64 i = 0; i < sect_count; ++i) { COFF_SectionHeader *header = sect_headers+i; String8 name = str8_cstring_capped(header->name, header->name+sizeof(header->name)); String8 full_name = coff_name_from_section_header(header, raw_data, string_table_off); String8 align; { U64 align_size = coff_align_size_from_section_flags(header->flags); align = push_str8f(scratch.arena, "%u", align_size); } String8 flags; { String8List mem_flags = {0}; if (header->flags & COFF_SectionFlag_MEM_READ) { str8_list_pushf(scratch.arena, &mem_flags, "r"); } if (header->flags & COFF_SectionFlag_MEM_WRITE) { str8_list_pushf(scratch.arena, &mem_flags, "w"); } if (header->flags & COFF_SectionFlag_MEM_EXECUTE) { str8_list_pushf(scratch.arena, &mem_flags, "x"); } String8List cnt_flags = {0}; if (header->flags & COFF_SectionFlag_CNT_CODE) { str8_list_pushf(scratch.arena, &cnt_flags, "c"); } if (header->flags & COFF_SectionFlag_CNT_INITIALIZED_DATA) { str8_list_pushf(scratch.arena, &cnt_flags, "d"); } if (header->flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA) { str8_list_pushf(scratch.arena, &cnt_flags, "u"); } String8List mem_extra_flags = {0}; if (header->flags & COFF_SectionFlag_MEM_SHARED) { str8_list_pushf(scratch.arena, &mem_flags, "s"); } if (header->flags & COFF_SectionFlag_MEM_16BIT) { str8_list_pushf(scratch.arena, &mem_extra_flags, "h"); } if (header->flags & COFF_SectionFlag_MEM_LOCKED) { str8_list_pushf(scratch.arena, &mem_extra_flags, "l"); } if (header->flags & COFF_SectionFlag_MEM_DISCARDABLE) { str8_list_pushf(scratch.arena, &mem_extra_flags, "d"); } if (header->flags & COFF_SectionFlag_MEM_NOT_CACHED) { str8_list_pushf(scratch.arena, &mem_extra_flags, "c"); } if (header->flags & COFF_SectionFlag_MEM_NOT_PAGED) { str8_list_pushf(scratch.arena, &mem_extra_flags, "p"); } String8List lnk_flags = {0}; if (header->flags & COFF_SectionFlag_LNK_REMOVE) { str8_list_pushf(scratch.arena, &lnk_flags, "r"); } if (header->flags & COFF_SectionFlag_LNK_COMDAT) { str8_list_pushf(scratch.arena, &lnk_flags, "c"); } if (header->flags & COFF_SectionFlag_LNK_OTHER) { str8_list_pushf(scratch.arena, &lnk_flags, "o"); } if (header->flags & COFF_SectionFlag_LNK_INFO) { str8_list_pushf(scratch.arena, &lnk_flags, "i"); } if (header->flags & COFF_SectionFlag_LNK_NRELOC_OVFL) { str8_list_pushf(scratch.arena, &lnk_flags, "f"); } String8List other_flags = {0}; if (header->flags & COFF_SectionFlag_TYPE_NO_PAD) { str8_list_pushf(scratch.arena, &other_flags, "n"); } if (header->flags & COFF_SectionFlag_GPREL) { str8_list_pushf(scratch.arena, &other_flags, "g"); } String8 mem = str8_list_join(scratch.arena, &mem_flags, 0); String8 cnt = str8_list_join(scratch.arena, &cnt_flags, 0); String8 lnk = str8_list_join(scratch.arena, &lnk_flags, 0); String8 ext = str8_list_join(scratch.arena, &mem_extra_flags, 0); String8 oth = str8_list_join(scratch.arena, &other_flags, 0); String8List f = {0}; str8_list_push(scratch.arena, &f, mem); str8_list_push(scratch.arena, &f, cnt); str8_list_push(scratch.arena, &f, ext); str8_list_push(scratch.arena, &f, lnk); str8_list_push(scratch.arena, &f, oth); flags = str8_list_join(scratch.arena, &f, &(StringJoin){ .sep = str8_lit("-") }); if (!flags.size) { flags = str8_lit("none"); } } String8List l = {0}; str8_list_pushf(scratch.arena, &l, "%-4x", i ); str8_list_pushf(scratch.arena, &l, "%-8S", name ); str8_list_pushf(scratch.arena, &l, "%08x", header->vsize ); str8_list_pushf(scratch.arena, &l, "%08x", header->voff ); str8_list_pushf(scratch.arena, &l, "%08x", header->fsize ); str8_list_pushf(scratch.arena, &l, "%08x", header->foff ); str8_list_pushf(scratch.arena, &l, "%08x", header->relocs_foff); str8_list_pushf(scratch.arena, &l, "%08x", header->lines_foff ); str8_list_pushf(scratch.arena, &l, "%08x", header->reloc_count); str8_list_pushf(scratch.arena, &l, "%08x", header->line_count ); str8_list_pushf(scratch.arena, &l, "%-5S", align ); str8_list_pushf(scratch.arena, &l, "%-10S", flags ); if (symlinks[i].size > 0) { str8_list_pushf(scratch.arena, &l, "%S", symlinks[i]); } else { str8_list_pushf(scratch.arena, &l, "[no symlink]"); } String8 line = str8_list_join(scratch.arena, &l, &(StringJoin){ .sep = str8_lit(" "), }); coff_printf("%S", line); if (full_name.size != name.size) { coff_indent(); coff_printf("Full Name: %S", full_name); coff_unindent(); } } coff_newline(); coff_printf("Flags:"); coff_indent(); coff_printf("r = MEM_READ w = MEM_WRITE x = MEM_EXECUTE"); coff_printf("c = CNT_CODE d = INITIALIZED_DATA u = UNINITIALIZED_DATA"); coff_printf("s = MEM_SHARED h = MEM_16BIT l = MEM_LOCKED d = MEM_DISCARDABLE c = MEM_NOT_CACHED p = MEM_NOT_PAGED"); coff_printf("r = LNK_REMOVE c = LNK_COMDAT o = LNK_OTHER i = LNK_INFO f = LNK_NRELOC_OVFL"); coff_printf("g = GPREL n = TYPE_NO_PAD"); coff_unindent(); coff_unindent(); coff_newline(); } scratch_end(scratch); } internal void coff_format_relocs(Arena *arena, String8List *out, String8 indent, String8 raw_data, U64 string_table_off, COFF_MachineType machine, U64 sect_count, COFF_SectionHeader *sect_headers, COFF_Symbol32Array symbols) { Temp scratch = scratch_begin(&arena, 1); B32 print_header = 1; for (U64 sect_idx = 0; sect_idx < sect_count; ++sect_idx) { COFF_SectionHeader *sect_header = sect_headers+sect_idx; COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(raw_data, sect_header); if (reloc_info.count) { if (print_header) { print_header = 0; coff_printf("# Relocations"); coff_indent(); } coff_printf("## Section %llx", sect_idx); coff_indent(); coff_printf("%-4s %-8s %-16s %-16s %-8s %-7s", "No.", "Offset", "Type", "ApplyTo", "SymIdx", "SymName"); for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; ++reloc_idx) { COFF_Reloc *reloc = (COFF_Reloc*)(raw_data.str + reloc_info.array_off) + reloc_idx; String8 type = coff_string_from_reloc(machine, reloc->type); U64 apply_size = coff_apply_size_from_reloc(machine, reloc->type); U64 apply_foff = sect_header->foff + reloc->apply_off; if (apply_foff + apply_size > raw_data.size) { coff_errorf("out of bounds apply file offset %#llx in relocation %#llx", apply_foff, reloc_idx); break; } U64 raw_apply; AssertAlways(apply_size <= sizeof(raw_apply)); MemoryCopy(&raw_apply, raw_data.str + apply_foff, apply_size); S64 apply = extend_sign64(raw_apply, apply_size); if (reloc->isymbol > symbols.count) { coff_errorf("out of bounds symbol index %u in relocation %#llx", reloc->isymbol, reloc_idx); break; } COFF_Symbol32 *symbol = symbols.v+reloc->isymbol; String8 symbol_name = coff_read_symbol_name(raw_data, string_table_off, &symbol->name); String8List line = {0}; str8_list_pushf(scratch.arena, &line, "%-4x", reloc_idx ); str8_list_pushf(scratch.arena, &line, "%08x", reloc->apply_off); str8_list_pushf(scratch.arena, &line, "%-16S", type ); str8_list_pushf(scratch.arena, &line, "%016x", apply ); str8_list_pushf(scratch.arena, &line, "%S", symbol_name ); String8 l = str8_list_join(scratch.arena, &line, &(StringJoin){.sep=str8_lit(" ")}); coff_printf("%S", l); } coff_unindent(); } } if (!print_header) { coff_unindent(); } coff_newline(); scratch_end(scratch); } internal void coff_format_symbol_table(Arena *arena, String8List *out, String8 indent, String8 raw_data, B32 is_big_obj, U64 string_table_off, COFF_Symbol32Array symbols) { Temp scratch = scratch_begin(&arena, 1); coff_printf("# Symbol Table"); coff_indent(); coff_printf("%-4s %-8s %-10s %-4s %-4s %-4s %-16s %-20s", "No.", "Value", "SectNum", "Aux", "Msb", "Lsb", "Storage", "Name"); for (U64 i = 0; i < symbols.count; ++i) { COFF_Symbol32 *symbol = &symbols.v[i]; String8 name = coff_read_symbol_name(raw_data, string_table_off, &symbol->name); String8 msb = coff_string_from_sym_dtype(symbol->type.u.msb); String8 lsb = coff_string_from_sym_type(symbol->type.u.lsb); String8 storage_class = coff_string_from_sym_storage_class(symbol->storage_class); String8 section_number; switch (symbol->section_number) { case COFF_SYMBOL_UNDEFINED_SECTION: section_number = str8_lit("UNDEF"); break; case COFF_SYMBOL_ABS_SECTION: section_number = str8_lit("ABS"); break; case COFF_SYMBOL_DEBUG_SECTION: section_number = str8_lit("DEBUG"); break; default: section_number = push_str8f(scratch.arena, "%010x", symbol->section_number); break; } String8List line = {0}; str8_list_pushf(scratch.arena, &line, "%-4x", i ); str8_list_pushf(scratch.arena, &line, "%08x", symbol->value ); str8_list_pushf(scratch.arena, &line, "%-10S", section_number ); str8_list_pushf(scratch.arena, &line, "%-4u", symbol->aux_symbol_count); str8_list_pushf(scratch.arena, &line, "%-4S", msb ); str8_list_pushf(scratch.arena, &line, "%-4S", lsb ); str8_list_pushf(scratch.arena, &line, "%-16S", storage_class ); str8_list_pushf(scratch.arena, &line, "%S", name ); String8 l = str8_list_join(scratch.arena, &line, &(StringJoin){.sep = str8_lit(" ")}); coff_printf("%S", l); coff_indent(); for (U64 k=i+1, c = i+symbol->aux_symbol_count; k <= c; ++k) { void *raw_aux = &symbols.v[k]; switch (symbol->storage_class) { case COFF_SymStorageClass_EXTERNAL: { COFF_SymbolFuncDef *func_def = (COFF_SymbolFuncDef*)&symbols.v[k]; coff_printf("Tag Index %#x, Total Size %#x, Line Numbers %#x, Next Function %#x", func_def->tag_index, func_def->total_size, func_def->ptr_to_ln, func_def->ptr_to_next_func); } break; case COFF_SymStorageClass_FUNCTION: { COFF_SymbolFunc *func = raw_aux; coff_printf("Ordinal Line Number %#x, Next Function %#x", func->ln, func->ptr_to_next_func); } break; case COFF_SymStorageClass_WEAK_EXTERNAL: { COFF_SymbolWeakExt *weak = raw_aux; String8 type = coff_string_from_weak_ext_type(weak->characteristics); coff_printf("Tag Index %#x, Characteristics %S", weak->tag_index, type); } break; case COFF_SymStorageClass_FILE: { COFF_SymbolFile *file = raw_aux; String8 name = str8_cstring_capped(file->name, file->name+sizeof(file->name)); coff_printf("Name %S", name); } break; case COFF_SymStorageClass_STATIC: { COFF_SymbolSecDef *sd = raw_aux; String8 selection = coff_string_from_selection(sd->selection); U32 number = sd->number_lo; if (is_big_obj) { number |= (U32)sd->number_hi << 16; } if (number) { coff_printf("Length %x, Reloc Count %u, Line Count %u, Checksum %x, Section %x, Selection %S", sd->length, sd->number_of_relocations, sd->number_of_ln, sd->check_sum, number, selection); } else { coff_printf("Length %x, Reloc Count %u, Line Count %u, Checksum %x", sd->length, sd->number_of_relocations, sd->number_of_ln, sd->check_sum); } } break; default: { coff_printf("???"); } break; } } i += symbol->aux_symbol_count; coff_unindent(); } coff_unindent(); coff_newline(); scratch_end(scratch); } internal void coff_format_big_obj_header(Arena *arena, String8List *out, String8 indent, COFF_HeaderBigObj *header) { Temp scratch = scratch_begin(&arena, 1); String8 time_stamp = coff_format_time_stamp(scratch.arena, header->time_stamp); String8 machine = coff_string_from_machine_type(header->machine); coff_printf("# Big Obj"); coff_indent(); coff_printf("Time Stamp: %S", time_stamp ); coff_printf("Machine: %S", machine ); coff_printf("Section Count: %u", header->section_count ); coff_printf("Symbol Table: %#x", header->symbol_table_foff); coff_printf("Symbol Count: %u", header->symbol_count ); coff_unindent(); scratch_end(scratch); } internal void coff_format_header(Arena *arena, String8List *out, String8 indent, COFF_Header *header) { Temp scratch = scratch_begin(&arena, 1); String8 time_stamp = coff_format_time_stamp(scratch.arena, header->time_stamp); String8 machine = coff_string_from_machine_type(header->machine); String8 flags = coff_string_from_flags(scratch.arena, header->flags); coff_printf("# COFF Header"); coff_indent(); coff_printf("Time Stamp: %S", time_stamp ); coff_printf("Machine: %S", machine ); coff_printf("Section Count: %u", header->section_count ); coff_printf("Symbol Table: %#x", header->symbol_table_foff ); coff_printf("Symbol Count: %u", header->symbol_count ); coff_printf("Optional Header Size: %m", header->optional_header_size); coff_printf("Flags: %S", flags ); coff_unindent(); scratch_end(scratch); } internal void coff_format_import(Arena *arena, String8List *out, String8 indent, COFF_ImportHeader *header) { Temp scratch = scratch_begin(&arena, 1); String8 machine = coff_string_from_machine_type(header->machine); String8 time_stamp = coff_format_time_stamp(scratch.arena, header->time_stamp); coff_printf("# Import"); coff_indent(); coff_printf("Version: %u", header->version ); coff_printf("Machine: %S", machine ); coff_printf("Time Stamp: %S", time_stamp ); coff_printf("Data Size: %m", header->data_size); coff_printf("Hint: %u", header->hint ); coff_printf("Type: %u", header->type ); coff_printf("Name Type: %u", header->name_type); coff_printf("Function: %S", header->func_name); coff_printf("DLL: %S", header->dll_name ); coff_unindent(); scratch_end(scratch); } internal void coff_format_big_obj(Arena *arena, String8List *out, String8 indent, String8 raw_data, Coffdump_Option opts) { Temp scratch = scratch_begin(&arena, 1); COFF_HeaderBigObj *big_obj = str8_deserial_get_raw_ptr(raw_data, 0, sizeof(COFF_HeaderBigObj)); COFF_SectionHeader *sections = str8_deserial_get_raw_ptr(raw_data, sizeof(COFF_HeaderBigObj), sizeof(COFF_SectionHeader)*big_obj->section_count); U64 string_table_off = big_obj->symbol_table_foff + sizeof(COFF_Symbol32)*big_obj->symbol_count; COFF_Symbol32Array symbols = coff_symbol_array_from_data_32(scratch.arena, raw_data, big_obj->symbol_table_foff, big_obj->symbol_count); if (opts & Coffdump_Option_Headers) { coff_format_big_obj_header(arena, out, indent, big_obj); coff_newline(); } if (opts & Coffdump_Option_Sections) { Rng1U64 sect_headers_range = rng_1u64(sizeof(*big_obj), sizeof(*big_obj) + sizeof(COFF_SectionHeader)*big_obj->section_count); Rng1U64 symbols_range = rng_1u64(big_obj->symbol_table_foff, big_obj->symbol_table_foff + sizeof(COFF_Symbol32)*big_obj->symbol_count); if (sect_headers_range.max > raw_data.size) { coff_errorf("not enough bytes to read big obj section headers"); goto exit; } if (big_obj->symbol_count) { if (symbols_range.max > raw_data.size) { coff_errorf("not enough bytes to read big obj symbol table"); goto exit; } if (contains_1u64(symbols_range, sect_headers_range.min) || contains_1u64(symbols_range, sect_headers_range.max)) { coff_errorf("section headers and symbol table ranges overlap"); goto exit; } } coff_format_section_table(arena, out, indent, raw_data, string_table_off, symbols, big_obj->section_count, sections); coff_newline(); } if (opts & Coffdump_Option_Relocs) { coff_format_relocs(arena, out, indent, raw_data, string_table_off, big_obj->machine, big_obj->section_count, sections, symbols); coff_newline(); } if (opts & Coffdump_Option_Symbols) { coff_format_symbol_table(arena, out, indent, raw_data, string_table_off, 1, symbols); coff_newline(); } exit:; scratch_end(scratch); } internal void coff_format_obj(Arena *arena, String8List *out, String8 indent, String8 raw_data, Coffdump_Option opts) { Temp scratch = scratch_begin(&arena, 1); COFF_Header *header = (COFF_Header *)raw_data.str; COFF_SectionHeader *sections = (COFF_SectionHeader *)(header+1); U64 string_table_off = header->symbol_table_foff + sizeof(COFF_Symbol16)*header->symbol_count; COFF_Symbol32Array symbols = coff_symbol_array_from_data_16(scratch.arena, raw_data, header->symbol_table_foff, header->symbol_count); if (opts & Coffdump_Option_Headers) { coff_format_header(arena, out, indent, header); coff_newline(); } if (opts & Coffdump_Option_Sections) { Rng1U64 sect_headers_range = rng_1u64(sizeof(*header), sizeof(*header) + sizeof(COFF_SectionHeader)*header->section_count); Rng1U64 symbols_range = rng_1u64(header->symbol_table_foff, header->symbol_table_foff + sizeof(COFF_Symbol16)*header->symbol_count); if (sect_headers_range.max > raw_data.size) { coff_errorf("not enough bytes to read obj section headers"); goto exit; } if (header->symbol_count) { if (symbols_range.max > raw_data.size) { coff_errorf("not enough bytes to read obj symbol table"); goto exit; } if (contains_1u64(symbols_range, sect_headers_range.min) || contains_1u64(symbols_range, sect_headers_range.max)) { coff_errorf("section headers and symbol table ranges overlap"); goto exit; } } coff_format_section_table(arena, out, indent, raw_data, string_table_off, symbols, header->section_count, sections); coff_newline(); } if (opts & Coffdump_Option_Relocs) { coff_format_relocs(arena, out, indent, raw_data, string_table_off, header->machine, header->section_count, sections, symbols); coff_newline(); } if (opts & Coffdump_Option_Symbols) { coff_format_symbol_table(arena, out, indent, raw_data, 0, string_table_off, symbols); coff_newline(); } exit:; scratch_end(scratch); } internal void coff_format_archive(Arena *arena, String8List *out, String8 indent, String8 raw_archive, Coffdump_Option opts) { Temp scratch = scratch_begin(&arena, 1); COFF_ArchiveParse archive_parse = coff_archive_parse_from_data(raw_archive); if (archive_parse.error.size) { coff_errorf("%S", archive_parse.error); return; } COFF_ArchiveFirstMember first_member = archive_parse.first_member; { coff_printf("# First Header"); coff_indent(); coff_printf("Symbol Count: %u", first_member.symbol_count); coff_printf("String Table Size: %M", first_member.string_table.size); coff_printf("Members:"); coff_indent(); String8List string_table = str8_split_by_string_chars(scratch.arena, first_member.string_table, str8_lit("\0"), 0); if (string_table.node_count == first_member.member_offset_count) { String8Node *string_n = string_table.first; for (U64 i = 0; i < string_table.node_count; ++i, string_n = string_n->next) { U32 offset = from_be_u32(first_member.member_offsets[i]); coff_printf("[%4u] 0x%08x %S", i, offset, string_n->string); } } else { coff_errorf("Member offset count (%llu) doesn't match string table count (%llu)", first_member.member_offset_count); } coff_unindent(); coff_unindent(); coff_newline(); } if (archive_parse.has_second_header) { COFF_ArchiveSecondMember second_member = archive_parse.second_member; coff_printf("# Second Header"); coff_indent(); coff_printf("Member Count: %u", second_member.member_count); coff_printf("Symbol Count: %u", second_member.symbol_count); coff_printf("String Table Size: %M", second_member.string_table.size); String8List string_table = str8_split_by_string_chars(scratch.arena, second_member.string_table, str8_lit("\0"), 0); coff_printf("Members:"); coff_indent(); if (second_member.symbol_index_count == second_member.symbol_count) { String8Node *string_n = string_table.first; for (U64 i = 0; i < second_member.symbol_count; ++i, string_n = string_n->next) { U16 symbol_number = second_member.symbol_indices[i]; if (symbol_number > 0 && symbol_number <= second_member.member_offset_count) { U16 symbol_idx = symbol_number - 1; U32 member_offset = second_member.member_offsets[i]; coff_printf("[%4u] 0x%08x %S", i, member_offset, string_n->string); } else { coff_errorf("[%4u] Out of bounds symbol number %u", i, symbol_number); break; } } } else { coff_errorf("Symbol index count %u doesn't match symbol count %u", second_member.symbol_index_count, second_member.symbol_count); } coff_unindent(); coff_unindent(); coff_newline(); } if (archive_parse.has_long_names && opts & Coffdump_Option_LongNames) { coff_printf("# Long Names"); coff_indent(); String8List long_names = str8_split_by_string_chars(scratch.arena, archive_parse.long_names, str8_lit("\0"), 0); U64 name_idx = 0; for (String8Node *name_n = long_names.first; name_n != 0; name_n = name_n->next, ++name_idx) { U64 offset = (U64)(name_n->string.str - archive_parse.long_names.str); coff_printf("[%-4u] 0x%08x %S", name_idx, offset, name_n->string); } coff_unindent(); coff_newline(); } U64 member_offset_count = 0; U32 *member_offsets = 0; if (archive_parse.has_second_header) { member_offset_count = archive_parse.second_member.member_offset_count; member_offsets = archive_parse.second_member.member_offsets; } else { HashTable *ht = hash_table_init(scratch.arena, 0x1000); for (U64 i = 0; i < archive_parse.first_member.member_offset_count; ++i) { U32 member_offset = from_be_u32(archive_parse.first_member.member_offsets[i]); if (!hash_table_search_u32(ht, member_offset)) { hash_table_push_u32_raw(scratch.arena, ht, member_offset, 0); } } member_offset_count = ht->count; member_offsets = keys_from_hash_table_u32(scratch.arena, ht); radsort(member_offsets, member_offset_count, u32_is_before); } coff_printf("# Members"); coff_indent(); for (U64 i = 0; i < member_offset_count; ++i) { U64 next_member_offset = i+1 < member_offset_count ? member_offsets[i+1] : raw_archive.size; U64 member_offset = member_offsets[i]; String8 raw_member = str8_substr(raw_archive, rng_1u64(member_offset, next_member_offset)); COFF_ArchiveMember member = coff_archive_member_from_data(raw_member); COFF_DataType member_type = coff_data_type_from_data(member.data); coff_printf("Member @ %#llx", member_offset); coff_indent(); if (opts & Coffdump_Option_Headers) { coff_format_archive_member_header(arena, out, indent, member.header, archive_parse.long_names); coff_newline(); } switch (member_type) { case COFF_DataType_BIG_OBJ: { coff_format_big_obj(arena, out, indent, member.data, opts); } break; case COFF_DataType_OBJ: { coff_format_obj(arena, out, indent, member.data, opts); } break; case COFF_DataType_IMPORT: { if (opts & Coffdump_Option_Headers) { COFF_ImportHeader header = {0}; U64 parse_size = coff_parse_archive_import(member.data, 0, &header); if (parse_size) { coff_format_import(arena, out, indent, &header); } else { coff_errorf("not enough bytes to parse import header"); } } } break; case COFF_DataType_NULL: { coff_errorf("unknown member format", member_offset); } break; } coff_unindent(); coff_newline(); } coff_unindent(); scratch_end(scratch); } //////////////////////////////// //~ MSVC CRT internal String8 mscrt_format_eh_adjectives(Arena *arena, MSCRT_EhHandlerTypeFlags adjectives) { Temp scratch = scratch_begin(&arena, 1); String8List adj_list = {0}; if (adjectives & MSCRT_EhHandlerTypeFlag_IsConst) { str8_list_pushf(scratch.arena, &adj_list, "Const"); } if (adjectives & MSCRT_EhHandlerTypeFlag_IsVolatile) { str8_list_pushf(scratch.arena, &adj_list, "Volatile"); } if (adjectives & MSCRT_EhHandlerTypeFlag_IsUnaligned) { str8_list_pushf(scratch.arena, &adj_list, "Unaligned"); } if (adjectives & MSCRT_EhHandlerTypeFlag_IsReference) { str8_list_pushf(scratch.arena, &adj_list, "Reference"); } if (adjectives & MSCRT_EhHandlerTypeFlag_IsResumable) { str8_list_pushf(scratch.arena, &adj_list, "Resumable"); } if (adjectives & MSCRT_EhHandlerTypeFlag_IsStdDotDot) { str8_list_pushf(scratch.arena, &adj_list, "StdDotDot"); } if (adjectives & MSCRT_EhHandlerTypeFlag_IsComplusEH) { str8_list_pushf(scratch.arena, &adj_list, "ComplusEH"); } String8 result = str8_list_join(arena, &adj_list, &(StringJoin){.sep=str8_lit(", ")}); scratch_end(scratch); return result; } internal void mscrt_format_eh_handler_type32(Arena *arena, String8List *out, String8 indent, MSCRT_EhHandlerType32 *handler) { String8 catch_line = str8_zero(); // TODO: syms_line_for_voff(scratch.arena, group, handler->catch_handler_voff); String8 adjectives_str = mscrt_format_eh_adjectives(arena, handler->adjectives); coff_printf("Adjectives: %S", adjectives_str, handler->adjectives); coff_printf("Descriptor: %#x", handler->descriptor_voff); coff_printf("Catch Object Frame Offset: %#x", handler->catch_obj_frame_offset); coff_printf("Catch Handler: %#x %S", handler->catch_handler_voff, catch_line); coff_printf("Delta to FP Handler: %#x", handler->fp_distance); } //////////////////////////////// //~ PE internal B32 is_pe(String8 raw_data) { PE_DosHeader header = {0}; str8_deserial_read_struct(raw_data, 0, &header); return header.magic == PE_DOS_MAGIC; } internal void pe_format_data_directory_ranges(Arena *arena, String8List *out, String8 indent, U64 count, PE_DataDirectory *dirs) { Temp scratch = scratch_begin(&arena, 1); coff_printf("# Data Directories"); coff_indent(); for (U64 i = 0; i < count; ++i) { String8 dir_name; if (i < PE_DataDirectoryIndex_COUNT) { dir_name = pe_string_from_data_directory_index(i); } else { dir_name = push_str8f(scratch.arena, "%#x", i); } coff_printf("%-16S [%08x-%08x)", dir_name, dirs[i].virt_off, dirs[i].virt_off+dirs[i].virt_size); } coff_unindent(); scratch_end(scratch); } internal void pe_format_optional_header32(Arena *arena, String8List *out, String8 indent, PE_OptionalHeader32 *opt_header, PE_DataDirectory *dirs) { Temp scratch = scratch_begin(&arena, 1); String8 subsystem = pe_string_from_subsystem(opt_header->subsystem); String8 dll_chars = pe_string_from_dll_characteristics(scratch.arena, opt_header->dll_characteristics); coff_printf("# PE Optional Header 32"); coff_indent(); coff_printf("Magic: %#x", opt_header->magic); coff_printf("Linker version: %u.%u", opt_header->major_linker_version, opt_header->minor_linker_version); coff_printf("Size of code: %m", opt_header->sizeof_code); coff_printf("Size of inited data: %m", opt_header->sizeof_inited_data); coff_printf("Size of uninited data: %m", opt_header->sizeof_uninited_data); coff_printf("Entry point: %#x", opt_header->entry_point_va); coff_printf("Code base: %#x", opt_header->code_base); coff_printf("Data base: %#x", opt_header->data_base); coff_printf("Image base: %#x", opt_header->image_base); coff_printf("Section align: %#x", opt_header->section_alignment); coff_printf("File align: %#x", opt_header->file_alignment); coff_printf("OS version: %u.%u", opt_header->major_os_ver, opt_header->minor_os_ver); coff_printf("Image Version: %u.%u", opt_header->major_img_ver, opt_header->minor_img_ver); coff_printf("Subsystem version: %u.%u", opt_header->major_subsystem_ver, opt_header->minor_subsystem_ver); coff_printf("Win32 version: %u", opt_header->win32_version_value); coff_printf("Size of image: %m", opt_header->sizeof_image); coff_printf("Size of headers: %m", opt_header->sizeof_headers); coff_printf("Checksum: %#x", opt_header->check_sum); coff_printf("Subsystem: %S", subsystem); coff_printf("DLL Characteristics: %S", dll_chars); coff_printf("Stack reserve: %m", opt_header->sizeof_stack_reserve); coff_printf("Stack commit: %m", opt_header->sizeof_stack_commit); coff_printf("Heap reserve: %m", opt_header->sizeof_heap_reserve); coff_printf("Heap commit: %m", opt_header->sizeof_heap_commit); coff_printf("Loader flags: %#x", opt_header->loader_flags); coff_printf("RVA and offset count: %u", opt_header->data_dir_count); coff_newline(); pe_format_data_directory_ranges(arena, out, indent, opt_header->data_dir_count, dirs); coff_newline(); coff_unindent(); scratch_end(scratch); } internal void pe_format_optional_header32plus(Arena *arena, String8List *out, String8 indent, PE_OptionalHeader32Plus *opt_header, PE_DataDirectory *dirs) { Temp scratch = scratch_begin(&arena, 1); String8 subsystem = pe_string_from_subsystem(opt_header->subsystem); String8 dll_chars = pe_string_from_dll_characteristics(scratch.arena, opt_header->dll_characteristics); coff_printf("# PE Optional Header 32+"); coff_indent(); coff_printf("Magic: %#x", opt_header->magic); coff_printf("Linker version: %u.%u", opt_header->major_linker_version, opt_header->minor_linker_version); coff_printf("Size of code: %m", opt_header->sizeof_code); coff_printf("Size of inited data: %m", opt_header->sizeof_inited_data); coff_printf("Size of uninited data: %m", opt_header->sizeof_uninited_data); coff_printf("Entry point: %#x", opt_header->entry_point_va); coff_printf("Code base: %#x", opt_header->code_base); coff_printf("Image base: %#llx", opt_header->image_base); coff_printf("Section align: %#x", opt_header->section_alignment); coff_printf("File align: %#x", opt_header->file_alignment); coff_printf("OS version: %u.%u", opt_header->major_os_ver, opt_header->minor_os_ver); coff_printf("Image Version: %u.%u", opt_header->major_img_ver, opt_header->minor_img_ver); coff_printf("Subsystem version: %u.%u", opt_header->major_subsystem_ver, opt_header->minor_subsystem_ver); coff_printf("Win32 version: %u", opt_header->win32_version_value); coff_printf("Size of image: %m", opt_header->sizeof_image); coff_printf("Size of headers: %m", opt_header->sizeof_headers); coff_printf("Checksum: %#x", opt_header->check_sum); coff_printf("Subsystem: %S", subsystem); coff_printf("DLL Characteristics: %S", dll_chars); coff_printf("Stack reserve: %M", opt_header->sizeof_stack_reserve); coff_printf("Stack commit: %M", opt_header->sizeof_stack_commit); coff_printf("Heap reserve: %M", opt_header->sizeof_heap_reserve); coff_printf("Heap commit: %M", opt_header->sizeof_heap_commit); coff_printf("Loader flags: %#x", opt_header->loader_flags); coff_printf("RVA and offset count: %u", opt_header->data_dir_count); coff_newline(); pe_format_data_directory_ranges(arena, out, indent, opt_header->data_dir_count, dirs); coff_newline(); coff_unindent(); scratch_end(scratch); } internal void pe_format_load_config32(Arena *arena, String8List *out, String8 indent, PE_LoadConfig32 *lc) { Temp scratch = scratch_begin(&arena, 1); String8 time_stamp = coff_format_time_stamp(scratch.arena, lc->time_stamp); String8 global_flag_clear = pe_string_from_global_flags(scratch.arena, lc->global_flag_clear); String8 global_flag_set = pe_string_from_global_flags(scratch.arena, lc->global_flag_set); coff_printf("# Load Config 32"); coff_indent(); coff_printf("Size: %m", lc->size); coff_printf("Time stamp: %#x (%S)", lc->time_stamp, time_stamp); coff_printf("Version: %u.%u", lc->major_version, lc->minor_version); coff_printf("Global flag clear: %#x %S", global_flag_clear); coff_printf("Global flag set: %#x %S", global_flag_set); coff_printf("Critical section timeout: %u", lc->critical_section_timeout); coff_printf("Decommit free block threshold: %#x", lc->decommit_free_block_threshold); coff_printf("Decommit total free threshold: %#x", lc->decommit_total_free_threshold); coff_printf("Lock prefix table: %#x", lc->lock_prefix_table); coff_printf("Maximum alloc size: %m", lc->maximum_allocation_size); coff_printf("Virtual memory threshold: %m", lc->virtual_memory_threshold); coff_printf("Process affinity mask: %#x", lc->process_affinity_mask); coff_printf("Process heap flags: %#x", lc->process_heap_flags); coff_printf("CSD version: %u", lc->csd_version); coff_printf("Edit list: %#x", lc->edit_list); coff_printf("Security Cookie: %#x", lc->security_cookie); if (lc->size < OffsetOf(PE_LoadConfig64, seh_handler_table)) { goto exit; } coff_newline(); coff_printf("SEH Handler Table: %#x", lc->seh_handler_table); coff_printf("SEH Handler Count: %u", lc->seh_handler_count); if (lc->size < OffsetOf(PE_LoadConfig64, guard_cf_check_func_ptr)) { goto exit; } coff_newline(); coff_printf("Guard CF Check Function: %#x", lc->guard_cf_check_func_ptr); coff_printf("Guard CF Dispatch Function: %#x", lc->guard_cf_dispatch_func_ptr); coff_printf("Guard CF Function Table: %#x", lc->guard_cf_func_table); coff_printf("Guard CF Function Count: %u", lc->guard_cf_func_count); coff_printf("Guard Flags: %#x", lc->guard_flags); if (lc->size < OffsetOf(PE_LoadConfig64, code_integrity)) { goto exit; } coff_newline(); coff_printf("Code integrity: { Flags = %#x, Catalog = %#x, Catalog Offset = %#x }", lc->code_integrity.flags, lc->code_integrity.catalog, lc->code_integrity.catalog_offset); coff_printf("Guard address taken IAT entry table: %#x", lc->guard_address_taken_iat_entry_table); coff_printf("Guard address taken IAT entry count: %u", lc->guard_address_taken_iat_entry_count); coff_printf("Guard long jump target table: %#x", lc->guard_long_jump_target_table); coff_printf("Guard long jump target count: %u", lc->guard_long_jump_target_count); coff_printf("Dynamic value reloc table: %#x", lc->dynamic_value_reloc_table); coff_printf("CHPE Metadata ptr: %#x", lc->chpe_metadata_ptr); coff_printf("Guard RF failure routine: %#x", lc->guard_rf_failure_routine); coff_printf("Guard RF failure routine func ptr: %#x", lc->guard_rf_failure_routine_func_ptr); coff_printf("Dynamic value reloc section: %#x", lc->dynamic_value_reloc_table_section); coff_printf("Dynamic value reloc section offset: %#x", lc->dynamic_value_reloc_table_offset); coff_printf("Guard RF verify SP func ptr: %#x", lc->guard_rf_verify_stack_pointer_func_ptr); coff_printf("Hot patch table offset: %#x", lc->hot_patch_table_offset); if (lc->size < OffsetOf(PE_LoadConfig64, enclave_config_ptr)) { goto exit; } coff_newline(); coff_printf("Enclave config ptr: %#x", lc->enclave_config_ptr); coff_printf("Volatile metadata ptr: %#x", lc->volatile_metadata_ptr); coff_printf("Guard EH continuation table: %#x", lc->guard_eh_continue_table); coff_printf("Guard EH continuation count: %u", lc->guard_eh_continue_count); coff_printf("Guard XFG check func ptr: %#x", lc->guard_xfg_check_func_ptr); coff_printf("Guard XFG dispatch func ptr: %#x", lc->guard_xfg_dispatch_func_ptr); coff_printf("Guard XFG table dispatch func ptr: %#x", lc->guard_xfg_table_dispatch_func_ptr); coff_printf("Cast guard OS determined failure mode: %#x", lc->cast_guard_os_determined_failure_mode); coff_newline(); exit:; coff_unindent(); scratch_end(scratch); } internal void pe_format_load_config64(Arena *arena, String8List *out, String8 indent, PE_LoadConfig64 *lc) { Temp scratch = scratch_begin(&arena, 1); String8 time_stamp = coff_format_time_stamp(scratch.arena, lc->time_stamp); String8 global_flag_clear = pe_string_from_global_flags(scratch.arena, lc->global_flag_clear); String8 global_flag_set = pe_string_from_global_flags(scratch.arena, lc->global_flag_set); coff_printf("# Load Config 64"); coff_indent(); coff_printf("Size: %m", lc->size); coff_printf("Time stamp: %#x (%S)", lc->time_stamp, time_stamp); coff_printf("Version: %u.%u", lc->major_version, lc->minor_version); coff_printf("Global flag clear: %#x %S", lc->global_flag_clear, global_flag_clear); coff_printf("Global flag set: %#x %S", lc->global_flag_set, global_flag_set); coff_printf("Critical section timeout: %u", lc->critical_section_timeout); coff_printf("Decommit free block threshold: %#llx", lc->decommit_free_block_threshold); coff_printf("Decommit total free threshold: %#llx", lc->decommit_total_free_threshold); coff_printf("Lock prefix table: %#llx", lc->lock_prefix_table); coff_printf("Maximum alloc size: %M", lc->maximum_allocation_size); coff_printf("Virtual memory threshold: %M", lc->virtual_memory_threshold); coff_printf("Process affinity mask: %#x", lc->process_affinity_mask); coff_printf("Process heap flags: %#x", lc->process_heap_flags); coff_printf("CSD version: %u", lc->csd_version); coff_printf("Edit list: %#llx", lc->edit_list); coff_printf("Security Cookie: %#llx", lc->security_cookie); if (lc->size < OffsetOf(PE_LoadConfig64, seh_handler_table)) { goto exit; } coff_newline(); coff_printf("SEH Handler Table: %#llx", lc->seh_handler_table); coff_printf("SEH Handler Count: %llu", lc->seh_handler_count); if (lc->size < OffsetOf(PE_LoadConfig64, guard_cf_check_func_ptr)) { goto exit; } coff_newline(); coff_printf("Guard CF Check Function: %#llx", lc->guard_cf_check_func_ptr); coff_printf("Guard CF Dispatch Function: %#llx", lc->guard_cf_dispatch_func_ptr); coff_printf("Guard CF Function Table: %#llx", lc->guard_cf_func_table); coff_printf("Guard CF Function Count: %llu", lc->guard_cf_func_count); coff_printf("Guard Flags: %#x", lc->guard_flags); if (lc->size < OffsetOf(PE_LoadConfig64, code_integrity)) { goto exit; } coff_newline(); coff_printf("Code integrity: { Flags = %#x, Catalog = %#x, Catalog Offset = %#x }", lc->code_integrity.flags, lc->code_integrity.catalog, lc->code_integrity.catalog_offset); coff_printf("Guard address taken IAT entry table: %#llx", lc->guard_address_taken_iat_entry_table); coff_printf("Guard address taken IAT entry count: %llu", lc->guard_address_taken_iat_entry_count); coff_printf("Guard long jump target table: %#llx", lc->guard_long_jump_target_table); coff_printf("Guard long jump target count: %llu", lc->guard_long_jump_target_count); coff_printf("Dynamic value reloc table: %#llx", lc->dynamic_value_reloc_table); coff_printf("CHPE Metadata ptr: %#llx", lc->chpe_metadata_ptr); coff_printf("Guard RF failure routine: %#llx", lc->guard_rf_failure_routine); coff_printf("Guard RF failure routine func ptr: %#llx", lc->guard_rf_failure_routine_func_ptr); coff_printf("Dynamic value reloc section: %#llx", lc->dynamic_value_reloc_table_section); coff_printf("Dynamic value reloc section offset: %#llx", lc->dynamic_value_reloc_table_offset); coff_printf("Guard RF verify SP func ptr: %#llx", lc->guard_rf_verify_stack_pointer_func_ptr); coff_printf("Hot patch table offset: %#llx", lc->hot_patch_table_offset); if (lc->size < OffsetOf(PE_LoadConfig64, enclave_config_ptr)) { goto exit; } coff_newline(); coff_printf("Enclave config ptr: %#llx", lc->enclave_config_ptr); coff_printf("Volatile metadata ptr: %#llx", lc->volatile_metadata_ptr); coff_printf("Guard EH continuation table: %#llx", lc->guard_eh_continue_table); coff_printf("Guard EH continuation count: %llu", lc->guard_eh_continue_count); coff_printf("Guard XFG check func ptr: %#llx", lc->guard_xfg_check_func_ptr); coff_printf("Guard XFG dispatch func ptr: %#llx", lc->guard_xfg_dispatch_func_ptr); coff_printf("Guard XFG table dispatch func ptr: %#llx", lc->guard_xfg_table_dispatch_func_ptr); coff_printf("Cast guard OS determined failure mode: %#llx", lc->cast_guard_os_determined_failure_mode); coff_newline(); exit:; coff_unindent(); scratch_end(scratch); } internal void pe_format_tls(Arena *arena, String8List *out, String8 indent, PE_ParsedTLS tls) { Temp scratch = scratch_begin(&arena, 1); coff_printf("# TLS"); coff_indent(); String8 tls_chars = coff_string_from_section_flags(scratch.arena, tls.header.characteristics); coff_printf("Raw data start: %#llx", tls.header.raw_data_start); coff_printf("Raw data end: %#llx", tls.header.raw_data_end); coff_printf("Index address: %#llx", tls.header.index_address); coff_printf("Callbacks address: %#llx", tls.header.callbacks_address); coff_printf("Zero-fill size: %m", tls.header.zero_fill_size); coff_printf("Characteristics: %S", tls_chars); if (tls.callback_count) { coff_newline(); coff_printf("## Callbacks"); coff_indent(); for (U64 i = 0; i < tls.callback_count; ++i) { coff_printf("%#llx", tls.callback_addrs[i]); } coff_unindent(); } coff_unindent(); coff_newline(); scratch_end(scratch); } internal void pe_format_debug_directory(Arena *arena, String8List *out, String8 indent, String8 raw_data, String8 raw_dir) { Temp scratch = scratch_begin(&arena, 1); coff_printf("# Debug"); coff_indent(); U64 entry_count = raw_dir.size / sizeof(PE_DebugDirectory); PE_DebugDirectory *entries = str8_deserial_get_raw_ptr(raw_dir, 0, sizeof(*entries)*entry_count); for (U64 i = 0; i < entry_count; ++i) { PE_DebugDirectory *de = entries+i; coff_printf("Entry[%u]", i); coff_indent(); { String8 time_stamp = coff_format_time_stamp(scratch.arena, de->time_stamp); String8 type = pe_string_from_debug_directory_type(de->type); coff_printf("Characteristics: %#x", de->characteristics); coff_printf("Time Stamp: %S", time_stamp); coff_printf("Version: %u.%u", de->major_ver, de->minor_ver); coff_printf("Type: %S", type); coff_printf("Size: %u", de->size); coff_printf("Data virt off: %#x", de->voff); coff_printf("Data file off: %#x", de->foff); coff_newline(); } String8 raw_entry = str8_substr(raw_data, rng_1u64(de->foff, de->foff+de->size)); if (raw_entry.size != de->size) { coff_errorf("unable to read debug entry @ %#x", de->foff); break; } coff_indent(); switch (de->type) { case PE_DebugDirectoryType_ILTCG: case PE_DebugDirectoryType_MPX: case PE_DebugDirectoryType_EXCEPTION: case PE_DebugDirectoryType_FIXUP: case PE_DebugDirectoryType_OMAP_TO_SRC: case PE_DebugDirectoryType_OMAP_FROM_SRC: case PE_DebugDirectoryType_BORLAND: case PE_DebugDirectoryType_CLSID: case PE_DebugDirectoryType_REPRO: case PE_DebugDirectoryType_EX_DLLCHARACTERISTICS: { NotImplemented; } break; case PE_DebugDirectoryType_COFF_GROUP: { U64 off = 0; // TODO: is this version? U32 unknown = 0; off += str8_deserial_read_struct(raw_entry, off, &unknown); if (unknown != 0) { coff_printf("TODO: unknown: %u", unknown); } coff_printf("%-8s %-8s %-8s", "VOFF", "Size", "Name"); for (; off < raw_entry.size; ) { U32 voff = 0; U32 size = 0; String8 name = str8_zero(); off += str8_deserial_read_struct(raw_entry, off, &voff); off += str8_deserial_read_struct(raw_entry, off, &size); if (voff == 0 && size == 0) { break; } off += str8_deserial_read_cstr(raw_entry, off, &name); off = AlignPow2(off, 4); coff_printf("%08x %08x %S", voff, size, name); } } break; case PE_DebugDirectoryType_VC_FEATURE: { MSCRT_VCFeatures *feat = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*feat)); if (feat) { coff_printf("Pre-VC++ 11.0: %u", feat->pre_vcpp); coff_printf("C/C++: %u", feat->c_cpp); coff_printf("/GS: %u", feat->gs); coff_printf("/sdl: %u", feat->sdl); coff_printf("guardN: %u", feat->guard_n); } else { coff_errorf("not enough bytes to read VC Features"); } } break; case PE_DebugDirectoryType_FPO: { PE_DebugFPO *fpo = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*fpo)); if (fpo) { U8 prolog_size = PE_FPOEncoded_Extract_PROLOG_SIZE(fpo->flags); U8 saved_regs_size = PE_FPOEncoded_Extract_SAVED_REGS_SIZE(fpo->flags); PE_FPOType type = PE_FPOEncoded_Extract_FRAME_TYPE(fpo->flags); PE_FPOFlags flags = PE_FPOEncoded_Extract_FLAGS(fpo->flags); String8 type_string = pe_string_from_fpo_type(type); String8 flags_string = pe_string_from_fpo_flags(scratch.arena, flags); coff_printf("Function offset: %#x", fpo->func_code_off); coff_printf("Function size: %#x", fpo->func_size); coff_printf("Locals size: %u", fpo->locals_size); coff_printf("Params size: %u", fpo->params_size); coff_printf("Prolog size: %u", prolog_size); coff_printf("Saved regs size: %u", saved_regs_size); coff_printf("Type: %S", type_string); coff_printf("Flags: %S", flags_string); } else { coff_errorf("not enough bytes to read FPO"); } } break; case PE_DebugDirectoryType_CODEVIEW: { U32 magic = 0; str8_deserial_read_struct(raw_entry, 0, &magic); switch (magic) { case PE_CODEVIEW_PDB20_MAGIC: { PE_CvHeaderPDB20 *cv20 = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*cv20)); String8 name; str8_deserial_read_cstr(raw_entry, sizeof(*cv20), &name); String8 time_stamp = coff_format_time_stamp(scratch.arena, cv20->time_stamp); coff_printf("Time stamp: %S", time_stamp); coff_printf("Age: %u", cv20->age); coff_printf("Name: %S", name); } break; case PE_CODEVIEW_PDB70_MAGIC: { PE_CvHeaderPDB70 *cv70 = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*cv70)); String8 name; str8_deserial_read_cstr(raw_entry, sizeof(*cv70), &name); String8 guid = string_from_guid(scratch.arena, cv70->guid); coff_printf("GUID: %S", guid); coff_printf("Age: %u", cv70->age); coff_printf("Name: %S", name); } break; default: { coff_errorf("unknown CodeView magic %#x", magic); } break; } } break; case PE_DebugDirectoryType_MISC: { PE_DebugMisc *misc = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*misc)); String8 type_string = pe_string_from_misc_type(misc->data_type); coff_printf("Data type: %S", type_string); coff_printf("Size: %u", misc->size); coff_printf("Unicode: %u", misc->unicode); switch (misc->data_type) { case PE_DebugMiscType_EXE_NAME: { String8 name; str8_deserial_read_cstr(raw_entry, sizeof(*misc), &name); coff_printf("Name: %S", name); } break; default: { coff_printf("???"); } break; } } break; } coff_unindent(); coff_unindent(); coff_newline(); } coff_unindent(); coff_newline(); scratch_end(scratch); } internal void pe_format_export_table(Arena *arena, String8List *out, String8 indent, PE_ParsedExportTable exptab) { Temp scratch = scratch_begin(&arena, 1); String8 time_stamp = coff_format_time_stamp(scratch.arena, exptab.time_stamp); coff_printf("# Export Table"); coff_indent(); coff_printf("Characteristics: %u", exptab.flags); coff_printf("Time stamp: %S", time_stamp); coff_printf("Version: %u.%02u", exptab.major_ver, exptab.minor_ver); coff_printf("Ordinal base: %u", exptab.ordinal_base); coff_printf(""); coff_printf("%-4s %-8s %-8s %-8s", "No.", "Oridnal", "VOff", "Name"); for (U64 i = 0; i < exptab.export_count; ++i) { PE_ParsedExport *exp = exptab.exports+i; if (exp->forwarder.size) { coff_printf("%4u %8u %8x %S (forwarded to %S)", i, exp->ordinal, exp->voff, exp->name, exp->forwarder); } else { coff_printf("%4u %8u %8x %S", i, exp->ordinal, exp->voff, exp->name); } } coff_unindent(); scratch_end(scratch); } internal void pe_format_static_import_table(Arena *arena, String8List *out, String8 indent, U64 image_base, PE_ParsedStaticImportTable imptab) { Temp scratch = scratch_begin(&arena, 1); if (imptab.count) { coff_printf("# Import Table"); coff_indent(); for (U64 dll_idx = 0; dll_idx < imptab.count; ++dll_idx) { PE_ParsedStaticDLLImport *dll = imptab.v+dll_idx; coff_printf("Name: %S", dll->name); coff_printf("Import address table: %#llx", image_base + dll->import_address_table_voff); coff_printf("Import name table: %#llx", image_base + dll->import_name_table_voff); coff_printf("Time stamp: %#x", dll->time_stamp); coff_newline(); if (dll->import_count) { coff_indent(); for (U64 imp_idx = 0; imp_idx < dll->import_count; ++imp_idx) { PE_ParsedImport *imp = dll->imports+imp_idx; if (imp->type == PE_ParsedImport_Ordinal) { coff_printf("%#-6x", imp->u.ordinal); } else if (imp->type == PE_ParsedImport_Name) { coff_printf("%#-6x %S", imp->u.name.hint, imp->u.name.string); } } coff_unindent(); coff_newline(); } } coff_unindent(); } scratch_end(scratch); } internal void pe_format_delay_import_table(Arena *arena, String8List *out, String8 indent, U64 image_base, PE_ParsedDelayImportTable imptab) { if (imptab.count) { Temp scratch = scratch_begin(&arena, 1); coff_printf("# Delay Import Table"); coff_indent(); for (U64 dll_idx = 0; dll_idx < imptab.count; ++dll_idx) { PE_ParsedDelayDLLImport *dll = imptab.v+dll_idx; coff_printf("Attributes: %#08x", dll->attributes); coff_printf("Name: %S", dll->name); coff_printf("HMODULE address: %#llx", image_base + dll->module_handle_voff); coff_printf("Import address table: %#llx", image_base + dll->iat_voff); coff_printf("Import name table: %#llx", image_base + dll->name_table_voff); coff_printf("Bound import name table: %#llx", image_base + dll->bound_table_voff); coff_printf("Unload import name table: %#llx", image_base + dll->unload_table_voff); coff_printf("Time stamp: %#x", dll->time_stamp); coff_newline(); coff_indent(); for (U64 imp_idx = 0; imp_idx < dll->import_count; ++imp_idx) { PE_ParsedImport *imp = dll->imports+imp_idx; String8 bound = str8_lit("NULL"); if (imp_idx < dll->bound_table_count) { U64 bound_addr = dll->bound_table[imp_idx]; bound = push_str8f(scratch.arena, "%#llx", bound_addr); } String8 unload = str8_lit("NULL"); if (imp_idx < dll->unload_table_count) { U64 unload_addr = dll->unload_table[imp_idx]; unload = push_str8f(scratch.arena, "%#llx", unload_addr); } if (imp->type == PE_ParsedImport_Ordinal) { coff_printf("%-16S %-16S %#-4x", bound, unload, imp->u.ordinal); } else if (imp->type == PE_ParsedImport_Name) { coff_printf("%-16S %-16S %#-4x %S", bound, unload, imp->u.name.hint, imp->u.name.string); } } coff_unindent(); coff_newline(); } coff_unindent(); scratch_end(scratch); } } internal void pe_format_resources(Arena *arena, String8List *out, String8 indent, PE_ResourceDir *root) { Temp scratch = scratch_begin(&arena, 1); // setup stack struct stack_s { struct stack_s *next; B32 print_table; B32 is_named; PE_ResourceNode *curr_name_node; PE_ResourceNode *curr_id_node; U64 name_idx; U64 id_idx; U64 dir_idx; U64 dir_id; String8 dir_name; PE_ResourceDir *table; } *stack = push_array(scratch.arena, struct stack_s, 1); stack->table = root; stack->print_table = 1; stack->is_named = 1; stack->dir_name = str8_lit("ROOT"); stack->curr_name_node = root->named_list.first; stack->curr_id_node = root->id_list.first; if (stack) { coff_printf("# Resources"); // traverse resource tree while (stack) { if (stack->print_table) { stack->print_table = 0; coff_indent(); if (stack->is_named) { coff_printf("[%u] %S { Time Stamp: %u, Version %u.%u Name Count: %u, ID Count %u, Characteristics: %u }", stack->dir_idx, stack->dir_name, stack->table->time_stamp, stack->table->major_version, stack->table->minor_version, stack->table->named_list.count, stack->table->id_list.count, stack->table->characteristics); } else { B32 is_actually_leaf = stack->table->id_list.count == 1 && stack->table->id_list.first->data.kind != PE_ResDataKind_DIR; if (is_actually_leaf) { coff_printf("[%u] %u { Time Stamp: %u, Version %u.%u Name Count: %u, ID Count %u, Characteristics: %u }", stack->dir_idx, stack->dir_id, stack->table->time_stamp, stack->table->major_version, stack->table->minor_version, stack->table->named_list.count, stack->table->id_list.count, stack->table->characteristics); } else { String8 id_str = pe_resource_kind_to_string(stack->dir_id); coff_printf("[%u] %S { Time Stamp: %u, Version %u.%u Name Count: %u, ID Count %u, Characteristics: %u }", stack->dir_idx, id_str, stack->dir_id, stack->table->time_stamp, stack->table->major_version, stack->table->minor_version, stack->table->named_list.count, stack->table->id_list.count, stack->table->characteristics); } } } while (stack->curr_name_node) { PE_ResourceNode *named_node = stack->curr_name_node; stack->curr_name_node = stack->curr_name_node->next; U64 name_idx = stack->name_idx++; PE_Resource *res = &named_node->data; if (res->kind == PE_ResDataKind_DIR) { struct stack_s *frame = push_array(scratch.arena, struct stack_s, 1); frame->table = res->u.dir; frame->print_table = 1; frame->dir_idx = stack->name_idx; frame->dir_name = res->id.u.string; frame->is_named = 1; frame->curr_name_node = frame->table->named_list.first; frame->curr_id_node = frame->table->id_list.first; SLLStackPush(stack, frame); goto yield; } else if (res->kind == PE_ResDataKind_COFF_LEAF) { COFF_ResourceDataEntry *entry = &res->u.leaf; coff_printf("[%u] %S Data VOFF: %#08x, Data Size: %#08x, Code Page: %u", name_idx, res->id.u.string, entry->data_voff, entry->data_size, entry->code_page); } else { InvalidPath; } } while (stack->curr_id_node) { PE_ResourceNode *id_node = stack->curr_id_node; PE_Resource *res = &id_node->data; stack->curr_id_node = stack->curr_id_node->next; U64 id_idx = stack->id_idx++; if (res->kind == PE_ResDataKind_DIR) { struct stack_s *frame = push_array(scratch.arena, struct stack_s, 1); frame->table = res->u.dir; frame->print_table = 1; frame->dir_idx = stack->table->named_list.count + id_idx; frame->dir_id = res->id.u.number; frame->curr_name_node = frame->table->named_list.first; frame->curr_id_node = frame->table->id_list.first; SLLStackPush(stack, frame); goto yield; } else if (res->kind == PE_ResDataKind_COFF_LEAF) { COFF_ResourceDataEntry *entry = &res->u.leaf; coff_printf("[%u] ID: %u Data VOFF: %#08x, Data Size: %#08x, Code Page: %u", id_idx, res->id.u.number, entry->data_voff, entry->data_size, entry->code_page); } else { InvalidPath; } } if (stack->curr_id_node == 0 && stack->curr_name_node == 0) { coff_unindent(); } SLLStackPop(stack); yield:; } coff_newline(); } scratch_end(scratch); } internal void pe_format_exceptions_x8664(Arena *arena, String8List *out, String8 indent, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 except_frange) { Temp scratch = scratch_begin(&arena, 1); String8 raw_except = str8_substr(raw_data, except_frange); U64 count = raw_except.size / sizeof(PE_IntelPdata); for (U64 i = 0; i < count; ++i) { Temp temp = temp_begin(scratch.arena); U64 pdata_offset = i * sizeof(PE_IntelPdata); PE_IntelPdata *pdata = str8_deserial_get_raw_ptr(raw_except, pdata_offset, sizeof(*pdata)); String8 pdata_name = str8_zero(); // TODO: syms_name_for_voff(arena, group, thunk_accel, thunk_table_set, pdata.voff_first); U64 unwind_info_offset = coff_foff_from_voff(sections, section_count, pdata->voff_unwind_info); PE_UnwindInfo *uwinfo = str8_deserial_get_raw_ptr(raw_data, unwind_info_offset, sizeof(*uwinfo)); U8 version = PE_UNWIND_INFO_VERSION_FROM_HDR(uwinfo->header); U8 flags = PE_UNWIND_INFO_FLAGS_FROM_HDR(uwinfo->header); U8 frame_register = PE_UNWIND_INFO_REG_FROM_FRAME(uwinfo->frame); U8 frame_offset = PE_UNWIND_INFO_OFF_FROM_FRAME(uwinfo->frame); B32 is_chained = (flags & PE_UnwindInfoFlag_CHAINED) != 0; B32 has_handler_data = !is_chained && (flags & (PE_UnwindInfoFlag_EHANDLER | PE_UnwindInfoFlag_UHANDLER)) != 0; String8 flags_str = str8_zero(); { U64 f = flags; String8List flags_list = {0}; if (f & PE_UnwindInfoFlag_EHANDLER) { f &= ~PE_UnwindInfoFlag_EHANDLER; str8_list_pushf(scratch.arena, &flags_list, "EHANDLER"); } if (f & PE_UnwindInfoFlag_UHANDLER) { f &= ~PE_UnwindInfoFlag_UHANDLER; str8_list_pushf(scratch.arena, &flags_list, "UHANDLER"); } if (f & PE_UnwindInfoFlag_CHAINED) { f &= ~PE_UnwindInfoFlag_CHAINED; str8_list_pushf(scratch.arena, &flags_list, "CHAINED"); } if (f) { str8_list_pushf(scratch.arena, &flags_list, "%#llx", f); } if (flags_list.node_count == 0) { str8_list_pushf(scratch.arena, &flags_list, "%#llx", f); } flags_str = str8_list_join(scratch.arena, &flags_list, &(StringJoin){.sep=str8_lit(", ")}); } U64 codes_offset = unwind_info_offset + sizeof(PE_UnwindInfo); PE_UnwindCode *code_ptr = str8_deserial_get_raw_ptr(raw_data, codes_offset, sizeof(*code_ptr) * uwinfo->codes_num); PE_UnwindCode *code_opl = code_ptr + uwinfo->codes_num; if (i > 0) { coff_newline(); } coff_printf("%08x %08x %08x %08x%s%S", pdata_offset, pdata->voff_first, pdata->voff_one_past_last, pdata->voff_unwind_info, pdata_name.size ? " " : "", pdata_name); coff_printf("Version: %u", version); coff_printf("Flags: %S", flags_str); coff_printf("Prolog Size: %#x", uwinfo->prolog_size); coff_printf("Code Count: %u", uwinfo->codes_num); coff_printf("Frame: %u", uwinfo->frame); coff_printf("Codes:"); coff_indent(); for (; code_ptr < code_opl;) { Temp code_temp = temp_begin(scratch.arena); String8List code_list = {0}; U8 operation_code = PE_UNWIND_OPCODE_FROM_FLAGS(code_ptr[0].flags); U8 operation_info = PE_UNWIND_INFO_FROM_FLAGS(code_ptr[0].flags); str8_list_pushf(code_temp.arena, &code_list, "%#04x:", code_ptr[0].off_in_prolog); switch (operation_code) { case PE_UnwindOpCode_PUSH_NONVOL: { String8 gpr = pe_string_from_unwind_gpr_x64(operation_info); str8_list_pushf(code_temp.arena, &code_list, "PUSH_NONVOL %S", gpr); code_ptr += 1; } break; case PE_UnwindOpCode_ALLOC_LARGE: { U64 size = 0; switch (operation_info) { case 0: { // 136B - 512K size = code_ptr[1].u16*8; } break; case 1: { // 512K - 4GB size = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); } break; default: break; } str8_list_pushf(code_temp.arena, &code_list, "ALLOC_LARGE size=%#x", size); code_ptr += 2; } break; case PE_UnwindOpCode_ALLOC_SMALL: { U64 size = operation_info*8 + 8; str8_list_pushf(code_temp.arena, &code_list, "ALLOC_SMALL size=%#x", size); code_ptr += 1; } break; case PE_UnwindOpCode_SET_FPREG: { U64 offset = frame_offset*16; String8 gpr = pe_string_from_unwind_gpr_x64(frame_register); str8_list_pushf(code_temp.arena, &code_list, "SET_FPREG %S, offset=%#x", gpr, offset); code_ptr += 1; } break; case PE_UnwindOpCode_SAVE_NONVOL: { String8 gpr = pe_string_from_unwind_gpr_x64(operation_info); U64 register_offset = code_ptr[1].u16*8; str8_list_pushf(code_temp.arena, &code_list, "SAVE_NONVOL %S, offset=%#x", gpr, register_offset); code_ptr += 2; } break; case PE_UnwindOpCode_SAVE_NONVOL_FAR: { String8 gpr = pe_string_from_unwind_gpr_x64(operation_info); U64 frame_offset = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); str8_list_pushf(code_temp.arena, &code_list, "SAVE_NONVOL_FAR %S, offset=%#x", gpr, frame_offset); code_ptr += 3; } break; case PE_UnwindOpCode_EPILOG: { str8_list_pushf(code_temp.arena, &code_list, "EPILOG flags=%#x", code_ptr[0].flags); code_ptr += 1; } break; case PE_UnwindOpCode_SPARE_CODE: { str8_list_pushf(code_temp.arena, &code_list, "SPARE_CODE"); code_ptr += 1; } break; case PE_UnwindOpCode_SAVE_XMM128: { String8 gpr = pe_string_from_unwind_gpr_x64(operation_info); U64 register_offset = code_ptr[1].u16*16; str8_list_pushf(code_temp.arena, &code_list, "SAVE_XMM128 %S, offset=%#x", gpr, register_offset); code_ptr += 2; } break; case PE_UnwindOpCode_SAVE_XMM128_FAR: { String8 gpr = pe_string_from_unwind_gpr_x64(operation_info); U64 frame_offset = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); str8_list_pushf(code_temp.arena, &code_list, "SAVE_XMM128_FAR %S, offset=%#x", gpr, frame_offset); code_ptr += 3; } break; case PE_UnwindOpCode_PUSH_MACHFRAME: { str8_list_pushf(code_temp.arena, &code_list, "PUSH_MACHFRAME %s", operation_info == 1 ? "with error code" : "without error code"); code_ptr += 1; } break; default: { str8_list_pushf(code_temp.arena, &code_list, "UNKNOWN_OPCODE %#x", operation_code); code_ptr += 1; } break; } String8 code_line = str8_list_join(code_temp.arena, &code_list, &(StringJoin){.sep=str8_lit(" ")}); coff_printf("%S", code_line); temp_end(code_temp); } coff_unindent(); if (is_chained) { U64 next_pdata_offset = codes_offset + sizeof(PE_UnwindCode) * AlignPow2(uwinfo->codes_num, 2); PE_IntelPdata *next_pdata = str8_deserial_get_raw_ptr(raw_data, next_pdata_offset, sizeof(*next_pdata)); coff_printf("Chained: %#08x %#08x %#08x", next_pdata->voff_first, next_pdata->voff_one_past_last, next_pdata->voff_unwind_info); } if (has_handler_data) { #define ExceptionHandlerDataFlag_FuncInfo (1 << 0) #define ExceptionHandlerDataFlag_FuncInfo4 (1 << 1) #define ExceptionHandlerDataFlag_ScopeTable (1 << 2) #define ExceptionHandlerDataFlag_GS (1 << 3u) U64 actual_code_count = PE_UNWIND_INFO_GET_CODE_COUNT(uwinfo->codes_num); U64 read_cursor = codes_offset + actual_code_count * sizeof(PE_UnwindCode); U32 handler = 0; read_cursor += str8_deserial_read_struct(raw_data, read_cursor, &handler); String8 handler_name = str8_zero(); // TODO: syms_name_for_voff(scratch.arena, group, thunk_accel, thunk_table_set, handler); coff_printf("Handler: %#llx%s%S", handler, handler_name.size ? " " : "", handler_name); U32 handler_data_flags = 0; if (str8_match(handler_name, str8_lit("__GSHandlerCheck_EH4"), 0)) { handler_data_flags = ExceptionHandlerDataFlag_FuncInfo4; } else if (str8_match(handler_name, str8_lit("__CxxFrameHandler4"), 0)) { handler_data_flags = ExceptionHandlerDataFlag_FuncInfo4; } else if (str8_match(handler_name, str8_lit("__CxxFrameHandler3"), 0)) { handler_data_flags = ExceptionHandlerDataFlag_FuncInfo; } else if (str8_match(handler_name, str8_lit("__C_specific_handler"), 0)) { handler_data_flags = ExceptionHandlerDataFlag_ScopeTable; } else if (str8_match(handler_name, str8_lit("__GSHandlerCheck"), 0)) { handler_data_flags = ExceptionHandlerDataFlag_GS; } else if (str8_match(handler_name, str8_lit("__GSHandlerCheck_SEH"), 0)) { handler_data_flags = ExceptionHandlerDataFlag_ScopeTable|ExceptionHandlerDataFlag_GS; } else if (str8_match(handler_name, str8_lit("__GSHandlerCheck_EH"), 0)) { handler_data_flags = ExceptionHandlerDataFlag_FuncInfo|ExceptionHandlerDataFlag_GS; } if (handler_data_flags & ExceptionHandlerDataFlag_FuncInfo) { MSCRT_FuncInfo func_info; read_cursor += mscrt_parse_func_info(arena, raw_data, section_count, sections, read_cursor, &func_info); coff_printf("Function Info:"); coff_indent(); coff_printf("Magic: %#x", func_info.magic); coff_printf("Max State: %u", func_info.max_state); coff_printf("Try Block Count: %u", func_info.try_block_map_count); coff_printf("IP Map Count: %u", func_info.ip_map_count); coff_printf("Frame Offset Unwind Helper: %#x", func_info.frame_offset_unwind_helper); coff_printf("ES Flags: %#x", func_info.eh_flags); coff_unindent(); if (func_info.ip_map_count > 0) { coff_printf("IP to State Map:"); coff_indent(); coff_printf("%8s %8s", "State", "IP"); for (U32 i = 0; i < func_info.ip_map_count; ++i) { MSCRT_IPState32 state = func_info.ip_map[i]; String8 line = str8_zero(); // TODO: syms_line_for_voff(temp.arena, group, state.ip); coff_printf("%8d %08x %S", state.state, state.ip, line); } coff_unindent(); } if (func_info.max_state > 0) { coff_printf("Unwind Map:"); coff_indent(); coff_printf("%13s %10s %8s", "Current State", "Next State", "Action @"); for (U32 i = 0; i < func_info.max_state; ++i) { MSCRT_UnwindMap32 map = func_info.unwind_map[i]; String8 line = str8_zero(); // TODO: syms_line_for_voff(temp.arena, group, map.action_virt_off); coff_printf("%13u %10d %8x %S", i, map.next_state, map.action_virt_off, line); } coff_unindent(); } for (U32 i = 0; i < func_info.try_block_map_count; ++i) { MSCRT_TryMapBlock try_block = func_info.try_block_map[i]; coff_printf("Try Map Block #%u", i); coff_indent(); coff_printf("Try State Low: %u", try_block.try_low); coff_printf("Try State High: %u", try_block.try_high); coff_printf("Catch State High: %u", try_block.catch_high); coff_printf("Catch Count: %u", try_block.catch_handlers_count); coff_printf("Catches:"); coff_indent(); for (U32 ihandler = 0; ihandler < try_block.catch_handlers_count; ++ihandler) { coff_printf("Catch #%u", ihandler); coff_indent(); mscrt_format_eh_handler_type32(arena, out, indent, &try_block.catch_handlers[ihandler]); coff_unindent(); } coff_unindent(); coff_unindent(); } if (func_info.es_type_list.count) { coff_printf("Exception Specific Types:"); coff_indent(); for (U32 i = 0; i < func_info.es_type_list.count; ++i) { if (i > 0) { coff_newline(); } mscrt_format_eh_handler_type32(arena, out, indent, &func_info.es_type_list.handlers[i]); } coff_unindent(); } } if (handler_data_flags & ExceptionHandlerDataFlag_FuncInfo4) { U32 handler_data_voff = 0; read_cursor += str8_deserial_read_struct(raw_data, read_cursor, &handler_data_voff); U32 unknown = 0; read_cursor += str8_deserial_read_struct(raw_data, read_cursor, &unknown); U64 func_info_foff = coff_foff_from_voff(sections, section_count, handler_data_voff); MSCRT_ParsedFuncInfoV4 func_info = {0}; mscrt_parse_func_info_v4(arena, raw_data, section_count, sections, func_info_foff, pdata->voff_first, &func_info); String8 header_str = str8_zero(); { String8List header_list = {0}; if (func_info.header & MSCRT_FuncInfoV4Flag_IsCatch) { str8_list_pushf(arena, &header_list, "IsCatch"); } if (func_info.header & MSCRT_FuncInfoV4Flag_IsSeparated) { str8_list_pushf(arena, &header_list, "IsSeparted"); } if (func_info.header & MSCRT_FuncInfoV4Flag_IsBBT) { str8_list_pushf(arena, &header_list, "IsBBT"); } if (func_info.header & MSCRT_FuncInfoV4Flag_UnwindMap) { str8_list_pushf(arena, &header_list, "UnwindMap"); } if (func_info.header & MSCRT_FuncInfoV4Flag_TryBlockMap) { str8_list_pushf(arena, &header_list, "TryBlockMap"); } if (func_info.header & MSCRT_FuncInfoV4Flag_EHs) { str8_list_pushf(arena, &header_list, "EHs"); } if (func_info.header & MSCRT_FuncInfoV4Flag_NoExcept) { str8_list_pushf(arena, &header_list, "NoExcept"); } header_str = str8_list_join(arena, &header_list, &(StringJoin){.sep=str8_lit(", ")}); } coff_printf("Function Info V4:"); coff_indent(); coff_printf("Header: %#x %S", func_info.header, header_str); coff_printf("BBT Flags: %#x", func_info.bbt_flags); MSCRT_IP2State32V4 ip2state_map = func_info.ip2state_map; coff_printf("IP To State Map:"); coff_indent(); coff_printf("%8s %8s", "State", "IP"); for (U32 i = 0; i < ip2state_map.count; ++i) { String8 line_str = str8_zero(); // TODO: syms_line_for_voff(arena, group, ip2state_map.voffs[i]); coff_printf("%8d %08X %S", ip2state_map.states[i], ip2state_map.voffs[i], line_str); } coff_unindent(); if (func_info.header & MSCRT_FuncInfoV4Flag_UnwindMap) { MSCRT_UnwindMapV4 unwind_map = func_info.unwind_map; coff_printf("Unwind Map:"); coff_indent(); for (U32 i = 0; i < unwind_map.count; ++i) { MSCRT_UnwindEntryV4 *ue = &unwind_map.v[i]; String8 type_str = str8_zero(); switch (ue->type) { case MSCRT_UnwindMapV4Type_NoUW: type_str = str8_lit("NoUW"); break; case MSCRT_UnwindMapV4Type_DtorWithObj: type_str = str8_lit("DtorWithObj"); break; case MSCRT_UnwindMapV4Type_DtorWithPtrToObj: type_str = str8_lit("DtorWithPtrToObj"); break; case MSCRT_UnwindMapV4Type_VOFF: type_str = str8_lit("VOFF"); break; } if (ue->type == MSCRT_UnwindMapV4Type_DtorWithObj || ue->type == MSCRT_UnwindMapV4Type_DtorWithPtrToObj) { coff_printf("[%2u] NextOff=%u Type=%-16S Action=%#08x Object=%#x", i, ue->next_off, type_str, ue->action, ue->object); } else if (ue->type == MSCRT_UnwindMapV4Type_VOFF) { coff_printf("[%2u] NextOff=%u Type=%-16S Action=%#08x", i, ue->next_off, type_str, ue->action); } else { coff_printf("[%2u] NextOff=%u Type=%S", i, ue->next_off, type_str); } } coff_unindent(); } if (func_info.header & MSCRT_FuncInfoV4Flag_TryBlockMap) { MSCRT_TryBlockMapV4Array try_block_map = func_info.try_block_map; coff_printf("Try/Catch Blocks:"); coff_indent(); for (U32 i = 0; i < try_block_map.count; ++i) { MSCRT_TryBlockMapV4 *try_block = &try_block_map.v[i]; coff_printf("[%2u] TryLow %u TryHigh %u CatchHigh %u", i, try_block->try_low, try_block->try_high, try_block->catch_high); if (try_block->handlers.count) { for (U32 k = 0; k < try_block->handlers.count; ++k) { MSCRT_EhHandlerTypeV4 *handler = &try_block->handlers.v[k]; String8List line_list = {0}; str8_list_pushf(arena, &line_list, " "); str8_list_pushf(arena, &line_list, "CatchCodeVOff=0x%08X", handler->catch_code_voff); if (handler->flags & MSCRT_EhHandlerV4Flag_Adjectives) { String8 adjectives = mscrt_format_eh_adjectives(arena, handler->adjectives); str8_list_pushf(arena, &line_list, "Adjectives=%S", adjectives); } if (handler->flags & MSCRT_EhHandlerV4Flag_DispType) { str8_list_pushf(arena, &line_list, "TypeVOff=%#x", handler->type_voff); } if (handler->flags & MSCRT_EhHandlerV4Flag_DispCatchObj) { str8_list_pushf(arena, &line_list, "CacthObjVOff=%#x", handler->catch_obj_voff); } if (handler->flags & MSCRT_EhHandlerV4Flag_ContIsVOff) { str8_list_pushf(arena, &line_list, "ContIsVOff"); } for (U32 icont = 0; icont < handler->catch_funclet_cont_addr_count; ++icont) { str8_list_pushf(arena, &line_list, "ContAddr[%u]=%#llx", icont, handler->catch_funclet_cont_addr[icont]); } String8 handler_str = str8_list_join(arena, &line_list, &(StringJoin){.sep=str8_lit(" ")}); coff_printf("%S", handler_str); } } } coff_unindent(); } } if (handler_data_flags & ExceptionHandlerDataFlag_ScopeTable) { U32 scope_count = 0; read_cursor += str8_deserial_read_struct(raw_data, read_cursor, &scope_count); PE_HandlerScope *scopes = str8_deserial_get_raw_ptr(raw_data, read_cursor, sizeof(PE_HandlerScope)*scope_count); read_cursor += scope_count*sizeof(scopes[0]); coff_printf("Count of scope table entries: %u", scope_count); coff_indent(); coff_printf("%-8s %-8s %-8s %-8s", "Begin", "End", "Handler", "Target"); for (U32 i = 0; i < scope_count; ++i) { PE_HandlerScope scope = scopes[i]; coff_printf("%08x %08x %08x %08x", scope.begin, scope.end, scope.handler, scope.target); } coff_unindent(); } if (handler_data_flags & ExceptionHandlerDataFlag_GS) { U32 gs_data = 0; read_cursor += str8_deserial_read_struct(raw_data, read_cursor, &gs_data); U32 flags = MSCRT_GSHandler_GetFlags(gs_data); U32 cookie_offset = MSCRT_GSHandler_GetCookieOffset(gs_data); U32 aligned_base_offset = 0; U32 alignment = 0; if (flags & MSCRT_GSHandlerFlag_HasAlignment) { read_cursor += str8_deserial_read_struct(raw_data, read_cursor, &aligned_base_offset); read_cursor += str8_deserial_read_struct(raw_data, read_cursor, &alignment); } String8 flags_str; { String8List flags_list = {0}; if (flags & MSCRT_GSHandlerFlag_EHandler) { str8_list_pushf(arena, &flags_list, "EHandler"); } if (flags & MSCRT_GSHandlerFlag_UHandler) { str8_list_pushf(arena, &flags_list, "UHandler"); } if (flags & MSCRT_GSHandlerFlag_HasAlignment) { str8_list_pushf(arena, &flags_list, "Has Alignment"); } if (flags == 0) { str8_list_pushf(arena, &flags_list, "None"); } flags_str = str8_list_join(arena, &flags_list, &(StringJoin){.sep=str8_lit(", ")}); } coff_printf("GS unwind flags: %S", flags_str); coff_printf("Cookie offset: %x", cookie_offset); if (flags & MSCRT_GSHandlerFlag_HasAlignment) { coff_printf("Aligned base offset: %x", aligned_base_offset); coff_printf("Alignment: %x", alignment); } } #undef ExceptionHandlerDataFlag_FuncInfo #undef ExceptionHandlerDataFlag_ScopeTable #undef ExceptionHandlerDataFlag_GS } temp_end(temp); } scratch_end(scratch); } internal void pe_format_exceptions(Arena *arena, String8List *out, String8 indent, COFF_MachineType machine, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 except_frange) { coff_printf("# Exceptions"); coff_indent(); coff_printf("%-8s %-8s %-8s %-8s", "Offset", "Begin", "End", "Unwind Info"); switch (machine) { case COFF_MachineType_UNKNOWN: break; case COFF_MachineType_X64: case COFF_MachineType_X86: { pe_format_exceptions_x8664(arena, out, indent, section_count, sections, raw_data, except_frange); } break; default: NotImplemented; break; } coff_unindent(); } internal void pe_format_base_relocs(Arena *arena, String8List *out, String8 indent, COFF_MachineType machine, U64 image_base, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 base_reloc_franges) { Temp scratch = scratch_begin(&arena, 1); String8 raw_base_relocs = str8_substr(raw_data, base_reloc_franges); PE_BaseRelocBlockList base_relocs = pe_base_reloc_block_list_from_data(scratch.arena, raw_base_relocs); if (base_relocs.count) { coff_printf("# Base Relocs"); coff_indent(); U32 addr_size = 0; switch (machine) { case COFF_MachineType_UNKNOWN: break; case COFF_MachineType_X86: addr_size = 4; break; case COFF_MachineType_X64: addr_size = 8; break; default: NotImplemented; } // convert blocks to string list U64 iblock = 0; for (PE_BaseRelocBlockNode *node = base_relocs.first; node != 0; node = node->next) { PE_BaseRelocBlock *block = &node->v; coff_printf("Block No. %u, Virt Off %#x, Reloc Count %u", iblock++, block->page_virt_off, block->entry_count); coff_indent(); for (U64 ientry = 0; ientry < block->entry_count; ++ientry) { PE_BaseRelocKind type = PE_BaseRelocKindFromEntry(block->entries[ientry]); U16 offset = PE_BaseRelocOffsetFromEntry(block->entries[ientry]); U64 apply_to_voff = block->page_virt_off + offset; U64 apply_to_foff = coff_foff_from_voff(sections, section_count, apply_to_voff); U64 apply_to = 0; str8_deserial_read(raw_data, apply_to_foff, &apply_to, addr_size, 1); U64 addr = image_base + apply_to; const char *type_str = "???"; switch (type) { case PE_BaseRelocKind_ABSOLUTE: type_str = "ABS"; break; case PE_BaseRelocKind_HIGH: type_str = "HIGH"; break; case PE_BaseRelocKind_LOW: type_str = "LOW"; break; case PE_BaseRelocKind_HIGHLOW: type_str = "HIGHLOW"; break; case PE_BaseRelocKind_HIGHADJ: type_str = "HIGHADJ"; break; case PE_BaseRelocKind_DIR64: type_str = "DIR64"; break; default: { switch (machine) { case COFF_MachineType_ARM: case COFF_MachineType_ARM64: case COFF_MachineType_ARMNT: { switch (type) { case PE_BaseRelocKind_ARM_MOV32: type_str = "ARM_MOV32"; break; case PE_BaseRelocKind_THUMB_MOV32: type_str = "THUMB_MOV32"; break; default: NotImplemented; } } break; // TODO: mips, loong, risc-v } } break; } if (type == PE_BaseRelocKind_ABSOLUTE) { coff_printf("%-4x %-12s", offset, type_str); } else { coff_printf("%-4x %-12s %016llx", offset, type_str, apply_to); // TODO #if 0 U64 reloc_voff = apply_to - image_base; SYMS_UnitID uid = syms_group_uid_from_voff__accelerated(group, reloc_voff); SYMS_SymbolID sid = syms_group_proc_sid_from_uid_voff__accelerated(group, uid, reloc_voff); if (sid) { SYMS_UnitAccel *unit = syms_group_unit_from_uid(group, uid); SYMS_String8 name = syms_group_symbol_name_from_sid(arena, group, unit, sid); syms_string_list_pushf__dev(arena, list, " %-4X %-12s %016llX %.*s", offset, type_str, apply_to, syms_expand_string(name)); } else { syms_string_list_pushf__dev(arena, list, " %-4X %-12s %016llX", offset, type_str, apply_to); } #endif } } coff_unindent(); coff_newline(); } coff_unindent(); } scratch_end(scratch); } internal void pe_format(Arena *arena, String8List *out, String8 indent, String8 raw_data, Coffdump_Option opts) { Temp scratch = scratch_begin(&arena, 1); PE_DosHeader *dos_header = str8_deserial_get_raw_ptr(raw_data, 0, sizeof(*dos_header)); if (!dos_header) { coff_errorf("not enough bytes to read DOS header"); goto exit; } Assert(dos_header->magic == PE_DOS_MAGIC); U32 pe_magic = 0; str8_deserial_read_struct(raw_data, dos_header->coff_file_offset, &pe_magic); if (pe_magic != PE_MAGIC) { coff_errorf("PE magic check failure, input file is not of PE format"); goto exit; } U64 coff_header_off = dos_header->coff_file_offset+sizeof(pe_magic); COFF_Header *coff_header = str8_deserial_get_raw_ptr(raw_data, coff_header_off, sizeof(*coff_header)); if (!coff_header) { coff_errorf("not enough bytes to read COFF header"); goto exit; } U64 opt_header_off = coff_header_off + sizeof(*coff_header); U16 opt_header_magic = 0; str8_deserial_read_struct(raw_data, opt_header_off, &opt_header_magic); if (opt_header_magic != PE_PE32_MAGIC && opt_header_magic != PE_PE32PLUS_MAGIC) { coff_errorf("unexpected optional header magic %#x", opt_header_magic); goto exit; } if (opt_header_magic == PE_PE32_MAGIC && coff_header->optional_header_size < sizeof(PE_OptionalHeader32)) { coff_errorf("unexpected optional header size in COFF header %m, expected at least %m", coff_header->optional_header_size, sizeof(PE_OptionalHeader32)); goto exit; } if (opt_header_magic == PE_PE32PLUS_MAGIC && coff_header->optional_header_size < sizeof(PE_OptionalHeader32Plus)) { coff_errorf("unexpected optional header size %m, expected at least %m", coff_header->optional_header_size, sizeof(PE_OptionalHeader32Plus)); goto exit; } U64 sections_off = coff_header_off + sizeof(*coff_header) + coff_header->optional_header_size; COFF_SectionHeader *sections = str8_deserial_get_raw_ptr(raw_data, sections_off, sizeof(*sections)*coff_header->section_count); if (!sections) { coff_errorf("not enough bytes to read COFF section headers"); goto exit; } U64 string_table_off = coff_header->symbol_table_foff + sizeof(COFF_Symbol16) * coff_header->symbol_count; COFF_Symbol32Array symbols = coff_symbol_array_from_data_16(scratch.arena, raw_data, coff_header->symbol_table_foff, coff_header->symbol_count); U8 *raw_opt_header = push_array(scratch.arena, U8, coff_header->optional_header_size); str8_deserial_read_array(raw_data, opt_header_off, raw_opt_header, coff_header->optional_header_size); if (opts & Coffdump_Option_Headers) { coff_format_header(arena, out, indent, coff_header); coff_newline(); } U64 image_base = 0; U64 dir_count = 0; PE_DataDirectory *dirs = 0; if (opt_header_magic == PE_PE32_MAGIC) { PE_OptionalHeader32 *opt_header = (PE_OptionalHeader32 *)raw_opt_header; image_base = opt_header->image_base; dir_count = opt_header->data_dir_count; dirs = str8_deserial_get_raw_ptr(raw_data, opt_header_off+sizeof(*opt_header), sizeof(*dirs) * opt_header->data_dir_count); if (!dirs) { coff_errorf("unable to read data directories"); goto exit; } if (opts & Coffdump_Option_Headers) { pe_format_optional_header32(arena, out, indent, opt_header, dirs); } } else if (opt_header_magic == PE_PE32PLUS_MAGIC) { PE_OptionalHeader32Plus *opt_header = (PE_OptionalHeader32Plus *)raw_opt_header; image_base = opt_header->image_base; dir_count = opt_header->data_dir_count; dirs = str8_deserial_get_raw_ptr(raw_data, opt_header_off+sizeof(*opt_header), sizeof(*dirs) * opt_header->data_dir_count); if (!dirs) { coff_errorf("unable to read data directories"); goto exit; } if (opts & Coffdump_Option_Headers) { pe_format_optional_header32plus(arena, out, indent, opt_header, dirs); } } // map data directory RVA to file offsets Rng1U64 *dirs_file_ranges = push_array(scratch.arena, Rng1U64, dir_count); Rng1U64 *dirs_virt_ranges = push_array(scratch.arena, Rng1U64, dir_count); for (U64 i = 0; i < dir_count; ++i) { PE_DataDirectory dir = dirs[i]; U64 file_off = coff_foff_from_voff(sections, coff_header->section_count, dir.virt_off); dirs_file_ranges[i] = r1u64(file_off, file_off+dir.virt_size); dirs_virt_ranges[i] = r1u64(dir.virt_off, dir.virt_off+dir.virt_size); } if (opts & Coffdump_Option_Sections) { coff_format_section_table(arena, out, indent, raw_data, string_table_off, symbols, coff_header->section_count, sections); } if (opts & Coffdump_Option_Relocs) { coff_format_relocs(arena, out, indent, raw_data, string_table_off, coff_header->machine, coff_header->section_count, sections, symbols); } if (opts & Coffdump_Option_Symbols) { coff_format_symbol_table(arena, out, indent, raw_data, 0, string_table_off, symbols); } if (opts & Coffdump_Option_Exports) { PE_ParsedExportTable exptab = pe_exports_from_data(arena, coff_header->section_count, sections, raw_data, dirs_file_ranges[PE_DataDirectoryIndex_EXPORT], dirs_virt_ranges[PE_DataDirectoryIndex_EXPORT]); } if (opts & Coffdump_Option_Imports) { B32 is_pe32 = opt_header_magic == PE_PE32_MAGIC; PE_ParsedStaticImportTable static_imptab = pe_static_imports_from_data(arena, is_pe32, coff_header->section_count, sections, raw_data, dirs_file_ranges[PE_DataDirectoryIndex_IMPORT]); PE_ParsedDelayImportTable delay_imptab = pe_delay_imports_from_data(arena, is_pe32, coff_header->section_count, sections, raw_data, dirs_file_ranges[PE_DataDirectoryIndex_DELAY_IMPORT]); pe_format_static_import_table(arena, out, indent, image_base, static_imptab); pe_format_delay_import_table(arena, out, indent, image_base, delay_imptab); } if (opts & Coffdump_Option_Resources) { String8 raw_dir = str8_substr(raw_data, dirs_file_ranges[PE_DataDirectoryIndex_RESOURCES]); PE_ResourceDir *dir_root = pe_resource_table_from_directory_data(scratch.arena, raw_dir); pe_format_resources(arena, out, indent, dir_root); } if (opts & Coffdump_Option_Exceptions) { pe_format_exceptions(arena, out, indent, coff_header->machine, coff_header->section_count, sections, raw_data, dirs_file_ranges[PE_DataDirectoryIndex_EXCEPTIONS]); } if (opts & Coffdump_Option_Relocs) { pe_format_base_relocs(arena, out, indent, coff_header->machine, image_base, coff_header->section_count, sections, raw_data, dirs_file_ranges[PE_DataDirectoryIndex_BASE_RELOC]); } if (opts & Coffdump_Option_Debug) { if (PE_DataDirectoryIndex_DEBUG < dir_count) { String8 raw_dir = str8_substr(raw_data, dirs_file_ranges[PE_DataDirectoryIndex_DEBUG]); pe_format_debug_directory(arena, out, indent, raw_data, raw_dir); } } if (opts & Coffdump_Option_Tls) { if (dim_1u64(dirs_file_ranges[PE_DataDirectoryIndex_TLS])) { PE_ParsedTLS tls = pe_tls_from_data(scratch.arena, coff_header->machine, image_base, coff_header->section_count, sections, raw_data, dirs_file_ranges[PE_DataDirectoryIndex_TLS]); pe_format_tls(arena, out, indent, tls); } } if (opts & Coffdump_Option_LoadConfig) { String8 raw_lc = str8_substr(raw_data, dirs_file_ranges[PE_DataDirectoryIndex_LOAD_CONFIG]); if (raw_lc.size) { switch (coff_header->machine) { case COFF_MachineType_UNKNOWN: break; case COFF_MachineType_X86: { PE_LoadConfig32 *lc = str8_deserial_get_raw_ptr(raw_lc, 0, sizeof(*lc)); if (lc) { pe_format_load_config32(arena, out, indent, lc); } else { coff_errorf("not enough bytes to parse 32bit load config"); } } break; case COFF_MachineType_X64: { PE_LoadConfig64 *lc = str8_deserial_get_raw_ptr(raw_lc, 0, sizeof(*lc)); if (lc) { pe_format_load_config64(arena, out, indent, lc); } else { coff_errorf("not enough bytes to parse 64bit load config"); } } break; default: NotImplemented; } } } exit:; scratch_end(scratch); } //////////////////////////////// internal void format_preamble(Arena *arena, String8List *out, String8 indent, String8 input_path, String8 raw_data) { Temp scratch = scratch_begin(&arena, 1); char *input_type_string = "???"; if (coff_is_archive(raw_data)) { input_type_string = "Archive"; } else if (coff_is_thin_archive(raw_data)) { input_type_string = "Thin Archive"; } else if (coff_is_big_obj(raw_data)) { input_type_string = "Big Obj"; } else if (coff_is_obj(raw_data)) { input_type_string = "Obj"; } else if (is_pe(raw_data)) { input_type_string = "COFF/PE"; } DateTime universal_dt = os_now_universal_time(); DateTime local_dt = os_local_time_from_universal(&universal_dt); String8 time = push_date_time_string(scratch.arena, &local_dt); coff_printf("# Input"); coff_indent(); coff_printf("Path: %S", input_path); coff_printf("Type: %s", input_type_string); coff_printf("Time: %S", time); coff_unindent(); coff_newline(); scratch_end(scratch); } internal void entry_point(CmdLine *cmdline) { Temp scratch = scratch_begin(0,0); // parse options Coffdump_Option opts = 0; { for (CmdLineOpt *cmd = cmdline->options.first; cmd != 0; cmd = cmd->next) { Coffdump_Option opt = 0; for (U64 opt_idx = 0; opt_idx < ArrayCount(g_coffdump_option_map); ++opt_idx) { String8 opt_name = str8_cstring(g_coffdump_option_map[opt_idx].name); if (str8_match(cmd->string, opt_name, StringMatchFlag_CaseInsensitive)) { opt = g_coffdump_option_map[opt_idx].opt; break; } else if (str8_match(cmd->string, str8_lit("all"), StringMatchFlag_CaseInsensitive)) { opt = ~0ull & ~(Coffdump_Option_Help|Coffdump_Option_Version); break; } } if (opt == 0) { fprintf(stderr, "Unknown argument: \"%.*s\"\n", str8_varg(cmd->string)); os_abort(1); } opts |= opt; } } // print help if (opts & Coffdump_Option_Help) { for (U64 opt_idx = 0; opt_idx < ArrayCount(g_coffdump_option_map); ++opt_idx) { fprintf(stdout, "-%s %s\n", g_coffdump_option_map[opt_idx].name, g_coffdump_option_map[opt_idx].help); } os_abort(0); } // print version if (opts & Coffdump_Option_Version) { fprintf(stdout, BUILD_TITLE_STRING_LITERAL "\n"); fprintf(stdout, "\tcoffdump \n"); os_abort(0); } // input check if (cmdline->inputs.node_count == 0) { fprintf(stderr, "No input file specified\n"); os_abort(1); } else if (cmdline->inputs.node_count > 1) { fprintf(stderr, "Too many inputs specified, expected one\n"); os_abort(1); } // read input String8 file_path = str8_list_first(&cmdline->inputs); String8 raw_data = os_data_from_file_path(scratch.arena, file_path); // is read ok? if (raw_data.size == 0) { fprintf(stderr, "Unable to read input file \"%.*s\"\n", str8_varg(file_path)); os_abort(1); } // make indent String8 indent; { U64 indent_buffer_size = COFF_INDENT_WIDTH * COFF_INDENT_MAX; U8 *indent_buffer = push_array(scratch.arena, U8, indent_buffer_size); MemorySet(indent_buffer, ' ', indent_buffer_size); indent = str8(indent_buffer, 0); } // format input String8List out = {0}; { format_preamble(scratch.arena, &out, indent, file_path, raw_data); if (coff_is_archive(raw_data) || coff_is_thin_archive(raw_data)) { coff_format_archive(scratch.arena, &out, indent, raw_data, opts); } else if (coff_is_big_obj(raw_data)) { coff_format_big_obj(scratch.arena, &out, indent, raw_data, opts); } else if (coff_is_obj(raw_data)) { coff_format_obj(scratch.arena, &out, indent, raw_data, opts); } else if (is_pe(raw_data)) { pe_format(scratch.arena, &out, indent, raw_data, opts); } else if (pe_is_res(raw_data)) { //tool_out_coff_res(stdout, file_data); } } // print formatted string String8 out_string = str8_list_join(scratch.arena, &out, &(StringJoin){ .sep = str8_lit("\n"),}); fprintf(stdout, "%.*s", str8_varg(out_string)); scratch_end(scratch); }