diff options
author | Andrew MacLeod <amacleod@cygnus.com> | 2000-05-25 15:21:51 +0000 |
---|---|---|
committer | Andrew Haley <aph@gcc.gnu.org> | 2000-05-25 15:21:51 +0000 |
commit | ce152ef8360320d27e1aacba9af8fdc4e1649941 (patch) | |
tree | feadfb24e9829d3678b304cad386af16dda71a31 /gcc/frame-dwarf2.c | |
parent | c66265e4833fb9553699839453725586cc248fc3 (diff) | |
download | gcc-ce152ef8360320d27e1aacba9af8fdc4e1649941.zip gcc-ce152ef8360320d27e1aacba9af8fdc4e1649941.tar.gz gcc-ce152ef8360320d27e1aacba9af8fdc4e1649941.tar.bz2 |
except.c (func_eh_entry): Add emitted field.
2000-05-25 Andrew MacLeod <amacleod@cygnus.com>
Andrew Haley <aph@cygnus.com>
* except.c (func_eh_entry): Add emitted field.
(new_eh_region_entry): Set emitted field to 0;
(output_exception_table_entry): Only emit previously un-emitted data,
and send it to the eh_data section.
(output_exception_table): Break out common parts. Output
exception table for entire compilation unit to eh_data section.
(output_exception_table_data): Common parts of output_exception_table.
Send output to eh_data section.
(output_function_exception_table): Output exception table data for
a single function to eh_data section.
(free_exception_table): New external to free the table.
* except.h (free_exception_table): Add prototype.
(output_function_exception_table): Add prototype.
* final.c (final_end_function): Output function exception table
for IA64_UNWIND_INFO.
(final_scan_insn): Emit any unwind directives for an insn.
* frame-dwarf2.c: New file containing all DWARF 2 specific code
from frame.c.
* frame.c: Remove all DWARF 2 specific code.
* config/ia64/frame-ia64.c: New file.
(gthread_stuff): Make all gthread available with
IA64_UNWIND_INFO.
(dwarf_fde): Define an IA64 struct for dwarf_fde.
(__register_frame_info, __register_frame): Move to common area of file.
(__register_frame_info_table, __register_frame_table): Move to common i
area.
(__deregister_frame_info, __deregister_frame): Move to common area.
(__frame_init, find_fde): New versions for IA64_UNWIND_INFO.
(read_uleb128): New version for ia64.
(get_unwind_record): Read the next IA-64 unwind record.
(read_R_record): Read a region header record.
(process_a_b_reg_code): X record helper.
(read_X_record): Read an X format record.
(read_B_record): Read a B format record.
(P3_record_types): List of record types matching the P3 format.
(P7_record_types): List of record types matching the P7 format.
(P8_record_types): List of record types matching the P8 format.
(read_P_record): Read a P format record.
(init_ia64_reg_loc): Set default fields for a register.
(init_ia64_unwind_frame): Set defaults for all register records.
(execute_one_ia64_descriptor): Execute one descriptor record.
(rse_address_add): Calculate the position of a local reg in memory.
(normalize_reg_loc): Turn a location descriptor into a memory address.
(maybe_normalize_reg_loc): Only normalize a descriptor if it falls
within a specified PC offset range.
(get_real_reg_value): Given a register location, retrieve its value.
(set_real_reg_value): Change the value of a register location.
(copy_reg_value): Copy reg values, if needed.
(copy_saved_reg_state): Copy all registers that need to be copied.
(process_state_between): Normalize all frame register records that
fall within the specified PC range.
(frame_translate): Take a processed frame description, and turn
everything into addresses.
(build_ia64_frame_state ): Find and create frame state record for a PC.
(get_personality): Get the personality routine for a given frame.
(get_except_table): Get the exception table for a given frame.
(record_name): Unwind record names for debugging.
(print_record): Print and unwind record.
(print_all_records): Print an entire unwind image.
(__ia64_backtrace): Print a backtrace.
(ia64_backtrace_helper): New function.
(__register_frame_info_aux): New function.
* config/ia64/crtend.asm (__do_frame_setup_aux): New function.
* frame.h (enum unw_record_type): New unwind record types.
(struct unw_p_record, unw_b_record, unw_x_record) : New unwind records.
(struct unw_r_record, unwind_record): New unwind record structs.
(struct unwind_info_ptr): Unwind information layout.
(IA64_UNW_LOC_TYPE_*): Macros for different types for location
descriptors.
(struct ia64_reg_loc): Register location description.
(struct ia64_frame_state): Location of all registers in a frame.
(struct object): Add pc_base and fde_end for IA64_UNWIND_INFO.
* libgcc2.c (__ia64_personality_v1): Personality routine.
(__calc_caller_bsp): Calculate the bsp register for the caller's
frame.
(ia64_throw_helper): Figure out who to return to and set up the
registers.
(__throw): Throw routine.
* output.h (assemble_eh_align, assemble_eh_label): New functions
to generate EH info where we want it.
(assemble_eh_integer): New function.
* toplev.c (compile_file): Output module level exception table for
non-ia64 targets.
(main): Set exceptions_via_longjump and flag_new_exceptions based
on IA64_UNWIND_INFO too.
* varasm.c (assemble_eh_label): Generate a label via
ASM_OUTPUT_EH_LABEL if it has been specified.
(assemble_eh_align): Generate an alignment directive via
ASM_OUTPUT_EH_ALIGN if it has been specified.
(assemble_eh_label): Generate an integer value via
ASM_OUTPUT_EH_type if they have been specified.
* config/ia64/ia64.c (rtx_needs_barrier): Add flushrs.
(ia64_init_builtins): Add __builtin_ia64_bsp
and __builtin_ia64_flushrs.
(ia64_expand_builtin): Add IA64_BUILTIN_BSP and
IA64_BUILTIN_FLUSHRS.
* config/ia64/ia64.h (ia64_builtins): Add IA64_BUILTIN_BSP and
IA64_BUILTIN_FLUSHRS.
* config/ia64/ia64.md (flushrs): New insn to flush the register
stack. Add to unspec list.
* config/ia64/crtbegin.asm (frame_object): Change size.
(__do_frame_setup_aux): New function.
* config/ia64/crtend.asm: call __do_frame_setup_aux.
* config/ia64/t-ia64 (LIB2ADDEH): Add.
* Makefile.in (LIB2ADDEH): Add.
(LIB2ADD): Use LIB2ADDEH.
Co-Authored-By: Andrew Haley <aph@cygnus.com>
From-SVN: r34169
Diffstat (limited to 'gcc/frame-dwarf2.c')
-rw-r--r-- | gcc/frame-dwarf2.c | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/gcc/frame-dwarf2.c b/gcc/frame-dwarf2.c new file mode 100644 index 0000000..fb2e9fb --- /dev/null +++ b/gcc/frame-dwarf2.c @@ -0,0 +1,663 @@ +/* Subroutines needed for unwinding DWARF 2 format stack frame info + for exception handling. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + Contributed by Jason Merrill <jason@cygnus.com>. + +This file is part of GNU CC. + +GNU CC 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. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* It is incorrect to include config.h here, because this file is being + compiled for the target, and hence definitions concerning only the host + do not apply. */ + +#include "tconfig.h" +#include "tsystem.h" + +#include "defaults.h" + +#ifdef DWARF2_UNWIND_INFO +#include "dwarf2.h" +#include "frame.h" +#include "gthr.h" + +#ifdef __GTHREAD_MUTEX_INIT +static __gthread_mutex_t object_mutex = __GTHREAD_MUTEX_INIT; +#else +static __gthread_mutex_t object_mutex; +#endif + +/* Don't use `fancy_abort' here even if config.h says to use it. */ +#ifdef abort +#undef abort +#endif + +/* Some types used by the DWARF 2 spec. */ + +typedef int sword __attribute__ ((mode (SI))); +typedef unsigned int uword __attribute__ ((mode (SI))); +typedef unsigned int uaddr __attribute__ ((mode (pointer))); +typedef int saddr __attribute__ ((mode (pointer))); +typedef unsigned char ubyte; + +/* Terminology: + CIE - Common Information Element + FDE - Frame Descriptor Element + + There is one per function, and it describes where the function code + is located, and what the register lifetimes and stack layout are + within the function. + + The data structures are defined in the DWARF specfication, although + not in a very readable way (see LITERATURE). + + Every time an exception is thrown, the code needs to locate the FDE + for the current function, and starts to look for exception regions + from that FDE. This works in a two-level search: + a) in a linear search, find the shared image (i.e. DLL) containing + the PC + b) using the FDE table for that shared object, locate the FDE using + binary search (which requires the sorting). */ + +/* The first few fields of a CIE. The CIE_id field is 0 for a CIE, + to distinguish it from a valid FDE. FDEs are aligned to an addressing + unit boundary, but the fields within are unaligned. */ + +struct dwarf_cie { + uword length; + sword CIE_id; + ubyte version; + char augmentation[0]; +} __attribute__ ((packed, aligned (__alignof__ (void *)))); + +/* The first few fields of an FDE. */ + +struct dwarf_fde { + uword length; + sword CIE_delta; + void* pc_begin; + uaddr pc_range; +} __attribute__ ((packed, aligned (__alignof__ (void *)))); + +typedef struct dwarf_fde fde; + +/* Objects to be searched for frame unwind info. */ + +static struct object *objects; + +/* The information we care about from a CIE. */ + +struct cie_info { + char *augmentation; + void *eh_ptr; + int code_align; + int data_align; + unsigned ra_regno; +}; + +/* The current unwind state, plus a saved copy for DW_CFA_remember_state. */ + +struct frame_state_internal +{ + struct frame_state s; + struct frame_state_internal *saved_state; +}; + +/* This is undefined below if we need it to be an actual function. */ +#define init_object_mutex_once() + +#if __GTHREADS +#ifdef __GTHREAD_MUTEX_INIT_FUNCTION + +/* Helper for init_object_mutex_once. */ + +static void +init_object_mutex (void) +{ + __GTHREAD_MUTEX_INIT_FUNCTION (&object_mutex); +} + +/* Call this to arrange to initialize the object mutex. */ + +#undef init_object_mutex_once +static void +init_object_mutex_once (void) +{ + static __gthread_once_t once = __GTHREAD_ONCE_INIT; + __gthread_once (&once, init_object_mutex); +} + +#endif /* __GTHREAD_MUTEX_INIT_FUNCTION */ +#endif /* __GTHREADS */ + +/* Decode the unsigned LEB128 constant at BUF into the variable pointed to + by R, and return the new value of BUF. */ + +static void * +decode_uleb128 (unsigned char *buf, unsigned *r) +{ + unsigned shift = 0; + unsigned result = 0; + + while (1) + { + unsigned byte = *buf++; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + *r = result; + return buf; +} + +/* Decode the signed LEB128 constant at BUF into the variable pointed to + by R, and return the new value of BUF. */ + +static void * +decode_sleb128 (unsigned char *buf, int *r) +{ + unsigned shift = 0; + unsigned result = 0; + unsigned byte; + + while (1) + { + byte = *buf++; + result |= (byte & 0x7f) << shift; + shift += 7; + if ((byte & 0x80) == 0) + break; + } + if (shift < (sizeof (*r) * 8) && (byte & 0x40) != 0) + result |= - (1 << shift); + + *r = result; + return buf; +} + +/* Read unaligned data from the instruction buffer. */ + +union unaligned { + void *p; + unsigned b2 __attribute__ ((mode (HI))); + unsigned b4 __attribute__ ((mode (SI))); + unsigned b8 __attribute__ ((mode (DI))); +} __attribute__ ((packed)); +static inline void * +read_pointer (void *p) +{ union unaligned *up = p; return up->p; } +static inline unsigned +read_1byte (void *p) +{ return *(unsigned char *)p; } +static inline unsigned +read_2byte (void *p) +{ union unaligned *up = p; return up->b2; } +static inline unsigned +read_4byte (void *p) +{ union unaligned *up = p; return up->b4; } +static inline unsigned long +read_8byte (void *p) +{ union unaligned *up = p; return up->b8; } + +/* Ordering function for FDEs. Functions can't overlap, so we just compare + their starting addresses. */ + +static inline saddr +fde_compare (fde *x, fde *y) +{ + return (saddr)x->pc_begin - (saddr)y->pc_begin; +} + +/* Return the address of the FDE after P. */ + +static inline fde * +next_fde (fde *p) +{ + return (fde *)(((char *)p) + p->length + sizeof (p->length)); +} + +#include "frame.c" + +static size_t +count_fdes (fde *this_fde) +{ + size_t count; + + for (count = 0; this_fde->length != 0; this_fde = next_fde (this_fde)) + { + /* Skip CIEs and linked once FDE entries. */ + if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0) + continue; + + ++count; + } + + return count; +} + +static void +add_fdes (fde *this_fde, fde_accumulator *accu, void **beg_ptr, void **end_ptr) +{ + void *pc_begin = *beg_ptr; + void *pc_end = *end_ptr; + + for (; this_fde->length != 0; this_fde = next_fde (this_fde)) + { + /* Skip CIEs and linked once FDE entries. */ + if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0) + continue; + + fde_insert (accu, this_fde); + + if (this_fde->pc_begin < pc_begin) + pc_begin = this_fde->pc_begin; + if (this_fde->pc_begin + this_fde->pc_range > pc_end) + pc_end = this_fde->pc_begin + this_fde->pc_range; + } + + *beg_ptr = pc_begin; + *end_ptr = pc_end; +} + +/* search this fde table for the one containing the pc */ +static fde * +search_fdes (fde *this_fde, void *pc) +{ + for (; this_fde->length != 0; this_fde = next_fde (this_fde)) + { + /* Skip CIEs and linked once FDE entries. */ + if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0) + continue; + + if ((uaddr)((char *)pc - (char *)this_fde->pc_begin) < this_fde->pc_range) + return this_fde; + } + return NULL; +} + +/* Set up a sorted array of pointers to FDEs for a loaded object. We + count up the entries before allocating the array because it's likely to + be faster. We can be called multiple times, should we have failed to + allocate a sorted fde array on a previous occasion. */ + +static void +frame_init (struct object* ob) +{ + size_t count; + fde_accumulator accu; + void *pc_begin, *pc_end; + fde **array; + + if (ob->pc_begin) + count = ob->count; + else if (ob->fde_array) + { + fde **p = ob->fde_array; + for (count = 0; *p; ++p) + count += count_fdes (*p); + } + else + count = count_fdes (ob->fde_begin); + ob->count = count; + + if (!start_fde_sort (&accu, count) && ob->pc_begin) + return; + + pc_begin = (void*)(uaddr)-1; + pc_end = 0; + + if (ob->fde_array) + { + fde **p = ob->fde_array; + for (; *p; ++p) + add_fdes (*p, &accu, &pc_begin, &pc_end); + } + else + add_fdes (ob->fde_begin, &accu, &pc_begin, &pc_end); + + array = end_fde_sort (&accu, count); + if (array) + ob->fde_array = array; + ob->pc_begin = pc_begin; + ob->pc_end = pc_end; +} + +/* Return a pointer to the FDE for the function containing PC. */ + +static fde * +find_fde (void *pc) +{ + struct object *ob; + size_t lo, hi; + + init_object_mutex_once (); + __gthread_mutex_lock (&object_mutex); + + /* Linear search through the objects, to find the one containing the pc. */ + for (ob = objects; ob; ob = ob->next) + { + if (ob->pc_begin == 0) + frame_init (ob); + if (pc >= ob->pc_begin && pc < ob->pc_end) + break; + } + + if (ob == 0) + { + __gthread_mutex_unlock (&object_mutex); + return 0; + } + + if (!ob->fde_array || (void *)ob->fde_array == (void *)ob->fde_begin) + frame_init (ob); + + if (ob->fde_array && (void *)ob->fde_array != (void *)ob->fde_begin) + { + __gthread_mutex_unlock (&object_mutex); + + /* Standard binary search algorithm. */ + for (lo = 0, hi = ob->count; lo < hi; ) + { + size_t i = (lo + hi) / 2; + fde *f = ob->fde_array[i]; + + if (pc < f->pc_begin) + hi = i; + else if (pc >= f->pc_begin + f->pc_range) + lo = i + 1; + else + return f; + } + } + else + { + /* Long slow labourious linear search, cos we've no memory. */ + fde *f; + + if (ob->fde_array) + { + fde **p = ob->fde_array; + + do + { + f = search_fdes (*p, pc); + if (f) + break; + p++; + } + while (*p); + } + else + f = search_fdes (ob->fde_begin, pc); + __gthread_mutex_unlock (&object_mutex); + return f; + } + return 0; +} + +static inline struct dwarf_cie * +get_cie (fde *f) +{ + return ((void *)&f->CIE_delta) - f->CIE_delta; +} + +/* Extract any interesting information from the CIE for the translation + unit F belongs to. */ + +static void * +extract_cie_info (fde *f, struct cie_info *c) +{ + void *p; + int i; + + c->augmentation = get_cie (f)->augmentation; + + if (strcmp (c->augmentation, "") != 0 + && strcmp (c->augmentation, "eh") != 0 + && c->augmentation[0] != 'z') + return 0; + + p = c->augmentation + strlen (c->augmentation) + 1; + + if (strcmp (c->augmentation, "eh") == 0) + { + c->eh_ptr = read_pointer (p); + p += sizeof (void *); + } + else + c->eh_ptr = 0; + + p = decode_uleb128 (p, &c->code_align); + p = decode_sleb128 (p, &c->data_align); + c->ra_regno = *(unsigned char *)p++; + + /* If the augmentation starts with 'z', we now see the length of the + augmentation fields. */ + if (c->augmentation[0] == 'z') + { + p = decode_uleb128 (p, &i); + p += i; + } + + return p; +} + +/* Decode one instruction's worth of DWARF 2 call frame information. + Used by __frame_state_for. Takes pointers P to the instruction to + decode, STATE to the current register unwind information, INFO to the + current CIE information, and PC to the current PC value. Returns a + pointer to the next instruction. */ + +static void * +execute_cfa_insn (void *p, struct frame_state_internal *state, + struct cie_info *info, void **pc) +{ + unsigned insn = *(unsigned char *)p++; + unsigned reg; + int offset; + + if (insn & DW_CFA_advance_loc) + *pc += ((insn & 0x3f) * info->code_align); + else if (insn & DW_CFA_offset) + { + reg = (insn & 0x3f); + p = decode_uleb128 (p, &offset); + if (reg == state->s.cfa_reg) + /* Don't record anything about this register; it's only used to + reload SP in the epilogue. We don't want to copy in SP + values for outer frames; we handle restoring SP specially. */; + else + { + offset *= info->data_align; + state->s.saved[reg] = REG_SAVED_OFFSET; + state->s.reg_or_offset[reg] = offset; + } + } + else if (insn & DW_CFA_restore) + { + reg = (insn & 0x3f); + state->s.saved[reg] = REG_UNSAVED; + } + else switch (insn) + { + case DW_CFA_set_loc: + *pc = read_pointer (p); + p += sizeof (void *); + break; + case DW_CFA_advance_loc1: + *pc += read_1byte (p); + p += 1; + break; + case DW_CFA_advance_loc2: + *pc += read_2byte (p); + p += 2; + break; + case DW_CFA_advance_loc4: + *pc += read_4byte (p); + p += 4; + break; + + case DW_CFA_offset_extended: + p = decode_uleb128 (p, ®); + p = decode_uleb128 (p, &offset); + if (reg == state->s.cfa_reg) + /* Don't record anything; see above. */; + else + { + offset *= info->data_align; + state->s.saved[reg] = REG_SAVED_OFFSET; + state->s.reg_or_offset[reg] = offset; + } + break; + case DW_CFA_restore_extended: + p = decode_uleb128 (p, ®); + state->s.saved[reg] = REG_UNSAVED; + break; + + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_nop: + break; + + case DW_CFA_register: + { + unsigned reg2; + p = decode_uleb128 (p, ®); + p = decode_uleb128 (p, ®2); + state->s.saved[reg] = REG_SAVED_REG; + state->s.reg_or_offset[reg] = reg2; + } + break; + + case DW_CFA_def_cfa: + p = decode_uleb128 (p, ®); + p = decode_uleb128 (p, &offset); + state->s.cfa_reg = reg; + state->s.cfa_offset = offset; + break; + case DW_CFA_def_cfa_register: + p = decode_uleb128 (p, ®); + state->s.cfa_reg = reg; + break; + case DW_CFA_def_cfa_offset: + p = decode_uleb128 (p, &offset); + state->s.cfa_offset = offset; + break; + + case DW_CFA_remember_state: + { + struct frame_state_internal *save = + (struct frame_state_internal *) + malloc (sizeof (struct frame_state_internal)); + memcpy (save, state, sizeof (struct frame_state_internal)); + state->saved_state = save; + } + break; + case DW_CFA_restore_state: + { + struct frame_state_internal *save = state->saved_state; + memcpy (state, save, sizeof (struct frame_state_internal)); + free (save); + } + break; + + /* FIXME: Hardcoded for SPARC register window configuration. */ + case DW_CFA_GNU_window_save: + for (reg = 16; reg < 32; ++reg) + { + state->s.saved[reg] = REG_SAVED_OFFSET; + state->s.reg_or_offset[reg] = (reg - 16) * sizeof (void *); + } + break; + + case DW_CFA_GNU_args_size: + p = decode_uleb128 (p, &offset); + state->s.args_size = offset; + break; + + case DW_CFA_GNU_negative_offset_extended: + p = decode_uleb128 (p, ®); + p = decode_uleb128 (p, &offset); + offset *= info->data_align; + state->s.saved[reg] = REG_SAVED_OFFSET; + state->s.reg_or_offset[reg] = -offset; + break; + + default: + abort (); + } + return p; +} + +/* Called from __throw to find the registers to restore for a given + PC_TARGET. The caller should allocate a local variable of `struct + frame_state' (declared in frame.h) and pass its address to STATE_IN. */ + +struct frame_state * +__frame_state_for (void *pc_target, struct frame_state *state_in) +{ + fde *f; + void *insn, *end, *pc; + struct cie_info info; + struct frame_state_internal state; + + f = find_fde (pc_target); + if (f == 0) + return 0; + + insn = extract_cie_info (f, &info); + if (insn == 0) + return 0; + + memset (&state, 0, sizeof (state)); + state.s.retaddr_column = info.ra_regno; + state.s.eh_ptr = info.eh_ptr; + + /* First decode all the insns in the CIE. */ + end = next_fde ((fde*) get_cie (f)); + while (insn < end) + insn = execute_cfa_insn (insn, &state, &info, 0); + + insn = ((fde *)f) + 1; + + if (info.augmentation[0] == 'z') + { + int i; + insn = decode_uleb128 (insn, &i); + insn += i; + } + + /* Then the insns in the FDE up to our target PC. */ + end = next_fde (f); + pc = f->pc_begin; + while (insn < end && pc <= pc_target) + insn = execute_cfa_insn (insn, &state, &info, &pc); + + memcpy (state_in, &state.s, sizeof (state.s)); + return state_in; +} +#endif /* DWARF2_UNWIND_INFO */ |