From: Jiri Smid Date: Fri, 7 Dec 2001 12:10:15 +0000 (+0000) Subject: * dwarf2cfi.c: New file. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b6af0555b58f4a5996fd3590f2cb40d3664136f7;p=binutils-gdb.git * dwarf2cfi.c: New file. * dwarf2cfi.h: New file. * dwarf2read.c (dwarf_frame_offset, dwarf_frame_size): New variables. (dwarf_eh_frame_offset, dwarf_eh_frame_size): New variables. (dwarf2_read_section): Change to non static. (dwarf2_locate_sections): Add .debug_frame and .eh_frame section recognition. (FRAME_SECTION, EH_FRAME_SECTION): New define. * elfread.c (elf_symfile_read): Add call of frame informations build. * frame.h (frame_info): Add pointer to unwind_context. * symfile.h (dwarf2_build_frame_info): Add declaration. * gdbarch.sh (DWARF2_BUILD_FRAME_INFO): Add. * gdbarch.h, gdbarch.c: Regenerate. * Makefile.in: Add dwarf2cfi_h, dwarf2cfi.o * x86-64-tdep.c (i386_gdbarch_init): Initialize target vector to use debug frame info. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index e61cebab548..9b1fc38bff8 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,22 @@ +2001-12-07 Jiri Smid + + * dwarf2cfi.c: New file. + * dwarf2cfi.h: New file. + * dwarf2read.c (dwarf_frame_offset, dwarf_frame_size): New variables. + (dwarf_eh_frame_offset, dwarf_eh_frame_size): New variables. + (dwarf2_read_section): Change to non static. + (dwarf2_locate_sections): Add .debug_frame and .eh_frame section + recognition. + (FRAME_SECTION, EH_FRAME_SECTION): New define. + * elfread.c (elf_symfile_read): Add call of frame informations build. + * frame.h (frame_info): Add pointer to unwind_context. + * symfile.h (dwarf2_build_frame_info): Add declaration. + * gdbarch.sh (DWARF2_BUILD_FRAME_INFO): Add. + * gdbarch.h, gdbarch.c: Regenerate. + * Makefile.in: Add dwarf2cfi_h, dwarf2cfi.o + * x86-64-tdep.c (i386_gdbarch_init): Initialize target vector to + use debug frame info. + 2001-12-06 Andrew Cagney * defs.h: Do not include "mmalloc.h". diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 9d51e425a8a..cf654afb114 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -603,6 +603,7 @@ dcache_h = dcache.h defs_h = defs.h $(xm_h) $(tm_h) $(nm_h) config.status config.h \ gdbarch.h ui-file.h doublest_h = doublest.h $(floatformat_h) +dwarf2cfi_h = dwarf2cfi.h event_loop_h = event-loop.h event_top_h = event-top.h expression_h = expression.h $(doublest_h) $(symtab_h) @@ -1355,6 +1356,9 @@ dpx2-nat.o: dpx2-nat.c $(defs_h) $(gdbcore_h) $(gdb_string_h) dstread.o: dstread.c $(gdb_string_h) +dwarf2cfi.o: dwarf2cfi.c $(defs_h) $(symtab_h) $(symfile_h) $(objfiles_h) \ + $(target_h) $(inferior_h) $(regcache_h) $(dwarf2cfi_h) + dwarfread.o: dwarfread.c $(bfd_h) $(buildsym_h) $(complaints_h) $(defs_h) \ $(expression_h) $(gdbtypes_h) $(language_h) $(objfiles_h) \ $(symfile_h) $(symtab_h) $(gdb_string_h) diff --git a/gdb/dwarf2cfi.c b/gdb/dwarf2cfi.c new file mode 100644 index 00000000000..6c574bb06b7 --- /dev/null +++ b/gdb/dwarf2cfi.c @@ -0,0 +1,1740 @@ +/* Stack unwinding code based on dwarf2 frame info for GDB, the GNU debugger. + Copyright 2001 + Free Software Foundation, Inc. + Contributed by Jiri Smid, SuSE Labs. + + This file is part of GDB. + + 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 of the License, 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 "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" + +/* Common Information Entry - holds information that is shared among many + Frame Descriptors. */ +struct cie_unit +{ + /* Offset of this unit in dwarf_frame_buffer. */ + ULONGEST offset; + + /* A null-terminated string that identifies the augmentation to this CIE or + to the FDEs that use it. */ + char *augmentation; + + /* A constant that is factored out of all advance location instructions. */ + unsigned int code_align; + + /* A constant that is factored out of all offset instructions. */ + int data_align; + + /* A constant that indicates which regiter represents the return address + of a function. */ + unsigned char ra; + + /* Indicates how addresses are encoded. */ + unsigned char addr_encoding; + + /* Pointer and length of the cie program. */ + char *data; + unsigned int data_length; + + struct objfile *objfile; + + /* Next in chain. */ + struct cie_unit *next; +}; + +/* Frame Description Entry. */ +struct fde_unit +{ + /* Address of the first location associated with this entry. */ + CORE_ADDR initial_location; + + /* Length of program section described by this entry. */ + CORE_ADDR address_range; + + /* Pointer to asociated CIE. */ + struct cie_unit *cie_ptr; + + /* Pointer and length of the cie program. */ + 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; +}; + +/* This is the register and unwind state for a particular frame. */ +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 +{ + /* Each register save state can be described in terms of a CFA slot, + another register, or a location expression. */ + struct frame_state_regs + { + struct frame_state_reg *reg; + + /* Used to implement DW_CFA_remember_state. */ + struct frame_state_regs *prev; + } + regs; + + /* The CFA can be described in terms of a reg+offset or a + location expression. */ + long cfa_offset; + int cfa_reg; + unsigned char *cfa_exp; + enum + { + CFA_UNSET, + CFA_REG_OFFSET, + CFA_EXP, + } + cfa_how; + + /* The PC described by the current frame state. */ + CORE_ADDR pc; + + /* The information we care about from the CIE/FDE. */ + 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; +/* Obstack for allocating temporary storage used during unwind operations. */ +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); + + +/* Memory allocation functions. */ +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); +} + +/* Alocate a new `struct context' on temporary obstack. */ +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; +} + +/* Alocate a new `struct frame_state' on temporary obstack. */ +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; + + /* Don't allow remember/restore between CIE and FDE programs. */ + 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; + + /* From the 2.1 draft. */ + 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; + /* cfa_how deliberately not set. */ + break; + + case DW_CFA_GNU_window_save: + /* ??? Hardcoded for SPARC register window configuration. */ + 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: + /* Obsoleted by DW_CFA_offset_extended_sf, but used by + older PowerPC code. */ + 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"); + } +} + +/* Decode a DW_OP stack program. Return the top of stack. Push INITIAL + onto the stack to start. */ +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]; /* ??? Assume this is enough. */ + 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: + /* Unary operations. */ + 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: + { + /* Binary operations. */ + 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"); + } + + /* Most things push a result value. */ + if ((size_t) stack_elt >= sizeof (stack) / sizeof (*stack)) + internal_error (__FILE__, __LINE__, "execute_stack_op error"); + stack[++stack_elt] = result; + no_push:; + } + + /* We were executing this program to get a value. It should be + at top of stack. */ + 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); + /* Compute this frame's CFA. */ + switch (fs->cfa_how) + { + case CFA_REG_OFFSET: + get_reg ((char *) &cfa, context, fs->cfa_reg); + cfa += fs->cfa_offset; + break; + + case CFA_EXP: + /* ??? No way of knowing what register number is the stack pointer + to do the same sort of handling as above. Assume that if the + CFA calculation is so complicated as to require a stack program + that this will not be a problem. */ + { + 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; + + /* Compute the addresses of all registers saved in this frame. */ + 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; +} + +/* Build the cie_chunks and fde_chunks tables from informations + in .debug.frame section. */ +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++; /* version */ + + cie->augmentation = aug = start; + while (*start) + start++; + start++; /* skip past NUL */ + + 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); + } +} + + +/* Return the frame address. */ +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; +} + +/* Store the frame address. */ +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 (); +} + +/* Restore the machine to the state it had before the current frame + was created. */ +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 (); +} + +/* Determine the address of the calling function's frame. */ +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)); + + /* outermost frame */ + 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; +} + +/* Sets the pc of the frame. */ +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 (); +} + +/* Initialize unwind context informations of the frame. */ +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 (); +} + +/* Obtain return address of the frame. */ +CORE_ADDR +cfi_get_ra (struct frame_info *fi) +{ + return UNWIND_CONTEXT (fi)->ra; +} + +/* Find register number REGNUM relative to FRAME and put its + (raw) contents in *RAW_BUFFER. Set *OPTIMIZED if the variable + was optimized out (and thus can't be fetched). If the variable + was fetched from memory, set *ADDRP to where it was fetched from, + otherwise it was fetched from a register. + + The argument RAW_BUFFER must point to aligned memory. */ +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."); + + /* Normal systems don't optimize out things with register numbers. */ + if (optimized != NULL) + *optimized = 0; + + if (addrp) /* default assumption: not found in memory */ + *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"); + } + } +} + +/* Return the register that the function uses for a frame pointer, + plus any necessary offset to be applied to the register before + any frame pointer offsets. */ +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 (); +} diff --git a/gdb/dwarf2cfi.h b/gdb/dwarf2cfi.h new file mode 100644 index 00000000000..f4c675aca7b --- /dev/null +++ b/gdb/dwarf2cfi.h @@ -0,0 +1,66 @@ +/* Stack unwinding code based on dwarf2 frame info for GDB, the GNU debugger. + Copyright 2001 + Free Software Foundation, Inc. + + This file is part of GDB. + + 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 of the License, 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. */ + +#ifndef DWARF2CFI_H +#define DWARF2CFI_H + +/* Return the frame address. */ +CORE_ADDR cfi_read_fp (); + +/* Store the frame address. */ +void cfi_write_fp (CORE_ADDR val); + +/* Restore the machine to the state it had before the current frame + was created. */ +void cfi_pop_frame (struct frame_info *); + +/* Determine the address of the calling function's frame. */ +CORE_ADDR cfi_frame_chain (struct frame_info *fi); + +/* Sets the pc of the frame. */ +void cfi_init_frame_pc (int fromleaf, struct frame_info *fi); + +/* Initialize unwind context informations of the frame. */ +void cfi_init_extra_frame_info (int fromleaf, struct frame_info *fi); + +/* Obtain return address of the frame. */ +CORE_ADDR cfi_get_ra (struct frame_info *fi); + +/* Find register number REGNUM relative to FRAME and put its + (raw) contents in *RAW_BUFFER. Set *OPTIMIZED if the variable + was optimized out (and thus can't be fetched). If the variable + was fetched from memory, set *ADDRP to where it was fetched from, + otherwise it was fetched from a register. + + The argument RAW_BUFFER must point to aligned memory. */ +void cfi_get_saved_register (char *raw_buffer, + int *optimized, + CORE_ADDR * addrp, + struct frame_info *frame, + int regnum, enum lval_type *lval); + +/* Return the register that the function uses for a frame pointer, + plus any necessary offset to be applied to the register before + any frame pointer offsets. */ +void cfi_virtual_frame_pointer (CORE_ADDR pc, int *frame_regnum, + LONGEST * frame_offset); + +#endif diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index 2ac7e6ada6c..a788efbadc1 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -132,6 +132,8 @@ static file_ptr dwarf_aranges_offset; static file_ptr dwarf_loc_offset; static file_ptr dwarf_macinfo_offset; static file_ptr dwarf_str_offset; +file_ptr dwarf_frame_offset; +file_ptr dwarf_eh_frame_offset; static unsigned int dwarf_info_size; static unsigned int dwarf_abbrev_size; @@ -141,6 +143,8 @@ static unsigned int dwarf_aranges_size; static unsigned int dwarf_loc_size; static unsigned int dwarf_macinfo_size; static unsigned int dwarf_str_size; +unsigned int dwarf_frame_size; +unsigned int dwarf_eh_frame_size; /* names of the debugging sections */ @@ -152,6 +156,8 @@ static unsigned int dwarf_str_size; #define LOC_SECTION ".debug_loc" #define MACINFO_SECTION ".debug_macinfo" #define STR_SECTION ".debug_str" +#define FRAME_SECTION ".debug_frame" +#define EH_FRAME_SECTION ".eh_frame" /* local data types */ @@ -587,7 +593,7 @@ static void dwarf2_psymtab_to_symtab (struct partial_symtab *); static void psymtab_to_symtab_1 (struct partial_symtab *); -static char *dwarf2_read_section (struct objfile *, file_ptr, unsigned int); +char *dwarf2_read_section (struct objfile *, file_ptr, unsigned int); static void dwarf2_read_abbrevs (bfd *, unsigned int); @@ -807,6 +813,7 @@ dwarf2_has_info (bfd *abfd) { dwarf_info_offset = dwarf_abbrev_offset = dwarf_line_offset = 0; dwarf_str_offset = 0; + dwarf_frame_offset = dwarf_eh_frame_offset = 0; bfd_map_over_sections (abfd, dwarf2_locate_sections, NULL); if (dwarf_info_offset && dwarf_abbrev_offset) { @@ -865,6 +872,16 @@ dwarf2_locate_sections (bfd *ignore_abfd, asection *sectp, PTR ignore_ptr) dwarf_str_offset = sectp->filepos; dwarf_str_size = bfd_get_section_size_before_reloc (sectp); } + else if (STREQ (sectp->name, FRAME_SECTION)) + { + dwarf_frame_offset = sectp->filepos; + dwarf_frame_size = bfd_get_section_size_before_reloc (sectp); + } + else if (STREQ (sectp->name, EH_FRAME_SECTION)) + { + dwarf_eh_frame_offset = sectp->filepos; + dwarf_eh_frame_size = bfd_get_section_size_before_reloc (sectp); + } } /* Build a partial symbol table. */ @@ -3045,7 +3062,7 @@ make_cleanup_free_die_list (struct die_info *dies) /* Read the contents of the section at OFFSET and of size SIZE from the object file specified by OBJFILE into the psymbol_obstack and return it. */ -static char * +char * dwarf2_read_section (struct objfile *objfile, file_ptr offset, unsigned int size) { diff --git a/gdb/elfread.c b/gdb/elfread.c index 84807c066f3..068d412b6e5 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -656,6 +656,9 @@ elf_symfile_read (struct objfile *objfile, int mainline) ei.lnoffset, ei.lnsize); } + if (DWARF2_BUILD_FRAME_INFO_P ()) + DWARF2_BUILD_FRAME_INFO(objfile); + /* Install any minimal symbols that have been collected as the current minimal symbols for this objfile. */ diff --git a/gdb/frame.h b/gdb/frame.h index 66ffa5c26b2..f4c51e07b42 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -96,6 +96,10 @@ struct frame_info initialized by INIT_EXTRA_FRAME_INFO */ struct frame_extra_info *extra_info; + /* If dwarf2 unwind frame informations is used, this structure holds all + related unwind data. */ + struct unwind_contect *context; + /* Pointers to the next (down, inner) and previous (up, outer) frame_info's in the frame cache. */ struct frame_info *next; /* down, inner */ diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 83895a4594d..ffd1ac57c73 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -256,6 +256,7 @@ struct gdbarch gdbarch_in_solib_call_trampoline_ftype *in_solib_call_trampoline; gdbarch_in_function_epilogue_p_ftype *in_function_epilogue_p; gdbarch_construct_inferior_arguments_ftype *construct_inferior_arguments; + gdbarch_dwarf2_build_frame_info_ftype *dwarf2_build_frame_info; }; @@ -396,6 +397,7 @@ struct gdbarch startup_gdbarch = 0, generic_in_function_epilogue_p, construct_inferior_arguments, + 0, /* startup_gdbarch() */ }; @@ -759,6 +761,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of in_solib_call_trampoline, invalid_p == 0 */ /* Skip verify of in_function_epilogue_p, invalid_p == 0 */ /* Skip verify of construct_inferior_arguments, invalid_p == 0 */ + /* Skip verify of dwarf2_build_frame_info, has predicate */ buf = ui_file_xstrdup (log, &dummy); make_cleanup (xfree, buf); if (strlen (buf) > 0) @@ -1003,6 +1006,20 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) (long) current_gdbarch->do_registers_info /*DO_REGISTERS_INFO ()*/); #endif +#ifdef DWARF2_BUILD_FRAME_INFO +#if GDB_MULTI_ARCH + /* Macro might contain `[{}]' when not multi-arch */ + fprintf_unfiltered (file, + "gdbarch_dump: %s # %s\n", + "DWARF2_BUILD_FRAME_INFO(objfile)", + XSTRING (DWARF2_BUILD_FRAME_INFO (objfile))); +#endif + if (GDB_MULTI_ARCH) + fprintf_unfiltered (file, + "gdbarch_dump: DWARF2_BUILD_FRAME_INFO = 0x%08lx\n", + (long) current_gdbarch->dwarf2_build_frame_info + /*DWARF2_BUILD_FRAME_INFO ()*/); +#endif #ifdef DWARF2_REG_TO_REGNUM fprintf_unfiltered (file, "gdbarch_dump: %s # %s\n", @@ -4308,6 +4325,30 @@ set_gdbarch_construct_inferior_arguments (struct gdbarch *gdbarch, gdbarch->construct_inferior_arguments = construct_inferior_arguments; } +int +gdbarch_dwarf2_build_frame_info_p (struct gdbarch *gdbarch) +{ + return gdbarch->dwarf2_build_frame_info != 0; +} + +void +gdbarch_dwarf2_build_frame_info (struct gdbarch *gdbarch, struct objfile *objfile) +{ + if (gdbarch->dwarf2_build_frame_info == 0) + internal_error (__FILE__, __LINE__, + "gdbarch: gdbarch_dwarf2_build_frame_info invalid"); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_dwarf2_build_frame_info called\n"); + gdbarch->dwarf2_build_frame_info (objfile); +} + +void +set_gdbarch_dwarf2_build_frame_info (struct gdbarch *gdbarch, + gdbarch_dwarf2_build_frame_info_ftype dwarf2_build_frame_info) +{ + gdbarch->dwarf2_build_frame_info = dwarf2_build_frame_info; +} + /* Keep a registry of per-architecture data-pointers required by GDB modules. */ diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 22945959eff..21e531755fc 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -42,7 +42,7 @@ struct frame_info; struct value; - +struct objfile; extern struct gdbarch *current_gdbarch; @@ -2166,6 +2166,43 @@ typedef char * (gdbarch_construct_inferior_arguments_ftype) (struct gdbarch *gdb extern char * gdbarch_construct_inferior_arguments (struct gdbarch *gdbarch, int argc, char **argv); extern void set_gdbarch_construct_inferior_arguments (struct gdbarch *gdbarch, gdbarch_construct_inferior_arguments_ftype *construct_inferior_arguments); +#if defined (DWARF2_BUILD_FRAME_INFO) +/* Legacy for systems yet to multi-arch DWARF2_BUILD_FRAME_INFO */ +#if !defined (DWARF2_BUILD_FRAME_INFO_P) +#define DWARF2_BUILD_FRAME_INFO_P() (1) +#endif +#endif + +/* Default predicate for non- multi-arch targets. */ +#if (!GDB_MULTI_ARCH) && !defined (DWARF2_BUILD_FRAME_INFO_P) +#define DWARF2_BUILD_FRAME_INFO_P() (0) +#endif + +extern int gdbarch_dwarf2_build_frame_info_p (struct gdbarch *gdbarch); +#if (GDB_MULTI_ARCH > GDB_MULTI_ARCH_PARTIAL) && defined (DWARF2_BUILD_FRAME_INFO_P) +#error "Non multi-arch definition of DWARF2_BUILD_FRAME_INFO" +#endif +#if (GDB_MULTI_ARCH > GDB_MULTI_ARCH_PARTIAL) || !defined (DWARF2_BUILD_FRAME_INFO_P) +#define DWARF2_BUILD_FRAME_INFO_P() (gdbarch_dwarf2_build_frame_info_p (current_gdbarch)) +#endif + +/* Default (function) for non- multi-arch platforms. */ +#if (!GDB_MULTI_ARCH) && !defined (DWARF2_BUILD_FRAME_INFO) +#define DWARF2_BUILD_FRAME_INFO(objfile) (internal_error (__FILE__, __LINE__, "DWARF2_BUILD_FRAME_INFO"), 0) +#endif + +typedef void (gdbarch_dwarf2_build_frame_info_ftype) (struct objfile *objfile); +extern void gdbarch_dwarf2_build_frame_info (struct gdbarch *gdbarch, struct objfile *objfile); +extern void set_gdbarch_dwarf2_build_frame_info (struct gdbarch *gdbarch, gdbarch_dwarf2_build_frame_info_ftype *dwarf2_build_frame_info); +#if (GDB_MULTI_ARCH > GDB_MULTI_ARCH_PARTIAL) && defined (DWARF2_BUILD_FRAME_INFO) +#error "Non multi-arch definition of DWARF2_BUILD_FRAME_INFO" +#endif +#if GDB_MULTI_ARCH +#if (GDB_MULTI_ARCH > GDB_MULTI_ARCH_PARTIAL) || !defined (DWARF2_BUILD_FRAME_INFO) +#define DWARF2_BUILD_FRAME_INFO(objfile) (gdbarch_dwarf2_build_frame_info (current_gdbarch, objfile)) +#endif +#endif + extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch); diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 16b01a5fea3..d0a2569c70b 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -569,6 +569,7 @@ m:::int:in_function_epilogue_p:CORE_ADDR addr:addr::0:generic_in_function_epilog # ARGC is the number of elements in the vector. # ARGV is an array of strings, one per argument. m::CONSTRUCT_INFERIOR_ARGUMENTS:char *:construct_inferior_arguments:int argc, char **argv:argc, argv:::construct_inferior_arguments::0 +F:2:DWARF2_BUILD_FRAME_INFO:void:dwarf2_build_frame_info:struct objfile *objfile:objfile:::0 EOF } @@ -675,7 +676,7 @@ cat <