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