mirror of
https://github.com/dborth/vbagx.git
synced 2024-12-26 18:51:49 +01:00
2995 lines
77 KiB
C++
2995 lines
77 KiB
C++
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
|
// Copyright (C) 1999-2003 Forgotten
|
|
// Copyright (C) 2004 Forgotten and the VBA development team
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2, or(at your option)
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software Foundation,
|
|
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "agb/GBA.h"
|
|
#include "Port.h"
|
|
#include "elf.h"
|
|
#include "NLS.h"
|
|
|
|
#define elfReadMemory(addr) \
|
|
READ32LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]))
|
|
|
|
#define DW_TAG_array_type 0x01
|
|
#define DW_TAG_enumeration_type 0x04
|
|
#define DW_TAG_formal_parameter 0x05
|
|
#define DW_TAG_label 0x0a
|
|
#define DW_TAG_lexical_block 0x0b
|
|
#define DW_TAG_member 0x0d
|
|
#define DW_TAG_pointer_type 0x0f
|
|
#define DW_TAG_reference_type 0x10
|
|
#define DW_TAG_compile_unit 0x11
|
|
#define DW_TAG_structure_type 0x13
|
|
#define DW_TAG_subroutine_type 0x15
|
|
#define DW_TAG_typedef 0x16
|
|
#define DW_TAG_union_type 0x17
|
|
#define DW_TAG_unspecified_parameters 0x18
|
|
#define DW_TAG_inheritance 0x1c
|
|
#define DW_TAG_inlined_subroutine 0x1d
|
|
#define DW_TAG_subrange_type 0x21
|
|
#define DW_TAG_base_type 0x24
|
|
#define DW_TAG_const_type 0x26
|
|
#define DW_TAG_enumerator 0x28
|
|
#define DW_TAG_subprogram 0x2e
|
|
#define DW_TAG_variable 0x34
|
|
#define DW_TAG_volatile_type 0x35
|
|
|
|
#define DW_AT_sibling 0x01
|
|
#define DW_AT_location 0x02
|
|
#define DW_AT_name 0x03
|
|
#define DW_AT_byte_size 0x0b
|
|
#define DW_AT_bit_offset 0x0c
|
|
#define DW_AT_bit_size 0x0d
|
|
#define DW_AT_stmt_list 0x10
|
|
#define DW_AT_low_pc 0x11
|
|
#define DW_AT_high_pc 0x12
|
|
#define DW_AT_language 0x13
|
|
#define DW_AT_compdir 0x1b
|
|
#define DW_AT_const_value 0x1c
|
|
#define DW_AT_containing_type 0x1d
|
|
#define DW_AT_inline 0x20
|
|
#define DW_AT_producer 0x25
|
|
#define DW_AT_prototyped 0x27
|
|
#define DW_AT_upper_bound 0x2f
|
|
#define DW_AT_abstract_origin 0x31
|
|
#define DW_AT_accessibility 0x32
|
|
#define DW_AT_artificial 0x34
|
|
#define DW_AT_data_member_location 0x38
|
|
#define DW_AT_decl_file 0x3a
|
|
#define DW_AT_decl_line 0x3b
|
|
#define DW_AT_declaration 0x3c
|
|
#define DW_AT_encoding 0x3e
|
|
#define DW_AT_external 0x3f
|
|
#define DW_AT_frame_base 0x40
|
|
#define DW_AT_macro_info 0x43
|
|
#define DW_AT_specification 0x47
|
|
#define DW_AT_type 0x49
|
|
#define DW_AT_virtuality 0x4c
|
|
#define DW_AT_vtable_elem_location 0x4d
|
|
// DWARF 2.1/3.0 extensions
|
|
#define DW_AT_entry_pc 0x52
|
|
#define DW_AT_ranges 0x55
|
|
// ARM Compiler extensions
|
|
#define DW_AT_proc_body 0x2000
|
|
#define DW_AT_save_offset 0x2001
|
|
#define DW_AT_user_2002 0x2002
|
|
// MIPS extensions
|
|
#define DW_AT_MIPS_linkage_name 0x2007
|
|
|
|
#define DW_FORM_addr 0x01
|
|
#define DW_FORM_data2 0x05
|
|
#define DW_FORM_data4 0x06
|
|
#define DW_FORM_string 0x08
|
|
#define DW_FORM_block 0x09
|
|
#define DW_FORM_block1 0x0a
|
|
#define DW_FORM_data1 0x0b
|
|
#define DW_FORM_flag 0x0c
|
|
#define DW_FORM_sdata 0x0d
|
|
#define DW_FORM_strp 0x0e
|
|
#define DW_FORM_udata 0x0f
|
|
#define DW_FORM_ref_addr 0x10
|
|
#define DW_FORM_ref4 0x13
|
|
#define DW_FORM_ref_udata 0x15
|
|
#define DW_FORM_indirect 0x16
|
|
|
|
#define DW_OP_addr 0x03
|
|
#define DW_OP_plus_uconst 0x23
|
|
#define DW_OP_reg0 0x50
|
|
#define DW_OP_reg1 0x51
|
|
#define DW_OP_reg2 0x52
|
|
#define DW_OP_reg3 0x53
|
|
#define DW_OP_reg4 0x54
|
|
#define DW_OP_reg5 0x55
|
|
#define DW_OP_reg6 0x56
|
|
#define DW_OP_reg7 0x57
|
|
#define DW_OP_reg8 0x58
|
|
#define DW_OP_reg9 0x59
|
|
#define DW_OP_reg10 0x5a
|
|
#define DW_OP_reg11 0x5b
|
|
#define DW_OP_reg12 0x5c
|
|
#define DW_OP_reg13 0x5d
|
|
#define DW_OP_reg14 0x5e
|
|
#define DW_OP_reg15 0x5f
|
|
#define DW_OP_fbreg 0x91
|
|
|
|
#define DW_LNS_extended_op 0x00
|
|
#define DW_LNS_copy 0x01
|
|
#define DW_LNS_advance_pc 0x02
|
|
#define DW_LNS_advance_line 0x03
|
|
#define DW_LNS_set_file 0x04
|
|
#define DW_LNS_set_column 0x05
|
|
#define DW_LNS_negate_stmt 0x06
|
|
#define DW_LNS_set_basic_block 0x07
|
|
#define DW_LNS_const_add_pc 0x08
|
|
#define DW_LNS_fixed_advance_pc 0x09
|
|
|
|
#define DW_LNE_end_sequence 0x01
|
|
#define DW_LNE_set_address 0x02
|
|
#define DW_LNE_define_file 0x03
|
|
|
|
#define DW_CFA_advance_loc 0x01
|
|
#define DW_CFA_offset 0x02
|
|
#define DW_CFA_restore 0x03
|
|
#define DW_CFA_set_loc 0x01
|
|
#define DW_CFA_advance_loc1 0x02
|
|
#define DW_CFA_advance_loc2 0x03
|
|
#define DW_CFA_advance_loc4 0x04
|
|
#define DW_CFA_offset_extended 0x05
|
|
#define DW_CFA_restore_extended 0x06
|
|
#define DW_CFA_undefined 0x07
|
|
#define DW_CFA_same_value 0x08
|
|
#define DW_CFA_register 0x09
|
|
#define DW_CFA_remember_state 0x0a
|
|
#define DW_CFA_restore_state 0x0b
|
|
#define DW_CFA_def_cfa 0x0c
|
|
#define DW_CFA_def_cfa_register 0x0d
|
|
#define DW_CFA_def_cfa_offset 0x0e
|
|
#define DW_CFA_nop 0x00
|
|
|
|
#define CASE_TYPE_TAG \
|
|
case DW_TAG_const_type:\
|
|
case DW_TAG_volatile_type:\
|
|
case DW_TAG_pointer_type:\
|
|
case DW_TAG_base_type:\
|
|
case DW_TAG_array_type:\
|
|
case DW_TAG_structure_type:\
|
|
case DW_TAG_union_type:\
|
|
case DW_TAG_typedef:\
|
|
case DW_TAG_subroutine_type:\
|
|
case DW_TAG_enumeration_type:\
|
|
case DW_TAG_enumerator:\
|
|
case DW_TAG_reference_type
|
|
|
|
struct ELFcie {
|
|
ELFcie *next;
|
|
u32 offset;
|
|
u8 *augmentation;
|
|
u32 codeAlign;
|
|
s32 dataAlign;
|
|
int returnAddress;
|
|
u8 *data;
|
|
u32 dataLen;
|
|
};
|
|
|
|
struct ELFfde {
|
|
ELFcie *cie;
|
|
u32 address;
|
|
u32 end;
|
|
u8 *data;
|
|
u32 dataLen;
|
|
};
|
|
|
|
enum ELFRegMode {
|
|
REG_NOT_SET,
|
|
REG_OFFSET,
|
|
REG_REGISTER
|
|
};
|
|
|
|
|
|
struct ELFFrameStateRegister {
|
|
ELFRegMode mode;
|
|
int reg;
|
|
s32 offset;
|
|
};
|
|
|
|
struct ELFFrameStateRegisters {
|
|
ELFFrameStateRegister regs[16];
|
|
ELFFrameStateRegisters *previous;
|
|
};
|
|
|
|
enum ELFCfaMode {
|
|
CFA_NOT_SET,
|
|
CFA_REG_OFFSET
|
|
};
|
|
|
|
struct ELFFrameState {
|
|
ELFFrameStateRegisters registers;
|
|
|
|
ELFCfaMode cfaMode;
|
|
int cfaRegister;
|
|
s32 cfaOffset;
|
|
|
|
u32 pc;
|
|
|
|
int dataAlign;
|
|
int codeAlign;
|
|
int returnAddress;
|
|
};
|
|
|
|
extern bool cpuIsMultiBoot;
|
|
|
|
Symbol *elfSymbols = NULL;
|
|
char *elfSymbolsStrTab = NULL;
|
|
int elfSymbolsCount = 0;
|
|
|
|
ELFSectionHeader **elfSectionHeaders = NULL;
|
|
char *elfSectionHeadersStringTable = NULL;
|
|
int elfSectionHeadersCount = 0;
|
|
u8 *elfFileData = NULL;
|
|
|
|
CompileUnit *elfCompileUnits = NULL;
|
|
DebugInfo *elfDebugInfo = NULL;
|
|
char *elfDebugStrings = NULL;
|
|
|
|
ELFcie *elfCies = NULL;
|
|
ELFfde **elfFdes = NULL;
|
|
int elfFdeCount = 0;
|
|
|
|
CompileUnit *elfCurrentUnit = NULL;
|
|
|
|
u32 elfRead4Bytes(u8 *);
|
|
u16 elfRead2Bytes(u8 *);
|
|
|
|
CompileUnit *elfGetCompileUnit(u32 addr)
|
|
{
|
|
if(elfCompileUnits) {
|
|
CompileUnit *unit = elfCompileUnits;
|
|
while(unit) {
|
|
if(unit->lowPC) {
|
|
if(addr >= unit->lowPC && addr < unit->highPC)
|
|
return unit;
|
|
} else {
|
|
ARanges *r = unit->ranges;
|
|
if(r) {
|
|
int count = r->count;
|
|
for(int j = 0; j < count; j++) {
|
|
if(addr >= r->ranges[j].lowPC && addr < r->ranges[j].highPC)
|
|
return unit;
|
|
}
|
|
}
|
|
}
|
|
unit = unit->next;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const char *elfGetAddressSymbol(u32 addr)
|
|
{
|
|
static char buffer[256];
|
|
|
|
CompileUnit *unit = elfGetCompileUnit(addr);
|
|
// found unit, need to find function
|
|
if(unit) {
|
|
Function *func = unit->functions;
|
|
while(func) {
|
|
if(addr >= func->lowPC && addr < func->highPC) {
|
|
int offset = addr - func->lowPC;
|
|
const char *name = func->name;
|
|
if(!name)
|
|
name = "";
|
|
if(offset)
|
|
sprintf(buffer, "%s+%d", name, offset);
|
|
else
|
|
strcpy(buffer, name);
|
|
return buffer;
|
|
}
|
|
func = func->next;
|
|
}
|
|
}
|
|
|
|
if(elfSymbolsCount) {
|
|
for(int i = 0; i < elfSymbolsCount; i++) {
|
|
Symbol *s = &elfSymbols[i];
|
|
if((addr >= s->value) && addr < (s->value+s->size)) {
|
|
int offset = addr-s->value;
|
|
const char *name = s->name;
|
|
if(name == NULL)
|
|
name = "";
|
|
if(offset)
|
|
sprintf(buffer, "%s+%d", name, addr-s->value);
|
|
else
|
|
strcpy(buffer, name);
|
|
return buffer;
|
|
} else if(addr == s->value) {
|
|
if(s->name)
|
|
strcpy(buffer, s->name);
|
|
else
|
|
strcpy(buffer, "");
|
|
return buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
bool elfFindLineInModule(u32 *addr, const char *name, int line)
|
|
{
|
|
CompileUnit *unit = elfCompileUnits;
|
|
|
|
while(unit) {
|
|
if(unit->lineInfoTable) {
|
|
int i;
|
|
int count = unit->lineInfoTable->fileCount;
|
|
char *found = NULL;
|
|
for(i = 0; i < count; i++) {
|
|
if(strcmp(name, unit->lineInfoTable->files[i]) == 0) {
|
|
found = unit->lineInfoTable->files[i];
|
|
break;
|
|
}
|
|
}
|
|
// found a matching filename... try to find line now
|
|
if(found) {
|
|
LineInfoItem *table = unit->lineInfoTable->lines;
|
|
count = unit->lineInfoTable->number;
|
|
for(i = 0; i < count; i++) {
|
|
if(table[i].file == found && table[i].line == line) {
|
|
*addr = table[i].address;
|
|
return true;
|
|
}
|
|
}
|
|
// we can only find a single match
|
|
return false;
|
|
}
|
|
}
|
|
unit = unit->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int elfFindLine(CompileUnit *unit, Function * /* func */, u32 addr, const char **f)
|
|
{
|
|
int currentLine = -1;
|
|
if(unit->hasLineInfo) {
|
|
int count = unit->lineInfoTable->number;
|
|
LineInfoItem *table = unit->lineInfoTable->lines;
|
|
int i;
|
|
for(i = 0; i < count; i++) {
|
|
if(addr <= table[i].address)
|
|
break;
|
|
}
|
|
if(i == count)
|
|
i--;
|
|
*f = table[i].file;
|
|
currentLine = table[i].line;
|
|
}
|
|
return currentLine;
|
|
}
|
|
|
|
bool elfFindLineInUnit(u32 *addr, CompileUnit *unit, int line)
|
|
{
|
|
if(unit->hasLineInfo) {
|
|
int count = unit->lineInfoTable->number;
|
|
LineInfoItem *table = unit->lineInfoTable->lines;
|
|
int i;
|
|
for(i = 0; i < count; i++) {
|
|
if(line == table[i].line) {
|
|
*addr = table[i].address;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool elfGetCurrentFunction(u32 addr, Function **f, CompileUnit **u)
|
|
{
|
|
CompileUnit *unit = elfGetCompileUnit(addr);
|
|
// found unit, need to find function
|
|
if(unit) {
|
|
Function *func = unit->functions;
|
|
while(func) {
|
|
if(addr >= func->lowPC && addr < func->highPC) {
|
|
*f = func;
|
|
*u = unit;
|
|
return true;
|
|
}
|
|
func = func->next;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool elfGetObject(const char *name, Function *f, CompileUnit *u, Object **o)
|
|
{
|
|
if(f && u) {
|
|
Object *v = f->variables;
|
|
|
|
while(v) {
|
|
if(strcmp(name, v->name) == 0) {
|
|
*o = v;
|
|
return true;
|
|
}
|
|
v = v->next;
|
|
}
|
|
v = f->parameters;
|
|
while(v) {
|
|
if(strcmp(name, v->name) == 0) {
|
|
*o = v;
|
|
return true;
|
|
}
|
|
v = v->next;
|
|
}
|
|
v = u->variables;
|
|
while(v) {
|
|
if(strcmp(name, v->name) == 0) {
|
|
*o = v;
|
|
return true;
|
|
}
|
|
v = v->next;
|
|
}
|
|
}
|
|
|
|
CompileUnit *c = elfCompileUnits;
|
|
|
|
while(c) {
|
|
if(c != u) {
|
|
Object *v = c->variables;
|
|
while(v) {
|
|
if(strcmp(name, v->name) == 0) {
|
|
*o = v;
|
|
return true;
|
|
}
|
|
v = v->next;
|
|
}
|
|
}
|
|
c = c->next;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const char *elfGetSymbol(int i, u32 *value, u32 *size, int *type)
|
|
{
|
|
if(i < elfSymbolsCount) {
|
|
Symbol *s = &elfSymbols[i];
|
|
*value = s->value;
|
|
*size = s->size;
|
|
*type = s->type;
|
|
return s->name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool elfGetSymbolAddress(const char *sym, u32 *addr, u32 *size, int *type)
|
|
{
|
|
if(elfSymbolsCount) {
|
|
for(int i = 0; i < elfSymbolsCount; i++) {
|
|
Symbol *s = &elfSymbols[i];
|
|
if(strcmp(sym, s->name) == 0) {
|
|
*addr = s->value;
|
|
*size = s->size;
|
|
*type = s->type;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ELFfde *elfGetFde(u32 address)
|
|
{
|
|
if(elfFdes) {
|
|
int i;
|
|
for(i = 0; i < elfFdeCount; i++) {
|
|
if(address >= elfFdes[i]->address &&
|
|
address < elfFdes[i]->end) {
|
|
return elfFdes[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void elfExecuteCFAInstructions(ELFFrameState *state, u8 *data, u32 len,
|
|
u32 pc)
|
|
{
|
|
u8 *end = data + len;
|
|
int bytes;
|
|
int reg;
|
|
ELFFrameStateRegisters *fs;
|
|
|
|
while(data < end && state->pc < pc) {
|
|
u8 op = *data++;
|
|
|
|
switch(op >> 6) {
|
|
case DW_CFA_advance_loc:
|
|
state->pc += (op & 0x3f) * state->codeAlign;
|
|
break;
|
|
case DW_CFA_offset:
|
|
reg = op & 0x3f;
|
|
state->registers.regs[reg].mode = REG_OFFSET;
|
|
state->registers.regs[reg].offset = state->dataAlign *
|
|
(s32)elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_CFA_restore:
|
|
// we don't care much about the other possible settings,
|
|
// so just setting to unset is enough for now
|
|
state->registers.regs[op & 0x3f].mode = REG_NOT_SET;
|
|
break;
|
|
case 0:
|
|
switch(op & 0x3f) {
|
|
case DW_CFA_nop:
|
|
break;
|
|
case DW_CFA_advance_loc1:
|
|
state->pc += state->codeAlign * (*data++);
|
|
break;
|
|
case DW_CFA_advance_loc2:
|
|
state->pc += state->codeAlign * elfRead2Bytes(data);
|
|
data += 2;
|
|
break;
|
|
case DW_CFA_advance_loc4:
|
|
state->pc += state->codeAlign * elfRead4Bytes(data);
|
|
data += 4;
|
|
break;
|
|
case DW_CFA_offset_extended:
|
|
reg = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
state->registers.regs[reg].mode = REG_OFFSET;
|
|
state->registers.regs[reg].offset = state->dataAlign *
|
|
(s32)elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_CFA_restore_extended:
|
|
case DW_CFA_undefined:
|
|
case DW_CFA_same_value:
|
|
reg = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
state->registers.regs[reg].mode = REG_NOT_SET;
|
|
break;
|
|
case DW_CFA_register:
|
|
reg = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
state->registers.regs[reg].mode = REG_REGISTER;
|
|
state->registers.regs[reg].reg = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_CFA_remember_state:
|
|
fs = (ELFFrameStateRegisters *)calloc(1,
|
|
sizeof(ELFFrameStateRegisters));
|
|
memcpy(fs, &state->registers, sizeof(ELFFrameStateRegisters));
|
|
state->registers.previous = fs;
|
|
break;
|
|
case DW_CFA_restore_state:
|
|
if(state->registers.previous == NULL) {
|
|
printf("Error: previous frame state is NULL.\n");
|
|
return;
|
|
}
|
|
fs = state->registers.previous;
|
|
memcpy(&state->registers, fs, sizeof(ELFFrameStateRegisters));
|
|
free(fs);
|
|
break;
|
|
case DW_CFA_def_cfa:
|
|
state->cfaRegister = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
state->cfaOffset = (s32)elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
state->cfaMode = CFA_REG_OFFSET;
|
|
break;
|
|
case DW_CFA_def_cfa_register:
|
|
state->cfaRegister = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
state->cfaMode = CFA_REG_OFFSET;
|
|
break;
|
|
case DW_CFA_def_cfa_offset:
|
|
state->cfaOffset = (s32)elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
state->cfaMode = CFA_REG_OFFSET;
|
|
break;
|
|
default:
|
|
printf("Unknown CFA opcode %08x\n", op);
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
printf("Unknown CFA opcode %08x\n", op);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ELFFrameState *elfGetFrameState(ELFfde *fde, u32 address)
|
|
{
|
|
ELFFrameState *state = (ELFFrameState *)calloc(1, sizeof(ELFFrameState));
|
|
state->pc = fde->address;
|
|
state->dataAlign = fde->cie->dataAlign;
|
|
state->codeAlign = fde->cie->codeAlign;
|
|
state->returnAddress = fde->cie->returnAddress;
|
|
|
|
elfExecuteCFAInstructions(state,
|
|
fde->cie->data,
|
|
fde->cie->dataLen,
|
|
0xffffffff);
|
|
elfExecuteCFAInstructions(state,
|
|
fde->data,
|
|
fde->dataLen,
|
|
address);
|
|
|
|
return state;
|
|
}
|
|
|
|
void elfPrintCallChain(u32 address)
|
|
{
|
|
int count = 1;
|
|
|
|
reg_pair regs[15];
|
|
reg_pair newRegs[15];
|
|
|
|
memcpy(®s[0], ®[0], sizeof(reg_pair) * 15);
|
|
|
|
while(count < 20) {
|
|
const char *addr = elfGetAddressSymbol(address);
|
|
if(*addr == 0)
|
|
addr = "???";
|
|
|
|
printf("%08x %s\n", address, addr);
|
|
|
|
ELFfde *fde = elfGetFde(address);
|
|
|
|
if(fde == NULL) {
|
|
break;
|
|
}
|
|
|
|
ELFFrameState *state = elfGetFrameState(fde, address);
|
|
|
|
if(!state) {
|
|
break;
|
|
}
|
|
|
|
if(state->cfaMode == CFA_REG_OFFSET) {
|
|
memcpy(&newRegs[0], ®s[0], sizeof(reg_pair) * 15);
|
|
u32 addr = 0;
|
|
for(int i = 0; i < 15; i++) {
|
|
ELFFrameStateRegister *r = &state->registers.
|
|
regs[i];
|
|
|
|
switch(r->mode) {
|
|
case REG_NOT_SET:
|
|
newRegs[i].I = regs[i].I;
|
|
break;
|
|
case REG_OFFSET:
|
|
newRegs[i].I = elfReadMemory(regs[state->cfaRegister].I +
|
|
state->cfaOffset +
|
|
r->offset);
|
|
break;
|
|
case REG_REGISTER:
|
|
newRegs[i].I = regs[r->reg].I;
|
|
break;
|
|
default:
|
|
printf("Unknown register mode: %d\n", r->mode);
|
|
break;
|
|
}
|
|
}
|
|
memcpy(regs, newRegs, sizeof(reg_pair)*15);
|
|
addr = newRegs[14].I;
|
|
addr &= 0xfffffffe;
|
|
address = addr;
|
|
count++;
|
|
} else {
|
|
printf("CFA not set\n");
|
|
break;
|
|
}
|
|
if(state->registers.previous) {
|
|
ELFFrameStateRegisters *prev = state->registers.previous;
|
|
|
|
while(prev) {
|
|
ELFFrameStateRegisters *p = prev->previous;
|
|
free(prev);
|
|
prev = p;
|
|
}
|
|
}
|
|
free(state);
|
|
}
|
|
}
|
|
|
|
u32 elfDecodeLocation(Function *f, ELFBlock *o, LocationType *type, u32 base)
|
|
{
|
|
u32 framebase = 0;
|
|
if(f && f->frameBase) {
|
|
ELFBlock *b = f->frameBase;
|
|
switch(*b->data) {
|
|
case DW_OP_reg0:
|
|
case DW_OP_reg1:
|
|
case DW_OP_reg2:
|
|
case DW_OP_reg3:
|
|
case DW_OP_reg4:
|
|
case DW_OP_reg5:
|
|
case DW_OP_reg6:
|
|
case DW_OP_reg7:
|
|
case DW_OP_reg8:
|
|
case DW_OP_reg9:
|
|
case DW_OP_reg10:
|
|
case DW_OP_reg11:
|
|
case DW_OP_reg12:
|
|
case DW_OP_reg13:
|
|
case DW_OP_reg14:
|
|
case DW_OP_reg15:
|
|
framebase = reg[*b->data-0x50].I;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown frameBase %02x\n", *b->data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ELFBlock *loc = o;
|
|
u32 location = 0;
|
|
int bytes = 0;
|
|
if(loc) {
|
|
switch(*loc->data) {
|
|
case DW_OP_addr:
|
|
location = elfRead4Bytes(loc->data+1);
|
|
*type = LOCATION_memory;
|
|
break;
|
|
case DW_OP_plus_uconst:
|
|
location = base + elfReadLEB128(loc->data+1, &bytes);
|
|
*type = LOCATION_memory;
|
|
break;
|
|
case DW_OP_reg0:
|
|
case DW_OP_reg1:
|
|
case DW_OP_reg2:
|
|
case DW_OP_reg3:
|
|
case DW_OP_reg4:
|
|
case DW_OP_reg5:
|
|
case DW_OP_reg6:
|
|
case DW_OP_reg7:
|
|
case DW_OP_reg8:
|
|
case DW_OP_reg9:
|
|
case DW_OP_reg10:
|
|
case DW_OP_reg11:
|
|
case DW_OP_reg12:
|
|
case DW_OP_reg13:
|
|
case DW_OP_reg14:
|
|
case DW_OP_reg15:
|
|
location = *loc->data - 0x50;
|
|
*type = LOCATION_register;
|
|
break;
|
|
case DW_OP_fbreg:
|
|
{
|
|
int bytes;
|
|
s32 off = elfReadSignedLEB128(loc->data+1, &bytes);
|
|
location = framebase + off;
|
|
*type = LOCATION_memory;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown location %02x\n", *loc->data);
|
|
break;
|
|
}
|
|
}
|
|
return location;
|
|
}
|
|
|
|
u32 elfDecodeLocation(Function *f, ELFBlock *o, LocationType *type)
|
|
{
|
|
return elfDecodeLocation(f, o, type, 0);
|
|
}
|
|
|
|
// reading function
|
|
|
|
u32 elfRead4Bytes(u8 *data)
|
|
{
|
|
u32 value = *data++;
|
|
value |= (*data++ << 8);
|
|
value |= (*data++ << 16);
|
|
value |= (*data << 24);
|
|
return value;
|
|
}
|
|
|
|
u16 elfRead2Bytes(u8 *data)
|
|
{
|
|
u16 value = *data++;
|
|
value |= (*data << 8);
|
|
return value;
|
|
}
|
|
|
|
char *elfReadString(u8 *data, int *bytesRead)
|
|
{
|
|
if(*data == 0) {
|
|
*bytesRead = 1;
|
|
return NULL;
|
|
}
|
|
*bytesRead = (int)strlen((char *)data) + 1;
|
|
return (char *)data;
|
|
}
|
|
|
|
s32 elfReadSignedLEB128(u8 *data, int *bytesRead)
|
|
{
|
|
s32 result = 0;
|
|
int shift = 0;
|
|
int count = 0;
|
|
|
|
u8 byte;
|
|
do {
|
|
byte = *data++;
|
|
count++;
|
|
result |= (byte & 0x7f) << shift;
|
|
shift += 7;
|
|
} while(byte & 0x80);
|
|
if((shift < 32) && (byte & 0x40))
|
|
result |= -(1 << shift);
|
|
*bytesRead = count;
|
|
return result;
|
|
}
|
|
|
|
u32 elfReadLEB128(u8 *data, int *bytesRead)
|
|
{
|
|
u32 result = 0;
|
|
int shift = 0;
|
|
int count = 0;
|
|
u8 byte;
|
|
do {
|
|
byte = *data++;
|
|
count++;
|
|
result |= (byte & 0x7f) << shift;
|
|
shift += 7;
|
|
} while(byte & 0x80);
|
|
*bytesRead = count;
|
|
return result;
|
|
}
|
|
|
|
u8 *elfReadSection(u8 *data, ELFSectionHeader *sh)
|
|
{
|
|
return data + READ32LE(&sh->offset);
|
|
}
|
|
|
|
ELFSectionHeader *elfGetSectionByName(const char *name)
|
|
{
|
|
for(int i = 0; i < elfSectionHeadersCount; i++) {
|
|
if(strcmp(name,
|
|
&elfSectionHeadersStringTable[READ32LE(&elfSectionHeaders[i]->
|
|
name)]) == 0) {
|
|
return elfSectionHeaders[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ELFSectionHeader *elfGetSectionByNumber(int number)
|
|
{
|
|
if(number < elfSectionHeadersCount) {
|
|
return elfSectionHeaders[number];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CompileUnit *elfGetCompileUnitForData(u8 *data)
|
|
{
|
|
u8 *end = elfCurrentUnit->top + 4 + elfCurrentUnit->length;
|
|
|
|
if(data >= elfCurrentUnit->top && data < end)
|
|
return elfCurrentUnit;
|
|
|
|
CompileUnit *unit = elfCompileUnits;
|
|
|
|
while(unit) {
|
|
end = unit->top + 4 + unit->length;
|
|
|
|
if(data >= unit->top && data < end)
|
|
return unit;
|
|
|
|
unit = unit->next;
|
|
}
|
|
|
|
printf("Error: cannot find reference to compile unit at offset %08x\n",
|
|
(int)(data - elfDebugInfo->infodata));
|
|
exit(-1);
|
|
}
|
|
|
|
u8 *elfReadAttribute(u8 *data, ELFAttr *attr)
|
|
{
|
|
int bytes;
|
|
int form = attr->form;
|
|
start:
|
|
switch(form) {
|
|
case DW_FORM_addr:
|
|
attr->value = elfRead4Bytes(data);
|
|
data += 4;
|
|
break;
|
|
case DW_FORM_data2:
|
|
attr->value = elfRead2Bytes(data);
|
|
data += 2;
|
|
break;
|
|
case DW_FORM_data4:
|
|
attr->value = elfRead4Bytes(data);
|
|
data += 4;
|
|
break;
|
|
case DW_FORM_string:
|
|
attr->string = (char *)data;
|
|
data += strlen(attr->string)+1;
|
|
break;
|
|
case DW_FORM_strp:
|
|
attr->string = elfDebugStrings + elfRead4Bytes(data);
|
|
data += 4;
|
|
break;
|
|
case DW_FORM_block:
|
|
attr->block = (ELFBlock *)malloc(sizeof(ELFBlock));
|
|
attr->block->length = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
attr->block->data = data;
|
|
data += attr->block->length;
|
|
break;
|
|
case DW_FORM_block1:
|
|
attr->block = (ELFBlock *)malloc(sizeof(ELFBlock));
|
|
attr->block->length = *data++;
|
|
attr->block->data = data;
|
|
data += attr->block->length;
|
|
break;
|
|
case DW_FORM_data1:
|
|
attr->value = *data++;
|
|
break;
|
|
case DW_FORM_flag:
|
|
attr->flag = (*data++) ? true : false;
|
|
break;
|
|
case DW_FORM_sdata:
|
|
attr->value = elfReadSignedLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_FORM_udata:
|
|
attr->value = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_FORM_ref_addr:
|
|
attr->value = (u32)((elfDebugInfo->infodata + elfRead4Bytes(data)) - elfGetCompileUnitForData(data)->top);
|
|
data += 4;
|
|
break;
|
|
case DW_FORM_ref4:
|
|
attr->value = elfRead4Bytes(data);
|
|
data += 4;
|
|
break;
|
|
case DW_FORM_ref_udata:
|
|
attr->value = (u32)((elfDebugInfo->infodata + (elfGetCompileUnitForData(data)->top - elfDebugInfo->infodata) + elfReadLEB128(data, &bytes)) - elfCurrentUnit->top);
|
|
data += bytes;
|
|
break;
|
|
case DW_FORM_indirect:
|
|
form = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
goto start;
|
|
default:
|
|
fprintf(stderr, "Unsupported FORM %02x\n", form);
|
|
exit(-1);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
ELFAbbrev *elfGetAbbrev(ELFAbbrev **table, u32 number)
|
|
{
|
|
int hash = number % 121;
|
|
|
|
ELFAbbrev *abbrev = table[hash];
|
|
|
|
while(abbrev) {
|
|
if(abbrev->number == number)
|
|
return abbrev;
|
|
abbrev = abbrev->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ELFAbbrev **elfReadAbbrevs(u8 *data, u32 offset)
|
|
{
|
|
data += offset;
|
|
ELFAbbrev **abbrevs = (ELFAbbrev **)calloc(sizeof(ELFAbbrev *)*121,1);
|
|
int bytes = 0;
|
|
u32 number = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
while(number) {
|
|
ELFAbbrev *abbrev = (ELFAbbrev *)calloc(sizeof(ELFAbbrev),1);
|
|
|
|
// read tag information
|
|
abbrev->number = number;
|
|
abbrev->tag = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
abbrev->hasChildren = *data++ ? true: false;
|
|
|
|
// read attributes
|
|
int name = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
int form = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
while(name) {
|
|
if((abbrev->numAttrs % 4) == 0) {
|
|
abbrev->attrs = (ELFAttr *)realloc(abbrev->attrs,
|
|
(abbrev->numAttrs + 4) *
|
|
sizeof(ELFAttr));
|
|
}
|
|
abbrev->attrs[abbrev->numAttrs].name = name;
|
|
abbrev->attrs[abbrev->numAttrs++].form = form;
|
|
|
|
name = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
form = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
}
|
|
|
|
int hash = number % 121;
|
|
abbrev->next = abbrevs[hash];
|
|
abbrevs[hash] = abbrev;
|
|
|
|
number = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
if(elfGetAbbrev(abbrevs, number) != NULL)
|
|
break;
|
|
}
|
|
|
|
return abbrevs;
|
|
}
|
|
|
|
void elfParseCFA(u8 *top)
|
|
{
|
|
ELFSectionHeader *h = elfGetSectionByName(".debug_frame");
|
|
|
|
if(h == NULL) {
|
|
return;
|
|
}
|
|
|
|
u8 *data = elfReadSection(top, h);
|
|
|
|
u8 *topOffset = data;
|
|
|
|
u8 *end = data + READ32LE(&h->size);
|
|
|
|
ELFcie *cies = NULL;
|
|
|
|
while(data < end) {
|
|
u32 offset = (u32)(data - topOffset);
|
|
u32 len = elfRead4Bytes(data);
|
|
data += 4;
|
|
|
|
u8 *dataEnd = data + len;
|
|
|
|
u32 id = elfRead4Bytes(data);
|
|
data += 4;
|
|
|
|
if(id == 0xffffffff) {
|
|
// skip version
|
|
*data++;
|
|
|
|
ELFcie *cie = (ELFcie *)calloc(1, sizeof(ELFcie));
|
|
|
|
cie->next = cies;
|
|
cies = cie;
|
|
|
|
cie->offset = offset;
|
|
|
|
cie->augmentation = data;
|
|
while(*data)
|
|
data++;
|
|
data++;
|
|
|
|
if(*cie->augmentation) {
|
|
fprintf(stderr, "Error: augmentation not supported\n");
|
|
exit(-1);
|
|
}
|
|
|
|
int bytes;
|
|
cie->codeAlign = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
cie->dataAlign = elfReadSignedLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
cie->returnAddress = *data++;
|
|
|
|
cie->data = data;
|
|
cie->dataLen = (u32)(dataEnd - data);
|
|
} else {
|
|
ELFfde *fde = (ELFfde *)calloc(1, sizeof(ELFfde));
|
|
|
|
ELFcie *cie = cies;
|
|
|
|
while(cie != NULL) {
|
|
if(cie->offset == id)
|
|
break;
|
|
cie = cie->next;
|
|
}
|
|
|
|
if(!cie) {
|
|
fprintf(stderr, "Cannot find CIE %08x\n", id);
|
|
exit(-1);
|
|
}
|
|
|
|
fde->cie = cie;
|
|
|
|
fde->address = elfRead4Bytes(data);
|
|
data += 4;
|
|
|
|
fde->end = fde->address + elfRead4Bytes(data);
|
|
data += 4;
|
|
|
|
fde->data = data;
|
|
fde->dataLen = (u32)(dataEnd - data);
|
|
|
|
if((elfFdeCount %10) == 0) {
|
|
elfFdes = (ELFfde **)realloc(elfFdes, (elfFdeCount+10) *
|
|
sizeof(ELFfde *));
|
|
}
|
|
elfFdes[elfFdeCount++] = fde;
|
|
}
|
|
data = dataEnd;
|
|
}
|
|
|
|
elfCies = cies;
|
|
}
|
|
|
|
void elfAddLine(LineInfo *l, u32 a, int file, int line, int *max)
|
|
{
|
|
if(l->number == *max) {
|
|
*max += 1000;
|
|
l->lines = (LineInfoItem *)realloc(l->lines, *max*sizeof(LineInfoItem));
|
|
}
|
|
LineInfoItem *li = &l->lines[l->number];
|
|
li->file = l->files[file-1];
|
|
li->address = a;
|
|
li->line = line;
|
|
l->number++;
|
|
}
|
|
|
|
void elfParseLineInfo(CompileUnit *unit, u8 *top)
|
|
{
|
|
ELFSectionHeader *h = elfGetSectionByName(".debug_line");
|
|
if(h == NULL) {
|
|
fprintf(stderr, "No line information found\n");
|
|
return;
|
|
}
|
|
LineInfo *l = unit->lineInfoTable = (LineInfo *)calloc(1, sizeof(LineInfo));
|
|
l->number = 0;
|
|
int max = 1000;
|
|
l->lines = (LineInfoItem *)malloc(1000*sizeof(LineInfoItem));
|
|
|
|
u8 *data = elfReadSection(top, h);
|
|
data += unit->lineInfo;
|
|
u32 totalLen = elfRead4Bytes(data);
|
|
data += 4;
|
|
u8 *end = data + totalLen;
|
|
// u16 version = elfRead2Bytes(data);
|
|
data += 2;
|
|
// u32 offset = elfRead4Bytes(data);
|
|
data += 4;
|
|
int minInstrSize = *data++;
|
|
int defaultIsStmt = *data++;
|
|
int lineBase = (s8)*data++;
|
|
int lineRange = *data++;
|
|
int opcodeBase = *data++;
|
|
u8 *stdOpLen = (u8 *)malloc(opcodeBase * sizeof(u8));
|
|
stdOpLen[0] = 1;
|
|
int i;
|
|
for(i = 1; i < opcodeBase; i++)
|
|
stdOpLen[i] = *data++;
|
|
|
|
free(stdOpLen);// todo
|
|
int bytes = 0;
|
|
|
|
char *s;
|
|
while((s = elfReadString(data, &bytes)) != NULL) {
|
|
data += bytes;
|
|
// fprintf(stderr, "Directory is %s\n", s);
|
|
}
|
|
data += bytes;
|
|
int count = 4;
|
|
int index = 0;
|
|
l->files = (char **)malloc(sizeof(char *)*count);
|
|
|
|
while((s = elfReadString(data, &bytes)) != NULL) {
|
|
l->files[index++] = s;
|
|
|
|
data += bytes;
|
|
// directory
|
|
elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
// time
|
|
elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
// size
|
|
elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
// fprintf(stderr, "File is %s\n", s);
|
|
if(index == count) {
|
|
count += 4;
|
|
l->files = (char **)realloc(l->files, sizeof(char *)*count);
|
|
}
|
|
}
|
|
l->fileCount = index;
|
|
data += bytes;
|
|
|
|
while(data < end) {
|
|
u32 address = 0;
|
|
int file = 1;
|
|
int line = 1;
|
|
int col = 0;
|
|
int isStmt = defaultIsStmt;
|
|
int basicBlock = 0;
|
|
int endSeq = 0;
|
|
|
|
while(!endSeq) {
|
|
int op = *data++;
|
|
switch(op) {
|
|
case DW_LNS_extended_op:
|
|
{
|
|
data++;
|
|
op = *data++;
|
|
switch(op) {
|
|
case DW_LNE_end_sequence:
|
|
endSeq = 1;
|
|
break;
|
|
case DW_LNE_set_address:
|
|
address = elfRead4Bytes(data);
|
|
data += 4;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown extended LINE opcode %02x\n", op);
|
|
exit(-1);
|
|
}
|
|
}
|
|
break;
|
|
case DW_LNS_copy:
|
|
// fprintf(stderr, "Address %08x line %d (%d)\n", address, line, file);
|
|
elfAddLine(l, address, file, line, &max);
|
|
basicBlock = 0;
|
|
break;
|
|
case DW_LNS_advance_pc:
|
|
address += minInstrSize * elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_LNS_advance_line:
|
|
line += elfReadSignedLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_LNS_set_file:
|
|
file = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_LNS_set_column:
|
|
col = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
break;
|
|
case DW_LNS_negate_stmt:
|
|
isStmt = !isStmt;
|
|
break;
|
|
case DW_LNS_set_basic_block:
|
|
basicBlock = 1;
|
|
break;
|
|
case DW_LNS_const_add_pc:
|
|
address += (minInstrSize *((255 - opcodeBase)/lineRange));
|
|
break;
|
|
case DW_LNS_fixed_advance_pc:
|
|
address += elfRead2Bytes(data);
|
|
data += 2;
|
|
break;
|
|
default:
|
|
op = op - opcodeBase;
|
|
address += (op / lineRange) * minInstrSize;
|
|
line += lineBase + (op % lineRange);
|
|
elfAddLine(l, address, file, line, &max);
|
|
// fprintf(stderr, "Address %08x line %d (%d)\n", address, line,file);
|
|
basicBlock = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
l->lines = (LineInfoItem *)realloc(l->lines, l->number*sizeof(LineInfoItem));
|
|
}
|
|
|
|
u8 *elfSkipData(u8 *data, ELFAbbrev *abbrev, ELFAbbrev **abbrevs)
|
|
{
|
|
int i;
|
|
int bytes;
|
|
|
|
for(i = 0; i < abbrev->numAttrs; i++) {
|
|
data = elfReadAttribute(data, &abbrev->attrs[i]);
|
|
if(abbrev->attrs[i].form == DW_FORM_block1)
|
|
free(abbrev->attrs[i].block);
|
|
}
|
|
|
|
if(abbrev->hasChildren) {
|
|
int nesting = 1;
|
|
while(nesting) {
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
if(!abbrevNum) {
|
|
nesting--;
|
|
continue;
|
|
}
|
|
|
|
abbrev = elfGetAbbrev(abbrevs, abbrevNum);
|
|
|
|
for(i = 0; i < abbrev->numAttrs; i++) {
|
|
data = elfReadAttribute(data, &abbrev->attrs[i]);
|
|
if(abbrev->attrs[i].form == DW_FORM_block1)
|
|
free(abbrev->attrs[i].block);
|
|
}
|
|
|
|
if(abbrev->hasChildren) {
|
|
nesting++;
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
Type *elfParseType(CompileUnit *unit, u32);
|
|
u8 *elfParseObject(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit,
|
|
Object **object);
|
|
u8 *elfParseFunction(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit,
|
|
Function **function);
|
|
void elfCleanUp(Function *);
|
|
|
|
void elfAddType(Type *type, CompileUnit *unit, u32 offset)
|
|
{
|
|
if(type->next == NULL) {
|
|
if(unit->types != type && type->offset == 0) {
|
|
type->offset = offset;
|
|
type->next = unit->types;
|
|
unit->types = type;
|
|
}
|
|
}
|
|
}
|
|
|
|
void elfParseType(u8 *data, u32 offset, ELFAbbrev *abbrev, CompileUnit *unit,
|
|
Type **type)
|
|
{
|
|
switch(abbrev->tag) {
|
|
case DW_TAG_typedef:
|
|
{
|
|
u32 typeref = 0;
|
|
char *name = NULL;
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_name:
|
|
name = attr->string;
|
|
break;
|
|
case DW_AT_type:
|
|
typeref = attr->value;
|
|
break;
|
|
case DW_AT_decl_file:
|
|
case DW_AT_decl_line:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown attribute for typedef %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
if(abbrev->hasChildren)
|
|
fprintf(stderr, "Unexpected children for typedef\n");
|
|
*type = elfParseType(unit, typeref);
|
|
if(name)
|
|
(*type)->name = name;
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_union_type:
|
|
case DW_TAG_structure_type:
|
|
{
|
|
Type *t = (Type *)calloc(sizeof(Type), 1);
|
|
if(abbrev->tag == DW_TAG_structure_type)
|
|
t->type = TYPE_struct;
|
|
else
|
|
t->type = TYPE_union;
|
|
|
|
Struct *s = (Struct *)calloc(sizeof(Struct), 1);
|
|
t->structure = s;
|
|
elfAddType(t, unit, offset);
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_name:
|
|
t->name = attr->string;
|
|
break;
|
|
case DW_AT_byte_size:
|
|
t->size = attr->value;
|
|
break;
|
|
case DW_AT_decl_file:
|
|
case DW_AT_decl_line:
|
|
case DW_AT_sibling:
|
|
case DW_AT_containing_type: // todo?
|
|
case DW_AT_declaration:
|
|
case DW_AT_specification: // TODO:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown attribute for struct %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
if(abbrev->hasChildren) {
|
|
int bytes;
|
|
u32 num = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
int index = 0;
|
|
while(num) {
|
|
ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num);
|
|
|
|
switch(abbr->tag) {
|
|
case DW_TAG_member:
|
|
{
|
|
if((index % 4) == 0)
|
|
s->members = (Member *)realloc(s->members,
|
|
sizeof(Member)*(index+4));
|
|
Member *m = &s->members[index];
|
|
m->location = NULL;
|
|
m->bitOffset = 0;
|
|
m->bitSize = 0;
|
|
m->byteSize = 0;
|
|
for(int i = 0; i < abbr->numAttrs; i++) {
|
|
ELFAttr *attr = &abbr->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_name:
|
|
m->name = attr->string;
|
|
break;
|
|
case DW_AT_type:
|
|
m->type = elfParseType(unit, attr->value);
|
|
break;
|
|
case DW_AT_data_member_location:
|
|
m->location = attr->block;
|
|
break;
|
|
case DW_AT_byte_size:
|
|
m->byteSize = attr->value;
|
|
break;
|
|
case DW_AT_bit_offset:
|
|
m->bitOffset = attr->value;
|
|
break;
|
|
case DW_AT_bit_size:
|
|
m->bitSize = attr->value;
|
|
break;
|
|
case DW_AT_decl_file:
|
|
case DW_AT_decl_line:
|
|
case DW_AT_accessibility:
|
|
case DW_AT_artificial: // todo?
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown member attribute %02x\n",
|
|
attr->name);
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
break;
|
|
case DW_TAG_subprogram:
|
|
{
|
|
Function *fnc = NULL;
|
|
data = elfParseFunction(data, abbr, unit, &fnc);
|
|
if(fnc != NULL) {
|
|
if(unit->lastFunction)
|
|
unit->lastFunction->next = fnc;
|
|
else
|
|
unit->functions = fnc;
|
|
unit->lastFunction = fnc;
|
|
}
|
|
}
|
|
break;
|
|
case DW_TAG_inheritance:
|
|
// TODO: add support
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
CASE_TYPE_TAG:
|
|
// skip types... parsed only when used
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
case DW_TAG_variable:
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown struct tag %02x %s\n", abbr->tag, t->name);
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
}
|
|
num = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
}
|
|
s->memberCount = index;
|
|
}
|
|
*type = t;
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_base_type:
|
|
{
|
|
Type *t = (Type *)calloc(sizeof(Type), 1);
|
|
|
|
t->type = TYPE_base;
|
|
elfAddType(t, unit, offset);
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_name:
|
|
t->name = attr->string;
|
|
break;
|
|
case DW_AT_encoding:
|
|
t->encoding = attr->value;
|
|
break;
|
|
case DW_AT_byte_size:
|
|
t->size = attr->value;
|
|
break;
|
|
case DW_AT_bit_size:
|
|
t->bitSize = attr->value;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown attribute for base type %02x\n",
|
|
attr->name);
|
|
break;
|
|
}
|
|
}
|
|
if(abbrev->hasChildren)
|
|
fprintf(stderr, "Unexpected children for base type\n");
|
|
*type = t;
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_pointer_type:
|
|
{
|
|
Type *t = (Type *)calloc(sizeof(Type), 1);
|
|
|
|
t->type = TYPE_pointer;
|
|
|
|
elfAddType(t, unit, offset);
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data =elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_type:
|
|
t->pointer = elfParseType(unit, attr->value);
|
|
break;
|
|
case DW_AT_byte_size:
|
|
t->size = attr->value;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown pointer type attribute %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
if(abbrev->hasChildren)
|
|
fprintf(stderr, "Unexpected children for pointer type\n");
|
|
*type = t;
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_reference_type:
|
|
{
|
|
Type *t = (Type *)calloc(sizeof(Type), 1);
|
|
|
|
t->type = TYPE_reference;
|
|
|
|
elfAddType(t, unit, offset);
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data =elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_type:
|
|
t->pointer = elfParseType(unit, attr->value);
|
|
break;
|
|
case DW_AT_byte_size:
|
|
t->size = attr->value;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown ref type attribute %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
if(abbrev->hasChildren)
|
|
fprintf(stderr, "Unexpected children for ref type\n");
|
|
*type = t;
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_volatile_type:
|
|
{
|
|
u32 typeref = 0;
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_type:
|
|
typeref = attr->value;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown volatile attribute for type %02x\n",
|
|
attr->name);
|
|
break;
|
|
}
|
|
}
|
|
if(abbrev->hasChildren)
|
|
fprintf(stderr, "Unexpected children for volatile type\n");
|
|
*type = elfParseType(unit, typeref);
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_const_type:
|
|
{
|
|
u32 typeref = 0;
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_type:
|
|
typeref = attr->value;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown const attribute for type %02x\n",
|
|
attr->name);
|
|
break;
|
|
}
|
|
}
|
|
if(abbrev->hasChildren)
|
|
fprintf(stderr, "Unexpected children for const type\n");
|
|
*type = elfParseType(unit, typeref);
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_enumeration_type:
|
|
{
|
|
Type *t = (Type *)calloc(sizeof(Type), 1);
|
|
t->type = TYPE_enum;
|
|
Enum *e = (Enum *)calloc(sizeof(Enum), 1);
|
|
t->enumeration = e;
|
|
elfAddType(t, unit, offset);
|
|
int count = 0;
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_name:
|
|
t->name = attr->string;
|
|
break;
|
|
case DW_AT_byte_size:
|
|
t->size = attr->value;
|
|
break;
|
|
case DW_AT_sibling:
|
|
case DW_AT_decl_file:
|
|
case DW_AT_decl_line:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown enum attribute %02x\n", attr->name);
|
|
}
|
|
}
|
|
if(abbrev->hasChildren) {
|
|
int bytes;
|
|
u32 num = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
while(num) {
|
|
ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num);
|
|
|
|
switch(abbr->tag) {
|
|
case DW_TAG_enumerator:
|
|
{
|
|
count++;
|
|
e->members = (EnumMember *)realloc(e->members,
|
|
count*sizeof(EnumMember));
|
|
EnumMember *m = &e->members[count-1];
|
|
for(int i = 0; i < abbr->numAttrs; i++) {
|
|
ELFAttr *attr = &abbr->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_name:
|
|
m->name = attr->string;
|
|
break;
|
|
case DW_AT_const_value:
|
|
m->value = attr->value;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown sub param attribute %02x\n",
|
|
attr->name);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown enum tag %02x\n", abbr->tag);
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
}
|
|
num = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
}
|
|
}
|
|
e->count = count;
|
|
*type = t;
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_subroutine_type:
|
|
{
|
|
Type *t = (Type *)calloc(sizeof(Type), 1);
|
|
t->type = TYPE_function;
|
|
FunctionType *f = (FunctionType *)calloc(sizeof(FunctionType), 1);
|
|
t->function = f;
|
|
elfAddType(t, unit, offset);
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_prototyped:
|
|
case DW_AT_sibling:
|
|
break;
|
|
case DW_AT_type:
|
|
f->returnType = elfParseType(unit, attr->value);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown subroutine attribute %02x\n", attr->name);
|
|
}
|
|
}
|
|
if(abbrev->hasChildren) {
|
|
int bytes;
|
|
u32 num = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
Object *lastVar = NULL;
|
|
while(num) {
|
|
ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num);
|
|
|
|
switch(abbr->tag) {
|
|
case DW_TAG_formal_parameter:
|
|
{
|
|
Object *o;
|
|
data = elfParseObject(data, abbr, unit, &o);
|
|
if(f->args)
|
|
lastVar->next = o;
|
|
else
|
|
f->args = o;
|
|
lastVar = o;
|
|
}
|
|
break;
|
|
case DW_TAG_unspecified_parameters:
|
|
// no use in the debugger yet
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
CASE_TYPE_TAG:
|
|
// skip types... parsed only when used
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown subroutine tag %02x\n", abbr->tag);
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
}
|
|
num = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
}
|
|
}
|
|
*type = t;
|
|
return;
|
|
}
|
|
break;
|
|
case DW_TAG_array_type:
|
|
{
|
|
u32 typeref = 0;
|
|
int i;
|
|
Array *array = (Array *)calloc(sizeof(Array), 1);
|
|
Type *t = (Type *)calloc(sizeof(Type), 1);
|
|
t->type = TYPE_array;
|
|
elfAddType(t, unit, offset);
|
|
|
|
for(i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_sibling:
|
|
break;
|
|
case DW_AT_type:
|
|
typeref = attr->value;
|
|
array->type = elfParseType(unit, typeref);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown array attribute %02x\n", attr->name);
|
|
}
|
|
}
|
|
if(abbrev->hasChildren) {
|
|
int bytes;
|
|
u32 num = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
int index = 0;
|
|
int maxBounds = 0;
|
|
while(num) {
|
|
ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num);
|
|
|
|
switch(abbr->tag) {
|
|
case DW_TAG_subrange_type:
|
|
{
|
|
if(maxBounds == index) {
|
|
maxBounds += 4;
|
|
array->bounds = (int *)realloc(array->bounds,
|
|
sizeof(int)*maxBounds);
|
|
}
|
|
for(int i = 0; i < abbr->numAttrs; i++) {
|
|
ELFAttr *attr = &abbr->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_upper_bound:
|
|
array->bounds[index] = attr->value+1;
|
|
break;
|
|
case DW_AT_type: // ignore
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown subrange attribute %02x\n",
|
|
attr->name);
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown array tag %02x\n", abbr->tag);
|
|
data = elfSkipData(data, abbr, unit->abbrevs);
|
|
break;
|
|
}
|
|
num = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
}
|
|
array->maxBounds = index;
|
|
}
|
|
t->size = array->type->size;
|
|
for(i = 0; i < array->maxBounds; i++)
|
|
t->size *= array->bounds[i];
|
|
t->array = array;
|
|
*type = t;
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown type TAG %02x\n", abbrev->tag);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
Type *elfParseType(CompileUnit *unit, u32 offset)
|
|
{
|
|
Type *t = unit->types;
|
|
|
|
while(t) {
|
|
if(t->offset == offset)
|
|
return t;
|
|
t = t->next;
|
|
}
|
|
if(offset == 0) {
|
|
Type *t = (Type *)calloc(sizeof(Type), 1);
|
|
t->type = TYPE_void;
|
|
t->offset = 0;
|
|
elfAddType(t, unit, 0);
|
|
return t;
|
|
}
|
|
u8 *data = unit->top + offset;
|
|
int bytes;
|
|
int abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
Type *type = NULL;
|
|
|
|
ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum);
|
|
|
|
elfParseType(data, offset, abbrev, unit, &type);
|
|
return type;
|
|
}
|
|
|
|
void elfGetObjectAttributes(CompileUnit *unit, u32 offset, Object *o)
|
|
{
|
|
u8 *data = unit->top + offset;
|
|
int bytes;
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
if(!abbrevNum) {
|
|
return;
|
|
}
|
|
|
|
ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum);
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_location:
|
|
o->location = attr->block;
|
|
break;
|
|
case DW_AT_name:
|
|
if(o->name == NULL)
|
|
o->name = attr->string;
|
|
break;
|
|
case DW_AT_MIPS_linkage_name:
|
|
o->name = attr->string;
|
|
break;
|
|
case DW_AT_decl_file:
|
|
o->file = attr->value;
|
|
break;
|
|
case DW_AT_decl_line:
|
|
o->line = attr->value;
|
|
break;
|
|
case DW_AT_type:
|
|
o->type = elfParseType(unit, attr->value);
|
|
break;
|
|
case DW_AT_external:
|
|
o->external = attr->flag;
|
|
break;
|
|
case DW_AT_const_value:
|
|
case DW_AT_abstract_origin:
|
|
case DW_AT_declaration:
|
|
case DW_AT_artificial:
|
|
// todo
|
|
break;
|
|
case DW_AT_specification:
|
|
// TODO:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown object attribute %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
u8 *elfParseObject(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit,
|
|
Object **object)
|
|
{
|
|
Object *o = (Object *)calloc(sizeof(Object), 1);
|
|
|
|
o->next = NULL;
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_location:
|
|
o->location = attr->block;
|
|
break;
|
|
case DW_AT_name:
|
|
if(o->name == NULL)
|
|
o->name = attr->string;
|
|
break;
|
|
case DW_AT_MIPS_linkage_name:
|
|
o->name = attr->string;
|
|
break;
|
|
case DW_AT_decl_file:
|
|
o->file = attr->value;
|
|
break;
|
|
case DW_AT_decl_line:
|
|
o->line = attr->value;
|
|
break;
|
|
case DW_AT_type:
|
|
o->type = elfParseType(unit, attr->value);
|
|
break;
|
|
case DW_AT_external:
|
|
o->external = attr->flag;
|
|
break;
|
|
case DW_AT_abstract_origin:
|
|
elfGetObjectAttributes(unit, attr->value, o);
|
|
break;
|
|
case DW_AT_const_value:
|
|
case DW_AT_declaration:
|
|
case DW_AT_artificial:
|
|
break;
|
|
case DW_AT_specification:
|
|
// TODO:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown object attribute %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
*object = o;
|
|
return data;
|
|
}
|
|
|
|
u8 *elfParseBlock(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit,
|
|
Function *func, Object **lastVar)
|
|
{
|
|
int bytes;
|
|
u32 start = func->lowPC;
|
|
u32 end = func->highPC;
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_sibling:
|
|
break;
|
|
case DW_AT_low_pc:
|
|
start = attr->value;
|
|
break;
|
|
case DW_AT_high_pc:
|
|
end = attr->value;
|
|
break;
|
|
case DW_AT_ranges: // ignore for now
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown block attribute %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(abbrev->hasChildren) {
|
|
int nesting = 1;
|
|
|
|
while(nesting) {
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
if(!abbrevNum) {
|
|
nesting--;
|
|
continue;
|
|
}
|
|
|
|
abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum);
|
|
|
|
switch(abbrev->tag) {
|
|
CASE_TYPE_TAG: // types only parsed when used
|
|
case DW_TAG_label: // not needed
|
|
data = elfSkipData(data, abbrev, unit->abbrevs);
|
|
break;
|
|
case DW_TAG_lexical_block:
|
|
data = elfParseBlock(data, abbrev, unit, func, lastVar);
|
|
break;
|
|
case DW_TAG_subprogram:
|
|
{
|
|
Function *f = NULL;
|
|
data = elfParseFunction(data, abbrev, unit, &f);
|
|
if(f != NULL) {
|
|
if(unit->lastFunction)
|
|
unit->lastFunction->next = f;
|
|
else
|
|
unit->functions = f;
|
|
unit->lastFunction = f;
|
|
}
|
|
}
|
|
break;
|
|
case DW_TAG_variable:
|
|
{
|
|
Object *o;
|
|
data = elfParseObject(data, abbrev, unit, &o);
|
|
if(o->startScope == 0)
|
|
o->startScope = start;
|
|
if(o->endScope == 0)
|
|
o->endScope = 0;
|
|
if(func->variables)
|
|
(*lastVar)->next = o;
|
|
else
|
|
func->variables = o;
|
|
*lastVar = o;
|
|
}
|
|
break;
|
|
case DW_TAG_inlined_subroutine:
|
|
// TODO:
|
|
data = elfSkipData(data, abbrev, unit->abbrevs);
|
|
break;
|
|
default:
|
|
{
|
|
fprintf(stderr, "Unknown block TAG %02x\n", abbrev->tag);
|
|
data = elfSkipData(data, abbrev, unit->abbrevs);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void elfGetFunctionAttributes(CompileUnit *unit, u32 offset, Function *func)
|
|
{
|
|
u8 *data = unit->top + offset;
|
|
int bytes;
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
if(!abbrevNum) {
|
|
return;
|
|
}
|
|
|
|
ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum);
|
|
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
|
|
switch(attr->name) {
|
|
case DW_AT_sibling:
|
|
break;
|
|
case DW_AT_name:
|
|
if(func->name == NULL)
|
|
func->name = attr->string;
|
|
break;
|
|
case DW_AT_MIPS_linkage_name:
|
|
func->name = attr->string;
|
|
break;
|
|
case DW_AT_low_pc:
|
|
func->lowPC = attr->value;
|
|
break;
|
|
case DW_AT_high_pc:
|
|
func->highPC = attr->value;
|
|
break;
|
|
case DW_AT_decl_file:
|
|
func->file = attr->value;
|
|
break;
|
|
case DW_AT_decl_line:
|
|
func->line = attr->value;
|
|
break;
|
|
case DW_AT_external:
|
|
func->external = attr->flag;
|
|
break;
|
|
case DW_AT_frame_base:
|
|
func->frameBase = attr->block;
|
|
break;
|
|
case DW_AT_type:
|
|
func->returnType = elfParseType(unit, attr->value);
|
|
break;
|
|
case DW_AT_inline:
|
|
case DW_AT_specification:
|
|
case DW_AT_declaration:
|
|
case DW_AT_artificial:
|
|
case DW_AT_prototyped:
|
|
case DW_AT_proc_body:
|
|
case DW_AT_save_offset:
|
|
case DW_AT_user_2002:
|
|
case DW_AT_virtuality:
|
|
case DW_AT_containing_type:
|
|
case DW_AT_accessibility:
|
|
// todo;
|
|
break;
|
|
case DW_AT_vtable_elem_location:
|
|
free(attr->block);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown function attribute %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
u8 *elfParseFunction(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit,
|
|
Function **f)
|
|
{
|
|
Function *func = (Function *)calloc(sizeof(Function), 1);
|
|
*f = func;
|
|
|
|
int bytes;
|
|
bool mangled = false;
|
|
bool declaration = false;
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
switch(attr->name) {
|
|
case DW_AT_sibling:
|
|
break;
|
|
case DW_AT_name:
|
|
if(func->name == NULL)
|
|
func->name = attr->string;
|
|
break;
|
|
case DW_AT_MIPS_linkage_name:
|
|
func->name = attr->string;
|
|
mangled = true;
|
|
break;
|
|
case DW_AT_low_pc:
|
|
func->lowPC = attr->value;
|
|
break;
|
|
case DW_AT_high_pc:
|
|
func->highPC = attr->value;
|
|
break;
|
|
case DW_AT_prototyped:
|
|
break;
|
|
case DW_AT_decl_file:
|
|
func->file = attr->value;
|
|
break;
|
|
case DW_AT_decl_line:
|
|
func->line = attr->value;
|
|
break;
|
|
case DW_AT_external:
|
|
func->external = attr->flag;
|
|
break;
|
|
case DW_AT_frame_base:
|
|
func->frameBase = attr->block;
|
|
break;
|
|
case DW_AT_type:
|
|
func->returnType = elfParseType(unit, attr->value);
|
|
break;
|
|
case DW_AT_abstract_origin:
|
|
elfGetFunctionAttributes(unit, attr->value, func);
|
|
break;
|
|
case DW_AT_declaration:
|
|
declaration = attr->flag;
|
|
break;
|
|
case DW_AT_inline:
|
|
case DW_AT_specification:
|
|
case DW_AT_artificial:
|
|
case DW_AT_proc_body:
|
|
case DW_AT_save_offset:
|
|
case DW_AT_user_2002:
|
|
case DW_AT_virtuality:
|
|
case DW_AT_containing_type:
|
|
case DW_AT_accessibility:
|
|
// todo;
|
|
break;
|
|
case DW_AT_vtable_elem_location:
|
|
free(attr->block);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown function attribute %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(declaration) {
|
|
elfCleanUp(func);
|
|
free(func);
|
|
*f = NULL;
|
|
|
|
while(1) {
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
if(!abbrevNum) {
|
|
return data;
|
|
}
|
|
|
|
abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum);
|
|
|
|
data = elfSkipData(data, abbrev, unit->abbrevs);
|
|
}
|
|
}
|
|
|
|
if(abbrev->hasChildren) {
|
|
int nesting = 1;
|
|
Object *lastParam = NULL;
|
|
Object *lastVar = NULL;
|
|
|
|
while(nesting) {
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
if(!abbrevNum) {
|
|
nesting--;
|
|
continue;
|
|
}
|
|
|
|
abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum);
|
|
|
|
switch(abbrev->tag) {
|
|
CASE_TYPE_TAG: // no need to parse types. only parsed when used
|
|
case DW_TAG_label: // not needed
|
|
data = elfSkipData(data, abbrev, unit->abbrevs);
|
|
break;
|
|
case DW_TAG_subprogram:
|
|
{
|
|
Function *fnc=NULL;
|
|
data = elfParseFunction(data, abbrev, unit, &fnc);
|
|
if(fnc != NULL) {
|
|
if(unit->lastFunction == NULL)
|
|
unit->functions = fnc;
|
|
else
|
|
unit->lastFunction->next = fnc;
|
|
unit->lastFunction = fnc;
|
|
}
|
|
}
|
|
break;
|
|
case DW_TAG_lexical_block:
|
|
{
|
|
data = elfParseBlock(data, abbrev, unit, func, &lastVar);
|
|
}
|
|
break;
|
|
case DW_TAG_formal_parameter:
|
|
{
|
|
Object *o;
|
|
data = elfParseObject(data, abbrev, unit, &o);
|
|
if(func->parameters)
|
|
lastParam->next = o;
|
|
else
|
|
func->parameters = o;
|
|
lastParam = o;
|
|
}
|
|
break;
|
|
case DW_TAG_variable:
|
|
{
|
|
Object *o;
|
|
data = elfParseObject(data, abbrev, unit, &o);
|
|
if(func->variables)
|
|
lastVar->next = o;
|
|
else
|
|
func->variables = o;
|
|
lastVar = o;
|
|
}
|
|
break;
|
|
case DW_TAG_unspecified_parameters:
|
|
case DW_TAG_inlined_subroutine:
|
|
{
|
|
// todo
|
|
for(int i = 0; i < abbrev->numAttrs; i++) {
|
|
data = elfReadAttribute(data, &abbrev->attrs[i]);
|
|
if(abbrev->attrs[i].form == DW_FORM_block1)
|
|
free(abbrev->attrs[i].block);
|
|
}
|
|
|
|
if(abbrev->hasChildren)
|
|
nesting++;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
fprintf(stderr, "Unknown function TAG %02x\n", abbrev->tag);
|
|
data = elfSkipData(data, abbrev, unit->abbrevs);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
u8 *elfParseUnknownData(u8 *data, ELFAbbrev *abbrev, ELFAbbrev **abbrevs)
|
|
{
|
|
int i;
|
|
int bytes;
|
|
// switch(abbrev->tag) {
|
|
// default:
|
|
fprintf(stderr, "Unknown TAG %02x\n", abbrev->tag);
|
|
|
|
for(i = 0; i < abbrev->numAttrs; i++) {
|
|
data = elfReadAttribute(data, &abbrev->attrs[i]);
|
|
if(abbrev->attrs[i].form == DW_FORM_block1)
|
|
free(abbrev->attrs[i].block);
|
|
}
|
|
|
|
if(abbrev->hasChildren) {
|
|
int nesting = 1;
|
|
while(nesting) {
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
if(!abbrevNum) {
|
|
nesting--;
|
|
continue;
|
|
}
|
|
|
|
abbrev = elfGetAbbrev(abbrevs, abbrevNum);
|
|
|
|
fprintf(stderr, "Unknown TAG %02x\n", abbrev->tag);
|
|
|
|
for(i = 0; i < abbrev->numAttrs; i++) {
|
|
data = elfReadAttribute(data, &abbrev->attrs[i]);
|
|
if(abbrev->attrs[i].form == DW_FORM_block1)
|
|
free(abbrev->attrs[i].block);
|
|
}
|
|
|
|
if(abbrev->hasChildren) {
|
|
nesting++;
|
|
}
|
|
}
|
|
}
|
|
// }
|
|
return data;
|
|
}
|
|
|
|
u8 *elfParseCompileUnitChildren(u8 *data, CompileUnit *unit)
|
|
{
|
|
int bytes;
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
Object *lastObj = NULL;
|
|
while(abbrevNum) {
|
|
ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum);
|
|
switch(abbrev->tag) {
|
|
case DW_TAG_subprogram:
|
|
{
|
|
Function *func = NULL;
|
|
data = elfParseFunction(data, abbrev, unit, &func);
|
|
if(func != NULL) {
|
|
if(unit->lastFunction)
|
|
unit->lastFunction->next = func;
|
|
else
|
|
unit->functions = func;
|
|
unit->lastFunction = func;
|
|
}
|
|
}
|
|
break;
|
|
CASE_TYPE_TAG:
|
|
data = elfSkipData(data, abbrev, unit->abbrevs);
|
|
break;
|
|
case DW_TAG_variable:
|
|
{
|
|
Object *var = NULL;
|
|
data = elfParseObject(data, abbrev, unit, &var);
|
|
if(lastObj)
|
|
lastObj->next = var;
|
|
else
|
|
unit->variables = var;
|
|
lastObj = var;
|
|
}
|
|
break;
|
|
default:
|
|
data = elfParseUnknownData(data, abbrev, unit->abbrevs);
|
|
break;
|
|
}
|
|
|
|
abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
|
|
CompileUnit *elfParseCompUnit(u8 *data, u8 *abbrevData)
|
|
{
|
|
int bytes;
|
|
u8 *top = data;
|
|
|
|
u32 length = elfRead4Bytes(data);
|
|
data += 4;
|
|
|
|
u16 version = elfRead2Bytes(data);
|
|
data += 2;
|
|
|
|
u32 offset = elfRead4Bytes(data);
|
|
data += 4;
|
|
|
|
u8 addrSize = *data++;
|
|
|
|
if(version != 2) {
|
|
fprintf(stderr, "Unsupported debugging information version %d\n", version);
|
|
return NULL;
|
|
}
|
|
|
|
if(addrSize != 4) {
|
|
fprintf(stderr, "Unsupported address size %d\n", addrSize);
|
|
return NULL;
|
|
}
|
|
|
|
ELFAbbrev **abbrevs = elfReadAbbrevs(abbrevData, offset);
|
|
|
|
u32 abbrevNum = elfReadLEB128(data, &bytes);
|
|
data += bytes;
|
|
|
|
ELFAbbrev *abbrev = elfGetAbbrev(abbrevs, abbrevNum);
|
|
|
|
CompileUnit *unit = (CompileUnit *)calloc(sizeof(CompileUnit), 1);
|
|
unit->top = top;
|
|
unit->length = length;
|
|
unit->abbrevs = abbrevs;
|
|
unit->next = NULL;
|
|
|
|
elfCurrentUnit = unit;
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < abbrev->numAttrs; i++) {
|
|
ELFAttr *attr = &abbrev->attrs[i];
|
|
data = elfReadAttribute(data, attr);
|
|
|
|
switch(attr->name) {
|
|
case DW_AT_name:
|
|
unit->name = attr->string;
|
|
break;
|
|
case DW_AT_stmt_list:
|
|
unit->hasLineInfo = true;
|
|
unit->lineInfo = attr->value;
|
|
break;
|
|
case DW_AT_low_pc:
|
|
unit->lowPC = attr->value;
|
|
break;
|
|
case DW_AT_high_pc:
|
|
unit->highPC = attr->value;
|
|
break;
|
|
case DW_AT_compdir:
|
|
unit->compdir = attr->string;
|
|
break;
|
|
// ignore
|
|
case DW_AT_language:
|
|
case DW_AT_producer:
|
|
case DW_AT_macro_info:
|
|
case DW_AT_entry_pc:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown attribute %02x\n", attr->name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(abbrev->hasChildren)
|
|
elfParseCompileUnitChildren(data, unit);
|
|
|
|
return unit;
|
|
}
|
|
|
|
void elfParseAranges(u8 *data)
|
|
{
|
|
ELFSectionHeader *sh = elfGetSectionByName(".debug_aranges");
|
|
if(sh == NULL) {
|
|
fprintf(stderr, "No aranges found\n");
|
|
return;
|
|
}
|
|
|
|
data = elfReadSection(data, sh);
|
|
u8 *end = data + READ32LE(&sh->size);
|
|
|
|
int max = 4;
|
|
ARanges *ranges = (ARanges *)calloc(sizeof(ARanges), 4);
|
|
|
|
int index = 0;
|
|
|
|
while(data < end) {
|
|
u32 len = elfRead4Bytes(data);
|
|
data += 4;
|
|
// u16 version = elfRead2Bytes(data);
|
|
data += 2;
|
|
u32 offset = elfRead4Bytes(data);
|
|
data += 4;
|
|
// u8 addrSize = *data++;
|
|
// u8 segSize = *data++;
|
|
data += 2; // remove if uncommenting above
|
|
data += 4;
|
|
ranges[index].count = (len-20)/8;
|
|
ranges[index].offset = offset;
|
|
ranges[index].ranges = (ARange *)calloc(sizeof(ARange), (len-20)/8);
|
|
int i = 0;
|
|
while(true) {
|
|
u32 addr = elfRead4Bytes(data);
|
|
data += 4;
|
|
u32 len = elfRead4Bytes(data);
|
|
data += 4;
|
|
if(addr == 0 && len == 0)
|
|
break;
|
|
ranges[index].ranges[i].lowPC = addr;
|
|
ranges[index].ranges[i].highPC = addr+len;
|
|
i++;
|
|
}
|
|
index++;
|
|
if(index == max) {
|
|
max += 4;
|
|
ranges = (ARanges *)realloc(ranges, max*sizeof(ARanges));
|
|
}
|
|
}
|
|
elfDebugInfo->numRanges = index;
|
|
elfDebugInfo->ranges = ranges;
|
|
}
|
|
|
|
void elfReadSymtab(u8 *data)
|
|
{
|
|
ELFSectionHeader *sh = elfGetSectionByName(".symtab");
|
|
int table = READ32LE(&sh->link);
|
|
|
|
char *strtable = (char *)elfReadSection(data, elfGetSectionByNumber(table));
|
|
|
|
ELFSymbol *symtab = (ELFSymbol *)elfReadSection(data, sh);
|
|
|
|
int count = READ32LE(&sh->size) / sizeof(ELFSymbol);
|
|
elfSymbolsCount = 0;
|
|
|
|
elfSymbols = (Symbol *)malloc(sizeof(Symbol)*count);
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < count; i++) {
|
|
ELFSymbol *s = &symtab[i];
|
|
int type = s->info & 15;
|
|
int binding = s->info >> 4;
|
|
|
|
if(binding) {
|
|
Symbol *sym = &elfSymbols[elfSymbolsCount];
|
|
sym->name = &strtable[READ32LE(&s->name)];
|
|
sym->binding = binding;
|
|
sym->type = type;
|
|
sym->value = READ32LE(&s->value);
|
|
sym->size = READ32LE(&s->size);
|
|
elfSymbolsCount++;
|
|
}
|
|
}
|
|
for(i = 0; i < count; i++) {
|
|
ELFSymbol *s = &symtab[i];
|
|
int bind = s->info>>4;
|
|
int type = s->info & 15;
|
|
|
|
if(!bind) {
|
|
Symbol *sym = &elfSymbols[elfSymbolsCount];
|
|
sym->name = &strtable[READ32LE(&s->name)];
|
|
sym->binding = (s->info >> 4);
|
|
sym->type = type;
|
|
sym->value = READ32LE(&s->value);
|
|
sym->size = READ32LE(&s->size);
|
|
elfSymbolsCount++;
|
|
}
|
|
}
|
|
elfSymbolsStrTab = strtable;
|
|
// free(symtab);
|
|
}
|
|
|
|
bool elfReadProgram(ELFHeader *eh, u8 *data, int& size, bool parseDebug)
|
|
{
|
|
int count = READ16LE(&eh->e_phnum);
|
|
int i;
|
|
|
|
if(READ32LE(&eh->e_entry) == 0x2000000)
|
|
cpuIsMultiBoot = true;
|
|
|
|
// read program headers... should probably move this code down
|
|
u8 *p = data + READ32LE(&eh->e_phoff);
|
|
size = 0;
|
|
for(i = 0; i < count; i++) {
|
|
ELFProgramHeader *ph = (ELFProgramHeader *)p;
|
|
p += sizeof(ELFProgramHeader);
|
|
if(READ16LE(&eh->e_phentsize) != sizeof(ELFProgramHeader)) {
|
|
p += READ16LE(&eh->e_phentsize) - sizeof(ELFProgramHeader);
|
|
}
|
|
|
|
// printf("PH %d %08x %08x %08x %08x %08x %08x %08x %08x\n",
|
|
// i, ph->type, ph->offset, ph->vaddr, ph->paddr,
|
|
// ph->filesz, ph->memsz, ph->flags, ph->align);
|
|
if(cpuIsMultiBoot) {
|
|
if(READ32LE(&ph->paddr) >= 0x2000000 &&
|
|
READ32LE(&ph->paddr) <= 0x203ffff) {
|
|
memcpy(&workRAM[READ32LE(&ph->paddr) & 0x3ffff],
|
|
data + READ32LE(&ph->offset),
|
|
READ32LE(&ph->filesz));
|
|
size += READ32LE(&ph->filesz);
|
|
}
|
|
} else {
|
|
if(READ32LE(&ph->paddr) >= 0x8000000 &&
|
|
READ32LE(&ph->paddr) <= 0x9ffffff) {
|
|
memcpy(&rom[READ32LE(&ph->paddr) & 0x1ffffff],
|
|
data + READ32LE(&ph->offset),
|
|
READ32LE(&ph->filesz));
|
|
size += READ32LE(&ph->filesz);
|
|
}
|
|
}
|
|
}
|
|
|
|
char *stringTable = NULL;
|
|
|
|
// read section headers
|
|
p = data + READ32LE(&eh->e_shoff);
|
|
count = READ16LE(&eh->e_shnum);
|
|
|
|
ELFSectionHeader **sh = (ELFSectionHeader **)
|
|
malloc(sizeof(ELFSectionHeader *) * count);
|
|
|
|
for(i = 0; i < count; i++) {
|
|
sh[i] = (ELFSectionHeader *)p;
|
|
p += sizeof(ELFSectionHeader);
|
|
if(READ16LE(&eh->e_shentsize) != sizeof(ELFSectionHeader))
|
|
p += READ16LE(&eh->e_shentsize) - sizeof(ELFSectionHeader);
|
|
}
|
|
|
|
if(READ16LE(&eh->e_shstrndx) != 0) {
|
|
stringTable = (char *)elfReadSection(data,
|
|
sh[READ16LE(&eh->e_shstrndx)]);
|
|
}
|
|
|
|
elfSectionHeaders = sh;
|
|
elfSectionHeadersStringTable = stringTable;
|
|
elfSectionHeadersCount = count;
|
|
|
|
for(i = 0; i < count; i++) {
|
|
// printf("SH %d %-20s %08x %08x %08x %08x %08x %08x %08x %08x\n",
|
|
// i, &stringTable[sh[i]->name], sh[i]->name, sh[i]->type,
|
|
// sh[i]->flags, sh[i]->addr, sh[i]->offset, sh[i]->size,
|
|
// sh[i]->link, sh[i]->info);
|
|
if(READ32LE(&sh[i]->flags) & 2) { // load section
|
|
if(cpuIsMultiBoot) {
|
|
if(READ32LE(&sh[i]->addr) >= 0x2000000 &&
|
|
READ32LE(&sh[i]->addr) <= 0x203ffff) {
|
|
memcpy(&workRAM[READ32LE(&sh[i]->addr) & 0x3ffff], data +
|
|
READ32LE(&sh[i]->offset),
|
|
READ32LE(&sh[i]->size));
|
|
size += READ32LE(&sh[i]->size);
|
|
}
|
|
} else {
|
|
if(READ32LE(&sh[i]->addr) >= 0x8000000 &&
|
|
READ32LE(&sh[i]->addr) <= 0x9ffffff) {
|
|
memcpy(&rom[READ32LE(&sh[i]->addr) & 0x1ffffff],
|
|
data + READ32LE(&sh[i]->offset),
|
|
READ32LE(&sh[i]->size));
|
|
size += READ32LE(&sh[i]->size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(parseDebug) {
|
|
fprintf(stderr, "Parsing debug info\n");
|
|
|
|
ELFSectionHeader *dbgHeader = elfGetSectionByName(".debug_info");
|
|
if(dbgHeader == NULL) {
|
|
fprintf(stderr, "Cannot find debug information\n");
|
|
goto end;
|
|
}
|
|
|
|
ELFSectionHeader *h = elfGetSectionByName(".debug_abbrev");
|
|
if(h == NULL) {
|
|
fprintf(stderr, "Cannot find abbreviation table\n");
|
|
goto end;
|
|
}
|
|
|
|
elfDebugInfo = (DebugInfo *)calloc(sizeof(DebugInfo), 1);
|
|
u8 *abbrevdata = elfReadSection(data, h);
|
|
|
|
h = elfGetSectionByName(".debug_str");
|
|
|
|
if(h == NULL)
|
|
elfDebugStrings = NULL;
|
|
else
|
|
elfDebugStrings = (char *)elfReadSection(data, h);
|
|
|
|
u8 *debugdata = elfReadSection(data, dbgHeader);
|
|
|
|
elfDebugInfo->debugdata = data;
|
|
elfDebugInfo->infodata = debugdata;
|
|
|
|
u32 total = READ32LE(&dbgHeader->size);
|
|
u8 *end = debugdata + total;
|
|
u8 *ddata = debugdata;
|
|
|
|
CompileUnit *last = NULL;
|
|
CompileUnit *unit = NULL;
|
|
|
|
while(ddata < end) {
|
|
unit = elfParseCompUnit(ddata, abbrevdata);
|
|
unit->offset = (u32)(ddata-debugdata);
|
|
elfParseLineInfo(unit, data);
|
|
if(last == NULL)
|
|
elfCompileUnits = unit;
|
|
else
|
|
last->next = unit;
|
|
last = unit;
|
|
ddata += 4 + unit->length;
|
|
}
|
|
elfParseAranges(data);
|
|
CompileUnit *comp = elfCompileUnits;
|
|
while(comp) {
|
|
ARanges *r = elfDebugInfo->ranges;
|
|
for(int i = 0; i < elfDebugInfo->numRanges; i++)
|
|
if(r[i].offset == comp->offset) {
|
|
comp->ranges = &r[i];
|
|
break;
|
|
}
|
|
comp = comp->next;
|
|
}
|
|
elfParseCFA(data);
|
|
elfReadSymtab(data);
|
|
}
|
|
end:
|
|
if(sh) {
|
|
free(sh);
|
|
}
|
|
|
|
elfSectionHeaders = NULL;
|
|
elfSectionHeadersStringTable = NULL;
|
|
elfSectionHeadersCount = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
extern bool parseDebug;
|
|
|
|
bool elfRead(const char *name, int& siz, FILE *f)
|
|
{
|
|
fseek(f, 0, SEEK_END);
|
|
long size = ftell(f);
|
|
elfFileData = (u8 *)malloc(size);
|
|
fseek(f, 0, SEEK_SET);
|
|
fread(elfFileData, 1, size, f);
|
|
fclose(f);
|
|
|
|
ELFHeader *header = (ELFHeader *)elfFileData;
|
|
|
|
if(READ32LE(&header->magic) != 0x464C457F ||
|
|
READ16LE(&header->e_machine) != 40 ||
|
|
header->clazz != 1) {
|
|
systemMessage(0, N_("Not a valid ELF file %s"), name);
|
|
free(elfFileData);
|
|
elfFileData = NULL;
|
|
return false;
|
|
}
|
|
|
|
if(!elfReadProgram(header, elfFileData, siz, parseDebug)) {
|
|
free(elfFileData);
|
|
elfFileData = NULL;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void elfCleanUp(Object *o)
|
|
{
|
|
free(o->location);
|
|
}
|
|
|
|
void elfCleanUp(Function *func)
|
|
{
|
|
Object *o = func->parameters;
|
|
while(o) {
|
|
elfCleanUp(o);
|
|
Object *next = o->next;
|
|
free(o);
|
|
o = next;
|
|
}
|
|
|
|
o = func->variables;
|
|
while(o) {
|
|
elfCleanUp(o);
|
|
Object *next = o->next;
|
|
free(o);
|
|
o = next;
|
|
}
|
|
free(func->frameBase);
|
|
}
|
|
|
|
void elfCleanUp(ELFAbbrev **abbrevs)
|
|
{
|
|
for(int i = 0; i < 121; i++) {
|
|
ELFAbbrev *abbrev = abbrevs[i];
|
|
|
|
while(abbrev) {
|
|
free(abbrev->attrs);
|
|
ELFAbbrev *next = abbrev->next;
|
|
free(abbrev);
|
|
|
|
abbrev = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void elfCleanUp(Type *t)
|
|
{
|
|
switch(t->type) {
|
|
case TYPE_function:
|
|
if(t->function) {
|
|
Object *o = t->function->args;
|
|
while(o) {
|
|
elfCleanUp(o);
|
|
Object *next = o->next;
|
|
free(o);
|
|
o = next;
|
|
}
|
|
free(t->function);
|
|
}
|
|
break;
|
|
case TYPE_array:
|
|
if(t->array) {
|
|
free(t->array->bounds);
|
|
free(t->array);
|
|
}
|
|
break;
|
|
case TYPE_struct:
|
|
case TYPE_union:
|
|
if(t->structure) {
|
|
for(int i = 0; i < t->structure->memberCount; i++) {
|
|
free(t->structure->members[i].location);
|
|
}
|
|
free(t->structure->members);
|
|
free(t->structure);
|
|
}
|
|
break;
|
|
case TYPE_enum:
|
|
if(t->enumeration) {
|
|
free(t->enumeration->members);
|
|
free(t->enumeration);
|
|
}
|
|
break;
|
|
case TYPE_base:
|
|
case TYPE_pointer:
|
|
case TYPE_void:
|
|
case TYPE_reference:
|
|
break; // nothing to do
|
|
}
|
|
}
|
|
|
|
void elfCleanUp(CompileUnit *comp)
|
|
{
|
|
elfCleanUp(comp->abbrevs);
|
|
free(comp->abbrevs);
|
|
Function *func = comp->functions;
|
|
while(func) {
|
|
elfCleanUp(func);
|
|
Function *next = func->next;
|
|
free(func);
|
|
func = next;
|
|
}
|
|
Type *t = comp->types;
|
|
while(t) {
|
|
elfCleanUp(t);
|
|
Type *next = t->next;
|
|
free(t);
|
|
t = next;
|
|
}
|
|
Object *o = comp->variables;
|
|
while(o) {
|
|
elfCleanUp(o);
|
|
Object *next = o->next;
|
|
free(o);
|
|
o = next;
|
|
}
|
|
if(comp->lineInfoTable) {
|
|
free(comp->lineInfoTable->lines);
|
|
free(comp->lineInfoTable->files);
|
|
free(comp->lineInfoTable);
|
|
}
|
|
}
|
|
|
|
void elfCleanUp()
|
|
{
|
|
CompileUnit *comp = elfCompileUnits;
|
|
|
|
while(comp) {
|
|
elfCleanUp(comp);
|
|
CompileUnit *next = comp->next;
|
|
free(comp);
|
|
comp = next;
|
|
}
|
|
elfCompileUnits = NULL;
|
|
free(elfSymbols);
|
|
elfSymbols = NULL;
|
|
// free(elfSymbolsStrTab);
|
|
elfSymbolsStrTab = NULL;
|
|
|
|
elfDebugStrings = NULL;
|
|
if(elfDebugInfo) {
|
|
int num = elfDebugInfo->numRanges;
|
|
int i;
|
|
for(i = 0; i < num; i++) {
|
|
free(elfDebugInfo->ranges[i].ranges);
|
|
}
|
|
free(elfDebugInfo->ranges);
|
|
free(elfDebugInfo);
|
|
elfDebugInfo = NULL;
|
|
}
|
|
|
|
if(elfFdes) {
|
|
if(elfFdeCount) {
|
|
for(int i = 0; i < elfFdeCount; i++)
|
|
free(elfFdes[i]);
|
|
}
|
|
free(elfFdes);
|
|
|
|
elfFdes = NULL;
|
|
elfFdeCount = 0;
|
|
}
|
|
|
|
ELFcie *cie = elfCies;
|
|
while(cie) {
|
|
ELFcie *next = cie->next;
|
|
free(cie);
|
|
cie = next;
|
|
}
|
|
elfCies = NULL;
|
|
|
|
if(elfFileData) {
|
|
free(elfFileData);
|
|
elfFileData = NULL;
|
|
}
|
|
}
|