Files
raddebugger/src/dumpers/coffdump.c
T
2024-12-10 14:07:51 -08:00

2532 lines
105 KiB
C

// 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 <options> <inputs>\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);
}