#include "defs.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
#include "target.h"
#include "elf/dwarf2.h"
#include "inferior.h"
#include "regcache.h"
#include "dwarf2cfi.h"
struct cie_unit
{
ULONGEST offset;
char *augmentation;
unsigned int code_align;
int data_align;
unsigned char ra;
unsigned char addr_encoding;
char *data;
unsigned int data_length;
struct objfile *objfile;
struct cie_unit *next;
};
struct fde_unit
{
CORE_ADDR initial_location;
CORE_ADDR address_range;
struct cie_unit *cie_ptr;
char *data;
unsigned int data_length;
};
struct fde_array
{
struct fde_unit **array;
int elems;
int array_size;
};
struct context_reg
{
union
{
unsigned int reg;
long offset;
CORE_ADDR addr;
}
loc;
enum
{
REG_CTX_UNSAVED,
REG_CTX_SAVED_OFFSET,
REG_CTX_SAVED_REG,
REG_CTX_SAVED_ADDR,
REG_CTX_VALUE,
}
how;
};
struct context
{
struct context_reg *reg;
CORE_ADDR cfa;
CORE_ADDR ra;
void *lsda;
int args_size;
};
struct frame_state_reg
{
union
{
unsigned int reg;
long offset;
unsigned char *exp;
}
loc;
enum
{
REG_UNSAVED,
REG_SAVED_OFFSET,
REG_SAVED_REG,
REG_SAVED_EXP,
}
how;
};
struct frame_state
{
struct frame_state_regs
{
struct frame_state_reg *reg;
struct frame_state_regs *prev;
}
regs;
long cfa_offset;
int cfa_reg;
unsigned char *cfa_exp;
enum
{
CFA_UNSET,
CFA_REG_OFFSET,
CFA_EXP,
}
cfa_how;
CORE_ADDR pc;
int data_align;
unsigned int code_align;
unsigned char retaddr_column;
unsigned char addr_encoding;
struct objfile *objfile;
};
#define UNWIND_CONTEXT(fi) ((struct context *) (fi->context))
static struct cie_unit *cie_chunks;
static struct fde_array fde_chunks;
static struct obstack unwind_tmp_obstack;
extern file_ptr dwarf_frame_offset;
extern unsigned int dwarf_frame_size;
extern file_ptr dwarf_eh_frame_offset;
extern unsigned int dwarf_eh_frame_size;
static char *dwarf_frame_buffer;
extern char *dwarf2_read_section (struct objfile *objfile, file_ptr offset,
unsigned int size);
static struct fde_unit *fde_unit_alloc (void);
static struct cie_unit *cie_unit_alloc (void);
static void fde_chunks_need_space ();
static struct context *context_alloc ();
static struct frame_state *frame_state_alloc ();
static void unwind_tmp_obstack_free ();
static void context_cpy (struct context *dst, struct context *src);
static unsigned int read_1u (bfd *abfd, char **p);
static int read_1s (bfd *abfd, char **p);
static unsigned int read_2u (bfd *abfd, char **p);
static int read_2s (bfd *abfd, char **p);
static unsigned int read_4u (bfd *abfd, char **p);
static int read_4s (bfd *abfd, char **p);
static ULONGEST read_8u (bfd *abfd, char **p);
static LONGEST read_8s (bfd *abfd, char **p);
static ULONGEST read_uleb128 (bfd *abfd, char **p);
static LONGEST read_sleb128 (bfd *abfd, char **p);
static CORE_ADDR read_pointer (bfd *abfd, char **p);
static CORE_ADDR read_encoded_pointer (bfd *abfd, char **p,
unsigned char encoding);
static LONGEST read_initial_length (bfd *abfd, char *buf, int *bytes_read);
static ULONGEST read_length (bfd *abfd, char *buf, int *bytes_read,
int dwarf64);
static ULONGEST read_address (bfd *abfd, char **p);
static int is_cie (ULONGEST cie_id, int dwarf64);
static int compare_fde_unit (const void *a, const void *b);
void dwarf2_build_frame_info (struct objfile *objfile);
static void execute_cfa_program (struct objfile *objfile, char *insn_ptr,
char *insn_end, struct context *context,
struct frame_state *fs);
static struct fde_unit *get_fde_for_addr (CORE_ADDR pc);
static void frame_state_for (struct context *context, struct frame_state *fs);
static void get_reg (char *reg, struct context *context, int regnum);
static CORE_ADDR execute_stack_op (struct objfile *objfile,
char *op_ptr, char *op_end,
struct context *context, CORE_ADDR initial);
static void update_context (struct context *context, struct frame_state *fs,
int chain);
static struct fde_unit *
fde_unit_alloc (void)
{
struct fde_unit *fde;
fde = (struct fde_unit *) xmalloc (sizeof (struct fde_unit));
memset (fde, 0, sizeof (struct fde_unit));
return fde;
}
static struct cie_unit *
cie_unit_alloc (void)
{
struct cie_unit *cie;
cie = (struct cie_unit *) xmalloc (sizeof (struct cie_unit));
memset (cie, 0, sizeof (struct cie_unit));
return cie;
}
static void
fde_chunks_need_space ()
{
if (fde_chunks.elems < fde_chunks.array_size)
return;
fde_chunks.array_size =
fde_chunks.array_size ? 2 * fde_chunks.array_size : 1024;
fde_chunks.array =
xrealloc (fde_chunks.array,
sizeof (struct fde_unit) * fde_chunks.array_size);
}
static struct context *
context_alloc ()
{
struct context *context;
struct context_reg *reg;
int regs_size = sizeof (struct context_reg) * NUM_REGS;
context = (struct context *) obstack_alloc (&unwind_tmp_obstack,
sizeof (struct context));
memset (context, 0, sizeof (struct context));
context->reg = (struct context_reg *) obstack_alloc (&unwind_tmp_obstack,
regs_size);
memset (context->reg, 0, regs_size);
return context;
}
static struct frame_state *
frame_state_alloc ()
{
struct frame_state *fs;
struct frame_state_reg *reg;
int regs_size = sizeof (struct frame_state_reg) * NUM_REGS;
fs = (struct frame_state *) obstack_alloc (&unwind_tmp_obstack,
sizeof (struct frame_state));
memset (fs, 0, sizeof (struct frame_state));
fs->regs.reg = (struct frame_state_reg *) obstack_alloc (&unwind_tmp_obstack,
regs_size);
memset (fs->regs.reg, 0, regs_size);
return fs;
}
static void
unwind_tmp_obstack_free ()
{
obstack_free (&unwind_tmp_obstack, NULL);
obstack_init (&unwind_tmp_obstack);
}
static void
context_cpy (struct context *dst, struct context *src)
{
struct context_reg *reg = dst->reg;
int regs_size = sizeof (struct context_reg) * NUM_REGS;
*dst = *src;
memcpy (dst->reg, src->reg, regs_size);
}
static unsigned int
read_1u (bfd *abfd, char **p)
{
unsigned ret;
ret= bfd_get_8 (abfd, (bfd_byte *) *p);
(*p) ++;
return ret;
}
static int
read_1s (bfd *abfd, char **p)
{
int ret;
ret= bfd_get_signed_8 (abfd, (bfd_byte *) *p);
(*p) ++;
return ret;
}
static unsigned int
read_2u (bfd *abfd, char **p)
{
unsigned ret;
ret= bfd_get_16 (abfd, (bfd_byte *) *p);
(*p) ++;
return ret;
}
static int
read_2s (bfd *abfd, char **p)
{
int ret;
ret= bfd_get_signed_16 (abfd, (bfd_byte *) *p);
(*p) += 2;
return ret;
}
static unsigned int
read_4u (bfd *abfd, char **p)
{
unsigned int ret;
ret= bfd_get_32 (abfd, (bfd_byte *) *p);
(*p) += 4;
return ret;
}
static int
read_4s (bfd *abfd, char **p)
{
int ret;
ret= bfd_get_signed_32 (abfd, (bfd_byte *) *p);
(*p) += 4;
return ret;
}
static ULONGEST
read_8u (bfd *abfd, char **p)
{
ULONGEST ret;
ret = bfd_get_64 (abfd, (bfd_byte *) *p);
(*p) += 8;
return ret;
}
static LONGEST
read_8s (bfd *abfd, char **p)
{
LONGEST ret;
ret = bfd_get_signed_64 (abfd, (bfd_byte *) *p);
(*p) += 8;
return ret;
}
static ULONGEST
read_uleb128 (bfd *abfd, char **p)
{
ULONGEST ret;
int i, shift;
unsigned char byte;
ret = 0;
shift = 0;
i = 0;
while (1)
{
byte = bfd_get_8 (abfd, (bfd_byte *) *p);
(*p) ++;
ret |= ((unsigned long) (byte & 127) << shift);
if ((byte & 128) == 0)
{
break;
}
shift += 7;
}
return ret;
}
static LONGEST
read_sleb128 (bfd *abfd, char **p)
{
LONGEST ret;
int i, shift, size, num_read;
unsigned char byte;
ret = 0;
shift = 0;
size = 32;
num_read = 0;
i = 0;
while (1)
{
byte = bfd_get_8 (abfd, (bfd_byte *) *p);
(*p) ++;
ret |= ((long) (byte & 127) << shift);
shift += 7;
if ((byte & 128) == 0)
{
break;
}
}
if ((shift < size) && (byte & 0x40))
{
ret |= -(1 << shift);
}
return ret;
}
static CORE_ADDR
read_pointer (bfd *abfd, char **p)
{
switch (TARGET_ADDR_BIT / TARGET_CHAR_BIT)
{
case 4:
return read_4u (abfd, p);
case 8:
return read_8u (abfd, p);
default:
error ("dwarf cfi error: unsupported target address length.");
}
}
static CORE_ADDR
read_encoded_pointer (bfd *abfd, char **p, unsigned char encoding)
{
CORE_ADDR ret;
switch (encoding & 0x0f)
{
case DW_EH_PE_absptr:
ret = read_pointer (abfd, p);
break;
case DW_EH_PE_uleb128:
ret = read_uleb128 (abfd, p);
break;
case DW_EH_PE_sleb128:
ret = read_sleb128 (abfd, p);
break;
case DW_EH_PE_udata2:
ret = read_2u (abfd, p);
break;
case DW_EH_PE_udata4:
ret = read_4u (abfd, p);
break;
case DW_EH_PE_udata8:
ret = read_8u (abfd, p);
break;
case DW_EH_PE_sdata2:
ret = read_2s (abfd, p);
break;
case DW_EH_PE_sdata4:
ret = read_4s (abfd, p);
break;
case DW_EH_PE_sdata8:
ret = read_8s (abfd, p);
break;
default:
internal_error (__FILE__, __LINE__,
"read_encoded_pointer: unknown pointer encoding");
}
if (ret != 0)
switch (encoding & 0xf0)
{
case DW_EH_PE_absptr:
break;
case DW_EH_PE_pcrel:
ret += (CORE_ADDR) *p;
break;
case DW_EH_PE_textrel:
case DW_EH_PE_datarel:
case DW_EH_PE_funcrel:
default:
internal_error (__FILE__, __LINE__,
"read_encoded_pointer: unknown pointer encoding");
}
return ret;
}
static LONGEST
read_initial_length (bfd * abfd, char *buf, int *bytes_read)
{
LONGEST ret = 0;
ret = bfd_get_32 (abfd, (bfd_byte *) buf);
if (ret == 0xffffffff)
{
ret = bfd_get_64 (abfd, (bfd_byte *) buf + 4);
*bytes_read = 12;
}
else
{
*bytes_read = 4;
}
return ret;
}
static ULONGEST
read_length (bfd * abfd, char *buf, int *bytes_read, int dwarf64)
{
if (dwarf64)
{
*bytes_read = 8;
return read_8u (abfd, &buf);
}
else
{
*bytes_read = 4;
return read_4u (abfd, &buf);
}
}
static void
execute_cfa_program ( struct objfile *objfile, char *insn_ptr, char *insn_end,
struct context *context, struct frame_state *fs)
{
struct frame_state_regs *unused_rs = NULL;
fs->regs.prev = NULL;
while (insn_ptr < insn_end && fs->pc < context->ra)
{
unsigned char insn = *insn_ptr++;
ULONGEST reg, uoffset;
LONGEST offset;
int bytes_read;
if (insn & DW_CFA_advance_loc)
fs->pc += (insn & 0x3f) * fs->code_align;
else if (insn & DW_CFA_offset)
{
reg = insn & 0x3f;
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
offset = (long) uoffset * fs->data_align;
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = offset;
}
else if (insn & DW_CFA_restore)
{
reg = insn & 0x3f;
fs->regs.reg[reg].how = REG_UNSAVED;
}
else
switch (insn)
{
case DW_CFA_set_loc:
fs->pc = read_encoded_pointer (objfile->obfd, &insn_ptr,
fs->addr_encoding);
break;
case DW_CFA_advance_loc1:
fs->pc += read_1u (objfile->obfd, &insn_ptr);
break;
case DW_CFA_advance_loc2:
fs->pc += read_2u (objfile->obfd, &insn_ptr);
break;
case DW_CFA_advance_loc4:
fs->pc += read_4u (objfile->obfd, &insn_ptr);
break;
case DW_CFA_offset_extended:
reg = read_uleb128 (objfile->obfd, &insn_ptr);
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
offset = (long) uoffset *fs->data_align;
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = offset;
break;
case DW_CFA_restore_extended:
reg = read_uleb128 (objfile->obfd, &insn_ptr);
fs->regs.reg[reg].how = REG_UNSAVED;
break;
case DW_CFA_undefined:
case DW_CFA_same_value:
case DW_CFA_nop:
break;
case DW_CFA_register:
{
ULONGEST reg2;
reg = read_uleb128 (objfile->obfd, &insn_ptr);
reg2 = read_uleb128 (objfile->obfd, &insn_ptr);
fs->regs.reg[reg].how = REG_SAVED_REG;
fs->regs.reg[reg].loc.reg = reg2;
}
break;
case DW_CFA_remember_state:
{
struct frame_state_regs *new_rs;
if (unused_rs)
{
new_rs = unused_rs;
unused_rs = unused_rs->prev;
}
else
new_rs = xmalloc (sizeof (struct frame_state_regs));
*new_rs = fs->regs;
fs->regs.prev = new_rs;
}
break;
case DW_CFA_restore_state:
{
struct frame_state_regs *old_rs = fs->regs.prev;
fs->regs = *old_rs;
old_rs->prev = unused_rs;
unused_rs = old_rs;
}
break;
case DW_CFA_def_cfa:
reg = read_uleb128 (objfile->obfd, &insn_ptr);
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
fs->cfa_reg = reg;
fs->cfa_offset = uoffset;
fs->cfa_how = CFA_REG_OFFSET;
break;
case DW_CFA_def_cfa_register:
reg = read_uleb128 (objfile->obfd, &insn_ptr);
fs->cfa_reg = reg;
fs->cfa_how = CFA_REG_OFFSET;
break;
case DW_CFA_def_cfa_offset:
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
fs->cfa_offset = uoffset;
break;
case DW_CFA_def_cfa_expression:
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
fs->cfa_exp = insn_ptr;
fs->cfa_how = CFA_EXP;
insn_ptr += uoffset;
break;
case DW_CFA_expression:
reg = read_uleb128 (objfile->obfd, &insn_ptr);
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
fs->regs.reg[reg].how = REG_SAVED_EXP;
fs->regs.reg[reg].loc.exp = insn_ptr;
insn_ptr += uoffset;
break;
case DW_CFA_offset_extended_sf:
reg = read_uleb128 (objfile->obfd, &insn_ptr);
offset = read_sleb128 (objfile->obfd, &insn_ptr);
offset *= fs->data_align;
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = offset;
break;
case DW_CFA_def_cfa_sf:
reg = read_uleb128 (objfile->obfd, &insn_ptr);
offset = read_sleb128 (objfile->obfd, &insn_ptr);
fs->cfa_offset = offset;
fs->cfa_reg = reg;
fs->cfa_how = CFA_REG_OFFSET;
break;
case DW_CFA_def_cfa_offset_sf:
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
fs->cfa_offset = uoffset;
break;
case DW_CFA_GNU_window_save:
for (reg = 16; reg < 32; ++reg)
{
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
}
break;
case DW_CFA_GNU_args_size:
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
context->args_size = uoffset;
break;
case DW_CFA_GNU_negative_offset_extended:
reg = read_uleb128 (objfile->obfd, &insn_ptr);
uoffset = read_uleb128 (objfile->obfd, &insn_ptr);
offset = (long) uoffset *fs->data_align;
fs->regs.reg[reg].how = REG_SAVED_OFFSET;
fs->regs.reg[reg].loc.offset = -offset;
break;
default:
error ("dwarf cfi error: unknown cfa instruction %d.", insn);
}
}
}
static struct fde_unit *
get_fde_for_addr (CORE_ADDR pc)
{
size_t lo, hi;
struct fde_unit *fde = NULL;
lo = 0;
hi = fde_chunks.elems;
while (lo < hi)
{
size_t i = (lo + hi) / 2;
fde = fde_chunks.array[i];
if (pc < fde->initial_location)
hi = i;
else if (pc >= fde->initial_location + fde->address_range)
lo = i + 1;
else
return fde;
}
return 0;
}
static void
frame_state_for (struct context *context, struct frame_state *fs)
{
struct fde_unit *fde;
struct cie_unit *cie;
unsigned char *aug, *insn, *end;
context->args_size = 0;
context->lsda = 0;
if ((fde = get_fde_for_addr (context->ra - 1)) != NULL)
{
fs->pc = fde->initial_location;
cie = fde->cie_ptr;
fs->code_align = cie->code_align;
fs->data_align = cie->data_align;
fs->retaddr_column = cie->ra;
fs->addr_encoding = cie->addr_encoding;
fs->objfile = cie->objfile;
execute_cfa_program (cie->objfile, cie->data,
cie->data + cie->data_length, context, fs);
execute_cfa_program (cie->objfile, fde->data,
fde->data + fde->data_length, context, fs);
}
}
static void
get_reg (char *reg, struct context *context, int regnum)
{
switch (context->reg[regnum].how)
{
case REG_CTX_UNSAVED:
read_register_gen (regnum, reg);
break;
case REG_CTX_SAVED_OFFSET:
target_read_memory (context->cfa + context->reg[regnum].loc.offset,
reg, REGISTER_RAW_SIZE (regnum));
break;
case REG_CTX_SAVED_REG:
read_register_gen (context->reg[regnum].loc.reg, reg);
break;
case REG_CTX_SAVED_ADDR:
target_read_memory (context->reg[regnum].loc.addr,
reg, REGISTER_RAW_SIZE (regnum));
break;
case REG_CTX_VALUE:
memcpy (reg, &context->reg[regnum].loc.addr,
REGISTER_RAW_SIZE (regnum));
break;
default:
internal_error (__FILE__, __LINE__,
"get_reg: unknown register rule");
}
}
static CORE_ADDR
execute_stack_op (struct objfile *objfile,
char *op_ptr, char *op_end, struct context *context,
CORE_ADDR initial)
{
CORE_ADDR stack[64];
int stack_elt;
stack[0] = initial;
stack_elt = 1;
while (op_ptr < op_end)
{
enum dwarf_location_atom op = *op_ptr++;
ULONGEST result, reg;
LONGEST offset;
switch (op)
{
case DW_OP_lit0:
case DW_OP_lit1:
case DW_OP_lit2:
case DW_OP_lit3:
case DW_OP_lit4:
case DW_OP_lit5:
case DW_OP_lit6:
case DW_OP_lit7:
case DW_OP_lit8:
case DW_OP_lit9:
case DW_OP_lit10:
case DW_OP_lit11:
case DW_OP_lit12:
case DW_OP_lit13:
case DW_OP_lit14:
case DW_OP_lit15:
case DW_OP_lit16:
case DW_OP_lit17:
case DW_OP_lit18:
case DW_OP_lit19:
case DW_OP_lit20:
case DW_OP_lit21:
case DW_OP_lit22:
case DW_OP_lit23:
case DW_OP_lit24:
case DW_OP_lit25:
case DW_OP_lit26:
case DW_OP_lit27:
case DW_OP_lit28:
case DW_OP_lit29:
case DW_OP_lit30:
case DW_OP_lit31:
result = op - DW_OP_lit0;
break;
case DW_OP_addr:
result = read_pointer (objfile->obfd, &op_ptr);
break;
case DW_OP_const1u:
result = read_1u (objfile->obfd, &op_ptr);
break;
case DW_OP_const1s:
result = read_1s (objfile->obfd, &op_ptr);
break;
case DW_OP_const2u:
result = read_2u (objfile->obfd, &op_ptr);
break;
case DW_OP_const2s:
result = read_2s (objfile->obfd, &op_ptr);
break;
case DW_OP_const4u:
result = read_4u (objfile->obfd, &op_ptr);
break;
case DW_OP_const4s:
result = read_4s (objfile->obfd, &op_ptr);
break;
case DW_OP_const8u:
result = read_8u (objfile->obfd, &op_ptr);
break;
case DW_OP_const8s:
result = read_8s (objfile->obfd, &op_ptr);
break;
case DW_OP_constu:
result = read_uleb128 (objfile->obfd, &op_ptr);
break;
case DW_OP_consts:
result = read_sleb128 (objfile->obfd, &op_ptr);
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:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31:
get_reg ((char *) &result, context, op - DW_OP_reg0);
break;
case DW_OP_regx:
reg = read_uleb128 (objfile->obfd, &op_ptr);
get_reg ((char *) &result, context, reg);
break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31:
offset = read_sleb128 (objfile->obfd, &op_ptr);
get_reg ((char *) &result, context, op - DW_OP_breg0);
result += offset;
break;
case DW_OP_bregx:
reg = read_uleb128 (objfile->obfd, &op_ptr);
offset = read_sleb128 (objfile->obfd, &op_ptr);
get_reg ((char *) &result, context, reg);
result += offset;
break;
case DW_OP_dup:
if (stack_elt < 1)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
result = stack[stack_elt - 1];
break;
case DW_OP_drop:
if (--stack_elt < 0)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
goto no_push;
case DW_OP_pick:
offset = *op_ptr++;
if (offset >= stack_elt - 1)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
result = stack[stack_elt - 1 - offset];
break;
case DW_OP_over:
if (stack_elt < 2)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
result = stack[stack_elt - 2];
break;
case DW_OP_rot:
{
CORE_ADDR t1, t2, t3;
if (stack_elt < 3)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
t1 = stack[stack_elt - 1];
t2 = stack[stack_elt - 2];
t3 = stack[stack_elt - 3];
stack[stack_elt - 1] = t2;
stack[stack_elt - 2] = t3;
stack[stack_elt - 3] = t1;
goto no_push;
}
case DW_OP_deref:
case DW_OP_deref_size:
case DW_OP_abs:
case DW_OP_neg:
case DW_OP_not:
case DW_OP_plus_uconst:
if (--stack_elt < 0)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
result = stack[stack_elt];
switch (op)
{
case DW_OP_deref:
{
char *ptr = (char *) result;
result = read_pointer (objfile->obfd, &ptr);
}
break;
case DW_OP_deref_size:
{
char *ptr = (char *) result;
switch (*op_ptr++)
{
case 1:
result = read_1u (objfile->obfd, &ptr);
break;
case 2:
result = read_2u (objfile->obfd, &ptr);
break;
case 4:
result = read_4u (objfile->obfd, &ptr);
break;
case 8:
result = read_8u (objfile->obfd, &ptr);
break;
default:
internal_error (__FILE__, __LINE__,
"execute_stack_op error");
}
}
break;
case DW_OP_abs:
if (result < 0)
result = -result;
break;
case DW_OP_neg:
result = -result;
break;
case DW_OP_not:
result = ~result;
break;
case DW_OP_plus_uconst:
result += read_uleb128 (objfile->obfd, &op_ptr);
break;
}
break;
case DW_OP_and:
case DW_OP_div:
case DW_OP_minus:
case DW_OP_mod:
case DW_OP_mul:
case DW_OP_or:
case DW_OP_plus:
case DW_OP_le:
case DW_OP_ge:
case DW_OP_eq:
case DW_OP_lt:
case DW_OP_gt:
case DW_OP_ne:
{
CORE_ADDR first, second;
if ((stack_elt -= 2) < 0)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
second = stack[stack_elt];
first = stack[stack_elt + 1];
switch (op)
{
case DW_OP_and:
result = second & first;
break;
case DW_OP_div:
result = (LONGEST) second / (LONGEST) first;
break;
case DW_OP_minus:
result = second - first;
break;
case DW_OP_mod:
result = (LONGEST) second % (LONGEST) first;
break;
case DW_OP_mul:
result = second * first;
break;
case DW_OP_or:
result = second | first;
break;
case DW_OP_plus:
result = second + first;
break;
case DW_OP_shl:
result = second << first;
break;
case DW_OP_shr:
result = second >> first;
break;
case DW_OP_shra:
result = (LONGEST) second >> first;
break;
case DW_OP_xor:
result = second ^ first;
break;
case DW_OP_le:
result = (LONGEST) first <= (LONGEST) second;
break;
case DW_OP_ge:
result = (LONGEST) first >= (LONGEST) second;
break;
case DW_OP_eq:
result = (LONGEST) first == (LONGEST) second;
break;
case DW_OP_lt:
result = (LONGEST) first < (LONGEST) second;
break;
case DW_OP_gt:
result = (LONGEST) first > (LONGEST) second;
break;
case DW_OP_ne:
result = (LONGEST) first != (LONGEST) second;
break;
}
}
break;
case DW_OP_skip:
offset = read_2s (objfile->obfd, &op_ptr);
op_ptr += offset;
goto no_push;
case DW_OP_bra:
if (--stack_elt < 0)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
offset = read_2s (objfile->obfd, &op_ptr);
if (stack[stack_elt] != 0)
op_ptr += offset;
goto no_push;
case DW_OP_nop:
goto no_push;
default:
internal_error (__FILE__, __LINE__, "execute_stack_op error");
}
if ((size_t) stack_elt >= sizeof (stack) / sizeof (*stack))
internal_error (__FILE__, __LINE__, "execute_stack_op error");
stack[++stack_elt] = result;
no_push:;
}
if (--stack_elt < 0)
internal_error (__FILE__, __LINE__, "execute_stack_op error");
return stack[stack_elt];
}
static void
update_context (struct context *context, struct frame_state *fs, int chain)
{
struct context *orig_context;
CORE_ADDR cfa;
long i;
orig_context = context_alloc ();
context_cpy (orig_context, context);
switch (fs->cfa_how)
{
case CFA_REG_OFFSET:
get_reg ((char *) &cfa, context, fs->cfa_reg);
cfa += fs->cfa_offset;
break;
case CFA_EXP:
{
char *exp = fs->cfa_exp;
ULONGEST len;
len = read_uleb128 (fs->objfile->obfd, &exp);
cfa = (CORE_ADDR) execute_stack_op (fs->objfile, exp,
exp + len, context, 0);
break;
}
}
context->cfa = cfa;
if (!chain)
orig_context->cfa = cfa;
for (i = 0; i < NUM_REGS; ++i)
switch (fs->regs.reg[i].how)
{
case REG_UNSAVED:
if (i == SP_REGNUM)
{
context->reg[i].how = REG_CTX_VALUE;
context->reg[i].loc.addr = cfa;
}
else
context->reg[i].how = REG_CTX_UNSAVED;
break;
case REG_SAVED_OFFSET:
context->reg[i].how = REG_CTX_SAVED_OFFSET;
context->reg[i].loc.offset = fs->regs.reg[i].loc.offset;
break;
case REG_SAVED_REG:
switch (orig_context->reg[fs->regs.reg[i].loc.reg].how)
{
case REG_CTX_UNSAVED:
context->reg[i].how = REG_CTX_UNSAVED;
break;
case REG_CTX_SAVED_OFFSET:
context->reg[i].how = REG_CTX_SAVED_OFFSET;
context->reg[i].loc.offset = orig_context->cfa - context->cfa +
orig_context->reg[fs->regs.reg[i].loc.reg].loc.offset;
break;
case REG_CTX_SAVED_REG:
context->reg[i].how = REG_CTX_SAVED_REG;
context->reg[i].loc.reg =
orig_context->reg[fs->regs.reg[i].loc.reg].loc.reg;
break;
case REG_CTX_SAVED_ADDR:
context->reg[i].how = REG_CTX_SAVED_ADDR;
context->reg[i].loc.addr =
orig_context->reg[fs->regs.reg[i].loc.reg].loc.addr;
default:
internal_error (__FILE__, __LINE__,
"cfi_update_context: unknown register rule");
}
break;
case REG_SAVED_EXP:
{
char *exp = fs->regs.reg[i].loc.exp;
ULONGEST len;
CORE_ADDR val;
len = read_uleb128 (fs->objfile->obfd, &exp);
val = execute_stack_op (fs->objfile, exp, exp + len,
orig_context, cfa);
context->reg[i].how = REG_CTX_SAVED_ADDR;
context->reg[i].loc.addr = val;
}
break;
default:
internal_error (__FILE__, __LINE__,
"cfi_update_context: unknown register rule");
}
get_reg ((char *) &context->ra, context, fs->retaddr_column);
unwind_tmp_obstack_free ();
}
static int
is_cie (ULONGEST cie_id, int dwarf64)
{
return dwarf64 ? (cie_id == 0xffffffffffffffff) : (cie_id == 0xffffffff);
}
static int
compare_fde_unit (const void *a, const void *b)
{
struct fde_unit **first, **second;
first = (struct fde_unit **) a;
second = (struct fde_unit **) b;
if ((*first)->initial_location > (*second)->initial_location)
return 1;
else if ((*first)->initial_location < (*second)->initial_location)
return -1;
else
return 0;
}
void
dwarf2_build_frame_info (struct objfile *objfile)
{
bfd *abfd = objfile->obfd;
char *start = NULL;
char *end = NULL;
obstack_init (&unwind_tmp_obstack);
dwarf_frame_buffer = 0;
if (dwarf_frame_offset)
{
dwarf_frame_buffer = dwarf2_read_section (objfile,
dwarf_frame_offset,
dwarf_frame_size);
start = dwarf_frame_buffer;
end = dwarf_frame_buffer + dwarf_frame_size;
}
else if (dwarf_eh_frame_offset)
{
dwarf_frame_buffer = dwarf2_read_section (objfile,
dwarf_eh_frame_offset,
dwarf_eh_frame_size);
start = dwarf_frame_buffer;
end = dwarf_frame_buffer + dwarf_eh_frame_size;
}
if (start)
{
while (start < end)
{
unsigned long length;
ULONGEST cie_id;
ULONGEST unit_offset = start - dwarf_frame_buffer;
int bytes_read;
int dwarf64;
char *block_end;
length = read_initial_length (abfd, start, &bytes_read);
start += bytes_read;
dwarf64 = (bytes_read == 12);
block_end = start + length;
cie_id = read_length (abfd, start, &bytes_read, dwarf64);
start += bytes_read;
if (is_cie (cie_id, dwarf64))
{
struct cie_unit *cie = cie_unit_alloc ();
char *aug;
cie->objfile = objfile;
cie->next = cie_chunks;
cie_chunks = cie;
cie->objfile = objfile;
cie->offset = unit_offset;
start++;
cie->augmentation = aug = start;
while (*start)
start++;
start++;
cie->code_align = read_uleb128 (abfd, &start);
cie->data_align = read_sleb128 (abfd, &start);
cie->ra = read_1u (abfd, &start);
if (*aug == 'z')
{
int xtra = read_uleb128 (abfd, &start);
start += xtra;
++aug;
}
while (*aug != '\0')
{
if (aug[0] == 'e' && aug[1] == 'h')
{
start += sizeof (void *);
aug += 2;
}
else if (aug[0] == 'R')
{
cie->addr_encoding = *start++;
aug += 1;
}
else if (aug[0] == 'P')
{
CORE_ADDR ptr;
ptr = read_encoded_pointer (abfd, &start,
cie->addr_encoding);
aug += 1;
}
else
warning ("unknown augmentation");
}
cie->data = start;
cie->data_length = block_end - start;
}
else
{
struct fde_unit *fde;
struct cie_unit *cie;
fde_chunks_need_space ();
fde = fde_unit_alloc ();
fde_chunks.array[fde_chunks.elems++] = fde;
fde->initial_location = read_pointer (abfd, &start);
fde->address_range = read_pointer (abfd, &start);
for (cie = cie_chunks;
cie && (cie->offset != cie_id); cie = cie->next);
if (!cie)
error ("dwarf cfi error: can't find CIE pointer");
fde->cie_ptr = cie;
if (cie->augmentation[0] == 'z')
read_uleb128 (abfd, &start);
fde->data = start;
fde->data_length = block_end - start;
}
start = block_end;
}
qsort (fde_chunks.array, fde_chunks.elems,
sizeof (struct fde_unit *), compare_fde_unit);
}
}
CORE_ADDR
cfi_read_fp ()
{
struct context *context;
struct frame_state *fs;
CORE_ADDR cfa;
context = context_alloc ();
fs = frame_state_alloc ();
context->ra = read_pc () + 1;
frame_state_for (context, fs);
update_context (context, fs, 0);
cfa = context->cfa;
unwind_tmp_obstack_free ();
return cfa;
}
void
cfi_write_fp (CORE_ADDR val)
{
struct context *context;
struct frame_state *fs;
context = context_alloc ();
fs = frame_state_alloc ();
context->ra = read_pc () + 1;
frame_state_for (context, fs);
if (fs->cfa_how == CFA_REG_OFFSET)
{
val -= fs->cfa_offset;
write_register_gen (fs->cfa_reg, (char *) &val);
}
else
warning ("Can't write fp.");
unwind_tmp_obstack_free ();
}
void
cfi_pop_frame (struct frame_info *fi)
{
char regbuf[MAX_REGISTER_RAW_SIZE];
int regnum;
fi = get_current_frame ();
for (regnum = 0; regnum < NUM_REGS; regnum++)
{
get_reg (regbuf, UNWIND_CONTEXT (fi), regnum);
write_register_bytes (REGISTER_BYTE (regnum), regbuf,
REGISTER_RAW_SIZE (regnum));
}
write_register (PC_REGNUM, UNWIND_CONTEXT (fi)->ra);
flush_cached_frames ();
}
CORE_ADDR
cfi_frame_chain (struct frame_info *fi)
{
struct context *context;
struct frame_state *fs;
CORE_ADDR cfa;
context = context_alloc ();
fs = frame_state_alloc ();
context_cpy (context, UNWIND_CONTEXT (fi));
if (context->ra == 0)
{
unwind_tmp_obstack_free ();
return 0;
}
frame_state_for (context, fs);
update_context (context, fs, 1);
cfa = context->cfa;
unwind_tmp_obstack_free ();
return cfa;
}
void
cfi_init_frame_pc (int fromleaf, struct frame_info *fi)
{
if (fi->next)
get_reg ((char *) &(fi->pc), UNWIND_CONTEXT (fi->next), PC_REGNUM);
else
fi->pc = read_pc ();
}
void
cfi_init_extra_frame_info (int fromleaf, struct frame_info *fi)
{
struct frame_state *fs;
fs = frame_state_alloc ();
fi->context = frame_obstack_alloc (sizeof (struct context));
UNWIND_CONTEXT (fi)->reg =
frame_obstack_alloc (sizeof (struct context_reg) * NUM_REGS);
memset (UNWIND_CONTEXT (fi)->reg, 0,
sizeof (struct context_reg) * NUM_REGS);
if (fi->next)
{
context_cpy (UNWIND_CONTEXT (fi), UNWIND_CONTEXT (fi->next));
frame_state_for (UNWIND_CONTEXT (fi), fs);
update_context (UNWIND_CONTEXT (fi), fs, 1);
}
else
{
UNWIND_CONTEXT (fi)->ra = fi->pc + 1;
frame_state_for (UNWIND_CONTEXT (fi), fs);
update_context (UNWIND_CONTEXT (fi), fs, 0);
}
unwind_tmp_obstack_free ();
}
CORE_ADDR
cfi_get_ra (struct frame_info *fi)
{
return UNWIND_CONTEXT (fi)->ra;
}
void
cfi_get_saved_register (char *raw_buffer,
int *optimized,
CORE_ADDR * addrp,
struct frame_info *frame,
int regnum, enum lval_type *lval)
{
if (!target_has_registers)
error ("No registers.");
if (optimized != NULL)
*optimized = 0;
if (addrp)
*addrp = 0;
if (!frame->next)
{
read_register_gen (regnum, raw_buffer);
if (lval != NULL)
*lval = lval_register;
if (addrp != NULL)
*addrp = REGISTER_BYTE (regnum);
}
else
{
frame = frame->next;
switch (UNWIND_CONTEXT (frame)->reg[regnum].how)
{
case REG_CTX_UNSAVED:
read_register_gen (regnum, raw_buffer);
if (lval != NULL)
*lval = not_lval;
if (optimized != NULL)
*optimized = 1;
break;
case REG_CTX_SAVED_OFFSET:
target_read_memory (UNWIND_CONTEXT (frame)->cfa +
UNWIND_CONTEXT (frame)->reg[regnum].loc.offset,
raw_buffer, REGISTER_RAW_SIZE (regnum));
if (lval != NULL)
*lval = lval_memory;
if (addrp != NULL)
*addrp =
UNWIND_CONTEXT (frame)->cfa +
UNWIND_CONTEXT (frame)->reg[regnum].loc.offset;
break;
case REG_CTX_SAVED_REG:
read_register_gen (UNWIND_CONTEXT (frame)->reg[regnum].loc.reg,
raw_buffer);
if (lval != NULL)
*lval = lval_register;
if (addrp != NULL)
*addrp =
REGISTER_BYTE (UNWIND_CONTEXT (frame)->reg[regnum].loc.reg);
break;
case REG_CTX_SAVED_ADDR:
target_read_memory (UNWIND_CONTEXT (frame)->reg[regnum].loc.addr,
raw_buffer, REGISTER_RAW_SIZE (regnum));
if (lval != NULL)
*lval = lval_memory;
if (addrp != NULL)
*addrp = UNWIND_CONTEXT (frame)->reg[regnum].loc.addr;
break;
case REG_CTX_VALUE:
memcpy (raw_buffer, &UNWIND_CONTEXT (frame)->reg[regnum].loc.addr,
REGISTER_RAW_SIZE (regnum));
if (lval != NULL)
*lval = not_lval;
if (optimized != NULL)
*optimized = 0;
break;
default:
internal_error (__FILE__, __LINE__,
"cfi_get_saved_register: unknown register rule");
}
}
}
void
cfi_virtual_frame_pointer (CORE_ADDR pc, int *frame_reg,
LONGEST * frame_offset)
{
struct context *context;
struct frame_state *fs;
context = context_alloc ();
fs = frame_state_alloc ();
context->ra = read_pc () + 1;
frame_state_for (context, fs);
if (fs->cfa_how == CFA_REG_OFFSET)
{
*frame_reg = fs->cfa_reg;
*frame_offset = fs->cfa_offset;
}
else
error ("dwarf cfi error: CFA is not defined as CFA_REG_OFFSET");
unwind_tmp_obstack_free ();
}