initial upload

This commit is contained in:
Ryan Fleury
2024-01-10 19:57:50 -08:00
commit a42ec6aeff
308 changed files with 162362 additions and 0 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,905 @@
// 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 "raddbg_format/raddbg_format.h"
#include "raddbg_cons/raddbg_cons.h"
#include "raddbg_elf.h"
#include "raddbg_dwarf.h"
#include "raddbg_dwarf_stringize.h"
#include "raddbg_from_dwarf.h"
#include "base/base_inc.c"
#include "os/os_inc.c"
#include "raddbg_format/raddbg_format.c"
#include "raddbg_cons/raddbg_cons.c"
#include "raddbg_elf.c"
#include "raddbg_dwarf.c"
#include "raddbg_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
+488
View File
@@ -0,0 +1,488 @@
// 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){
CV_C13_SubSecLinesHeader *hdr = (CV_C13_SubSecLinesHeader*)first;
// read file
U32 file_info_off = sizeof(*hdr);
if (file_info_off + sizeof(CV_C13_File) <= cap){
CV_C13_File *file = (CV_C13_File*)(first + file_info_off);
// extract top level info
U32 sec_idx = hdr->sec;
if (1 <= sec_idx && sec_idx <= sections->count){
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;
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 = file_info_off + sizeof(*file);
U32 line_count_max = (cap - 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_C13LinesParsed *lines_parsed = push_array(arena, CV_C13LinesParsed, 1);
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;
node->lines = lines_parsed;
}
}
}
}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
+262
View File
@@ -0,0 +1,262 @@
// 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__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[1<<24];
} 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[1<<24];
} 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[1<<24];
} PDBCONV_KnownGlobalSet;
typedef struct PDBCONV_TypesSymbolsParams{
RADDBG_Arch architecture;
CV_SymParsed *sym;
CV_SymParsed **sym_for_unit;
U64 unit_count;
PDB_TpiHashParsed *tpi_hash;
CV_LeafParsed *tpi_leaf;
PDB_CoffSectionArray *sections;
} PDBCONV_TypesSymbolsParams;
typedef struct PDBCONV_LinkNameNode{
struct PDBCONV_LinkNameNode *next;
U64 voff;
String8 name;
} PDBCONV_LinkNameNode;
typedef struct PDBCONV_LinkNameMap{
PDBCONV_LinkNameNode *buckets[1<<24];
} PDBCONV_LinkNameMap;
typedef struct PDBCONV_Ctx{
// 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
Arena *temp_arena;
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;
//- pdb types and symbols
static void pdbconv_types_and_symbols(PDBCONV_TypesSymbolsParams *params, CONS_Root *out_root);
//- 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 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_pos,
U32 reg_byte_size,
U64 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 "raddbg_format/raddbg_format.h"
#include "raddbg_cons/raddbg_cons.h"
#include "raddbg_coff.h"
#include "raddbg_codeview.h"
#include "raddbg_msf.h"
#include "raddbg_pdb.h"
#include "raddbg_coff_conversion.h"
#include "raddbg_codeview_conversion.h"
#include "raddbg_codeview_stringize.h"
#include "raddbg_pdb_stringize.h"
#include "raddbg_from_pdb.h"
#include "base/base_inc.c"
#include "coff/coff.c"
#include "os/os_inc.c"
#include "raddbg_format/raddbg_format.c"
#include "raddbg_cons/raddbg_cons.c"
#include "raddbg_msf.c"
#include "raddbg_codeview.c"
#include "raddbg_pdb.c"
#include "raddbg_coff_conversion.c"
#include "raddbg_codeview_conversion.c"
#include "raddbg_codeview_stringize.c"
#include "raddbg_pdb_stringize.c"
#include "raddbg_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 = 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_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
+908
View File
@@ -0,0 +1,908 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ PDB Parser Functions
static PDB_Info*
pdb_info_from_data(Arena *arena, String8 data){
ProfBegin("pdb_info_from_data");
// get header
PDB_InfoHeader *header = 0;
if (data.size >= sizeof(*header)){
header = (PDB_InfoHeader*)data.str;
}
PDB_Info *result = 0;
if (header != 0){
// read guid
COFF_Guid *auth_guid = 0;
U32 after_auth_guid_off = sizeof(*header);
switch (header->version){
case PDB_Version_VC70_DEP:
case PDB_Version_VC70:
case PDB_Version_VC80:
case PDB_Version_VC110:
case PDB_Version_VC140:
{
auth_guid = (COFF_Guid*)(data.str + after_auth_guid_off);
after_auth_guid_off = sizeof(*header) + sizeof(*auth_guid);
}break;
default:
{}break;
}
if (header->version != 0){
// table layout: names
U32 names_len_off = after_auth_guid_off;
U32 names_len = 0;
if (names_len_off + 4 <= data.size){
names_len = *(U32*)(data.str + names_len_off);
}
U32 names_base_off = names_len_off + 4;
U32 names_base_opl = names_base_off + names_len;
// table layout: hash table
U32 hash_table_count_off = names_base_opl;
U32 hash_table_max_off = hash_table_count_off + 4;
U32 hash_table_count = 0;
U32 hash_table_max = 0;
if (hash_table_max_off + 4 <= data.size){
hash_table_count = *(U32*)(data.str + hash_table_count_off);
hash_table_max = *(U32*)(data.str + hash_table_max_off);
}
// table layout: words
U32 num_present_words_off = hash_table_max_off + 4;
U32 num_present_words = 0;
if (hash_table_max_off + 4 <= data.size){
num_present_words = *(U32*)(data.str + num_present_words_off);
}
U32 present_words_array_off = num_present_words_off + 4;
U32 num_deleted_words_off = present_words_array_off + num_present_words*sizeof(U32);
U32 num_deleted_words = 0;
if (num_deleted_words_off + 4 <= data.size){
num_deleted_words = *(U32*)(data.str + num_deleted_words_off);
}
U32 deleted_words_array_off = num_deleted_words_off + 4;
// table layout: epilogue
U32 epilogue_base_off = deleted_words_array_off + num_deleted_words*sizeof(U32);
// read table
if (hash_table_count > 0 && epilogue_base_off <= data.size){
PDB_InfoNode *first = 0;
PDB_InfoNode *last = 0;
U32 record_off = epilogue_base_off;
for (U32 i = 0; i < hash_table_count; i += 1, record_off += 8){
U32 *record = (U32*)(data.str + record_off);
U32 relative_name_off = record[0];
MSF_StreamNumber sn = (MSF_StreamNumber)record[1];
U32 name_off = names_base_off + relative_name_off;
String8 name = str8_cstring_capped((char*)(data.str + name_off),
(char*)(data.str + names_base_opl));
// push info node
PDB_InfoNode *node = push_array(arena, PDB_InfoNode, 1);
SLLQueuePush(first, last, node);
node->string = name;
node->sn = sn;
}
result = push_array(arena, PDB_Info, 1);
result->first = first;
result->last = last;
result->auth_guid = *auth_guid;
}
}
}
ProfEnd();
return(result);
}
static PDB_NamedStreamTable*
pdb_named_stream_table_from_info(Arena *arena, PDB_Info *info){
ProfBegin("pdb_named_stream_table_from_info");
// mapping "NamedStream" indexes to strings
struct StreamNameIndexPair{
PDB_NamedStream index;
String8 name;
};
struct StreamNameIndexPair pairs[] = {
{PDB_NamedStream_HEADER_BLOCK, str8_lit("/src/headerblock")},
{PDB_NamedStream_STRTABLE , str8_lit("/names")},
{PDB_NamedStream_LINK_INFO , str8_lit("/LinkInfo")},
};
// build baked table
PDB_NamedStreamTable *result = push_array(arena, PDB_NamedStreamTable, 1);
struct StreamNameIndexPair *p = pairs;
for (U64 i = 0; i < ArrayCount(pairs); i += 1, p += 1){
String8 name = p->name;
// get info node with this name
PDB_InfoNode *match = 0;
for (PDB_InfoNode *node = info->first;
node != 0;
node = node->next){
if (str8_match(name, node->string, 0)){
match = node;
break;
}
}
// if match found save stream number
if (match != 0){
result->sn[p->index] = match->sn;
}
else{
result->sn[p->index] = 0xFFFF;
}
}
ProfEnd();
return(result);
}
static PDB_Strtbl*
pdb_strtbl_from_data(Arena *arena, String8 data){
ProfBegin("pdb_strtbl_from_data");
// get header
PDB_StrtblHeader *header = 0;
if (sizeof(*header) <= data.size){
header = (PDB_StrtblHeader*)data.str;
}
PDB_Strtbl *result = 0;
if (header != 0 && header->magic == PDB_StrtblHeader_MAGIC && header->version == 1){
U32 strblock_size_off = sizeof(*header);
U32 strblock_size = 0;
if (strblock_size_off + 4 <= data.size){
strblock_size = *(U32*)(data.str + strblock_size_off);
}
U32 strblock_off = strblock_size_off + 4;
U32 bucket_count_off = strblock_off + strblock_size;
U32 bucket_count = 0;
if (bucket_count_off + 4 <= data.size){
bucket_count = *(U32*)(data.str + bucket_count_off);
}
U32 bucket_array_off = bucket_count_off + 4;
U32 bucket_array_size = bucket_count*sizeof(PDB_StringIndex);
if (bucket_array_off + bucket_array_size <= data.size){
result = push_array(arena, PDB_Strtbl, 1);
result->data = data;
result->bucket_count = bucket_count;
result->strblock_min = strblock_off;
result->strblock_max = strblock_off + strblock_size;
result->buckets_min = bucket_array_off;
result->buckets_min = bucket_array_off + bucket_array_size;
}
}
ProfEnd();
return(result);
}
static PDB_DbiParsed*
pdb_dbi_from_data(Arena *arena, String8 data){
ProfBegin("pdb_dbi_from_data");
// get header
PDB_DbiHeader *header = 0;
if (sizeof(*header) <= data.size){
header = (PDB_DbiHeader*)data.str;
}
PDB_DbiParsed *result = 0;
if (header != 0 && header->sig == PDB_DbiHeaderSignature_V1){
// extract range sizes
U64 range_size[PDB_DbiRange_COUNT];
range_size[PDB_DbiRange_ModuleInfo] = header->module_info_size;
range_size[PDB_DbiRange_SecCon] = header->sec_con_size;
range_size[PDB_DbiRange_SecMap] = header->sec_map_size;
range_size[PDB_DbiRange_FileInfo] = header->file_info_size;
range_size[PDB_DbiRange_TSM] = header->tsm_size;
range_size[PDB_DbiRange_EcInfo] = header->ec_info_size;
range_size[PDB_DbiRange_DbgHeader] = header->dbg_header_size;
// fill result
result = push_array(arena, PDB_DbiParsed, 1);
result->data = data;
result->arch = header->machine;
result->gsi_sn = header->gsi_sn;
result->psi_sn = header->psi_sn;
result->sym_sn = header->sym_sn;
// fill result's range offsets
{
U64 cursor = sizeof(*header);
for (U64 i = 0; i < (U64)(PDB_DbiRange_COUNT); i += 1){
result->range_off[i] = cursor;
cursor += range_size[i];
cursor = ClampTop(cursor, data.size);
}
result->range_off[PDB_DbiRange_COUNT] = cursor;
}
// fill result's debug streams
U64 dbg_streams_min = result->range_off[PDB_DbiRange_DbgHeader];
U64 dbg_streams_max = result->range_off[PDB_DbiRange_DbgHeader + 1];
U64 dbg_streams_size_raw = dbg_streams_max - dbg_streams_min;
U64 dbg_streams_size = ClampTop(dbg_streams_size_raw, sizeof(result->dbg_streams));
MemoryCopy(result->dbg_streams, data.str + dbg_streams_min, dbg_streams_size);
if (dbg_streams_size < sizeof(result->dbg_streams)){
U64 filled_count = dbg_streams_size/sizeof(MSF_StreamNumber);
MemorySet(result->dbg_streams + filled_count, 0xff,
(ArrayCount(result->dbg_streams) - filled_count)*sizeof(MSF_StreamNumber));
}
}
ProfEnd();
return(result);
}
static PDB_TpiParsed*
pdb_tpi_from_data(Arena *arena, String8 data){
ProfBegin("pdb_tpi_from_data");
// get header
PDB_TpiHeader *header = 0;
if (sizeof(*header) <= data.size){
header = (PDB_TpiHeader*)data.str;
}
PDB_TpiParsed *result = 0;
if (header != 0 && header->version == PDB_TpiVersion_IMPV80){
U64 leaf_first_raw = header->header_size;
U64 leaf_first = ClampTop(leaf_first_raw, data.size);
U64 leaf_opl_raw = leaf_first + header->leaf_data_size;
U64 leaf_opl = ClampTop(leaf_opl_raw, data.size);
result = push_array(arena, PDB_TpiParsed, 1);
result->data = data;
result->leaf_first = leaf_first;
result->leaf_opl = leaf_opl;
result->itype_first = header->ti_lo;
result->itype_opl = header->ti_hi;
result->hash_sn = header->hash_sn;
result->hash_sn_aux = header->hash_sn_aux;
result->hash_key_size = header->hash_key_size;
result->hash_bucket_count = header->hash_bucket_count;
result->hash_vals_off = header->hash_vals_off;
result->hash_vals_size = header->hash_vals_size;
result->itype_off = header->itype_off;
result->itype_size = header->itype_size;
result->hash_adj_off = header->hash_adj_off;
result->hash_adj_size = header->hash_adj_size;
}
ProfEnd();
return(result);
}
static PDB_TpiHashParsed*
pdb_tpi_hash_from_data(Arena *arena, PDB_TpiParsed *tpi, String8 data, String8 aux_data){
ProfBegin("pdb_tpi_hash_from_data");
PDB_TpiHashParsed *result = 0;
U32 stride = tpi->hash_key_size;
U32 bucket_count = tpi->hash_bucket_count;
if (1 <= stride && stride <= 8 && bucket_count > 0){
// allocate buckets
PDB_TpiHashBlock **buckets = push_array(arena, PDB_TpiHashBlock*, bucket_count);
// extract "hash" array
U8 *hashes = data.str + tpi->hash_vals_off;
U8 *hash_opl = hashes + tpi->hash_vals_size;
// for each index in the array...
CV_TypeId itype = tpi->itype_first;
U8 *hash_cursor = hashes;
for (;hash_cursor + stride <= hash_opl;){
// read index
U64 bucket_idx = 0;
MemoryCopy(&bucket_idx, hash_cursor, stride);
// save to map
if (bucket_idx < bucket_count){
PDB_TpiHashBlock *block = buckets[bucket_idx];
if (block == 0 || block->local_count == ArrayCount(block->itypes)){
block = push_array(arena, PDB_TpiHashBlock, 1);
SLLStackPush(buckets[bucket_idx], block);
}
block->itypes[block->local_count] = itype;
block->local_count += 1;
}
// advance cursor
hash_cursor += stride;
itype += 1;
}
// fill result
result = push_array(arena, PDB_TpiHashParsed, 1);
result->data = data;
result->aux_data = aux_data;
result->buckets = buckets;
result->bucket_count = bucket_count;
if (IsPow2OrZero(bucket_count)){
result->bucket_mask = bucket_count - 1;
}
}
ProfEnd();
return(result);
}
static PDB_GsiParsed*
pdb_gsi_from_data(Arena *arena, String8 data){
ProfBegin("pdb_gsi_from_data");
// get header
PDB_GsiHeader *header = 0;
if (sizeof(*header) <= data.size){
header = (PDB_GsiHeader*)data.str;
}
PDB_GsiParsed *result = 0;
if (header != 0 && header->signature == PDB_GsiSignature_Basic &&
header->version == PDB_GsiVersion_V70 && header->num_buckets != 0){
Temp scratch = scratch_begin(&arena, 1);
// hash offset
U32 hash_record_array_off = sizeof(*header);
// bucket count
U32 slot_count = 4097;
// array offsets
U32 bitmask_u32_count = CeilIntegerDiv(slot_count, 32);
U32 bitmask_byte_size = bitmask_u32_count*4;
U32 bitmask_off = hash_record_array_off + header->hr_len;
U32 offsets_off = bitmask_off + bitmask_byte_size;
// get bitmask & packed offset arrays
U8 *bitmasks = 0;
U8 *packed_offsets = 0;
if (bitmask_off + bitmask_byte_size <= data.size){
bitmasks = (data.str + bitmask_off);
packed_offsets = (data.str + offsets_off);
}
U32 packed_offset_count = (data.size - offsets_off)/4;
// unpack
U32 *unpacked_offsets = 0;
if (packed_offsets != 0){
unpacked_offsets = push_array(scratch.arena, U32, slot_count);
U32 *bitmask_ptr = (U32*)bitmasks;
U32 *bitmask_opl = bitmask_ptr + bitmask_u32_count;
U32 *src_ptr = (U32*)packed_offsets;
U32 *src_opl = src_ptr + packed_offset_count;
U32 *dst_ptr = unpacked_offsets;
U32 *dst_opl = dst_ptr + slot_count;
for (; bitmask_ptr < bitmask_opl && src_ptr < src_opl; bitmask_ptr += 1){
U32 bits = *bitmask_ptr;
U32 src_max = (U32)(src_opl - src_ptr);
U32 dst_max = (U32)(dst_opl - dst_ptr);
U32 k_max0 = ClampTop(32, dst_max);
U32 k_max = ClampTop(k_max0, src_max);
for (U32 k = 0; k < k_max; k += 1){
if ((bits & 1) == 1){
*dst_ptr = *src_ptr;
src_ptr += 1;
}
else{
*dst_ptr = 0xFFFFFFFF;
}
dst_ptr += 1;
bits >>= 1;
}
}
for (; dst_ptr < dst_opl; dst_ptr += 1){
*dst_ptr = 0xFFFFFFFF;
}
}
// construct table
B32 bad_table = 0;
if (unpacked_offsets != 0){
result = push_array(arena, PDB_GsiParsed, 1);
// hash records
PDB_GsiHashRecord *hash_records = (PDB_GsiHashRecord*)(data.str + hash_record_array_off);
U32 hash_record_count = header->hr_len/sizeof(PDB_GsiHashRecord);
// * We unpack hash records into the the table by scanning backwards through the
// * hash records. Neighboring values in unpacked_offsets *sort of* form counts, but we
// * have to skip the max-U32s (sloppy PDB nonsense).
// * PDBs put one extra slot at the beginning of the encoded buckets that is mean
// * to be padding for modifying the buffer in place. After decoding there are 4096 buckets,
// * in the encoded buckets there are 4097. We are meant to drop the first one.
// build table
PDB_GsiHashRecord *hash_record_ptr = hash_records + hash_record_count - 1;
U32 prev_n = hash_record_count;
for (U32 i = slot_count; i > 1;){
i -= 1;
if (unpacked_offsets[i] != 0xFFFFFFFF){
// determine hash record range to use
// * The "12" here is the result of some really sloppy PDB magic.
U32 n = unpacked_offsets[i]/12;
if (n > prev_n){
bad_table = 1;
break;
}
U32 num_steps = prev_n - n;
// fill this bucket
arena_push_align(arena, 4);
U32 *bucket_offs = push_array_no_zero(arena, U32, num_steps);
for (U32 j = num_steps; j > 0;){
j -= 1;
// * The "- 1" is more sloppy PDB magic.
bucket_offs[j] = hash_record_ptr->symbol_off - 1;
hash_record_ptr -= 1;
}
PDB_GsiBucket *bucket = &result->buckets[i - 1];
bucket->count = num_steps;
bucket->offs = bucket_offs;
// update prev_n
prev_n = n;
}
}
}
scratch_end(scratch);
}
ProfEnd();
return(result);
}
static PDB_CoffSectionArray*
pdb_coff_section_array_from_data(Arena *arena, String8 data){
U64 count = data.size/sizeof(COFF_SectionHeader);
PDB_CoffSectionArray *result = push_array(arena, PDB_CoffSectionArray, 1);
result->sections = (COFF_SectionHeader*)data.str;
result->count = count;
return(result);
}
static PDB_CompUnitArray*
pdb_comp_unit_array_from_data(Arena *arena, String8 data){
PDB_CompUnitNode *first = 0;
PDB_CompUnitNode *last = 0;
U64 count = 0;
U64 cursor = 0;
for (;cursor + sizeof(PDB_DbiCompUnitHeader) <= data.size;){
// get header
PDB_DbiCompUnitHeader *header = (PDB_DbiCompUnitHeader*)(data.str + cursor);
// get names
U64 name_off = cursor + sizeof(*header);
String8 name = str8_cstring_capped((char *)(data.str + name_off), (char *)(data.str + data.size));
U64 name2_off = name_off + name.size + 1;
String8 name2 = str8_cstring_capped((char *)(data.str + name2_off), (char *)(data.str + data.size));
U64 after_name2_off = name2_off + name2.size + 1;
// save mod info
PDB_CompUnitNode *node = push_array_no_zero(arena, PDB_CompUnitNode, 1);
SLLQueuePush(first, last, node);
count += 1;
node->unit.sn = header->sn;
node->unit.obj_name = name;
node->unit.group_name = name2;
// fill range offsets
U32 *range_buf = node->unit.range_off;
{
// fill the buffer with size of each range
range_buf[PDB_DbiCompUnitRange_Symbols] = header->symbols_size;
range_buf[PDB_DbiCompUnitRange_C11] = header->c11_lines_size;
range_buf[PDB_DbiCompUnitRange_C13] = header->c13_lines_size;
Assert(PDB_DbiCompUnitRange_C13 + 1 == PDB_DbiCompUnitRange_COUNT);
// in-place sizes -> offs conversion
U64 i = 0;
U32 range_cursor = 0;
for (; i < (U64)(PDB_DbiCompUnitRange_COUNT); i += 1){
U64 adv = range_buf[i];
range_buf[i] = range_cursor;
range_cursor += adv;
}
range_buf[i] = range_cursor;
// skip 4 byte signature in symbols range
if (range_buf[1] >= 4){
range_buf[0] += 4;
}
}
// update cursor
cursor = AlignPow2(after_name2_off, 4);
}
// fill result
PDB_CompUnit **units = push_array_no_zero(arena, PDB_CompUnit*, count);
{
U64 idx = 0;
for (PDB_CompUnitNode *node = first;
node != 0;
node = node->next, idx += 1){
units[idx] = &node->unit;
}
}
PDB_CompUnitArray *result = push_array(arena, PDB_CompUnitArray, 1);
result->units = units;
result->count = count;
return(result);
}
static PDB_CompUnitContributionArray*
pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 data,
PDB_CoffSectionArray *sections){
PDB_CompUnitContribution *contributions = 0;
U64 count = 0;
if (data.size >= sizeof(PDB_DbiSectionContribVersion)){
PDB_DbiSectionContribVersion *version = (PDB_DbiSectionContribVersion*)data.str;
// determine array layout from version
U32 item_size = 0;
U32 array_off = 0;
switch (*version){
default:
{
// TODO(allen): do we have a test case for this?
item_size = sizeof(PDB_DbiSectionContrib40);
}break;
case PDB_DbiSectionContribVersion_1:
{
item_size = sizeof(PDB_DbiSectionContrib);
array_off = sizeof(*version);
}break;
case PDB_DbiSectionContribVersion_2:
{
item_size = sizeof(PDB_DbiSectionContrib2);
array_off = sizeof(*version);
}break;
}
// allocate ranges
U64 max_count = (data.size - array_off)/item_size;
contributions = push_array_no_zero(arena, PDB_CompUnitContribution, max_count);
// binary section info
U64 section_count = sections->count;
COFF_SectionHeader* section_headers = sections->sections;
// fill array
PDB_CompUnitContribution *contribution_ptr = contributions;
U64 cursor = array_off;
for (; cursor + item_size <= data.size; cursor += item_size){
PDB_DbiSectionContrib40 *sc = (PDB_DbiSectionContrib40*)(data.str + cursor);
if (sc->size > 0 && 1 <= sc->sec && sc->sec <= section_count){
U64 voff = section_headers[sc->sec - 1].voff + sc->sec_off;
contribution_ptr->mod = sc->mod;
contribution_ptr->voff_first = voff;
contribution_ptr->voff_opl = voff + sc->size;
contribution_ptr += 1;
}
}
count = (U64)(contribution_ptr - contributions);
}
// fill result
PDB_CompUnitContributionArray *result = push_array(arena, PDB_CompUnitContributionArray, 1);
result->contributions = contributions;
result->count = count;
return(result);
}
////////////////////////////////
//~ PDB Definition Functions
static U32
pdb_string_hash1(String8 string){
U32 result = 0;
U8 *ptr = string.str;
U8 *opl = ptr + (string.size&(~3));
for (; ptr < opl; ptr += 4){ result ^= *(U32*)ptr; }
if ((string.size&2) != 0){ result ^= *(U16*)ptr; ptr += 2; }
if ((string.size&1) != 0){ result ^= *ptr; }
result |= 0x20202020;
result ^= (result >> 11);
result ^= (result >> 16);
return(result);
}
////////////////////////////////
//~ PDB Dbi Functions
static String8
pdb_data_from_dbi_range(PDB_DbiParsed *dbi, PDB_DbiRange range){
String8 result = {0};
if (range < PDB_DbiRange_COUNT){
U64 first = dbi->range_off[range];
U64 opl = dbi->range_off[range + 1];
result.str = dbi->data.str + first;
result.size = opl - first;
}
return(result);
}
static String8
pdb_data_from_unit_range(MSF_Parsed *msf, PDB_CompUnit *unit, PDB_DbiCompUnitRange range){
String8 result = {0};
if (range < PDB_DbiCompUnitRange_COUNT){
String8 full_stream_data = msf_data_from_stream(msf, unit->sn);
U64 first_raw = unit->range_off[range];
U64 opl_raw = unit->range_off[range + 1];
U64 opl = ClampTop(opl_raw, full_stream_data.size);
U64 first = ClampTop(first_raw, opl);
result.str = full_stream_data.str + first;
result.size = opl - first;
}
return(result);
}
////////////////////////////////
//~ PDB Tpi Functions
static String8
pdb_leaf_data_from_tpi(PDB_TpiParsed *tpi){
String8 data = tpi->data;
U8 *first = data.str + tpi->leaf_first;
U8 *opl = data.str + tpi->leaf_opl;
String8 result = str8_range(first, opl);
return(result);
}
static CV_TypeIdArray
pdb_tpi_itypes_from_name(Arena *arena, PDB_TpiHashParsed *tpi_hash, CV_LeafParsed *leaf,
String8 name, B32 compare_unique_name, U32 output_cap){
ProfBegin("pdb_tpi_itypes_from_name");
U32 hash = pdb_string_hash1(name);
U32 bucket_idx = ((tpi_hash->bucket_mask != 0) ?
hash&tpi_hash->bucket_mask :
hash%tpi_hash->bucket_count);
CV_TypeId itype_first = leaf->itype_first;
CV_TypeId itype_opl = leaf->itype_opl;
String8 data = leaf->data;
Temp scratch = scratch_begin(&arena, 1);
struct Chain{
struct Chain *next;
CV_TypeId itype;
};
struct Chain *first = 0;
struct Chain *last = 0;
U32 count = 0;
for (PDB_TpiHashBlock *block = tpi_hash->buckets[bucket_idx];
block != 0;
block = block->next){
U32 local_count = block->local_count;
CV_TypeId *itype_ptr = block->itypes;
for (U32 i = 0; i < local_count; i += 1, itype_ptr += 1){
String8 extracted_name = {0};
CV_TypeId itype = *itype_ptr;
if (itype_first <= itype && itype < itype_opl){
CV_RecRange *range = &leaf->leaf_ranges.ranges[itype - leaf->itype_first];
if (range->off + range->hdr.size <= data.size){
U8 *first = data.str + range->off + 2;
U64 cap = range->hdr.size - 2;
switch (range->hdr.kind){
default:break;
case CV_LeafKind_CLASS:
case CV_LeafKind_STRUCTURE:
{
if (sizeof(CV_LeafStruct) <= cap){
CV_LeafStruct *lf_struct = (CV_LeafStruct*)first;
if (!(lf_struct->props & CV_TypeProp_FwdRef)){
// size
U8 *numeric_ptr = (U8*)(lf_struct + 1);
CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap);
// name
U8 *name_ptr = numeric_ptr + size.encoded_size;
String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap));
// unique name
if (compare_unique_name){
if (lf_struct->props & CV_TypeProp_HasUniqueName) {
U8 *unique_name_ptr = name_ptr + name.size + 1;
String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap));
extracted_name = unique_name;
}
}
else{
extracted_name = name;
}
}
}
}break;
case CV_LeafKind_UNION:
{
if (sizeof(CV_LeafUnion) <= cap){
CV_LeafUnion *lf_union = (CV_LeafUnion*)first;
if (!(lf_union->props & CV_TypeProp_FwdRef)){
// size
U8 *numeric_ptr = (U8*)(lf_union + 1);
CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap);
// name
U8 *name_ptr = numeric_ptr + size.encoded_size;
String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap));
// unique name
if (compare_unique_name){
if (lf_union->props & CV_TypeProp_HasUniqueName) {
U8 *unique_name_ptr = name_ptr + name.size + 1;
String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap));
extracted_name = unique_name;
}
}
else{
extracted_name = name;
}
}
}
}break;
case CV_LeafKind_ENUM:
{
if (sizeof(CV_LeafEnum) <= cap){
CV_LeafEnum *lf_enum = (CV_LeafEnum*)first;
if (!(lf_enum->props & CV_TypeProp_FwdRef)){
// name
U8 *name_ptr = (U8*)(lf_enum + 1);
String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap));
// unique name
if (compare_unique_name){
if (lf_enum->props & CV_TypeProp_HasUniqueName) {
U8 *unique_name_ptr = name_ptr + name.size + 1;
String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap));
extracted_name = unique_name;
}
}
else{
extracted_name = name;
}
}
}
}break;
}
}
}
if (str8_match(extracted_name, name, 0)){
struct Chain *chain = push_array(scratch.arena, struct Chain, 1);
SLLQueuePush(first, last, chain);
count += 1;
chain->itype = itype;
if (count == output_cap){
goto dblbreak;
}
}
}
}
dblbreak:;
// assemble result
CV_TypeId *itypes = push_array(arena, CV_TypeId, count);
{
CV_TypeId *itype_ptr = itypes;
for (struct Chain *node = first;
node != 0;
node = node->next, itype_ptr += 1){
*itype_ptr = node->itype;
}
}
CV_TypeIdArray result = {0};
result.itypes = itypes;
result.count = count;
scratch_end(scratch);
ProfEnd();
return(result);
}
static CV_TypeId
pdb_tpi_first_itype_from_name(PDB_TpiHashParsed *tpi_hash, CV_LeafParsed *tpi_leaf,
String8 name, B32 compare_unique_name){
ProfBegin("pdb_tpi_first_itype_from_name");
Temp scratch = scratch_begin(0, 0);
CV_TypeIdArray array = pdb_tpi_itypes_from_name(scratch.arena, tpi_hash, tpi_leaf,
name, compare_unique_name, 1);
CV_TypeId result = 0;
if (array.count > 0){
result = array.itypes[0];
}
scratch_end(scratch);
ProfEnd();
return(result);
}
////////////////////////////////
//~ PDB Strtbl Functions
static String8
pdb_strtbl_string_from_off(PDB_Strtbl *strtbl, U32 off){
U32 strblock_max = strtbl->strblock_max;
U32 full_off_raw = strtbl->strblock_min + off;
U32 full_off = ClampTop(full_off_raw, strblock_max);
String8 result = str8_cstring_capped((char*)(strtbl->data.str + full_off),
(char*)(strtbl->data.str + strblock_max));
return(result);
}
static String8
pdb_strtbl_string_from_index(PDB_Strtbl *strtbl, PDB_StringIndex idx){
String8 result = {0};
if (idx < strtbl->bucket_count){
U32 off = *(U32*)(strtbl->data.str + strtbl->buckets_min + idx*4);
result = pdb_strtbl_string_from_off(strtbl, off);
}
return(result);
}
+461
View File
@@ -0,0 +1,461 @@
// 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_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