raddbg format stuff -> raddbgi; partial naming/organization over raddbgi_cons

This commit is contained in:
Ryan Fleury
2024-02-12 14:29:24 -08:00
parent cc26a22e40
commit 4b68967fbe
47 changed files with 2589 additions and 2471 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,102 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ DWARF Stringize Functions
static char dwarf_spaces[] = " ";
static void
dwarf_stringize_info(Arena *arena, String8List *out, DWARF_InfoUnit *unit, U32 indent){
String8 unit_type_string = dwarf_string_from_unit_type((DWARF_UnitType)unit->unit_type);
str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off);
str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off);
str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off);
str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces,
unit->offset_size);
str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version);
str8_list_pushf(arena, out, "%.*sunit_type=%.*s\n", indent, dwarf_spaces,
str8_varg(unit_type_string));
str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces,
unit->address_size);
str8_list_pushf(arena, out, "%.*sabbrev_off=0x%llx\n", indent, dwarf_spaces,
unit->abbrev_off);
switch (unit->unit_type){
case DWARF_UnitType_skeleton: case DWARF_UnitType_split_compile:
{
str8_list_pushf(arena, out, "%.*sdwo_id=%llu\n", indent, dwarf_spaces, unit->dwo_id);
}break;
case DWARF_UnitType_type: case DWARF_UnitType_split_type:
{
str8_list_pushf(arena, out, "%.*stype_signature=%llu\n", indent, dwarf_spaces,
unit->type_signature);
str8_list_pushf(arena, out, "%.*stype_offset=%llu\n", indent, dwarf_spaces,
unit->type_offset);
}break;
}
}
static void
dwarf_stringize_pubnames(Arena *arena, String8List *out, DWARF_PubNamesUnit *unit,
U32 indent){
str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off);
str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off);
str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off);
str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, unit->offset_size);
str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version);
str8_list_pushf(arena, out, "%.*sinfo_off=0x%llx\n", indent, dwarf_spaces, unit->info_off);
str8_list_pushf(arena, out, "%.*sinfo_length=0x%llx\n", indent, dwarf_spaces,
unit->info_length);
}
static void
dwarf_stringize_names(Arena *arena, String8List *out, DWARF_NamesUnit *unit, U32 indent){
str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off);
str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off);
str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off);
str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version);
str8_list_pushf(arena, out, "%.*scomp_unit_count=%u\n", indent, dwarf_spaces,
unit->comp_unit_count);
str8_list_pushf(arena, out, "%.*slocal_type_unit_count=%u\n", indent, dwarf_spaces,
unit->local_type_unit_count);
str8_list_pushf(arena, out, "%.*sforeign_type_unit_count=%u\n", indent, dwarf_spaces,
unit->foreign_type_unit_count);
str8_list_pushf(arena, out, "%.*sbucket_count=%u\n", indent, dwarf_spaces,
unit->bucket_count);
str8_list_pushf(arena, out, "%.*sname_count=%u\n", indent, dwarf_spaces, unit->name_count);
str8_list_pushf(arena, out, "%.*sabbrev_table_size=%u\n", indent, dwarf_spaces,
unit->abbrev_table_size);
str8_list_pushf(arena, out, "%.*saugmentation_string=%.*s\n", indent, dwarf_spaces,
str8_varg(unit->augmentation_string));
}
static void
dwarf_stringize_aranges(Arena *arena, String8List *out, DWARF_ArangesUnit *unit, U32 indent){
str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off);
str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off);
str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off);
str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version);
str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces,
unit->address_size);
str8_list_pushf(arena, out, "%.*ssegment_selector_size=%u\n", indent, dwarf_spaces,
unit->segment_selector_size);
str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, unit->offset_size);
str8_list_pushf(arena, out, "%.*sinfo_off=0x%llx\n", indent, dwarf_spaces, unit->info_off);
}
static void
dwarf_stringize_addr(Arena *arena, String8List *out, DWARF_AddrUnit *unit, U32 indent){
str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off);
str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off);
str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off);
str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces,
unit->offset_size);
str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->dwarf_version);
str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces,
unit->address_size);
str8_list_pushf(arena, out, "%.*ssegment_selector_size=%u\n", indent, dwarf_spaces,
unit->segment_selector_size);
}
@@ -0,0 +1,28 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_DWARF_STRINGIZE_H
#define RADDBG_DWARF_STRINGIZE_H
////////////////////////////////
//~ DWARF Stringize Functions
static void
dwarf_stringize_info(Arena *arena, String8List *out, DWARF_InfoUnit *unit, U32 indent);
static void
dwarf_stringize_pubnames(Arena *arena, String8List *out, DWARF_PubNamesUnit *unit,
U32 indent);
static void
dwarf_stringize_names(Arena *arena, String8List *out, DWARF_NamesUnit *unit, U32 indent);
static void
dwarf_stringize_aranges(Arena *arena, String8List *out, DWARF_ArangesUnit *unit, U32 indent);
static void
dwarf_stringize_addr(Arena *arena, String8List *out, DWARF_AddrUnit *unit, U32 indent);
#endif //RADDBG_DWARF_STRINGIZE_H
+555
View File
@@ -0,0 +1,555 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ ELF Parser Functions
static ELF_Parsed*
elf_parsed_from_data(Arena *arena, String8 elf_data){
//- test magic number
B32 has_good_magic_number = 0;
if (elf_data.size >= sizeof(ELF_NIDENT) &&
MemoryMatch(elf_data.str, elf_magic, sizeof(elf_magic))){
has_good_magic_number = 1;
}
//- determine elf class
U8 elf_class = ELF_Class_NONE;
if (has_good_magic_number){
elf_class = elf_data.str[ELF_Identification_CLASS];
}
//- extract header information
B32 decoded_header = 0;
U8 e_data_encoding = ELF_DataEncoding_NONE;
U16 e_machine = ELF_Machine_NONE;
U64 e_entry = 0;
U64 e_shoff = 0;
U16 e_shentsize = 0;
U16 e_shnum = 0;
U64 e_phoff = 0;
U16 e_phentsize = 0;
U16 e_phnum = 0;
U16 e_shstrndx = 0;
switch (elf_class){
case ELF_Class_NONE: /* not good */ break;
case ELF_Class_32:
{
if (elf_data.size >= sizeof(ELF_Ehdr32)){
ELF_Ehdr32 *hdr = (ELF_Ehdr32*)elf_data.str;
decoded_header = 1;
e_data_encoding = hdr->e_ident[ELF_Identification_DATA];
e_machine = hdr->e_machine;
e_entry = hdr->e_entry;
e_phoff = hdr->e_phoff;
e_shoff = hdr->e_shoff;
e_phentsize = hdr->e_phentsize;
e_phnum = hdr->e_phnum;
e_shentsize = hdr->e_shentsize;
e_shnum = hdr->e_shnum;
e_shstrndx = hdr->e_shstrndx;
}
}break;
case ELF_Class_64:
{
if (elf_data.size >= sizeof(ELF_Ehdr64)){
ELF_Ehdr64 *hdr = (ELF_Ehdr64*)elf_data.str;
decoded_header = 1;
e_data_encoding = hdr->e_ident[ELF_Identification_DATA];
e_machine = hdr->e_machine;
e_entry = hdr->e_entry;
e_phoff = hdr->e_phoff;
e_shoff = hdr->e_shoff;
e_phentsize = hdr->e_phentsize;
e_phnum = hdr->e_phnum;
e_shentsize = hdr->e_shentsize;
e_shnum = hdr->e_shnum;
e_shstrndx = hdr->e_shstrndx;
}
}break;
}
//- validate & translate header values
B32 header_is_good = 0;
Architecture arch = Architecture_Null;
if (decoded_header){
header_is_good = 1;
// only supporting little-endian versions right now
if (header_is_good){
if (e_data_encoding != ELF_DataEncoding_2LSB){
header_is_good = 0;
}
}
// make sure this is a supported machine type
if (header_is_good){
switch (e_machine){
default: header_is_good = 0;
case ELF_Machine_386: arch = Architecture_x86; break;
case ELF_Machine_X86_64: arch = Architecture_x64; break;
}
}
// make sure section & segment sizes are correct
if (header_is_good){
switch (elf_class){
case ELF_Class_32:
{
if (e_shentsize != sizeof(ELF_Shdr32) ||
e_phentsize != sizeof(ELF_Phdr32)){
header_is_good = 0;
}
}break;
case ELF_Class_64:
{
if (e_shentsize != sizeof(ELF_Shdr64) ||
e_phentsize != sizeof(ELF_Phdr64)){
header_is_good = 0;
}
}break;
}
}
}
//- extract extra information from the special first section
U64 section_count_raw = e_shnum;
U32 section_header_string_table_index = e_shstrndx;
if (header_is_good){
if (e_shoff <= elf_data.size && e_shentsize <= elf_data.size &&
e_shoff + e_shentsize <= elf_data.size){
U64 size = 0;
U32 link = 0;
switch (elf_class){
case ELF_Class_32:
{
ELF_Shdr32 *shdr = (ELF_Shdr32*)(elf_data.str + e_shoff);
size = shdr->sh_size;
link = shdr->sh_link;
}break;
case ELF_Class_64:
{
ELF_Shdr64 *shdr = (ELF_Shdr64*)(elf_data.str + e_shoff);
size = shdr->sh_size;
link = shdr->sh_link;
}break;
}
// extended section count
if (size != 0){
section_count_raw = size;
}
// extended section header string table index
if (link != 0){
section_header_string_table_index = link;
}
}
}
//- clamp section & program arrays to size
U64 section_foff = 0;
U64 section_size = 0;
U64 section_count = 0;
U64 segment_foff = 0;
U64 segment_size = 0;
U64 segment_count = 0;
if (header_is_good){
if (e_shentsize > 0){
U64 section_opl_raw = e_shoff + e_shentsize*section_count_raw;
U64 section_opl = ClampTop(section_opl_raw, elf_data.size);
if (section_opl > e_shoff){
section_foff = e_shoff;
section_size = e_shentsize;
section_count = (section_opl - e_shoff)/e_shentsize;
}
}
if (e_phentsize > 0){
U64 segment_opl_raw = e_phoff + e_phentsize*e_phnum;
U64 segment_opl = ClampTop(segment_opl_raw, elf_data.size);
if (segment_opl > e_phoff){
segment_foff = e_phoff;
segment_size = e_phentsize;
segment_count = (segment_opl - e_phoff)/e_phentsize;
}
}
}
//- determine the vbase for this file
U64 vbase = 0;
if (header_is_good){
// find the first LOAD segment
U64 load_segment_off = 0;
{
U64 segment_cursor = segment_foff;
U64 segment_opl = segment_foff + segment_size*segment_count;
for (;segment_cursor < segment_opl; segment_cursor += segment_size){
U32 p_type = *(U32*)(elf_data.str + segment_cursor);
if (p_type == ELF_SegmentType_LOAD){
load_segment_off = segment_cursor;
break;
}
}
}
// use the segment's p_vaddr to determine vbase
if (load_segment_off != 0){
switch (elf_class){
case ELF_Class_32:
{
ELF_Phdr32 *phdr = (ELF_Phdr32*)(elf_data.str + load_segment_off);
vbase = phdr->p_vaddr;
}break;
case ELF_Class_64:
{
ELF_Phdr64 *phdr = (ELF_Phdr64*)(elf_data.str + load_segment_off);
vbase = phdr->p_vaddr;
}break;
}
}
}
//- locate the section header string table
U64 section_name_table_foff = 0;
U64 section_name_table_opl = 0;
if (header_is_good){
if (section_header_string_table_index < section_count){
U64 sec_foff = section_foff + section_header_string_table_index*section_size;
switch (elf_class){
case ELF_Class_32:
{
ELF_Shdr32 *shdr = (ELF_Shdr32*)(elf_data.str + sec_foff);
section_name_table_foff = shdr->sh_offset;
section_name_table_opl = shdr->sh_offset + shdr->sh_size;
}break;
case ELF_Class_64:
{
ELF_Shdr64 *shdr = (ELF_Shdr64*)(elf_data.str + sec_foff);
section_name_table_foff = shdr->sh_offset;
section_name_table_opl = shdr->sh_offset + shdr->sh_size;
}break;
}
}
}
//- format sections data
ELF_Shdr64 *sections = 0;
if (header_is_good && section_count > 0){
switch (elf_class){
case ELF_Class_32:
{
sections = push_array(arena, ELF_Shdr64, section_count);
{
ELF_Shdr32 *shdr32 = (ELF_Shdr32*)(elf_data.str + section_foff);
ELF_Shdr64 *shdr64 = sections;
for (U64 i = 0; i < section_count; i += 1, shdr32 += 1, shdr64 += 1){
shdr64->sh_name = shdr32->sh_name;
shdr64->sh_type = shdr32->sh_type;
shdr64->sh_flags = shdr32->sh_flags;
shdr64->sh_addr = shdr32->sh_addr;
shdr64->sh_offset = shdr32->sh_offset;
shdr64->sh_size = shdr32->sh_size;
shdr64->sh_link = shdr32->sh_link;
shdr64->sh_info = shdr32->sh_info;
shdr64->sh_addralign = shdr32->sh_addralign;
shdr64->sh_entsize = shdr32->sh_entsize;
}
}
}break;
case ELF_Class_64:
{
sections = (ELF_Shdr64*)(elf_data.str + section_foff);
}break;
}
}
//- extract section names
String8 *section_names = 0;
if (sections != 0 && section_count > 0){
U8 *string_table_opl = elf_data.str + section_name_table_opl;
section_names = push_array(arena, String8, section_count);
String8 *sec_name = section_names;
ELF_Shdr64 *sec = sections;
for (U64 i = 0;
i < section_count;
i += 1, sec += 1, sec_name += 1){
U64 name_foff = section_name_table_foff + sec->sh_name;
if (section_name_table_foff <= name_foff && name_foff < section_name_table_opl){
U8 *base = elf_data.str + name_foff;
U8 *opl = base;
for (;opl < string_table_opl && *opl != 0; opl += 1);
sec_name->str = base;
sec_name->size = (U64)(opl - base);
}
}
}
//- format segments data
ELF_Phdr64 *segments = 0;
if (header_is_good && segment_count > 0){
switch (elf_class){
case ELF_Class_32:
{
segments = push_array(arena, ELF_Phdr64, segment_count);
{
ELF_Phdr32 *phdr32 = (ELF_Phdr32*)(elf_data.str + segment_foff);
ELF_Phdr64 *phdr64 = segments;
for (U64 i = 0; i < segment_count; i += 1, phdr32 += 1, phdr64 += 1){
phdr64->p_type = phdr32->p_type;
phdr64->p_flags = phdr32->p_flags;
phdr64->p_offset = phdr32->p_offset;
phdr64->p_vaddr = phdr32->p_vaddr;
phdr64->p_paddr = phdr32->p_paddr;
phdr64->p_filesz = phdr32->p_filesz;
phdr64->p_memsz = phdr32->p_memsz;
phdr64->p_align = phdr32->p_align;
}
}
}break;
case ELF_Class_64:
{
segments = (ELF_Phdr64*)(elf_data.str + segment_foff);
}break;
}
}
//- find special sections
U64 strtab_idx = 0;
U64 symtab_idx = 0;
U64 dynsym_idx = 0;
if (section_names != 0){
for (U64 i = 0; i < section_count; i += 1){
String8 name = section_names[i];
if (str8_match(name, str8_lit(".strtab"), 0)){
strtab_idx = i;
}
else if (str8_match(name, str8_lit(".symtab"), 0)){
symtab_idx = i;
}
else if (str8_match(name, str8_lit(".dynsym"), 0)){
dynsym_idx = i;
}
}
}
//- fill result
ELF_Parsed *result = 0;
if (header_is_good){
result = push_array(arena, ELF_Parsed, 1);
result->data = elf_data;
result->elf_class = elf_class;
result->arch = arch;
result->sections = sections;
result->section_names = section_names;
result->section_foff = section_foff;
result->section_count = section_count;
result->segments = segments;
result->segment_foff = segment_foff;
result->segment_count = segment_count;
result->vbase = vbase;
result->entry_vaddr = e_entry;
result->section_name_table_foff = section_name_table_foff;
result->section_name_table_opl = section_name_table_opl;
result->strtab_idx = strtab_idx;
result->symtab_idx = symtab_idx;
result->dynsym_idx = dynsym_idx;
}
return(result);
}
static ELF_SectionArray
elf_section_array_from_elf(ELF_Parsed *elf){
ELF_SectionArray result = {0};
if (elf != 0){
result.sections = elf->sections;
result.count = elf->section_count;
}
return(result);
}
static String8Array
elf_section_name_array_from_elf(ELF_Parsed *elf){
String8Array result = {0};
if (elf != 0){
result.strings = elf->section_names;
result.count = elf->section_count;
}
return(result);
}
static ELF_SegmentArray
elf_segment_array_from_elf(ELF_Parsed *elf){
ELF_SegmentArray result = {0};
if (elf != 0){
result.segments = elf->segments;
result.count = elf->segment_count;
}
return(result);
}
static String8
elf_section_name_from_name_offset(ELF_Parsed *elf, U64 offset){
String8 result = {0};
if (elf != 0){
if (offset > 0){
U64 foff = elf->section_name_table_foff + offset;
if (elf->section_name_table_foff <= foff && foff < elf->section_name_table_opl){
U8 *base = elf->data.str + foff;
U8 *section_opl = elf->data.str + elf->section_name_table_opl;
U8 *opl = base;
for (;opl < section_opl && *opl != 0; opl += 1);
result.str = base;
result.size = opl - base;
}
}
}
return(result);
}
static String8
elf_section_name_from_idx(ELF_Parsed *elf, U32 idx){
String8 result = {0};
if (elf != 0){
if (idx < elf->section_count){
result = elf->section_names[idx];
}
}
return(result);
}
static U32
elf_section_idx_from_name(ELF_Parsed *elf, String8 name){
U32 result = 0;
if (elf != 0){
String8 *sec_name = elf->section_names;
U64 count = elf->section_count;
for (U64 i = 0; i < count; i += 1, sec_name += 1){
if (str8_match(*sec_name, name, 0)){
result = i;
break;
}
}
}
return(result);
}
static String8
elf_section_data_from_idx(ELF_Parsed *elf, U32 idx){
String8 result = {0};
if (elf != 0){
if (idx < elf->section_count){
ELF_Shdr64 *shdr = elf->sections + idx;
U64 off_raw = shdr->sh_offset;
U64 size = shdr->sh_size;
if (shdr->sh_flags & ELF_SectionType_NOBITS){
size = 0;
}
U64 opl_raw = off_raw + size;
U64 opl = ClampTop(opl_raw, elf->data.size);
U64 off = ClampTop(off_raw, opl);
result.str = elf->data.str + off;
result.size = opl - off;
}
}
return(result);
}
static ELF_SymArray
elf_sym_array_from_data(Arena *arena, ELF_Class elf_class, String8 data){
// converge to sym64 layout
ELF_Sym64 *symbols = 0;
U64 count = 0;
switch (elf_class){
case ELF_Class_32:
{
count = data.size/sizeof(ELF_Sym32);
symbols = push_array(arena, ELF_Sym64, count);
{
ELF_Sym32 *sym32 = (ELF_Sym32*)(data.str);
ELF_Sym64 *sym64 = symbols;
for (U64 i = 0; i < count; i += 1, sym32 += 1, sym64 += 1){
sym64->st_name = sym32->st_name;
sym64->st_value = sym32->st_value;
sym64->st_size = sym32->st_size;
sym64->st_info = sym32->st_info;
sym64->st_other = sym32->st_other;
sym64->st_shndx = sym32->st_shndx;
}
}
}break;
case ELF_Class_64:
{
count = data.size/sizeof(ELF_Sym64);
symbols = (ELF_Sym64*)(data.str);
}break;
}
// fill result
ELF_SymArray result = {0};
result.symbols = symbols;
result.count = count;
return(result);
}
// string functions
static String8
elf_string_from_section_type(ELF_SectionType section_type){
String8 result = str8_lit("INVALID_SECTION_TYPE");
switch (section_type){
#define X(N,C) case C: result = str8_lit(#N); break;
ELF_SectionTypeXList(X)
#undef X
}
return(result);
}
static String8
elf_string_from_symbol_binding(ELF_SymbolBinding binding){
String8 result = str8_lit("INVALID_SYMBOL_BINDING");
switch (binding){
#define X(N,C) case C: result = str8_lit(#N); break;
ELF_SymbolBindingXList(X)
#undef X
}
return(result);
}
static String8
elf_string_from_symbol_type(ELF_SymbolType type){
String8 result = str8_lit("INVALID_SYMBOL_TYPE");
switch (type){
#define X(N,C) case C: result = str8_lit(#N); break;
ELF_SymbolTypeXList(X)
#undef X
}
return(result);
}
static String8
elf_string_from_symbol_visibility(ELF_SymbolVisibility visibility){
String8 result = str8_lit("INVALID_SYMBOL_VISIBILITY");
switch (visibility){
#define X(N,C) case C: result = str8_lit(#N); break;
ELF_SymbolVisibilityXList(X)
#undef X
}
return(result);
}
+517
View File
@@ -0,0 +1,517 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_ELF_H
#define RADDBG_ELF_H
// https://refspecs.linuxfoundation.org/elf/elf.pdf
////////////////////////////////
//~ Elf Format Types
// elf header
#define ELF_NIDENT 16
typedef struct ELF_Ehdr32{
U8 e_ident[ELF_NIDENT];
U16 e_type;
U16 e_machine;
U32 e_version;
U32 e_entry;
U32 e_phoff;
U32 e_shoff;
U32 e_flags;
U16 e_ehsize;
U16 e_phentsize;
U16 e_phnum;
U16 e_shentsize;
U16 e_shnum;
U16 e_shstrndx;
} ELF_Ehdr32;
typedef struct ELF_Ehdr64{
U8 e_ident[ELF_NIDENT];
U16 e_type;
U16 e_machine;
U32 e_version;
U64 e_entry;
U64 e_phoff;
U64 e_shoff;
U32 e_flags;
U16 e_ehsize;
U16 e_phentsize;
U16 e_phnum;
U16 e_shentsize;
U16 e_shnum;
U16 e_shstrndx;
} ELF_Ehdr64;
typedef enum ELF_Type{
ELF_Type_NONE = 0,
ELF_Type_REL = 1,
ELF_Type_EXEC = 2,
ELF_Type_DYN = 3,
ELF_Type_CORE = 4,
ELF_Type_LOOS = 0xfe00,
ELF_Type_HIOS = 0xfeff,
ELF_Type_LOPROC = 0xff00,
ELF_Type_HIPROC = 0xffff,
} ELF_Type;
typedef enum ELF_Machine{
ELF_Machine_NONE = 0,
ELF_Machine_M32 = 1,
ELF_Machine_SPARC = 2,
ELF_Machine_386 = 3,
ELF_Machine_68K = 4,
ELF_Machine_88K = 5,
ELF_Machine_860 = 7,
ELF_Machine_MIPS = 8,
ELF_Machine_S370 = 9,
ELF_Machine_MIPS_RS3_LE = 10,
ELF_Machine_PARISC = 15,
ELF_Machine_VPP500 = 17,
ELF_Machine_SPARC32PLUS = 18,
ELF_Machine_960 = 19,
ELF_Machine_PPC = 20,
ELF_Machine_PPC64 = 21,
ELF_Machine_S390 = 22,
ELF_Machine_V800 = 36,
ELF_Machine_FR20 = 37,
ELF_Machine_RH32 = 38,
ELF_Machine_RCE = 39,
ELF_Machine_ARM = 40,
ELF_Machine_ALPHA = 41,
ELF_Machine_SH = 42,
ELF_Machine_SPARCV9 = 43,
ELF_Machine_TRICORE = 44,
ELF_Machine_ARC = 45,
ELF_Machine_H8_300 = 46,
ELF_Machine_H8_300H = 47,
ELF_Machine_H8S = 48,
ELF_Machine_H8_500 = 49,
ELF_Machine_IA_64 = 50,
ELF_Machine_MIPS_X = 51,
ELF_Machine_COLDFIRE = 52,
ELF_Machine_68HC12 = 53,
ELF_Machine_MMA = 54,
ELF_Machine_PCP = 55,
ELF_Machine_NCPU = 56,
ELF_Machine_NDR1 = 57,
ELF_Machine_STARCORE = 58,
ELF_Machine_ME16 = 59,
ELF_Machine_ST100 = 60,
ELF_Machine_TINYJ = 61,
ELF_Machine_X86_64 = 62,
ELF_Machine_PDSP = 63,
ELF_Machine_PDP10 = 64,
ELF_Machine_PDP11 = 65,
ELF_Machine_FX66 = 66,
ELF_Machine_ST9PLUS = 67,
ELF_Machine_ST7 = 68,
ELF_Machine_68HC16 = 69,
ELF_Machine_68HC11 = 70,
ELF_Machine_68HC08 = 71,
ELF_Machine_68HC05 = 72,
ELF_Machine_SVX = 73,
ELF_Machine_ST19 = 74,
ELF_Machine_VAX = 75,
ELF_Machine_CRIS = 76,
ELF_Machine_JAVELIN = 77,
ELF_Machine_FIREPATH = 78,
ELF_Machine_ZSP = 79,
ELF_Machine_MMIX = 80,
ELF_Machine_HUANY = 81,
ELF_Machine_PRISM = 82,
ELF_Machine_AVR = 83,
ELF_Machine_FR30 = 84,
ELF_Machine_D10V = 85,
ELF_Machine_D30V = 86,
ELF_Machine_V850 = 87,
ELF_Machine_M32R = 88,
ELF_Machine_MN10300 = 89,
ELF_Machine_MN10200 = 90,
ELF_Machine_PJ = 91,
ELF_Machine_OPENRISC = 92,
ELF_Machine_ARC_A5 = 93,
ELF_Machine_XTENSA = 94,
ELF_Machine_VIDEOCORE = 95,
ELF_Machine_TMM_GPP = 96,
ELF_Machine_NS32K = 97,
ELF_Machine_TPC = 98,
ELF_Machine_SNP1K = 99,
ELF_Machine_ST200 = 100,
} ELF_Machine;
typedef enum ELF_Version{
ELF_Version_NONE = 0,
ELF_Version_CURRENT = 1,
} ELF_Version;
typedef enum ELF_Identification{
ELF_Identification_MAG0 = 0,
ELF_Identification_MAG1 = 1,
ELF_Identification_MAG2 = 2,
ELF_Identification_MAG3 = 3,
ELF_Identification_CLASS = 4,
ELF_Identification_DATA = 5,
ELF_Identification_VERSION = 6,
ELF_Identification_OSABI = 7,
ELF_Identification_ABIVERSION = 8,
ELF_Identification_PAD = 9,
} ELF_Identification;
read_only global U8 elf_magic[] = {0x7F, 'E', 'L', 'F'};
typedef enum ELF_Class{
ELF_Class_NONE = 0,
ELF_Class_32 = 1,
ELF_Class_64 = 2,
} ELF_Class;
typedef enum ELF_DataEncoding{
ELF_DataEncoding_NONE = 0,
ELF_DataEncoding_2LSB = 1,
ELF_DataEncoding_2MSB = 2,
} ELF_DataEncoding;
typedef enum ELF_OsAbi{
ELF_OsAbi_NONE = 0,
ELF_OsAbi_HPUX = 1,
ELF_OsAbi_NETBSD = 2,
ELF_OsAbi_LINUX = 3,
ELF_OsAbi_SOLARIS = 6,
ELF_OsAbi_AIX = 7,
ELF_OsAbi_IRIX = 8,
ELF_OsAbi_FREEBSD = 9,
ELF_OsAbi_TRU64 = 10,
ELF_OsAbi_MODESTO = 11,
ELF_OsAbi_OPENBSD = 12,
ELF_OsAbi_OPENVMS = 13,
ELF_OsAbi_NSK = 14,
} ELF_OsAbi;
// sections
typedef enum ELF_ReservedSectionIndex{
ELF_ReservedSectionIndex_UNDEF = 0,
ELF_ReservedSectionIndex_LORESERVE = 0xFF00,
ELF_ReservedSectionIndex_LOPROC = 0xFF00,
ELF_ReservedSectionIndex_HIPROC = 0xFF1F,
ELF_ReservedSectionIndex_LOOS = 0xFF20,
ELF_ReservedSectionIndex_HIOS = 0xFF3F,
ELF_ReservedSectionIndex_ABS = 0xFFF1,
ELF_ReservedSectionIndex_COMMON = 0xFFF2,
ELF_ReservedSectionIndex_XINDEX = 0xFFFF,
ELF_ReservedSectionIndex_HIRESERVE = 0xFFFF,
} ELF_ReservedSectionIndex;
typedef struct ELF_Shdr32{
U32 sh_name;
U32 sh_type;
U32 sh_flags;
U32 sh_addr;
U32 sh_offset;
U32 sh_size;
U32 sh_link;
U32 sh_info;
U32 sh_addralign;
U32 sh_entsize;
} ELF_Shdr32;
typedef struct ELF_Shdr64{
U32 sh_name;
U32 sh_type;
U64 sh_flags;
U64 sh_addr;
U64 sh_offset;
U64 sh_size;
U32 sh_link;
U32 sh_info;
U64 sh_addralign;
U64 sh_entsize;
} ELF_Shdr64;
// X(name, code)
#define ELF_SectionTypeXList(X)\
X(NULL, 0)\
X(PROGBITS, 1)\
X(SYMTAB, 2)\
X(STRTAB, 3)\
X(RELA, 4)\
X(HASH, 5)\
X(DYNAMIC, 6)\
X(NOTE, 7)\
X(NOBITS, 8)\
X(REL, 9)\
X(SHLIB, 10)\
X(DYNSYM, 11)\
X(INIT_ARRAY, 14)\
X(FINI_ARRAY, 15)\
X(PREINIT_ARRAY, 16)\
X(GROUP, 17)\
X(SYMTAB_SHNDX, 18)\
X(LOOS, 0x60000000)\
X(HIOS, 0x6FFFFFFF)\
X(LOPROC, 0x70000000)\
X(HIPROC, 0x7FFFFFFF)\
X(LOUSER, 0x80000000)\
X(HIUSER, 0x8FFFFFFF)
typedef enum ELF_SectionType{
#define X(N,C) ELF_SectionType_##N = C,
ELF_SectionTypeXList(X)
#undef X
} ELF_SectionType;
typedef enum ELF_SectionAttributeFlags{
ELF_SectionAttributeFlag_WRITE = 0x001,
ELF_SectionAttributeFlag_ALLOC = 0x002,
ELF_SectionAttributeFlag_EXECINSTR = 0x004,
ELF_SectionAttributeFlag_MERGE = 0x010,
ELF_SectionAttributeFlag_STRINGS = 0x020,
ELF_SectionAttributeFlag_INFO_LINK = 0x040,
ELF_SectionAttributeFlag_LINK_ORDER = 0x080,
ELF_SectionAttributeFlag_OS_NONCONFORMING = 0x100,
ELF_SectionAttributeFlag_GROUP = 0x200,
ELF_SectionAttributeFlag_TLS = 0x400,
ELF_SectionAttributeFlag_MASKOS = 0x0FF00000,
ELF_SectionAttributeFlag_MASKPROC = 0xF0000000,
} ELF_SectionAttributeFlags;
typedef enum ELF_SectionGroupFlags{
ELF_SectionGroupFlag_COMDAT = 0x1,
ELF_SectionGroupFlag_MASKOS = 0x0FF00000,
ELF_SectionGroupFlag_MASKPROC = 0xF0000000,
} ELF_SectionGroupFlags;
typedef enum ELF_ReservedSymbolTableIndex{
ELF_ReservedSymbolTableIndex_UNDEF = 0,
} ELF_ReservedSymbolTableIndex;
// symbol table
typedef struct ELF_Sym32{
U32 st_name;
U32 st_value;
U32 st_size;
U8 st_info;
U8 st_other;
U16 st_shndx;
} ELF_Sym32;
typedef struct ELF_Sym64{
U32 st_name;
U8 st_info;
U8 st_other;
U16 st_shndx;
U64 st_value;
U64 st_size;
} ELF_Sym64;
#define ELF_SymBindingFromInfo(x) (ELF_SymbolBinding)((x)>>4)
#define ELF_SymTypeFromInfo(x) (ELF_SymbolType)((x)&0xF)
#define ELF_SymInfoFromBindingType(b,t) ((((b)<<4)&0xF)|((t)&0xF))
#define ELF_SymVisibilityFromOther(x) ((x)&0x3)
#define ELF_SymOtherFromVisibility(x) ((x)&0x3)
#define ELF_SymbolBindingXList(X)\
X(LOCAL, 0)\
X(GLOBAL, 1)\
X(WEAK, 2)\
X(LOOS, 10)\
X(HIOS, 12)\
X(LOPROC, 13)\
X(HIPROC, 15)\
typedef enum ELF_SymbolBinding{
#define X(N,C) ELF_SymbolBinding_##N = C,
ELF_SymbolBindingXList(X)
#undef X
} ELF_SymbolBinding;
#define ELF_SymbolTypeXList(X)\
X(NOTYPE, 0)\
X(OBJECT, 1)\
X(FUNC, 2)\
X(SECTION, 3)\
X(FILE, 4)\
X(COMMON, 5)\
X(TLS, 6)\
X(LOOS, 10)\
X(HIOS, 12)\
X(LOPROC, 13)\
X(HIPROC, 15)
typedef enum ELF_SymbolType{
#define X(N,C) ELF_SymbolType_##N = C,
ELF_SymbolTypeXList(X)
#undef X
} ELF_SymbolType;
#define ELF_SymbolVisibilityXList(X)\
X(DEFAULT, 0)\
X(INTERNAL, 1)\
X(HIDDEN, 2)\
X(PROTECTED, 3)
typedef enum ELF_SymbolVisibility{
#define X(N,C) ELF_SymbolVisibility_##N = C,
ELF_SymbolVisibilityXList(X)
#undef X
} ELF_SymbolVisibility;
// relocation
typedef struct ELF_Rel32{
U32 r_offset;
U32 r_info;
} ELF_Rel32;
typedef struct ELF_Rela32{
U32 r_offset;
U32 r_info;
S32 r_addend;
} ELF_Rela32;
typedef struct ELF_Rel64{
U64 r_offset;
U64 r_info;
} ELF_Rel64;
typedef struct ELF_Rela64{
U64 r_offset;
U64 r_info;
S64 r_addend;
} ELF_Rela64;
#define ELF_RelSymIndexFromInfo32(x) ((x)>>8)
#define ELF_RelTypeFromInfo32(x) ((x)&0xF)
#define ELF_RelInfoFromSymIndexType32(n,t) (((n)<<8)|((t)&0xF))
#define ELF_RelSymIndexFromInfo64(x) ((x)>>32)
#define ELF_RelTypeFromInfo64(x) ((x)&0xFFFFFFFFL)
#define ELF_RelInfoFromSymIndexType64(n,t) (((n)<<8)|((t)&0xFFFFFFFFL))
// program header
typedef struct ELF_Phdr32{
U32 p_type;
U32 p_offset;
U32 p_vaddr;
U32 p_paddr;
U32 p_filesz;
U32 p_memsz;
U32 p_flags;
U32 p_align;
} ELF_Phdr32;
typedef struct ELF_Phdr64{
U32 p_type;
U32 p_flags;
U64 p_offset;
U64 p_vaddr;
U64 p_paddr;
U64 p_filesz;
U64 p_memsz;
U64 p_align;
} ELF_Phdr64;
typedef enum ELF_SegmentType{
ELF_SegmentType_NULL = 0,
ELF_SegmentType_LOAD = 1,
ELF_SegmentType_DYNAMIC = 2,
ELF_SegmentType_INTERP = 3,
ELF_SegmentType_NOTE = 4,
ELF_SegmentType_SHLIB = 5,
ELF_SegmentType_PHDR = 6,
ELF_SegmentType_TLS = 7,
ELF_SegmentType_LOOS = 0x60000000,
ELF_SegmentType_HIOS = 0x6fffffff,
ELF_SegmentType_LOPROC = 0x70000000,
ELF_SegmentType_HIPROC = 0x7fffffff,
} ELF_SegmentType;
typedef enum ELF_SegmentFlags{
ELF_SegmentFlag_X = 0x1,
ELF_SegmentFlag_W = 0x2,
ELF_SegmentFlag_R = 0x4,
ELF_SegmentFlag_MASKOS = 0x0FF00000,
ELF_SegmentFlag_MASKPROC = 0xF0000000,
} ELF_SegmentFlags;
////////////////////////////////
//~ ELF Parser Types
// elf top level
typedef struct ELF_SectionArray{
ELF_Shdr64 *sections;
U64 count;
} ELF_SectionArray;
typedef struct ELF_SegmentArray{
ELF_Phdr64 *segments;
U64 count;
} ELF_SegmentArray;
typedef struct ELF_Parsed{
String8 data;
ELF_Class elf_class;
Architecture arch;
ELF_Shdr64 *sections;
String8 *section_names;
U64 section_foff;
U64 section_count;
ELF_Phdr64 *segments;
U64 segment_foff;
U64 segment_count;
U64 vbase;
U64 entry_vaddr;
U64 section_name_table_foff;
U64 section_name_table_opl;
U64 strtab_idx;
U64 symtab_idx;
U64 dynsym_idx;
} ELF_Parsed;
// elf symtab
typedef struct ELF_SymArray{
ELF_Sym64 *symbols;
U64 count;
} ELF_SymArray;
////////////////////////////////
//~ ELF Parser Functions
static ELF_Parsed* elf_parsed_from_data(Arena *arena, String8 elf_data);
static ELF_SectionArray elf_section_array_from_elf(ELF_Parsed *elf);
static String8Array elf_section_name_array_from_elf(ELF_Parsed *elf);
static ELF_SegmentArray elf_segment_array_from_elf(ELF_Parsed *elf);
static String8 elf_section_name_from_name_offset(ELF_Parsed *elf, U64 offset);
static String8 elf_section_name_from_idx(ELF_Parsed *elf, U32 idx);
static U32 elf_section_idx_from_name(ELF_Parsed *elf, String8 name);
static String8 elf_section_data_from_idx(ELF_Parsed *elf, U32 idx);
static ELF_SymArray elf_sym_array_from_data(Arena *arena, ELF_Class elf_class, String8 data);
// string functions
static String8 elf_string_from_section_type(ELF_SectionType section_type);
static String8 elf_string_from_symbol_binding(ELF_SymbolBinding binding);
static String8 elf_string_from_symbol_type(ELF_SymbolType type);
static String8 elf_string_from_symbol_visibility(ELF_SymbolVisibility visibility);
#endif //RADDBG_ELF_H
@@ -0,0 +1,904 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "base/base_inc.h"
#include "os/os_inc.h"
#include "raddbgi_format/raddbgi_format.h"
#include "raddbgi_cons/raddbgi_cons.h"
#include "raddbgi_elf.h"
#include "raddbgi_dwarf.h"
#include "raddbgi_dwarf_stringize.h"
#include "raddbgi_from_dwarf.h"
#include "base/base_inc.c"
#include "os/os_inc.c"
#include "raddbgi_format/raddbgi_format.c"
#include "raddbgi_cons/raddbgi_cons.c"
#include "raddbgi_elf.c"
#include "raddbgi_dwarf.c"
#include "raddbgi_dwarf_stringize.c"
// TODO(allen):
// [ ] need sample data for .debug_names
////////////////////////////////
//~ Program Parameters Parser
static DWARFCONV_Params*
dwarf_convert_params_from_cmd_line(Arena *arena, CmdLine *cmdline){
DWARFCONV_Params *result = push_array(arena, DWARFCONV_Params, 1);
result->unit_idx_max = ~0ull;
// get input pdb
{
String8 input_name = cmd_line_string(cmdline, str8_lit("elf"));
if (input_name.size == 0){
str8_list_push(arena, &result->errors,
str8_lit("missing required parameter '--elf:<elf_file>'"));
}
if (input_name.size > 0){
String8 input_data = os_data_from_file_path(arena, input_name);
if (input_data.size == 0){
str8_list_pushf(arena, &result->errors,
"could not load input file '%.*s'", str8_varg(input_name));
}
if (input_data.size != 0){
result->input_elf_name = input_name;
result->input_elf_data = input_data;
}
}
}
// get output name
{
result->output_name = cmd_line_string(cmdline, str8_lit("out"));
}
// error options
if (cmd_line_has_flag(cmdline, str8_lit("hide_errors"))){
String8List vals = cmd_line_strings(cmdline, str8_lit("hide_errors"));
// if no values - set all to hidden
if (vals.node_count == 0){
B8 *ptr = (B8*)&result->hide_errors;
B8 *opl = ptr + sizeof(result->hide_errors);
for (;ptr < opl; ptr += 1){
*ptr = 1;
}
}
// for each explicit value set the corresponding flag to hidden
for (String8Node *node = vals.first;
node != 0;
node = node->next){
if (str8_match(node->string, str8_lit("input"), 0)){
result->hide_errors.input = 1;
}
}
}
// unit idx selector
if (cmd_line_has_flag(cmdline, str8_lit("unit_idx"))){
String8List vals = cmd_line_strings(cmdline, str8_lit("unit_idx"));
// single value unit index
if (vals.node_count == 1){
U64 idx = u64_from_str8(vals.first->string, 10);
result->unit_idx_min = idx;
result->unit_idx_max = idx;
}
// range value unit index
else if (vals.node_count >= 2){
U64 idx_a = u64_from_str8(vals.first->string, 10);
U64 idx_b = u64_from_str8(vals.first->next->string, 10);
result->unit_idx_min = Min(idx_a, idx_b);
result->unit_idx_max = Max(idx_a, idx_b);
}
}
// dump options
if (cmd_line_has_flag(cmdline, str8_lit("dump"))){
result->dump = 1;
String8List vals = cmd_line_strings(cmdline, str8_lit("dump"));
if (vals.first == 0){
B8 *ptr = &result->dump__first;
for (; ptr < &result->dump__last; ptr += 1){
*ptr = 1;
}
}
else{
for (String8Node *node = vals.first;
node != 0;
node = node->next){
if (str8_match(node->string, str8_lit("header"), 0)){
result->dump_header = 1;
}
else if (str8_match(node->string, str8_lit("sections"), 0)){
result->dump_sections = 1;
}
else if (str8_match(node->string, str8_lit("segments"), 0)){
result->dump_segments = 1;
}
else if (str8_match(node->string, str8_lit("symtab"), 0)){
result->dump_symtab = 1;
}
else if (str8_match(node->string, str8_lit("dynsym"), 0)){
result->dump_dynsym = 1;
}
else if (str8_match(node->string, str8_lit("debug_sections"), 0)){
result->dump_debug_sections = 1;
}
else if (str8_match(node->string, str8_lit("debug_info"), 0)){
result->dump_debug_info = 1;
}
else if (str8_match(node->string, str8_lit("debug_abbrev"), 0)){
result->dump_debug_abbrev = 1;
}
else if (str8_match(node->string, str8_lit("debug_pubnames"), 0)){
result->dump_debug_pubnames = 1;
}
else if (str8_match(node->string, str8_lit("debug_pubtypes"), 0)){
result->dump_debug_pubtypes = 1;
}
else if (str8_match(node->string, str8_lit("debug_names"), 0)){
result->dump_debug_names = 1;
}
else if (str8_match(node->string, str8_lit("debug_aranges"), 0)){
result->dump_debug_aranges = 1;
}
else if (str8_match(node->string, str8_lit("debug_addr"), 0)){
result->dump_debug_addr = 1;
}
}
}
}
return(result);
}
////////////////////////////////
//~ Entry Point
static void
dump_symtab(Arena *arena, String8List *out, ELF_SymArray *symbols, String8 strtab,
U32 indent){
static char spaces[] = " ";
U8 *str_first = strtab.str;
U8 *str_opl = strtab.str + strtab.size;
ELF_Sym64 *symbol = symbols->symbols;
U64 count = symbols->count;
for (U64 i = 0; i < count; i += 1, symbol += 1){
U8 *name_first = str_first + symbol->st_name;
U8 *name_opl = name_first;
for (;name_opl < str_opl && *name_opl != 0;) name_opl += 1;
String8 name = str8_range(name_first, name_opl);
ELF_SymbolBinding binding = ELF_SymBindingFromInfo(symbol->st_info);
String8 binding_string = elf_string_from_symbol_binding(binding);
ELF_SymbolType type = ELF_SymTypeFromInfo(symbol->st_info);
String8 type_string = elf_string_from_symbol_type(type);
ELF_SymbolVisibility vis = ELF_SymVisibilityFromOther(symbol->st_other);
String8 vis_string = elf_string_from_symbol_visibility(vis);
str8_list_pushf(arena, out,
"%.*ssymbol[%5llu] %6.*s %7.*s %9.*s 0x%08llx size=%-5llu sec=%-5u "
"%.*s\n",
indent, spaces, i,
str8_varg(binding_string), str8_varg(type_string),
str8_varg(vis_string),
symbol->st_value, symbol->st_size,
symbol->st_shndx, str8_varg(name));
}
}
#if 0
static void
dump_entry_tree(Arena *arena, String8List *out,
DWARF_Parsed *dwarf, DWARF_InfoUnit *unit,
DWARF_InfoEntry *entry, U32 indent){
static char spaces[] = " ";
DWARF_AbbrevDecl *abbrev_decl = entry->abbrev_decl;
// tag
DWARF_Tag tag = abbrev_decl->tag;
String8 tag_string = dwarf_string_from_tag(tag);
str8_list_pushf(arena, out, "%.*sentry(@%llx) TAG %.*s\n",
indent, spaces, entry->info_offset, str8_varg(tag_string));
// attributes
U32 attrib_count = abbrev_decl->attrib_count;
DWARF_AbbrevAttribSpec *attrib_spec = abbrev_decl->attrib_specs;
DWARF_InfoAttribVal *attrib_val = entry->attrib_vals;
for (U32 i = 0; i < attrib_count; i += 1, attrib_spec += 1, attrib_val += 1){
// attribute name
DWARF_AttributeName name = attrib_spec->name;
String8 name_string = dwarf_string_from_attribute_name(name);
str8_list_pushf(arena, out, "%.*sATTR %.*s ", indent + 4, spaces, str8_varg(name_string));
// attribute value
switch (attrib_spec->form){
default:
{
String8 form_string = dwarf_string_from_attribute_form(attrib_spec->form);
str8_list_pushf(arena, out, "<form: %.*s> {%llu, 0x%p}\n",
str8_varg(form_string), attrib_val->val, attrib_val->dataptr);
}break;
case DWARF_AttributeForm_strp:
{
String8 str = {0};
String8 data = dwarf->debug_data[DWARF_SectionCode_Str];
U64 off = attrib_val->val;
if (off < data.size){
U8 *start = data.str + off;
U8 *opl = data.str + data.size;
U8 *ptr = start;
for (;ptr < opl && *ptr != 0;) ptr += 1;
str = str8_range(start, ptr);
}
str8_list_pushf(arena, out, "'%.*s'\n", str8_varg(str));
}break;
case DWARF_AttributeForm_sec_offset:
{
DWARF_AttributeClassFlags attr_classes1 = dwarf_attribute_class_from_name(name);
DWARF_AttributeClassFlags attr_classes2 = DWARF_AttributeClassFlag_sec_offset_classes;
DWARF_AttributeClassFlags attr_classes = attr_classes1&attr_classes2;
DWARF_SectionCode sec_code = DWARF_SectionCode_Null;
if (unit->dwarf_version == 5){
switch (attr_classes){
case DWARF_AttributeClassFlag_addrptr: sec_code = DWARF_SectionCode_Addr; break;
case DWARF_AttributeClassFlag_lineptr: sec_code = DWARF_SectionCode_Line; break;
case DWARF_AttributeClassFlag_loclist: sec_code = DWARF_SectionCode_LocLists; break;
case DWARF_AttributeClassFlag_loclistsptr: sec_code = DWARF_SectionCode_LocLists; break;
case DWARF_AttributeClassFlag_macptr: sec_code = DWARF_SectionCode_Macro; break;
case DWARF_AttributeClassFlag_rnglist: sec_code = DWARF_SectionCode_RngLists; break;
case DWARF_AttributeClassFlag_rnglistsptr: sec_code = DWARF_SectionCode_RngLists; break;
case DWARF_AttributeClassFlag_stroffsetsptr: sec_code = DWARF_SectionCode_StrOffsets; break;
}
}
else if (unit->dwarf_version == 4){
switch (attr_classes){
case DWARF_AttributeClassFlag_lineptr: sec_code = DWARF_SectionCode_Line; break;
case DWARF_AttributeClassFlag_loclist: sec_code = DWARF_SectionCode_Loc; break;
case DWARF_AttributeClassFlag_macptr: sec_code = DWARF_SectionCode_MacInfo; break;
case DWARF_AttributeClassFlag_rnglist: sec_code = DWARF_SectionCode_Ranges; break;
}
}
String8 sec_name = dwarf_name_from_debug_section(dwarf, sec_code);
str8_list_pushf(arena, out, "sec(%.*s) + %llu\n", str8_varg(sec_name), attrib_val->val);
}break;
case DWARF_AttributeForm_ref1:
case DWARF_AttributeForm_ref2:
case DWARF_AttributeForm_ref4:
case DWARF_AttributeForm_ref8:
case DWARF_AttributeForm_ref_udata:
{
str8_list_pushf(arena, out, "entry(@%llx)\n", attrib_val->val);
}break;
case DWARF_AttributeForm_addr:
{
str8_list_pushf(arena, out, "0x%llx\n", attrib_val->val);
}break;
case DWARF_AttributeForm_exprloc:
{
str8_list_pushf(arena, out, "expression\n");
// TODO(allen): dwarf expression dumping
}break;
case DWARF_AttributeForm_strx1:
case DWARF_AttributeForm_strx2:
case DWARF_AttributeForm_strx3:
case DWARF_AttributeForm_strx4:
{
String8 str = {0};
U32 idx = attrib_val->val;
U64 str_offsets_off = unit->str_offsets_base + idx*unit->offset_size;
String8 str_offsets = dwarf->debug_data[DWARF_SectionCode_StrOffsets];
if (str_offsets_off + unit->offset_size < str_offsets.size){
U64 off = 0;
MemoryCopy(&off, str_offsets.str + str_offsets_off, unit->offset_size);
String8 data = dwarf->debug_data[DWARF_SectionCode_Str];
if (off < data.size){
U8 *start = data.str + off;
U8 *opl = data.str + data.size;
U8 *ptr = start;
for (;ptr < opl && *ptr != 0;) ptr += 1;
str = str8_range(start, ptr);
}
}
str8_list_pushf(arena, out, "'%.*s'\n", str8_varg(str));
}break;
case DWARF_AttributeForm_addrx:
case DWARF_AttributeForm_addrx1:
case DWARF_AttributeForm_addrx2:
case DWARF_AttributeForm_addrx3:
case DWARF_AttributeForm_addrx4:
{
U64 address = 0;
U32 idx = attrib_val->val;
U64 address_off = unit->addr_base + idx*unit->address_size;
String8 data = dwarf->debug_data[DWARF_SectionCode_Addr];
if (address_off + unit->address_size < data.size){
MemoryCopy(&address, data.str + address_off, unit->address_size);
}
str8_list_pushf(arena, out, "0x%x\n", address);
}break;
case DWARF_AttributeForm_rnglistx:
{
U64 rnglist_off = unit->rnglists_base + attrib_val->val;
int x = 0;
}break;
case DWARF_AttributeForm_data1:
case DWARF_AttributeForm_data2:
case DWARF_AttributeForm_data4:
case DWARF_AttributeForm_data8:
case DWARF_AttributeForm_data16:
case DWARF_AttributeForm_udata:
case DWARF_AttributeForm_implicit_const:
case DWARF_AttributeForm_flag:
case DWARF_AttributeForm_flag_present:
{
str8_list_pushf(arena, out, "%llu\n", attrib_val->val);
}break;
case DWARF_AttributeForm_sdata:
{
str8_list_pushf(arena, out, "%lld\n", (S64)attrib_val->val);
}break;
case DWARF_AttributeForm_string:
{
str8_list_pushf(arena, out, "'%.*s'\n", (int)attrib_val->val, attrib_val->dataptr);
}break;
}
}
// dump children
for (DWARF_InfoEntry *child = entry->first_child;
child != 0;
child = child->next_sibling){
dump_entry_tree(arena, out, dwarf, unit, child, indent + 1);
}
}
#endif
int
main(int argc, char **argv){
local_persist TCTX main_thread_tctx = {0};
tctx_init_and_equip(&main_thread_tctx);
#if PROFILE_TELEMETRY
U64 tm_data_size = GB(1);
U8 *tm_data = os_reserve(tm_data_size);
os_commit(tm_data, tm_data_size);
tmLoadLibrary(TM_RELEASE);
tmSetMaxThreadCount(1024);
tmInitialize(tm_data_size, tm_data);
#endif
ThreadName("[main]");
Arena *arena = arena_alloc();
String8List args = os_string_list_from_argcv(arena, argc, argv);
CmdLine cmdline = cmd_line_from_string_list(arena, args);
ProfBeginCapture("raddbg_from_dwarf");
// parse arguments
DWARFCONV_Params *params = dwarf_convert_params_from_cmd_line(arena, &cmdline);
// show input errors
if (params->errors.node_count > 0 &&
!params->hide_errors.input){
for (String8Node *node = params->errors.first;
node != 0;
node = node->next){
fprintf(stdout, "error(input): %.*s\n", str8_varg(node->string));
}
}
// will we try to parse an input file?
B32 try_parse_input = (params->errors.node_count == 0);
// track parse success
B32 successful_parse = 1;
#define PARSE_CHECK_ERROR(p,fmt,...) do{ if ((p) == 0){ \
successful_parse = 0; \
fprintf(stdout, "error(parsing): " fmt "\n", __VA_ARGS__); \
} }while(0)
// parse elf
ELF_Parsed *elf = 0;
if (try_parse_input) ProfScope("parse elf"){
elf = elf_parsed_from_data(arena, params->input_elf_data);
PARSE_CHECK_ERROR(elf, "ELF");
}
// parse strtab
String8 strtab = {0};
if (elf != 0) ProfScope("parse strtab"){
strtab = elf_section_data_from_idx(elf, elf->strtab_idx);
}
// parse symtab
ELF_SymArray symtab = {0};
if (elf != 0) ProfScope("parse symtab"){
String8 data = elf_section_data_from_idx(elf, elf->symtab_idx);
symtab = elf_sym_array_from_data(arena, elf->elf_class, data);
}
// parse dynsym
ELF_SymArray dynsym = {0};
if (elf != 0) ProfScope("parse dynsym"){
String8 data = elf_section_data_from_idx(elf, elf->dynsym_idx);
dynsym = elf_sym_array_from_data(arena, elf->elf_class, data);
}
// parse dwarf
DWARF_Parsed *dwarf = 0;
if (elf != 0) ProfScope("parse dwarf"){
dwarf = dwarf_parsed_from_elf(arena, elf);
PARSE_CHECK_ERROR(dwarf, "DWARF");
}
// parse info
DWARF_InfoParsed *info = 0;
if (dwarf != 0){
String8 data = dwarf->debug_data[DWARF_SectionCode_Info];
if (data.size > 0) ProfScope("parse .debug_info"){
info = dwarf_info_from_data(arena, data);
PARSE_CHECK_ERROR(info, "DEBUG INFO");
}
}
// parse pubnames
DWARF_PubNamesParsed *pubnames = 0;
if (dwarf != 0){
String8 data = dwarf->debug_data[DWARF_SectionCode_PubNames];
if (data.size) ProfScope("parse .debug_pubnames"){
pubnames = dwarf_pubnames_from_data(arena, data);
PARSE_CHECK_ERROR(pubnames, "DEBUG PUBNAMES");
}
}
// parse pubtypes
DWARF_PubNamesParsed *pubtypes = 0;
if (dwarf != 0){
String8 data = dwarf->debug_data[DWARF_SectionCode_PubTypes];
if (data.size) ProfScope("parse .debug_pubtypes"){
pubtypes = dwarf_pubnames_from_data(arena, data);
PARSE_CHECK_ERROR(pubtypes, "DEBUG PUBTYPES");
}
}
// parse names
DWARF_NamesParsed *names = 0;
if (dwarf != 0){
String8 data = dwarf->debug_data[DWARF_SectionCode_Names];
if (data.size) ProfScope("parse .debug_names"){
names = dwarf_names_from_data(arena, data);
PARSE_CHECK_ERROR(names, "DEBUG NAMES");
}
}
// parse aranges
DWARF_ArangesParsed *aranges = 0;
if (dwarf != 0){
String8 data = dwarf->debug_data[DWARF_SectionCode_Aranges];
if (data.size) ProfScope("parse .debug_aranges"){
aranges = dwarf_aranges_from_data(arena, data);
PARSE_CHECK_ERROR(aranges, "DEBUG ARANGES");
}
}
// parse addr
DWARF_AddrParsed *addr = 0;
if (dwarf != 0){
String8 data = dwarf->debug_data[DWARF_SectionCode_Addr];
if (data.size) ProfScope("parse .debug_addr"){
addr = dwarf_addr_from_data(arena, data);
PARSE_CHECK_ERROR(addr, "DEBUG ADDR");
}
}
#if 0
// parse abbrev
DWARF_AbbrevParsed *abbrev = 0;
if (dwarf != 0){
String8 data = dwarf->debug_data[DWARF_SectionCode_Abbrev];
if (data.size > 0) ProfScope("parse .debug_abbrev"){
DWARF_AbbrevParams abbrev_params = {0};
abbrev_params.unit_idx_min = params->unit_idx_min;
abbrev_params.unit_idx_max = params->unit_idx_max;
abbrev = dwarf_abbrev_from_data(arena, data, &abbrev_params);
PARSE_CHECK_ERROR(abbrev, "DEBUG ABBREV");
}
}
// parse info
DWARF_InfoParsed *info = 0;
if (abbrev != 0){
String8 data = dwarf->debug_data[DWARF_SectionCode_Info];
if (data.size > 0) ProfScope("parse .debug_info"){
DWARF_InfoParams info_params = {0};
info_params.unit_idx_min = params->unit_idx_min;
info_params.unit_idx_max = params->unit_idx_max;
info = dwarf_info_from_data(arena, data, &info_params, abbrev);
PARSE_CHECK_ERROR(info, "DEBUG INFO");
}
}
#endif
// dump
if (params->dump) ProfScope("dump"){
String8List dump = {0};
// ELF
if (params->dump_header){
if (elf != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"ELF:\n"));
// TODO: better stringizers for fields here
str8_list_pushf(arena, &dump, " elf_class=%u\n", elf->elf_class);
str8_list_pushf(arena, &dump, " arch=%u\n", elf->arch);
str8_list_pushf(arena, &dump, " section_count=%llu\n", elf->section_count);
str8_list_pushf(arena, &dump, " segment_count=%llu\n", elf->segment_count);
str8_list_pushf(arena, &dump, " vbase=0x%llx\n", elf->vbase);
str8_list_pushf(arena, &dump, " entry_vaddr=0x%llx\n", elf->vbase);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
// SECTIONS
if (params->dump_sections){
if (elf != 0){
ELF_SectionArray section_array = elf_section_array_from_elf(elf);
String8Array section_name_array = elf_section_name_array_from_elf(elf);
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"SECTIONS:\n"));
ELF_Shdr64 *sec = section_array.sections;
String8 *sec_name = section_name_array.strings;
U64 count = section_array.count;
for (U64 i = 0 ; i < count; i += 1, sec += 1, sec_name += 1){
String8 type_string = elf_string_from_section_type(sec->sh_type);
// TODO: better stringizers for fields here
str8_list_pushf(arena, &dump, " section[%llu]:\n", i);
str8_list_pushf(arena, &dump, " name='%.*s'\n", str8_varg(*sec_name));
str8_list_pushf(arena, &dump, " type=%.*s\n", str8_varg(type_string));
str8_list_pushf(arena, &dump, " flags=0x%llx\n", sec->sh_flags);
str8_list_pushf(arena, &dump, " addr=0x%llx\n", sec->sh_addr);
str8_list_pushf(arena, &dump, " offset=0x%llx\n", sec->sh_offset);
str8_list_pushf(arena, &dump, " size=%llu\n", sec->sh_size);
str8_list_pushf(arena, &dump, " link=%u\n", sec->sh_link);
str8_list_pushf(arena, &dump, " info=%u\n", sec->sh_info);
str8_list_pushf(arena, &dump, " addralign=0x%llx\n", sec->sh_addralign);
str8_list_pushf(arena, &dump, " entsize=%llu\n", sec->sh_entsize);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
// SYMTAB
if (symtab.count > 0 && params->dump_symtab){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"SYMTAB:\n"));
str8_list_pushf(arena, &dump, " section: %llu\n", elf->symtab_idx);
dump_symtab(arena, &dump, &symtab, strtab, 1);
str8_list_push(arena, &dump, str8_lit("\n"));
}
// DYNSYM
if (dynsym.count > 0 && params->dump_dynsym){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DYNSYM:\n"));
str8_list_pushf(arena, &dump, " section: %llu\n", elf->dynsym_idx);
dump_symtab(arena, &dump, &dynsym, strtab, 1);
str8_list_push(arena, &dump, str8_lit("\n"));
}
// SEGMENTS
if (params->dump_segments){
if (elf != 0){
ELF_SegmentArray segment_array = elf_segment_array_from_elf(elf);
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"SEGMENTS:\n"));
ELF_Phdr64 *segments = segment_array.segments;
U64 count = segment_array.count;
for (U64 i = 0 ; i < count; i += 1){
ELF_Phdr64 *seg = segments + i;
// TODO: better stringizers for fields here
str8_list_pushf(arena, &dump, " segment[%llu]:\n", i);
str8_list_pushf(arena, &dump, " p_type=%u\n", seg->p_type);
str8_list_pushf(arena, &dump, " p_flags=0x%x\n", seg->p_flags);
str8_list_pushf(arena, &dump, " p_offset=0x%llx\n", seg->p_offset);
str8_list_pushf(arena, &dump, " p_vaddr=0x%llx\n", seg->p_vaddr);
str8_list_pushf(arena, &dump, " p_paddr=0x%llx\n", seg->p_paddr);
str8_list_pushf(arena, &dump, " p_filesz=%llu\n", seg->p_filesz);
str8_list_pushf(arena, &dump, " p_memsz=%llu\n", seg->p_memsz);
str8_list_pushf(arena, &dump, " p_align=%llu\n", seg->p_align);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
// DEBUG SECTIONS
if (params->dump_debug_sections){
if (dwarf != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG SECTIONS:\n"));
U32 *debug_section_idx = dwarf->debug_section_idx;
String8 *debug_data = dwarf->debug_data;
for (U32 i = 1; i < DWARF_SectionCode_COUNT; i += 1, debug_data += 1){
U32 idx = debug_section_idx[i];
String8 name = dwarf_string_from_section_code(i);
str8_list_pushf(arena, &dump, " %-10.*s section_idx=%u\n", str8_varg(name), idx);
}
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
// DEBUG INFO
if (params->dump_debug_info){
if (info != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG INFO:\n"));
U32 i = 0;
for (DWARF_InfoUnit *unit = info->unit_first;
unit != 0;
unit = unit->next, i += 1){
str8_list_pushf(arena, &dump, " unit[%u]:\n", i);
dwarf_stringize_info(arena, &dump, unit, 2);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
// DEBUG PUBNAMES
if (params->dump_debug_pubnames){
if (pubnames != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG PUBNAMES:\n"));
U32 i = 0;
for (DWARF_PubNamesUnit *unit = pubnames->unit_first;
unit != 0;
unit = unit->next, i += 1){
str8_list_pushf(arena, &dump, " unit[%u]:\n", i);
dwarf_stringize_pubnames(arena, &dump, unit, 2);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
// DEBUG PUBTYPES
if (params->dump_debug_pubtypes){
if (pubtypes != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG PUBTYPES:\n"));
U32 i = 0;
for (DWARF_PubNamesUnit *unit = pubtypes->unit_first;
unit != 0;
unit = unit->next, i += 1){
str8_list_pushf(arena, &dump, " unit[%u]:\n", i);
dwarf_stringize_pubnames(arena, &dump, unit, 2);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
// DEBUG NAMES
if (params->dump_debug_names){
if (names != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG NAMES:\n"));
U32 i = 0;
for (DWARF_NamesUnit *unit = names->unit_first;
unit != 0;
unit = unit->next, i += 1){
str8_list_pushf(arena, &dump, " unit[%u]:\n", i);
dwarf_stringize_names(arena, &dump, unit, 2);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
// DEBUG ARANGES
if (params->dump_debug_aranges){
if (aranges != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG ARANGES:\n"));
U32 i = 0;
for (DWARF_ArangesUnit *unit = aranges->unit_first;
unit != 0;
unit = unit->next, i += 1){
str8_list_pushf(arena, &dump, " unit[%u]:\n", i);
dwarf_stringize_aranges(arena, &dump, unit, 2);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
// DEBUG ADDR
if (params->dump_debug_addr){
if (addr != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG ADDR:\n"));
U32 i = 0;
for (DWARF_AddrUnit *unit = addr->unit_first;
unit != 0;
unit = unit->next, i += 1){
str8_list_pushf(arena, &dump, " unit[%u]:\n", i);
dwarf_stringize_addr(arena, &dump, unit, 2);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
#if 0
// DEBUG ABBREV
if (params->dump_debug_abbrev){
if (abbrev != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG ABBREV:\n"));
U32 i = 0;
for (DWARF_AbbrevUnit *unit = abbrev->unit_first;
unit != 0;
unit = unit->next, i += 1){
U32 j = 0;
for (DWARF_AbbrevDecl *abbrev_decl = unit->first;
abbrev_decl != 0;
abbrev_decl = abbrev_decl->next, j += 1){
String8 tag_string = dwarf_string_from_tag(abbrev_decl->tag);
str8_list_pushf(arena, &dump, " unit[%u],abbrev[%u]:\n", i, j);
str8_list_pushf(arena, &dump, " code=%llu\n", abbrev_decl->abbrev_code);
str8_list_pushf(arena, &dump, " tag=%.*s\n", str8_varg(tag_string));
str8_list_pushf(arena, &dump, " has_children=%u\n", abbrev_decl->has_children);
str8_list_pushf(arena, &dump, " attrib_count=%u\n", abbrev_decl->attrib_count);
str8_list_pushf(arena, &dump, " attribs:\n", abbrev_decl->attrib_count);
U32 attrib_count = abbrev_decl->attrib_count;
DWARF_AbbrevAttribSpec *attrib_spec = abbrev_decl->attrib_specs;
for (U32 k = 0; k < attrib_count; k += 1, attrib_spec += 1){
String8 name_string = dwarf_string_from_attribute_name(attrib_spec->name);
String8 form_string = dwarf_string_from_attribute_form(attrib_spec->form);
str8_list_pushf(arena, &dump, " [%-14.*s %-10.*s]\n",
str8_varg(name_string), str8_varg(form_string));
}
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
}
#endif
#if 0
// DEBUG INFO
if (params->dump_debug_info){
if (info != 0){
str8_list_push(arena, &dump,
str8_lit("################################"
"################################\n"
"DEBUG INFO:\n"));
U32 i = 0;
for (DWARF_InfoUnit *unit = info->unit_first;
unit != 0;
unit = unit->next, i += 1){
str8_list_pushf(arena, &dump, " unit[%u]:\n", i);
str8_list_pushf(arena, &dump, " [header]\n");
str8_list_pushf(arena, &dump, " version=%u\n", unit->dwarf_version);
str8_list_pushf(arena, &dump, " offset_size=%u\n", unit->offset_size);
str8_list_pushf(arena, &dump, " address_size=%u\n", unit->address_size);
str8_list_pushf(arena, &dump, " [extracted attributes]\n");
str8_list_pushf(arena, &dump, " langauge=%u\n", (U32)unit->language);
str8_list_pushf(arena, &dump, " line_info_offset=%llu\n", unit->line_info_offset);
str8_list_pushf(arena, &dump, " vbase=0x%llx\n", unit->vbase);
str8_list_pushf(arena, &dump, " str_offsets_base=%llu\n", unit->str_offsets_base);
str8_list_pushf(arena, &dump, " addr_base=%llu\n", unit->addr_base);
str8_list_pushf(arena, &dump, " rnglists_base=%llu\n", unit->rnglists_base);
str8_list_pushf(arena, &dump, " loclists_base=%llu\n", unit->loclists_base);
dump_entry_tree(arena, &dump, dwarf, unit, unit->entry_root, 2);
str8_list_push(arena, &dump, str8_lit("\n"));
}
}
}
#endif
// print dump
for (String8Node *node = dump.first;
node != 0;
node = node->next){
fwrite(node->string.str, 1, node->string.size, stdout);
}
}
ProfEndCapture();
return(0);
}
@@ -0,0 +1,50 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_FROM_DWARF_H
#define RADDBG_FROM_DWARF_H
////////////////////////////////
//~ Program Parameters Type
typedef struct DWARFCONV_Params{
String8 input_elf_name;
String8 input_elf_data;
String8 output_name;
U64 unit_idx_min;
U64 unit_idx_max;
struct{
B8 input;
} hide_errors;
B8 dump;
B8 dump__first;
B8 dump_header;
B8 dump_sections;
B8 dump_segments;
B8 dump_symtab;
B8 dump_dynsym;
B8 dump_debug_sections;
B8 dump_debug_info;
B8 dump_debug_abbrev;
B8 dump_debug_pubnames;
B8 dump_debug_pubtypes;
B8 dump_debug_names;
B8 dump_debug_aranges;
B8 dump_debug_addr;
B8 dump__last;
String8List errors;
} DWARFCONV_Params;
////////////////////////////////
//~ Program Parameters Parser
static DWARFCONV_Params *dwarf_convert_params_from_cmd_line(Arena *arena, CmdLine *cmdline);
#endif //RADDBG_FROM_DWARF_H
+498
View File
@@ -0,0 +1,498 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ CodeView Common Functions
static CV_NumericParsed
cv_numeric_from_data_range(U8 *first, U8 *opl){
CV_NumericParsed result = {0};
if (first + 2 <= opl){
U16 x = *(U16*)first;
if (x < 0x8000){
result.kind = CV_NumericKind_USHORT;
result.val = first;
result.encoded_size = 2;
}
else{
U64 val_size = 0;
switch (x){
case CV_NumericKind_CHAR: val_size = 1; break;
case CV_NumericKind_SHORT:
case CV_NumericKind_USHORT: val_size = 2; break;
case CV_NumericKind_LONG:
case CV_NumericKind_ULONG: val_size = 4; break;
case CV_NumericKind_FLOAT32: val_size = 4; break;
case CV_NumericKind_FLOAT64: val_size = 8; break;
case CV_NumericKind_FLOAT80: val_size = 10; break;
case CV_NumericKind_FLOAT128: val_size = 16; break;
case CV_NumericKind_QUADWORD:
case CV_NumericKind_UQUADWORD: val_size = 8; break;
case CV_NumericKind_FLOAT48: val_size = 6; break;
case CV_NumericKind_COMPLEX32: val_size = 8; break;
case CV_NumericKind_COMPLEX64: val_size = 16; break;
case CV_NumericKind_COMPLEX80: val_size = 20; break;
case CV_NumericKind_COMPLEX128:val_size = 32; break;
case CV_NumericKind_VARSTRING: val_size = 0; break; // TODO: ???
case CV_NumericKind_OCTWORD:
case CV_NumericKind_UOCTWORD: val_size = 16; break;
case CV_NumericKind_DECIMAL: val_size = 0; break; // TODO: ???
case CV_NumericKind_DATE: val_size = 0; break; // TODO: ???
case CV_NumericKind_UTF8STRING:val_size = 0; break; // TODO: ???
case CV_NumericKind_FLOAT16: val_size = 2; break;
}
if (first + 2 + val_size <= opl){
result.kind = x;
result.val = (first + 2);
result.encoded_size = 2 + val_size;
}
}
}
return(result);
}
static B32
cv_numeric_fits_in_u64(CV_NumericParsed *num){
B32 result = 0;
switch (num->kind){
case CV_NumericKind_USHORT:
case CV_NumericKind_ULONG:
case CV_NumericKind_UQUADWORD:
{
result = 1;
}break;
}
return(result);
}
static B32
cv_numeric_fits_in_s64(CV_NumericParsed *num){
B32 result = 0;
switch (num->kind){
case CV_NumericKind_CHAR:
case CV_NumericKind_SHORT:
case CV_NumericKind_LONG:
case CV_NumericKind_QUADWORD:
{
result = 1;
}break;
}
return(result);
}
static B32
cv_numeric_fits_in_f64(CV_NumericParsed *num){
B32 result = 0;
switch (num->kind){
case CV_NumericKind_FLOAT32:
case CV_NumericKind_FLOAT64:
{
result = 1;
}break;
}
return(result);
}
static U64
cv_u64_from_numeric(CV_NumericParsed *num){
U64 result = 0;
switch (num->kind){
case CV_NumericKind_USHORT:
{
result = *(U16*)num->val;
}break;
case CV_NumericKind_ULONG:
{
result = *(U32*)num->val;
}break;
case CV_NumericKind_UQUADWORD:
{
result = *(U64*)num->val;
}break;
}
return(result);
}
static S64
cv_s64_from_numeric(CV_NumericParsed *num){
S64 result = 0;
switch (num->kind){
case CV_NumericKind_CHAR:
{
result = *(S8*)num->val;
}break;
case CV_NumericKind_SHORT:
{
result = *(S16*)num->val;
}break;
case CV_NumericKind_LONG:
{
result = *(S32*)num->val;
}break;
case CV_NumericKind_QUADWORD:
{
result = *(S64*)num->val;
}break;
}
return(result);
}
static F64
cv_f64_from_numeric(CV_NumericParsed *num){
F64 result = 0;
switch (num->kind){
case CV_NumericKind_FLOAT32:
{
result = *(F32*)num->val;
}break;
case CV_NumericKind_FLOAT64:
{
result = *(F64*)num->val;
}break;
}
return(result);
}
////////////////////////////////
//~ CodeView Sym Parser Functions
static CV_SymParsed*
cv_sym_from_data(Arena *arena, String8 sym_data, U64 sym_align){
Assert(1 <= sym_align && IsPow2OrZero(sym_align));
ProfBegin("cv_sym_from_data");
Temp scratch = scratch_begin(&arena, 1);
// gather up symbols
CV_RecRangeStream *stream = cv_rec_range_stream_from_data(scratch.arena, sym_data, sym_align);
// convert to result
CV_SymParsed *result = push_array(arena, CV_SymParsed, 1);
result->data = sym_data;
result->sym_align = sym_align;
result->sym_ranges = cv_rec_range_array_from_stream(arena, stream);
cv_sym_top_level_info_from_syms(arena, sym_data, &result->sym_ranges, &result->info);
scratch_end(scratch);
ProfEnd();
return(result);
}
static CV_LeafParsed*
cv_leaf_from_data(Arena *arena, String8 leaf_data, CV_TypeId itype_first){
ProfBegin("cv_leaf_from_data");
Temp scratch = scratch_begin(&arena, 1);
// gather up symbols
CV_RecRangeStream *stream = cv_rec_range_stream_from_data(scratch.arena, leaf_data, 1);
// convert to result
CV_LeafParsed *result = push_array(arena, CV_LeafParsed, 1);
result->data = leaf_data;
result->itype_first = itype_first;
result->itype_opl = itype_first + stream->total_count;
result->leaf_ranges = cv_rec_range_array_from_stream(arena, stream);
scratch_end(scratch);
ProfEnd();
return(result);
}
static CV_RecRangeStream*
cv_rec_range_stream_from_data(Arena *arena, String8 sym_data, U64 sym_align){
Assert(1 <= sym_align && IsPow2OrZero(sym_align));
CV_RecRangeStream *result = push_array(arena, CV_RecRangeStream, 1);
U8 *data = sym_data.str;
U64 cursor = 0;
U64 cap = sym_data.size;
for (;cursor + sizeof(CV_RecHeader) <= cap;){
// setup a new chunk
arena_push_align(arena, 64);
CV_RecRangeChunk *cur_chunk = cv_rec_range_stream_push_chunk(arena, result);
U64 partial_count = 0;
for (;partial_count < CV_REC_RANGE_CHUNK_SIZE && cursor + sizeof(CV_RecHeader) <= cap;
partial_count += 1){
// compute cap
CV_RecHeader *hdr = (CV_RecHeader*)(data + cursor);
U64 symbol_cap_unclamped = cursor + 2 + hdr->size;
U64 symbol_cap = ClampTop(symbol_cap_unclamped, cap);
// push on range
cur_chunk->ranges[partial_count].off = cursor + 2;
cur_chunk->ranges[partial_count].hdr = *hdr;
// update cursor
U32 next_pos = AlignPow2(symbol_cap, sym_align);
cursor = next_pos;
}
result->total_count += partial_count;
}
return(result);
}
static void
cv_sym_top_level_info_from_syms(Arena *arena, String8 sym_data,
CV_RecRangeArray *ranges,
CV_SymTopLevelInfo *info_out){
MemoryZeroStruct(info_out);
CV_RecRange *range = ranges->ranges;
CV_RecRange *opl = range + ranges->count;
for (; range < opl; range += 1){
U8 *first = sym_data.str + range->off + 2;
U64 cap = range->hdr.size - 2;
switch (range->hdr.kind){
case CV_SymKind_COMPILE:
{
if (sizeof(CV_SymCompile) <= cap){
CV_SymCompile *compile = (CV_SymCompile*)first;
String8 ver_str = str8_cstring_capped((char*)(compile + 1), (char *)(first + cap));
info_out->arch = compile->machine;
info_out->language = CV_CompileFlags_ExtractLanguage(compile->flags);;
info_out->compiler_name = ver_str;
}
}break;
case CV_SymKind_COMPILE2:
{
if (sizeof(CV_SymCompile2) <= cap){
CV_SymCompile2 *compile2 = (CV_SymCompile2*)first;
String8 ver_str = str8_cstring_capped((char*)(compile2 + 1), (char*)(first + cap));
String8 compiler_name = push_str8f(arena, "%.*s %u.%u.%u",
str8_varg(ver_str),
compile2->ver_major,
compile2->ver_minor,
compile2->ver_build);
info_out->arch = compile2->machine;
info_out->language = CV_Compile2Flags_ExtractLanguage(compile2->flags);;
info_out->compiler_name = compiler_name;
}
}break;
case CV_SymKind_COMPILE3:
{
if (sizeof(CV_SymCompile3) <= cap){
CV_SymCompile3 *compile3 = (CV_SymCompile3*)first;
String8 ver_str = str8_cstring_capped((char*)(compile3 + 1), (char *)(first + cap));
String8 compiler_name = push_str8f(arena, "%.*s %u.%u.%u",
str8_varg(ver_str),
compile3->ver_major,
compile3->ver_minor,
compile3->ver_build);
info_out->arch = compile3->machine;
info_out->language = CV_Compile3Flags_ExtractLanguage(compile3->flags);;
info_out->compiler_name = compiler_name;
}
}break;
}
}
}
//- range streams
static CV_RecRangeChunk*
cv_rec_range_stream_push_chunk(Arena *arena, CV_RecRangeStream *stream){
CV_RecRangeChunk *result = push_array_no_zero(arena, CV_RecRangeChunk, 1);
SLLQueuePush(stream->first_chunk, stream->last_chunk, result);
return(result);
}
static CV_RecRangeArray
cv_rec_range_array_from_stream(Arena *arena, CV_RecRangeStream *stream){
U64 total_count = stream->total_count;
CV_RecRange *ranges = push_array_no_zero(arena, CV_RecRange, total_count);
U64 idx = 0;
for (CV_RecRangeChunk *chunk = stream->first_chunk;
chunk != 0;
chunk = chunk->next){
U64 copy_count_raw = total_count - idx;
U64 copy_count = ClampTop(copy_count_raw, CV_REC_RANGE_CHUNK_SIZE);
MemoryCopy(ranges + idx, chunk->ranges, copy_count*sizeof(CV_RecRange));
idx += copy_count;
}
CV_RecRangeArray result = {0};
result.ranges = ranges;
result.count = total_count;
return(result);
}
////////////////////////////////
//~ CodeView C13 Parser Functions
static CV_C13Parsed*
cv_c13_from_data(Arena *arena, String8 c13_data,
PDB_Strtbl *strtbl, PDB_CoffSectionArray *sections){
ProfBegin("cv_c13_from_data");
// gather c13 data
CV_C13SubSectionNode *file_chksms = 0;
CV_C13SubSectionNode *first = 0;
CV_C13SubSectionNode *last = 0;
U64 count = 0;
{
U32 cursor = 0;
for (; cursor + sizeof(CV_C13_SubSectionHeader) <= c13_data.size;){
// read header
CV_C13_SubSectionHeader *hdr = (CV_C13_SubSectionHeader*)(c13_data.str + cursor);
// get sub section info
U32 sub_section_off = cursor + sizeof(*hdr);
U32 sub_section_size_raw = hdr->size;
U32 after_sub_section_off_unclamped = sub_section_off + sub_section_size_raw;
U32 after_sub_section_off = ClampTop(after_sub_section_off_unclamped, c13_data.size);
U32 sub_section_size = after_sub_section_off - sub_section_off;
// emit sub section
if (!(hdr->kind & CV_C13_SubSectionKind_IgnoreFlag)){
CV_C13SubSectionNode *node = push_array(arena, CV_C13SubSectionNode, 1);
SLLQueuePush(first, last, node);
count += 1;
node->kind = hdr->kind;
node->off = sub_section_off;
node->size = sub_section_size;
if (hdr->kind == CV_C13_SubSectionKind_FileChksms){
file_chksms = node;
}
}
// move cursor
cursor = AlignPow2(after_sub_section_off, 4);
}
}
// parse each section
for (CV_C13SubSectionNode *node = first;
node != 0;
node = node->next){
U8 *first = c13_data.str + node->off;
U32 cap = node->size;
switch (node->kind){
case CV_C13_SubSectionKind_Lines:
{
// read header
if (sizeof(CV_C13_SubSecLinesHeader) <= cap){
U32 read_off = 0;
U64 read_off_opl = node->size;
CV_C13_SubSecLinesHeader *hdr = (CV_C13_SubSecLinesHeader*)(first + read_off);
read_off += sizeof(*hdr);
// extract top level info
U32 sec_idx = hdr->sec;
B32 has_cols = !!(hdr->flags & CV_C13_SubSecLinesFlag_HasColumns);
U64 secrel_off = hdr->sec_off;
U64 secrel_opl = secrel_off + hdr->len;
U64 sec_base_off = sections->sections[sec_idx - 1].voff;
// rjf: bad section index -> skip
if(sec_idx < 1 || sections->count < sec_idx)
{
continue;
}
// read files
for(;read_off+sizeof(CV_C13_File) <= read_off_opl;)
{
// rjf: grab next file header
CV_C13_File *file = (CV_C13_File*)(first + read_off);
U32 file_off = file->file_off;
U32 line_count_unclamped = file->num_lines;
U32 block_size = file->block_size;
// file_name from file_off
String8 file_name = {0};
if (file_off + sizeof(CV_C13_Checksum) <= file_chksms->size){
CV_C13_Checksum *checksum = (CV_C13_Checksum*)(c13_data.str + file_chksms->off + file_off);
U32 name_off = checksum->name_off;
file_name = pdb_strtbl_string_from_off(strtbl, name_off);
}
// array layouts
U32 line_item_size = sizeof(CV_C13_Line);
if (has_cols){
line_item_size += sizeof(CV_C13_Column);
}
U32 line_array_off = read_off + sizeof(*file);
U32 line_count_max = (read_off_opl - line_array_off) / line_item_size;
U32 line_count = ClampTop(line_count_unclamped, line_count_max);
U32 col_array_off = line_array_off + line_count*sizeof(CV_C13_Line);
// parse lines
U64 *voffs = push_array_no_zero(arena, U64, line_count + 1);
U32 *line_nums = push_array_no_zero(arena, U32, line_count);
{
CV_C13_Line *line_ptr = (CV_C13_Line*)(first + line_array_off);
CV_C13_Line *line_opl = line_ptr + line_count;
// TODO(allen): check order correctness here
U32 i = 0;
for (; line_ptr < line_opl; line_ptr += 1, i += 1){
voffs[i] = line_ptr->off + secrel_off + sec_base_off;
line_nums[i] = CV_C13_LineFlags_ExtractLineNumber(line_ptr->flags);
}
voffs[i] = secrel_opl + sec_base_off;
}
// emit parsed lines
CV_C13LinesParsedNode *lines_parsed_node = push_array(arena, CV_C13LinesParsedNode, 1);
CV_C13LinesParsed *lines_parsed = &lines_parsed_node->v;
lines_parsed->sec_idx = sec_idx;
lines_parsed->file_off = file_off;
lines_parsed->secrel_base_off = secrel_off;
lines_parsed->file_name = file_name;
lines_parsed->voffs = voffs;
lines_parsed->line_nums = line_nums;
lines_parsed->line_count = line_count;
SLLQueuePush(node->lines_first, node->lines_last, lines_parsed_node);
// rjf: advance
read_off += sizeof(*file);
read_off += line_item_size*line_count;
}
}
}break;
}
}
// convert to result
CV_C13Parsed *result = push_array(arena, CV_C13Parsed, 1);
result->first_sub_section = first;
result->last_sub_section = last;
result->sub_section_count = count;
result->file_chksms_sub_section = file_chksms;
ProfEnd();
return(result);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,124 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ CodeView Conversion Functions
static RADDBG_Arch
raddbg_arch_from_cv_arch(CV_Arch cv_arch){
RADDBG_Arch result = 0;
switch (cv_arch){
case CV_Arch_8086: result = RADDBG_Arch_X86; break;
case CV_Arch_X64: result = RADDBG_Arch_X64; break;
//case CV_Arch_8080: break;
//case CV_Arch_80286: break;
//case CV_Arch_80386: break;
//case CV_Arch_80486: break;
//case CV_Arch_PENTIUM: break;
//case CV_Arch_PENTIUMII: break;
//case CV_Arch_PENTIUMIII: break;
//case CV_Arch_MIPS: break;
//case CV_Arch_MIPS16: break;
//case CV_Arch_MIPS32: break;
//case CV_Arch_MIPS64: break;
//case CV_Arch_MIPSI: break;
//case CV_Arch_MIPSII: break;
//case CV_Arch_MIPSIII: break;
//case CV_Arch_MIPSIV: break;
//case CV_Arch_MIPSV: break;
//case CV_Arch_M68000: break;
//case CV_Arch_M68010: break;
//case CV_Arch_M68020: break;
//case CV_Arch_M68030: break;
//case CV_Arch_M68040: break;
//case CV_Arch_ALPHA: break;
//case CV_Arch_ALPHA_21164: break;
//case CV_Arch_ALPHA_21164A: break;
//case CV_Arch_ALPHA_21264: break;
//case CV_Arch_ALPHA_21364: break;
//case CV_Arch_PPC601: break;
//case CV_Arch_PPC603: break;
//case CV_Arch_PPC604: break;
//case CV_Arch_PPC620: break;
//case CV_Arch_PPCFP: break;
//case CV_Arch_PPCBE: break;
//case CV_Arch_SH3: break;
//case CV_Arch_SH3E: break;
//case CV_Arch_SH3DSP: break;
//case CV_Arch_SH4: break;
//case CV_Arch_SHMEDIA: break;
//case CV_Arch_ARM3: break;
//case CV_Arch_ARM4: break;
//case CV_Arch_ARM4T: break;
//case CV_Arch_ARM5: break;
//case CV_Arch_ARM5T: break;
//case CV_Arch_ARM6: break;
//case CV_Arch_ARM_XMAC: break;
//case CV_Arch_ARM_WMMX: break;
//case CV_Arch_ARM7: break;
//case CV_Arch_OMNI: break;
//case CV_Arch_IA64_1: break;
//case CV_Arch_IA64_2: break;
//case CV_Arch_CEE: break;
//case CV_Arch_AM33: break;
//case CV_Arch_M32R: break;
//case CV_Arch_TRICORE: break;
//case CV_Arch_EBC: break;
//case CV_Arch_THUMB: break;
//case CV_Arch_ARMNT: break;
//case CV_Arch_ARM64: break;
//case CV_Arch_D3D11_SHADER: break;
}
return(result);
}
static RADDBG_RegisterCode
raddbg_reg_code_from_cv_reg_code(RADDBG_Arch arch, CV_Reg reg_code){
RADDBG_RegisterCode result = 0;
switch (arch){
case RADDBG_Arch_X86:
{
switch (reg_code){
#define X(CVN,C,RDN,BP,BZ) case C: result = RADDBG_RegisterCode_X86_##RDN; break;
CV_Reg_X86_XList(X)
#undef X
}
}break;
case RADDBG_Arch_X64:
{
switch (reg_code){
#define X(CVN,C,RDN,BP,BZ) case C: result = RADDBG_RegisterCode_X64_##RDN; break;
CV_Reg_X64_XList(X)
#undef X
}
}break;
}
return(result);
}
static RADDBG_Language
raddbg_language_from_cv_language(CV_Language cv_language){
RADDBG_Language result = 0;
switch (cv_language){
case CV_Language_C: result = RADDBG_Language_C; break;
case CV_Language_CXX: result = RADDBG_Language_CPlusPlus; break;
//case CV_Language_FORTRAN: result = ; break;
//case CV_Language_MASM: result = ; break;
//case CV_Language_PASCAL: result = ; break;
//case CV_Language_BASIC: result = ; break;
//case CV_Language_COBOL: result = ; break;
//case CV_Language_LINK: result = ; break;
//case CV_Language_CVTRES: result = ; break;
//case CV_Language_CVTPGD: result = ; break;
//case CV_Language_CSHARP: result = ; break;
//case CV_Language_VB: result = ; break;
//case CV_Language_ILASM: result = ; break;
//case CV_Language_JAVA: result = ; break;
//case CV_Language_JSCRIPT: result = ; break;
//case CV_Language_MSIL: result = ; break;
//case CV_Language_HLSL: result = ; break;
}
return(result);
}
@@ -0,0 +1,14 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_CODEVIEW_CONVERSION_H
#define RADDBG_CODEVIEW_CONVERSION_H
////////////////////////////////
//~ CodeView Conversion Functions
static RADDBG_Arch raddbg_arch_from_cv_arch(CV_Arch arch);
static RADDBG_RegisterCode raddbg_reg_code_from_cv_reg_code(RADDBG_Arch arch, CV_Reg reg_code);
static RADDBG_Language raddbg_language_from_cv_language(CV_Language language);
#endif //RADDBG_CODEVIEW_CONVERSION_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,86 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_CODEVIEW_STRINGIZE_H
#define RADDBG_CODEVIEW_STRINGIZE_H
////////////////////////////////
//~ CodeView Stringize Helper Types
typedef struct CV_StringizeSymParams{
CV_Arch arch;
} CV_StringizeSymParams;
typedef struct CV_StringizeLeafParams{
U32 dummy;
} CV_StringizeLeafParams;
////////////////////////////////
//~ CodeView Common Stringize Functions
static void cv_stringize_numeric(Arena *arena, String8List *out, CV_NumericParsed *num);
static void cv_stringize_lvar_addr_range(Arena *arena, String8List *out,
CV_LvarAddrRange *range);
static void cv_stringize_lvar_addr_gap(Arena *arena, String8List *out, CV_LvarAddrGap *gap);
static void cv_stringize_lvar_addr_gap_list(Arena *arena, String8List *out,
void *first, void *opl);
static String8 cv_string_from_sym_kind(CV_SymKind kind);
static String8 cv_string_from_basic_type(CV_BasicType basic_type);
static String8 cv_string_from_leaf_kind(CV_LeafKind kind);
static String8 cv_string_from_numeric_kind(CV_NumericKind kind);
static String8 cv_string_from_c13_sub_section_kind(CV_C13_SubSectionKind kind);
static String8 cv_string_from_machine(CV_Arch arch);
static String8 cv_string_from_reg(CV_Arch arch, CV_Reg reg);
static String8 cv_string_from_pointer_kind(CV_PointerKind ptr_kind);
static String8 cv_string_from_pointer_mode(CV_PointerMode ptr_mode);
static String8 cv_string_from_hfa_kind(CV_HFAKind hfa_kind);
static String8 cv_string_from_mo_com_udt_kind(CV_MoComUDTKind mo_com_udt_kind);
////////////////////////////////
//~ CodeView Flags Stringize Functions
static void cv_stringize_modifier_flags(Arena *arena, String8List *out,
U32 indent, CV_ModifierFlags flags);
static void cv_stringize_type_props(Arena *arena, String8List *out,
U32 indent, CV_TypeProps props);
static void cv_stringize_pointer_attribs(Arena *arena, String8List *out,
U32 indent, CV_PointerAttribs attribs);
static void cv_stringize_local_flags(Arena *arena, String8List *out,
U32 indent, CV_LocalFlags flags);
////////////////////////////////
//~ CodeView Sym Stringize Functions
static void cv_stringize_sym_parsed(Arena *arena, String8List *out, CV_SymParsed *sym);
static void cv_stringize_sym_range(Arena *arena, String8List *out,
CV_RecRange *range, String8 data,
CV_StringizeSymParams *p);
static void cv_stringize_sym_array(Arena *arena, String8List *out,
CV_RecRangeArray *ranges, String8 data,
CV_StringizeSymParams *p);
////////////////////////////////
//~ CodeView Leaf Stringize Functions
static void cv_stringize_leaf_parsed(Arena *arena, String8List *out, CV_LeafParsed *leaf);
static void cv_stringize_leaf_range(Arena *arena, String8List *out,
CV_RecRange *range, CV_TypeId itype, String8 data,
CV_StringizeLeafParams *p);
static void cv_stringize_leaf_array(Arena *arena, String8List *out,
CV_RecRangeArray *ranges, CV_TypeId itype_first,
String8 data,
CV_StringizeLeafParams *p);
////////////////////////////////
//~ CodeView C13 Stringize Functions
static void cv_stringize_c13_parsed(Arena *arena, String8List *out, CV_C13Parsed *c13);
#endif //RADDBG_CODEVIEW_STRINGIZE_H
+52
View File
@@ -0,0 +1,52 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_COFF_H
#define RADDBG_COFF_H
////////////////////////////////
//~ COFF Format Types
typedef struct COFF_Guid{
U32 data1;
U16 data2;
U16 data3;
U32 data4;
U32 data5;
} COFF_Guid;
#define COFF_ArchXList(X)\
X(UNKNOWN, 0x0)\
X(X86, 0x14c)\
X(X64, 0x8664)\
X(ARM33, 0x1d3)\
X(ARM, 0x1c0)\
X(ARM64, 0xaa64)\
X(ARMNT, 0x1c4)\
X(EBC, 0xebc)\
X(IA64, 0x200)\
X(M32R, 0x9041)\
X(MIPS16, 0x266)\
X(MIPSFPU, 0x366)\
X(MIPSFPU16, 0x466)\
X(POWERPC, 0x1f0)\
X(POWERPCFP, 0x1f1)\
X(R4000, 0x166)\
X(RISCV32, 0x5032)\
X(RISCV64, 0x5064)\
X(RISCV128, 0x5128)\
X(SH3, 0x1a2)\
X(SH3DSP, 0x1a3)\
X(SH4, 0x1a6)\
X(SH5, 0x1a8)\
X(THUMB, 0x1c2)\
X(WCEMIPSV2, 0x169)
typedef U16 COFF_Arch;
enum{
#define X(N,c) COFF_Arch_##N = c,
COFF_ArchXList(X)
#undef X
};
#endif //COFF_H
@@ -0,0 +1,22 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ COFF Conversion Functions
static RADDBG_BinarySectionFlags
raddbg_binary_section_flags_from_coff_section_flags(COFF_SectionFlags flags){
RADDBG_BinarySectionFlags result = 0;
if (flags & COFF_SectionFlag_MEM_READ){
result |= RADDBG_BinarySectionFlag_Read;
}
if (flags & COFF_SectionFlag_MEM_WRITE){
result |= RADDBG_BinarySectionFlag_Write;
}
if (flags & COFF_SectionFlag_MEM_EXECUTE){
result |= RADDBG_BinarySectionFlag_Execute;
}
return(result);
}
@@ -0,0 +1,13 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_COFF_CONVERSION_H
#define RADDBG_COFF_CONVERSION_H
////////////////////////////////
//~ COFF Conversion Functions
static RADDBG_BinarySectionFlags
raddbg_binary_section_flags_from_coff_section_flags(COFF_SectionFlags flags);
#endif //RADDBG_COFF_CONVERSION_H
File diff suppressed because it is too large Load Diff
+291
View File
@@ -0,0 +1,291 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_FROM_PDB_H
#define RADDBG_FROM_PDB_H
////////////////////////////////
//~ Program Parameters Type
typedef struct PDBCONV_Params{
String8 input_pdb_name;
String8 input_pdb_data;
String8 input_exe_name;
String8 input_exe_data;
String8 output_name;
struct{
B8 input;
B8 output;
B8 parsing;
B8 converting;
} hide_errors;
B8 dump;
B8 dump__first;
B8 dump_coff_sections;
B8 dump_msf;
B8 dump_sym;
B8 dump_tpi_hash;
B8 dump_leaf;
B8 dump_c13;
B8 dump_contributions;
B8 dump_table_diagnostics;
B8 dump__last;
String8List errors;
} PDBCONV_Params;
////////////////////////////////
//~ Program Parameters Parser
static PDBCONV_Params *pdb_convert_params_from_cmd_line(Arena *arena, CmdLine *cmdline);
////////////////////////////////
//~ PDB Type & Symbol Info Translation Helpers
//- translation helper types
typedef struct PDBCONV_FwdNode{
struct PDBCONV_FwdNode *next;
CV_TypeId key;
CV_TypeId val;
} PDBCONV_FwdNode;
typedef struct PDBCONV_FwdMap{
PDBCONV_FwdNode **buckets;
U64 buckets_count;
U64 bucket_collision_count;
U64 pair_count;
} PDBCONV_FwdMap;
typedef struct PDBCONV_TypeRev{
struct PDBCONV_TypeRev *next;
CONS_Type *owner_type;
CV_TypeId field_itype;
} PDBCONV_TypeRev;
typedef struct PDBCONV_FrameProcData{
U32 frame_size;
CV_FrameprocFlags flags;
} PDBCONV_FrameProcData;
typedef struct PDBCONV_FrameProcNode{
struct PDBCONV_FrameProcNode *next;
CONS_Symbol *key;
PDBCONV_FrameProcData data;
} PDBCONV_FrameProcNode;
typedef struct PDBCONV_FrameProcMap{
PDBCONV_FrameProcNode **buckets;
U64 buckets_count;
U64 bucket_collision_count;
U64 pair_count;
} PDBCONV_FrameProcMap;
typedef struct PDBCONV_ScopeNode{
struct PDBCONV_ScopeNode *next;
CONS_Scope *scope;
CONS_Symbol *symbol;
} PDBCONV_ScopeNode;
typedef struct PDBCONV_KnownGlobalNode{
struct PDBCONV_KnownGlobalNode *next;
String8 key_name;
U64 key_voff;
U64 hash;
} PDBCONV_KnownGlobalNode;
typedef struct PDBCONV_KnownGlobalSet{
PDBCONV_KnownGlobalNode **buckets;
U64 buckets_count;
U64 bucket_collision_count;
U64 global_count;
} PDBCONV_KnownGlobalSet;
typedef struct PDBCONV_CtxParams PDBCONV_CtxParams;
struct PDBCONV_CtxParams
{
RADDBG_Arch arch;
PDB_TpiHashParsed *tpi_hash;
CV_LeafParsed *tpi_leaf;
PDB_CoffSectionArray *sections;
U64 fwd_map_bucket_count;
U64 frame_proc_map_bucket_count;
U64 known_global_map_bucket_count;
U64 link_name_map_bucket_count;
};
typedef struct PDBCONV_TypesSymbolsParams{
CV_SymParsed *sym;
CV_SymParsed **sym_for_unit;
U64 unit_count;
} PDBCONV_TypesSymbolsParams;
typedef struct PDBCONV_LinkNameNode{
struct PDBCONV_LinkNameNode *next;
U64 voff;
String8 name;
} PDBCONV_LinkNameNode;
typedef struct PDBCONV_LinkNameMap{
PDBCONV_LinkNameNode **buckets;
U64 buckets_count;
U64 bucket_collision_count;
U64 link_name_count;
} PDBCONV_LinkNameMap;
typedef struct PDBCONV_Ctx{
Arena *arena;
// INPUT data
RADDBG_Arch arch;
U64 addr_size;
PDB_TpiHashParsed *hash;
CV_LeafParsed *leaf;
COFF_SectionHeader *sections;
U64 section_count;
// OUTPUT data
CONS_Root *root;
// TEMPORARY STATE
PDBCONV_FwdMap fwd_map;
PDBCONV_TypeRev *member_revisit_first;
PDBCONV_TypeRev *member_revisit_last;
PDBCONV_TypeRev *enum_revisit_first;
PDBCONV_TypeRev *enum_revisit_last;
PDBCONV_FrameProcMap frame_proc_map;
PDBCONV_ScopeNode *scope_stack;
PDBCONV_ScopeNode *scope_node_free;
PDBCONV_KnownGlobalSet known_globals;
PDBCONV_LinkNameMap link_names;
} PDBCONV_Ctx;
//- rjf: pdb conversion context creation
static PDBCONV_Ctx *pdbconv_ctx_alloc(PDBCONV_CtxParams *params, CONS_Root *out_root);
//- pdb types and symbols
static void pdbconv_types_and_symbols(PDBCONV_Ctx *pdb_ctx, PDBCONV_TypesSymbolsParams *params);
//- decoding helpers
static U32 pdbconv_u32_from_numeric(PDBCONV_Ctx *ctx, CV_NumericParsed *num);
static COFF_SectionHeader* pdbconv_sec_header_from_sec_num(PDBCONV_Ctx *ctx, U32 sec_num);
//- type info
// TODO(allen): explain the overarching pattern of PDB type info translation here
// 1. main passes (out of order necessity) & after
// 2. resolve forward
// 3. cons type info
// 4. "resolve itype"
// 5. equipping members & enumerates
// 6. equipping source coordinates
// type info construction passes
static void pdbconv_type_cons_main_passes(PDBCONV_Ctx *ctx);
static CV_TypeId pdbconv_type_resolve_fwd(PDBCONV_Ctx *ctx, CV_TypeId itype);
static CONS_Type* pdbconv_type_resolve_itype(PDBCONV_Ctx *ctx, CV_TypeId itype);
static void pdbconv_type_equip_members(PDBCONV_Ctx *ctx, CONS_Type *owern_type,
CV_TypeId field_itype);
static void pdbconv_type_equip_enumerates(PDBCONV_Ctx *ctx, CONS_Type *owner_type,
CV_TypeId field_itype);
// type info construction helpers
static CONS_Type* pdbconv_type_cons_basic(PDBCONV_Ctx *ctx, CV_TypeId itype);
static CONS_Type* pdbconv_type_cons_leaf_record(PDBCONV_Ctx *ctx, CV_TypeId itype);
static CONS_Type* pdbconv_type_resolve_and_check(PDBCONV_Ctx *ctx, CV_TypeId itype);
static void pdbconv_type_resolve_arglist(Arena *arena, CONS_TypeList *out,
PDBCONV_Ctx *ctx, CV_TypeId arglist_itype);
// type info resolution helpers
static CONS_Type* pdbconv_type_from_name(PDBCONV_Ctx *ctx, String8 name);
// type fwd map
static void pdbconv_type_fwd_map_set(Arena *arena, PDBCONV_FwdMap *map,
CV_TypeId key, CV_TypeId val);
static CV_TypeId pdbconv_type_fwd_map_get(PDBCONV_FwdMap *map, CV_TypeId key);
//- symbol info
// symbol info construction
static U64 pdbconv_hash_from_local_user_id(U64 sym_hash, U64 id);
static U64 pdbconv_hash_from_scope_user_id(U64 sym_hash, U64 id);
static U64 pdbconv_hash_from_symbol_user_id(U64 sym_hash, U64 id);
static void pdbconv_symbol_cons(PDBCONV_Ctx *ctx, CV_SymParsed *sym, U32 sym_unique_id);
static void pdbconv_gather_link_names(PDBCONV_Ctx *ctx, CV_SymParsed *sym);
// "frameproc" map
static void pdbconv_symbol_frame_proc_write(PDBCONV_Ctx *ctx,CONS_Symbol *key,
PDBCONV_FrameProcData *data);
static PDBCONV_FrameProcData* pdbconv_symbol_frame_proc_read(PDBCONV_Ctx *ctx, CONS_Symbol *key);
// scope stack
static void pdbconv_symbol_push_scope(PDBCONV_Ctx *ctx, CONS_Scope *scope, CONS_Symbol *symbol);
static void pdbconv_symbol_pop_scope(PDBCONV_Ctx *ctx);
static void pdbconv_symbol_clear_scope_stack(PDBCONV_Ctx *ctx);
#define pdbconv_symbol_current_scope(ctx) \
((ctx)->scope_stack == 0)?0:((ctx)->scope_stack->scope)
#define pdbconv_symbol_current_symbol(ctx) \
((ctx)->scope_stack == 0)?0:((ctx)->scope_stack->symbol)
// PDB/C++ name parsing helper
static U64 pdbconv_end_of_cplusplus_container_name(String8 str);
// global deduplication
static U64 pdbconv_known_global_hash(String8 name, U64 voff);
static B32 pdbconv_known_global_lookup(PDBCONV_KnownGlobalSet *set, String8 name, U64 voff);
static void pdbconv_known_global_insert(Arena *arena, PDBCONV_KnownGlobalSet *set,
String8 name, U64 voff);
// location info helpers
static CONS_Location* pdbconv_location_from_addr_reg_off(PDBCONV_Ctx *ctx,
RADDBG_RegisterCode reg_code,
U32 reg_byte_size,
U32 reg_byte_pos,
S64 offset,
B32 extra_indirection);
static CV_EncodedFramePtrReg pdbconv_cv_encoded_fp_reg_from_proc(PDBCONV_Ctx *ctx,
CONS_Symbol *proc,
B32 param_base);
static RADDBG_RegisterCode pdbconv_reg_code_from_arch_encoded_fp_reg(RADDBG_Arch arch,
CV_EncodedFramePtrReg encoded_reg);
static void pdbconv_location_over_lvar_addr_range(PDBCONV_Ctx *ctx,
CONS_LocationSet *locset,
CONS_Location *location,
CV_LvarAddrRange *range,
CV_LvarAddrGap *gaps, U64 gap_count);
// link names
static void pdbconv_link_name_save(Arena *arena, PDBCONV_LinkNameMap *map,
U64 voff, String8 name);
static String8 pdbconv_link_name_find(PDBCONV_LinkNameMap *map, U64 voff);
////////////////////////////////
//~ Conversion Output Type
typedef struct PDBCONV_Out PDBCONV_Out;
struct PDBCONV_Out
{
B32 good_parse;
CONS_Root *root;
String8List dump;
String8List errors;
};
////////////////////////////////
//~ Conversion Path
static PDBCONV_Out *pdbconv_convert(Arena *arena, PDBCONV_Params *params);
#endif //RADDBG_FROM_PDB_H
@@ -0,0 +1,116 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "base/base_inc.h"
#include "os/os_inc.h"
#include "coff/coff.h"
#include "raddbgi_format/raddbgi_format.h"
#include "raddbgi_cons/raddbgi_cons.h"
#include "raddbgi_coff.h"
#include "raddbgi_codeview.h"
#include "raddbgi_msf.h"
#include "raddbgi_pdb.h"
#include "raddbgi_coff_conversion.h"
#include "raddbgi_codeview_conversion.h"
#include "raddbgi_codeview_stringize.h"
#include "raddbgi_pdb_stringize.h"
#include "raddbgi_from_pdb.h"
#include "base/base_inc.c"
#include "coff/coff.c"
#include "os/os_inc.c"
#include "raddbgi_format/raddbgi_format.c"
#include "raddbgi_cons/raddbgi_cons.c"
#include "raddbgi_msf.c"
#include "raddbgi_codeview.c"
#include "raddbgi_pdb.c"
#include "raddbgi_coff_conversion.c"
#include "raddbgi_codeview_conversion.c"
#include "raddbgi_codeview_stringize.c"
#include "raddbgi_pdb_stringize.c"
#include "raddbgi_from_pdb.c"
int
main(int argc, char **argv){
local_persist TCTX main_thread_tctx = {0};
tctx_init_and_equip(&main_thread_tctx);
#if PROFILE_TELEMETRY
U64 tm_data_size = MB(512);
U8 *tm_data = os_reserve(tm_data_size);
os_commit(tm_data, tm_data_size);
tmLoadLibrary(TM_RELEASE);
tmSetMaxThreadCount(1024);
tmInitialize(tm_data_size, tm_data);
#endif
ThreadName("[main]");
Arena *arena = arena_alloc();
String8List args = os_string_list_from_argcv(arena, argc, argv);
CmdLine cmdline = cmd_line_from_string_list(arena, args);
ProfBeginCapture("raddbg_from_pdb");
//- rjf: parse arguments
PDBCONV_Params *params = pdb_convert_params_from_cmd_line(arena, &cmdline);
//- rjf: show input errors
if (params->errors.node_count > 0 &&
!params->hide_errors.input){
for (String8Node *node = params->errors.first;
node != 0;
node = node->next){
fprintf(stderr, "error(input): %.*s\n", str8_varg(node->string));
}
}
//- rjf: open output file
String8 output_name = push_str8_copy(arena, params->output_name);
FILE *out_file = fopen((char*)output_name.str, "wb");
if(out_file == 0 && !params->hide_errors.output)
{
fprintf(stderr, "error(output): could not open output file\n");
}
//- rjf: convert
PDBCONV_Out *out = 0;
if(out_file != 0)
{
out = pdbconv_convert(arena, params);
}
//- rjf: print dump
if(out != 0)
{
for(String8Node *node = out->dump.first; node != 0; node = node->next)
{
fwrite(node->string.str, 1, node->string.size, stdout);
}
}
//- rjf: bake file
if(out != 0 && out->good_parse && params->output_name.size > 0 && out->good_parse)
{
String8List baked = {0};
cons_bake_file(arena, out->root, &baked);
for(String8Node *node = baked.first; node != 0; node = node->next)
{
fwrite(node->string.str, node->string.size, 1, out_file);
}
}
//- rjf: close output file
if(out_file != 0)
{
fclose(out_file);
}
ProfEndCapture();
return(0);
}
+284
View File
@@ -0,0 +1,284 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ MSF Parser Function
static MSF_Parsed*
msf_parsed_from_data(Arena *arena, String8 msf_data){
ProfBegin("msf_parsed_from_data");
Temp scratch = scratch_begin(&arena, 1);
MSF_Parsed *result = 0;
//- determine msf type
U32 index_size = 0;
if (msf_data.size >= MSF_MIN_SIZE){
if (str8_match(msf_data, str8_lit(msf_msf20_magic),
StringMatchFlag_RightSideSloppy)){
index_size = 2;
}
else if (str8_match(msf_data, str8_lit(msf_msf70_magic),
StringMatchFlag_RightSideSloppy)){
index_size = 4;
}
}
if (index_size == 2 || index_size == 4){
//- extract info from header
U32 block_size_raw = 0;
U32 whole_file_block_count_raw = 0;
U32 directory_size_raw = 0;
U32 directory_super_map_raw = 0;
if (index_size == 2){
MSF_Header20 *header = (MSF_Header20*)(msf_data.str + MSF_MSF20_MAGIC_SIZE);
block_size_raw = header->block_size;
whole_file_block_count_raw = header->block_count;
directory_size_raw = header->directory_size;
}
else if (index_size == 4){
MSF_Header70 *header = (MSF_Header70*)(msf_data.str + MSF_MSF70_MAGIC_SIZE);
block_size_raw = header->block_size;
whole_file_block_count_raw = header->block_count;
directory_size_raw = header->directory_size;
directory_super_map_raw = header->directory_super_map;
}
//- setup important sizes & counts
// (blocks)
U32 block_size = ClampTop(block_size_raw, msf_data.size);
// (whole file block count)
U32 whole_file_block_count_max = CeilIntegerDiv(msf_data.size, block_size);
U32 whole_file_block_count = ClampTop(whole_file_block_count_raw, whole_file_block_count_max);
// (directory)
U32 directory_size = ClampTop(directory_size_raw, msf_data.size);
U32 block_count_in_directory = CeilIntegerDiv(directory_size, block_size);
// (map)
U32 directory_map_size = block_count_in_directory*index_size;
U32 block_count_in_directory_map = CeilIntegerDiv(directory_map_size, block_size);
// Layout of the "directory":
//
// super map: [s1, s2, s3, ...]
// map: s1 -> [i1, i2, i3, ...]; s2 -> [...]; s3 -> [...]; ...
// directory: i1 -> [data]; i2 -> [data]; i3 -> [data]; ... i1 -> [data]; ...
//
// The "data" in the directory describes streams:
// PDB20:
// struct Pdb20StreamSize{
// U32 size;
// U32 unknown; // looks like kind codes or revision counters or something
// }
// struct{
// U32 stream_count;
// Pdb20StreamSize stream_sizes[stream_count];
// U16 stream_indices[stream_count][...];
// }
//
// PDB70:
// struct{
// U32 stream_count;
// U32 stream_sizes[stream_count];
// U32 stream_indices[stream_count][...];
// }
//- parse stream directory
U8 *directory_buf = push_array(scratch.arena, U8, directory_size);
B32 got_directory = 1;
{
U32 directory_super_map_dummy = 0;
U32 *directory_super_map = 0;
U32 directory_map_block_skip_size = 0;
if (index_size == 2){
directory_super_map = &directory_super_map_dummy;
directory_map_block_skip_size =
MSF_MSF20_MAGIC_SIZE + OffsetOf(MSF_Header20, directory_map);
}
else{
U64 super_map_off =
MSF_MSF70_MAGIC_SIZE + OffsetOf(MSF_Header70, directory_super_map);
directory_super_map = (U32*)(msf_data.str + super_map_off);
}
U32 max_index_count_in_map_block = (block_size - directory_map_block_skip_size)/index_size;
// for each index in super map ...
U8 *out_ptr = directory_buf;
U32 *super_map_ptr = directory_super_map;
for (U32 i = 0; i < block_count_in_directory_map; i += 1, super_map_ptr += 1){
U32 directory_map_block_index = *super_map_ptr;
if (directory_map_block_index >= whole_file_block_count){
got_directory = 0;
goto parse_directory_done;
}
U64 directory_map_block_off = (U64)(directory_map_block_index)*block_size;
U8 *directory_map_block_base = (msf_data.str + directory_map_block_off);
// clamp index count by end of directory
U32 index_count = 0;
{
U32 directory_pos = (U32)(out_ptr - directory_buf);
U32 remaining_size = directory_size - directory_pos;
U32 remaining_map_block_count = CeilIntegerDiv(remaining_size, block_size);
index_count = ClampTop(max_index_count_in_map_block, remaining_map_block_count);
}
// for each index in map ...
U8 *map_ptr = directory_map_block_base + directory_map_block_skip_size;
for (U32 j = 0; j < index_count; j += 1, map_ptr += index_size){
// read index
U32 directory_block_index = 0;
if (index_size == 4){
directory_block_index = *(U32*)(map_ptr);
}
else{
directory_block_index = *(U16*)(map_ptr);
}
if (directory_block_index >= whole_file_block_count){
got_directory = 0;
goto parse_directory_done;
}
U64 directory_block_off = (U64)(directory_block_index)*block_size;
U8 *directory_block_base = (msf_data.str + directory_block_off);
// clamp copy size by end of directory
U32 copy_size = 0;
{
U32 directory_pos = (U32)(out_ptr - directory_buf);
U32 remaining_size = directory_size - directory_pos;
copy_size = ClampTop(block_size, remaining_size);
}
// copy block data
MemoryCopy(out_ptr, directory_block_base, copy_size);
out_ptr += copy_size;
}
}
parse_directory_done:;
}
//- parse streams from directory
U32 stream_count = 0;
B32 got_streams = 0;
String8 *streams = 0;
if (got_directory){
got_streams = 1;
// read stream count
U32 stream_count_raw = *(U32*)(directory_buf);
// setup counts, sizes, and offsets
U32 size_of_stream_entry = 4;
if (index_size == 2){
size_of_stream_entry = 8;
}
U32 stream_count_max = (directory_size - 4)/size_of_stream_entry;
U32 stream_count__inner = ClampTop(stream_count_raw, stream_count_max);
U32 all_stream_entries_off = 4;
U32 all_indices_off = all_stream_entries_off + stream_count__inner*size_of_stream_entry;
// set output buffer and count
stream_count = stream_count__inner;
streams = push_array(arena, String8, stream_count);
// iterate sizes and indices in lock step
U32 entry_cursor = all_stream_entries_off;
U32 index_cursor = all_indices_off;
String8 *stream_ptr = streams;
for (U32 i = 0; i < stream_count; i += 1){
// read stream size
U32 stream_size_raw = *(U32*)(directory_buf + entry_cursor);
if (stream_size_raw == 0xffffffff){
stream_size_raw = 0;
}
// compute block count
U32 stream_block_count_raw = CeilIntegerDiv(stream_size_raw, block_size);
U32 stream_block_count_max = (directory_size - index_cursor)/index_size;;
U32 stream_block_count = ClampTop(stream_block_count_raw, stream_block_count_max);
U32 stream_size = ClampTop(stream_size_raw, stream_block_count*block_size);
// copy stream data
U8 *stream_buf = push_array(arena, U8, stream_size);
stream_ptr->str = stream_buf;
stream_ptr->size = stream_size;
U32 sub_index_cursor = index_cursor;
U8 *stream_out_ptr = stream_buf;
for (U32 i = 0; i < stream_block_count; i += 1, sub_index_cursor += index_size){
// read index
U32 stream_block_index = 0;
if (index_size == 4){
stream_block_index = *(U32*)(directory_buf + sub_index_cursor);
}
else{
stream_block_index = *(U16*)(directory_buf + sub_index_cursor);
}
if (stream_block_index >= whole_file_block_count){
got_streams = 0;
goto parse_streams_done;
}
U64 stream_block_off = (U64)(stream_block_index)*block_size;
U8 *stream_block_base = (msf_data.str + stream_block_off);
// clamp copy size by end of stream
U32 copy_size = 0;
{
U32 stream_pos = (U32)(stream_out_ptr - stream_buf);
U32 remaining_size = stream_size - stream_pos;
copy_size = ClampTop(block_size, remaining_size);
}
// copy block data
MemoryCopy(stream_out_ptr, stream_block_base, copy_size);
stream_out_ptr += copy_size;
}
// advance cursors
entry_cursor += size_of_stream_entry;
index_cursor = sub_index_cursor;
stream_ptr += 1;
}
parse_streams_done:;
}
if (got_streams){
result = push_array(arena, MSF_Parsed, 1);
result->streams = streams;
result->stream_count = stream_count;
result->block_size = block_size;
result->block_count = whole_file_block_count;
}
}
scratch_end(scratch);
ProfEnd();
return(result);
}
static String8
msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn){
String8 result = {0};
if (sn < msf->stream_count){
result = msf->streams[sn];
}
return(result);
}
+60
View File
@@ -0,0 +1,60 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_MSF_H
#define RADDBG_MSF_H
////////////////////////////////
//~ MSF Format Types
#define MSF_INVALID_STREAM_NUMBER 0xFFFF
typedef U16 MSF_StreamNumber;
static char msf_msf20_magic[] = "Microsoft C/C++ program database 2.00\r\n\x1aJG\0\0";
static char msf_msf70_magic[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0";
#define MSF_MSF20_MAGIC_SIZE 44
#define MSF_MSF70_MAGIC_SIZE 32
#define MSF_MAX_MAGIC_SIZE 44
typedef struct MSF_Header20{
U32 block_size;
U16 free_block_map_block;
U16 block_count;
U32 directory_size;
U32 unknown;
U16 directory_map;
} MSF_Header20;
typedef struct MSF_Header70{
U32 block_size;
U32 free_block_map_block;
U32 block_count;
U32 directory_size;
U32 unknown;
U32 directory_super_map;
} MSF_Header70;
// magic(20) + header(20) = 44 + 20 = 64
// magic(70) + header(70) = 32 + 24 = 56
#define MSF_MIN_SIZE 64
////////////////////////////////
//~ MSF Parser Helper Types
typedef struct MSF_Parsed{
String8 *streams;
U64 stream_count;
U64 block_size;
U64 block_count;
} MSF_Parsed;
////////////////////////////////
//~ MSF Parser Function
static MSF_Parsed* msf_parsed_from_data(Arena *arena, String8 msf_data);
static String8 msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn);
#endif //RADDBG_MSF_H
File diff suppressed because it is too large Load Diff
+462
View File
@@ -0,0 +1,462 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_PDB_H
#define RADDBG_PDB_H
// https://github.com/microsoft/microsoft-pdb/tree/master/PDB
////////////////////////////////
//~ PDB Format Types
typedef U32 PDB_Version;
enum{
PDB_Version_VC2 = 19941610,
PDB_Version_VC4 = 19950623,
PDB_Version_VC41 = 19950814,
PDB_Version_VC50 = 19960307,
PDB_Version_VC98 = 19970604,
PDB_Version_VC70_DEP = 19990604,
PDB_Version_VC70 = 20000404,
PDB_Version_VC80 = 20030901,
PDB_Version_VC110 = 20091201,
PDB_Version_VC140 = 20140508
};
typedef U16 PDB_ModIndex;
typedef U32 PDB_StringIndex;
typedef enum PDB_FixedStream{
PDB_FixedStream_PdbInfo = 1,
PDB_FixedStream_Tpi = 2,
PDB_FixedStream_Dbi = 3,
PDB_FixedStream_Ipi = 4
} PDB_FixedStream;
typedef enum PDB_NamedStream{
PDB_NamedStream_HEADER_BLOCK,
PDB_NamedStream_STRTABLE,
PDB_NamedStream_LINK_INFO,
PDB_NamedStream_COUNT
} PDB_NamedStream;
typedef struct PDB_InfoHeader{
PDB_Version version;
U32 time;
U32 age;
} PDB_InfoHeader;
enum{
PDB_StrtblHeader_MAGIC = 0xEFFEEFFE
};
typedef struct PDB_StrtblHeader{
U32 magic;
U32 version;
} PDB_StrtblHeader;
////////////////////////////////
//~ PDB Format DBI Types
typedef U32 PDB_DbiStream;
enum{
PDB_DbiStream_FPO,
PDB_DbiStream_EXCEPTION,
PDB_DbiStream_FIXUP,
PDB_DbiStream_OMAP_TO_SRC,
PDB_DbiStream_OMAP_FROM_SRC,
PDB_DbiStream_SECTION_HEADER,
PDB_DbiStream_TOKEN_RDI_MAP,
PDB_DbiStream_XDATA,
PDB_DbiStream_PDATA,
PDB_DbiStream_NEW_FPO,
PDB_DbiStream_SECTION_HEADER_ORIG,
PDB_DbiStream_COUNT
};
typedef U32 PDB_DbiHeaderSignature;
enum{
PDB_DbiHeaderSignature_V1 = 0xFFFFFFFF
};
typedef U32 PDB_DbiVersion;
enum{
PDB_DbiVersion_41 = 930803,
PDB_DbiVersion_50 = 19960307,
PDB_DbiVersion_60 = 19970606,
PDB_DbiVersion_70 = 19990903,
PDB_DbiVersion_110 = 20091201,
};
typedef U16 PDB_DbiBuildNumber;
#define PDB_DbiBuildNumberNewFormatFlag 0x8000
#define PDB_DbiBuildNumberMinor(bn) ((bn)&0xFF)
#define PDB_DbiBuildNumberMajor(bn) (((bn) >> 8)&0x7F)
#define PDB_DbiBuildNumberNewFormat(bn) (!!((bn)&PDB_DbiBuildNumberNewFormatFlag))
#define PDB_DbiBuildNumber(maj, min) \
(PDB_DbiBuildNumberNewFormatFlag | ((min)&0xFF) | (((maj)&0x7F) << 16))
typedef U16 PDB_DbiHeaderFlags;
enum{
PDB_DbiHeaderFlag_Incremental = 0x1,
PDB_DbiHeaderFlag_Stripped = 0x2,
PDB_DbiHeaderFlag_CTypes = 0x4
};
typedef struct PDB_DbiHeader{
PDB_DbiHeaderSignature sig;
PDB_DbiVersion version;
U32 age;
MSF_StreamNumber gsi_sn;
PDB_DbiBuildNumber build_number;
MSF_StreamNumber psi_sn;
U16 pdb_version;
MSF_StreamNumber sym_sn;
U16 pdb_version2;
U32 module_info_size;
U32 sec_con_size;
U32 sec_map_size;
U32 file_info_size;
U32 tsm_size;
U32 mfc_index;
U32 dbg_header_size;
U32 ec_info_size;
PDB_DbiHeaderFlags flags;
COFF_Arch machine;
U32 reserved;
} PDB_DbiHeader;
// (this is not "literally" defined by the format - but helpful to have)
typedef enum PDB_DbiRange{
PDB_DbiRange_ModuleInfo,
PDB_DbiRange_SecCon,
PDB_DbiRange_SecMap,
PDB_DbiRange_FileInfo,
PDB_DbiRange_TSM,
PDB_DbiRange_EcInfo,
PDB_DbiRange_DbgHeader,
PDB_DbiRange_COUNT
} PDB_DbiRange;
// "ModuleInfo" DBI range
typedef U32 PDB_DbiSectionContribVersion;
#define PDB_DbiSectionContribVersion_1 (0xeffe0000u + 19970605u)
#define PDB_DbiSectionContribVersion_2 (0xeffe0000u + 20140516u)
typedef struct PDB_DbiSectionContrib40{
CV_SectionIndex sec;
U32 sec_off;
U32 size;
U32 flags;
PDB_ModIndex mod;
} PDB_DbiSectionContrib40;
typedef struct PDB_DbiSectionContrib{
PDB_DbiSectionContrib40 base;
U32 data_crc;
U32 reloc_crc;
} PDB_DbiSectionContrib;
typedef struct PDB_DbiSectionContrib2{
PDB_DbiSectionContrib40 base;
U32 data_crc;
U32 reloc_crc;
U32 sec_coff;
} PDB_DbiSectionContrib2;
typedef struct PDB_DbiCompUnitHeader{
U32 unused;
PDB_DbiSectionContrib contribution;
U16 flags; // unknown
MSF_StreamNumber sn;
U32 symbols_size;
U32 c11_lines_size;
U32 c13_lines_size;
U16 num_contrib_files;
U16 unused2;
U32 file_names_offset;
PDB_StringIndex src_file;
PDB_StringIndex pdb_file;
// U8[] module_name (null terminated)
// U8[] obj_name (null terminated)
} PDB_DbiCompUnitHeader;
// (this is not "literally" defined by the format - but helpful to have)
typedef enum{
PDB_DbiCompUnitRange_Symbols,
PDB_DbiCompUnitRange_C11,
PDB_DbiCompUnitRange_C13,
PDB_DbiCompUnitRange_COUNT
} PDB_DbiCompUnitRange;
////////////////////////////////
//~ PDB Format TPI Types
typedef U32 PDB_TpiVersion;
enum{
PDB_TpiVersion_INTV_VC2 = 920924,
PDB_TpiVersion_IMPV40 = 19950410,
PDB_TpiVersion_IMPV41 = 19951122,
PDB_TpiVersion_IMPV50_INTERIM = 19960307,
PDB_TpiVersion_IMPV50 = 19961031,
PDB_TpiVersion_IMPV70 = 19990903,
PDB_TpiVersion_IMPV80 = 20040203,
};
typedef struct PDB_TpiHeader{
// (HDR)
PDB_TpiVersion version;
U32 header_size;
U32 ti_lo;
U32 ti_hi;
U32 leaf_data_size;
// (PdbTpiHash)
MSF_StreamNumber hash_sn;
MSF_StreamNumber hash_sn_aux;
U32 hash_key_size;
U32 hash_bucket_count;
U32 hash_vals_off;
U32 hash_vals_size;
U32 itype_off;
U32 itype_size;
U32 hash_adj_off;
U32 hash_adj_size;
} PDB_TpiHeader;
typedef struct PDB_TpiOffHint{
CV_TypeId itype;
U32 off;
} PDB_TpiOffHint;
////////////////////////////////
//~ PDB Format GSI Types
typedef U32 PDB_GsiSignature;
enum{
PDB_GsiSignature_Basic = 0xffffffff,
};
typedef U32 PDB_GsiVersion;
enum{
PDB_GsiVersion_V70 = 0xeffe0000 + 19990810,
};
typedef struct PDB_GsiHeader{
PDB_GsiSignature signature;
PDB_GsiVersion version;
U32 hr_len;
U32 num_buckets;
} PDB_GsiHeader;
typedef struct PDB_GsiHashRecord{
U32 symbol_off;
U32 cref;
} PDB_GsiHashRecord;
typedef struct PDB_PsiHeader{
U32 sym_hash_size;
U32 addr_map_size;
U32 thunk_count;
U32 thunk_size;
CV_SectionIndex isec_thunk_table;
U16 padding;
U32 sec_thunk_table_off;
U32 sec_count;
} PDB_PsiHeader;
////////////////////////////////
//~ PDB Parser Types
typedef struct PDB_InfoNode{
struct PDB_InfoNode *next;
String8 string;
MSF_StreamNumber sn;
} PDB_InfoNode;
typedef struct PDB_Info{
PDB_InfoNode *first;
PDB_InfoNode *last;
COFF_Guid auth_guid;
} PDB_Info;
typedef struct PDB_NamedStreamTable{
MSF_StreamNumber sn[PDB_NamedStream_COUNT];
} PDB_NamedStreamTable;
typedef struct PDB_Strtbl{
String8 data;
U32 bucket_count;
U32 strblock_min;
U32 strblock_max;
U32 buckets_min;
U32 buckets_max;
} PDB_Strtbl;
typedef struct PDB_DbiParsed{
String8 data;
COFF_Arch arch;
MSF_StreamNumber gsi_sn;
MSF_StreamNumber psi_sn;
MSF_StreamNumber sym_sn;
U64 range_off[(U64)(PDB_DbiRange_COUNT) + 1];
MSF_StreamNumber dbg_streams[PDB_DbiStream_COUNT];
} PDB_DbiParsed;
typedef struct PDB_TpiParsed{
String8 data;
// leaf info
U64 leaf_first;
U64 leaf_opl;
U32 itype_first;
U32 itype_opl;
// hash info
MSF_StreamNumber hash_sn;
MSF_StreamNumber hash_sn_aux;
U32 hash_key_size;
U32 hash_bucket_count;
U32 hash_vals_off;
U32 hash_vals_size;
U32 itype_off;
U32 itype_size;
U32 hash_adj_off;
U32 hash_adj_size;
} PDB_TpiParsed;
typedef struct PDB_TpiHashBlock{
struct PDB_TpiHashBlock *next;
U32 local_count;
CV_TypeId itypes[13]; // 13 = (64 - 12)/4
} PDB_TpiHashBlock;
typedef struct PDB_TpiHashParsed{
String8 data;
String8 aux_data;
PDB_TpiHashBlock **buckets;
U32 bucket_count;
U32 bucket_mask;
} PDB_TpiHashParsed;
typedef struct PDB_GsiBucket{
U32 *offs;
U64 count;
} PDB_GsiBucket;
typedef struct PDB_GsiParsed{
PDB_GsiBucket buckets[4096];
} PDB_GsiParsed;
typedef struct PDB_CompUnit{
MSF_StreamNumber sn;
U32 range_off[(U32)(PDB_DbiCompUnitRange_COUNT) + 1];
String8 obj_name;
String8 group_name;
} PDB_CompUnit;
typedef struct PDB_CoffSectionArray{
COFF_SectionHeader *sections;
U64 count;
} PDB_CoffSectionArray;
typedef struct PDB_CompUnitNode{
struct PDB_CompUnitNode *next;
PDB_CompUnit unit;
} PDB_CompUnitNode;
typedef struct PDB_CompUnitArray{
PDB_CompUnit **units;
U64 count;
} PDB_CompUnitArray;
typedef struct PDB_CompUnitContribution{
U32 mod;
U64 voff_first;
U64 voff_opl;
} PDB_CompUnitContribution;
typedef struct PDB_CompUnitContributionArray{
PDB_CompUnitContribution *contributions;
U64 count;
} PDB_CompUnitContributionArray;
////////////////////////////////
//~ PDB Parser Functions
static PDB_Info* pdb_info_from_data(Arena *arena, String8 pdb_info_data);
static PDB_NamedStreamTable*pdb_named_stream_table_from_info(Arena *arena, PDB_Info *info);
static PDB_Strtbl* pdb_strtbl_from_data(Arena *arena, String8 strtbl_data);
static PDB_DbiParsed* pdb_dbi_from_data(Arena *arena, String8 dbi_data);
static PDB_TpiParsed* pdb_tpi_from_data(Arena *arena, String8 tpi_data);
static PDB_TpiHashParsed* pdb_tpi_hash_from_data(Arena *arena,
PDB_Strtbl *strtbl,
PDB_TpiParsed *tpi,
String8 tpi_hash_data,
String8 tpi_hash_aux_data);
static PDB_GsiParsed* pdb_gsi_from_data(Arena *arena, String8 gsi_data);
static PDB_CoffSectionArray*pdb_coff_section_array_from_data(Arena *arena,
String8 section_data);
static PDB_CompUnitArray* pdb_comp_unit_array_from_data(Arena *arena,
String8 module_info_data);
static PDB_CompUnitContributionArray*
pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 seccontrib_data,
PDB_CoffSectionArray *sections);
////////////////////////////////
//~ PDB Definition Functions
static U32 pdb_string_hash1(String8 string);
////////////////////////////////
//~ PDB Dbi Functions
static String8 pdb_data_from_dbi_range(PDB_DbiParsed *dbi, PDB_DbiRange range);
static String8 pdb_data_from_unit_range(MSF_Parsed *msf, PDB_CompUnit *unit,
PDB_DbiCompUnitRange range);
////////////////////////////////
//~ PDB Tpi Functions
static String8 pdb_leaf_data_from_tpi(PDB_TpiParsed *tpi);
static CV_TypeIdArray pdb_tpi_itypes_from_name(Arena *arena,
PDB_TpiHashParsed *tpi_hash,
CV_LeafParsed *tpi_leaf,
String8 name,
B32 compare_unique_name,
U32 output_cap);
static CV_TypeId pdb_tpi_first_itype_from_name(PDB_TpiHashParsed *tpi_hash,
CV_LeafParsed *tpi_leaf,
String8 name,
B32 compare_unique_name);
////////////////////////////////
//~ PDB Strtbl Functions
static String8 pdb_strtbl_string_from_off(PDB_Strtbl *strtbl, U32 off);
static String8 pdb_strtbl_string_from_index(PDB_Strtbl *strtbl,
PDB_StringIndex idx);
#endif //RADDBG_PDB_H
@@ -0,0 +1,26 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ PDB Stringize Functions
static void
pdb_stringize_tpi_hash(Arena *arena, String8List *out, PDB_TpiHashParsed *hash){
U32 bucket_count = hash->bucket_count;
str8_list_pushf(arena, out, "bucket_count=%u\n\n", bucket_count);
for (U32 i = 0; i < bucket_count; i += 1){
if (hash->buckets[i] != 0){
str8_list_pushf(arena, out, "bucket[%u]:\n", i);
for (PDB_TpiHashBlock *block = hash->buckets[i];
block != 0;
block = block->next){
U32 local_count = block->local_count;
CV_TypeId *itype_ptr = block->itypes;
for (U32 j = 0; j < local_count; j += 1, itype_ptr += 1){
str8_list_pushf(arena, out, " %u\n", *itype_ptr);
}
}
str8_list_push(arena, out, str8_lit("\n"));
}
}
}
@@ -0,0 +1,12 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_PDB_STRINGIZE_H
#define RADDBG_PDB_STRINGIZE_H
////////////////////////////////
//~ PDB Stringize Functions
static void pdb_stringize_tpi_hash(Arena *arena, String8List *out, PDB_TpiHashParsed *hash);
#endif //RADDBG_PDB_STRINGIZE_H