| /**************************************************************************** |
| * * |
| * GNAT COMPILER COMPONENTS * |
| * * |
| * T R A C E B A C K - A l p h a / V x W o r k s * |
| * * |
| * C Implementation File * |
| * * |
| * Copyright (C) 2000-2005, AdaCore * |
| * * |
| * GNAT is free software; you can redistribute it and/or modify it under * |
| * terms of the GNU General Public License as published by the Free Soft- * |
| * ware Foundation; either version 2, or (at your option) any later ver- * |
| * sion. GNAT is distributed in the hope that it will be useful, but WITH- * |
| * OUT 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 distributed with GNAT; see file COPYING. If not, write * |
| * to the Free Software Foundation, 51 Franklin Street, Fifth Floor, * |
| * Boston, MA 02110-1301, USA. * |
| * * |
| * As a special exception, if you link this file with other files to * |
| * produce an executable, this file does not by itself cause the resulting * |
| * executable to be covered by the GNU General Public License. This except- * |
| * ion does not however invalidate any other reasons why the executable * |
| * file might be covered by the GNU Public License. * |
| * * |
| * GNAT was originally developed by the GNAT team at New York University. * |
| * Extensive contributions were provided by Ada Core Technologies Inc. * |
| * * |
| ****************************************************************************/ |
| |
| /* Alpha vxWorks requires a special, complex treatment that is extracted |
| from GDB. This file is #included within tracebak.c in the appropriate |
| case. */ |
| |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <limits.h> |
| #include <string.h> |
| |
| extern void kerTaskEntry(void); |
| |
| /* We still use a number of macros similar to the ones for the generic |
| __gnat_backtrace implementation. */ |
| #define SKIP_FRAME 1 |
| #define PC_ADJUST -4 |
| |
| #define STOP_FRAME \ |
| (current == NULL \ |
| || ((CORE_ADDR) &kerTaskEntry >= PROC_LOW_ADDR (current->proc_desc) \ |
| && current->pc >= (CORE_ADDR) &kerTaskEntry)) |
| |
| /* Register numbers of various important registers. |
| Note that most of these values are "real" register numbers, |
| and correspond to the general registers of the machine, |
| and FP_REGNUM is a "phony" register number which is too large |
| to be an actual register number as far as the user is concerned |
| but serves to get the desired value when passed to read_register. */ |
| |
| #define T7_REGNUM 8 /* Return address register for OSF/1 __add* */ |
| #define GCC_FP_REGNUM 15 /* Used by gcc as frame register */ |
| #define T9_REGNUM 23 /* Return address register for OSF/1 __div* */ |
| #define SP_REGNUM 30 /* Contains address of top of stack */ |
| #define RA_REGNUM 26 /* Contains return address value */ |
| #define FP0_REGNUM 32 /* Floating point register 0 */ |
| #define PC_REGNUM 64 /* Contains program counter */ |
| #define NUM_REGS 66 |
| |
| #define VM_MIN_ADDRESS (CORE_ADDR)0x120000000 |
| |
| #define SIZEOF_FRAME_SAVED_REGS (sizeof (CORE_ADDR) * (NUM_REGS)) |
| #define INIT_EXTRA_FRAME_INFO(fromleaf, fci) init_extra_frame_info(fci) |
| |
| #define FRAME_CHAIN(thisframe) (CORE_ADDR) alpha_frame_chain (thisframe) |
| |
| #define FRAME_CHAIN_VALID(CHAIN, THISFRAME) \ |
| ((CHAIN) != 0 \ |
| && !inside_entry_file (FRAME_SAVED_PC (THISFRAME))) |
| |
| #define FRAME_SAVED_PC(FRAME) (alpha_frame_saved_pc (FRAME)) |
| |
| #define FRAME_CHAIN_COMBINE(CHAIN, THISFRAME) (CHAIN) |
| |
| #define INIT_FRAME_PC(FROMLEAF, PREV) |
| |
| #define INIT_FRAME_PC_FIRST(FROMLEAF, PREV) \ |
| (PREV)->pc = ((FROMLEAF) ? SAVED_PC_AFTER_CALL ((PREV)->next) \ |
| : (PREV)->next ? FRAME_SAVED_PC ((PREV)->next) : read_pc ()); |
| |
| #define SAVED_PC_AFTER_CALL(FRAME) alpha_saved_pc_after_call (FRAME) |
| |
| typedef unsigned long long int bfd_vma; |
| |
| typedef bfd_vma CORE_ADDR; |
| |
| typedef struct pdr |
| { |
| bfd_vma adr; /* memory address of start of procedure */ |
| long isym; /* start of local symbol entries */ |
| long iline; /* start of line number entries*/ |
| long regmask; /* save register mask */ |
| long regoffset; /* save register offset */ |
| long iopt; /* start of optimization symbol entries*/ |
| long fregmask; /* save floating point register mask */ |
| long fregoffset; /* save floating point register offset */ |
| long frameoffset; /* frame size */ |
| short framereg; /* frame pointer register */ |
| short pcreg; /* offset or reg of return pc */ |
| long lnLow; /* lowest line in the procedure */ |
| long lnHigh; /* highest line in the procedure */ |
| bfd_vma cbLineOffset; /* byte offset for this procedure from the fd base */ |
| /* These fields are new for 64 bit ECOFF. */ |
| unsigned gp_prologue : 8; /* byte size of GP prologue */ |
| unsigned gp_used : 1; /* true if the procedure uses GP */ |
| unsigned reg_frame : 1; /* true if register frame procedure */ |
| unsigned prof : 1; /* true if compiled with -pg */ |
| unsigned reserved : 13; /* reserved: must be zero */ |
| unsigned localoff : 8; /* offset of local variables from vfp */ |
| } PDR; |
| |
| typedef struct alpha_extra_func_info |
| { |
| long numargs; /* number of args to procedure (was iopt) */ |
| PDR pdr; /* Procedure descriptor record */ |
| } |
| *alpha_extra_func_info_t; |
| |
| struct frame_info |
| { |
| /* Nominal address of the frame described. See comments at FRAME_FP |
| about what this means outside the *FRAME* macros; in the *FRAME* |
| macros, it can mean whatever makes most sense for this machine. */ |
| CORE_ADDR frame; |
| |
| /* Address at which execution is occurring in this frame. For the |
| innermost frame, it's the current pc. For other frames, it is a |
| pc saved in the next frame. */ |
| CORE_ADDR pc; |
| |
| /* For each register, address of where it was saved on entry to the |
| frame, or zero if it was not saved on entry to this frame. This |
| includes special registers such as pc and fp saved in special |
| ways in the stack frame. The SP_REGNUM is even more special, the |
| address here is the sp for the next frame, not the address where |
| the sp was saved. Allocated by frame_saved_regs_zalloc () which |
| is called and initialized by FRAME_INIT_SAVED_REGS. */ |
| CORE_ADDR *saved_regs; /*NUM_REGS */ |
| |
| int localoff; |
| int pc_reg; |
| alpha_extra_func_info_t proc_desc; |
| |
| /* Pointers to the next and previous frame_info's in the frame cache. */ |
| struct frame_info *next, *prev; |
| }; |
| |
| struct frame_saved_regs |
| { |
| /* For each register R (except the SP), regs[R] is the address at |
| which it was saved on entry to the frame, or zero if it was not |
| saved on entry to this frame. This includes special registers |
| such as pc and fp saved in special ways in the stack frame. |
| |
| regs[SP_REGNUM] is different. It holds the actual SP, not the |
| address at which it was saved. */ |
| |
| CORE_ADDR regs[NUM_REGS]; |
| }; |
| |
| static CORE_ADDR theRegisters[32]; |
| |
| /* Prototypes for local functions. */ |
| |
| static CORE_ADDR read_next_frame_reg (struct frame_info *, int); |
| static CORE_ADDR heuristic_proc_start (CORE_ADDR); |
| static int alpha_about_to_return (CORE_ADDR pc); |
| static void init_extra_frame_info (struct frame_info *); |
| static CORE_ADDR alpha_frame_chain (struct frame_info *); |
| static CORE_ADDR alpha_frame_saved_pc (struct frame_info *frame); |
| static void *trace_alloc (unsigned int); |
| static struct frame_info *create_new_frame (CORE_ADDR, CORE_ADDR); |
| |
| static alpha_extra_func_info_t |
| heuristic_proc_desc (CORE_ADDR, CORE_ADDR, struct frame_info *, |
| struct frame_saved_regs *); |
| |
| static alpha_extra_func_info_t |
| find_proc_desc (CORE_ADDR, struct frame_info *, struct frame_saved_regs *); |
| |
| /* Heuristic_proc_start may hunt through the text section for a long |
| time across a 2400 baud serial line. Allows the user to limit this |
| search. */ |
| static unsigned int heuristic_fence_post = 1<<16; |
| |
| /* Layout of a stack frame on the alpha: |
| |
| | | |
| pdr members: | 7th ... nth arg, | |
| | `pushed' by caller. | |
| | | |
| ----------------|-------------------------------|<-- old_sp == vfp |
| ^ ^ ^ ^ | | |
| | | | | | | |
| | |localoff | Copies of 1st .. 6th | |
| | | | | | argument if necessary. | |
| | | | v | | |
| | | | --- |-------------------------------|<-- FRAME_LOCALS_ADDRESS |
| | | | | | |
| | | | | Locals and temporaries. | |
| | | | | | |
| | | | |-------------------------------| |
| | | | | | |
| |-fregoffset | Saved float registers. | |
| | | | | F9 | |
| | | | | . | |
| | | | | . | |
| | | | | F2 | |
| | | v | | |
| | | -------|-------------------------------| |
| | | | | |
| | | | Saved registers. | |
| | | | S6 | |
| |-regoffset | . | |
| | | | . | |
| | | | S0 | |
| | | | pdr.pcreg | |
| | v | | |
| | ----------|-------------------------------| |
| | | | |
| frameoffset | Argument build area, gets | |
| | | 7th ... nth arg for any | |
| | | called procedure. | |
| v | | |
| -------------|-------------------------------|<-- sp |
| | | */ |
| |
| #define PROC_LOW_ADDR(PROC) ((PROC)->pdr.adr) /* least address */ |
| #define PROC_HIGH_ADDR(PROC) ((PROC)->pdr.iline) /* upper address bound */ |
| #define PROC_DUMMY_FRAME(PROC) ((PROC)->pdr.cbLineOffset) /*CALL_DUMMY frame */ |
| #define PROC_FRAME_OFFSET(PROC) ((PROC)->pdr.frameoffset) |
| #define PROC_FRAME_REG(PROC) ((PROC)->pdr.framereg) |
| #define PROC_REG_MASK(PROC) ((PROC)->pdr.regmask) |
| #define PROC_FREG_MASK(PROC) ((PROC)->pdr.fregmask) |
| #define PROC_REG_OFFSET(PROC) ((PROC)->pdr.regoffset) |
| #define PROC_FREG_OFFSET(PROC) ((PROC)->pdr.fregoffset) |
| #define PROC_PC_REG(PROC) ((PROC)->pdr.pcreg) |
| #define PROC_LOCALOFF(PROC) ((PROC)->pdr.localoff) |
| |
| /* Local storage allocation/deallocation functions. trace_alloc does |
| a malloc, but also chains allocated blocks on trace_alloc_chain, so |
| they may all be freed on exit from __gnat_backtrace. */ |
| |
| struct alloc_chain |
| { |
| struct alloc_chain *next; |
| double x[0]; |
| }; |
| struct alloc_chain *trace_alloc_chain; |
| |
| static void * |
| trace_alloc (unsigned int n) |
| { |
| struct alloc_chain * result = malloc (n + sizeof(struct alloc_chain)); |
| |
| result->next = trace_alloc_chain; |
| trace_alloc_chain = result; |
| return (void*) result->x; |
| } |
| |
| static void |
| free_trace_alloc (void) |
| { |
| while (trace_alloc_chain != 0) |
| { |
| struct alloc_chain *old = trace_alloc_chain; |
| |
| trace_alloc_chain = trace_alloc_chain->next; |
| free (old); |
| } |
| } |
| |
| /* Read value at ADDR into *DEST, returning 0 if this is valid, != 0 |
| otherwise. */ |
| |
| static int |
| read_memory_safe4 (CORE_ADDR addr, unsigned int *dest) |
| { |
| *dest = *((unsigned int*) addr); |
| return 0; |
| } |
| |
| /* Read value at ADDR into *DEST, returning 0 if this is valid, != 0 |
| otherwise. */ |
| |
| static int |
| read_memory_safe8 (CORE_ADDR addr, CORE_ADDR *dest) |
| { |
| *dest = *((CORE_ADDR*) addr); |
| return 0; |
| } |
| |
| static CORE_ADDR |
| read_register (int regno) |
| { |
| if (regno >= 0 && regno < 31) |
| return theRegisters[regno]; |
| |
| return (CORE_ADDR) 0; |
| } |
| |
| static void |
| frame_saved_regs_zalloc (struct frame_info *fi) |
| { |
| fi->saved_regs = (CORE_ADDR *) trace_alloc (SIZEOF_FRAME_SAVED_REGS); |
| memset (fi->saved_regs, 0, SIZEOF_FRAME_SAVED_REGS); |
| } |
| |
| static void * |
| frame_obstack_alloc (unsigned long size) |
| { |
| return (void *) trace_alloc (size); |
| } |
| |
| static int |
| inside_entry_file (CORE_ADDR addr) |
| { |
| if (addr == 0) |
| return 1; |
| else |
| return 0; |
| } |
| |
| static CORE_ADDR |
| alpha_saved_pc_after_call (struct frame_info *frame) |
| { |
| CORE_ADDR pc = frame->pc; |
| alpha_extra_func_info_t proc_desc; |
| int pcreg; |
| |
| proc_desc = find_proc_desc (pc, frame->next, NULL); |
| pcreg = proc_desc ? PROC_PC_REG (proc_desc) : RA_REGNUM; |
| |
| return read_register (pcreg); |
| } |
| |
| /* Guaranteed to set frame->saved_regs to some values (it never leaves it |
| NULL). */ |
| |
| static void |
| alpha_find_saved_regs (struct frame_info *frame) |
| { |
| int ireg; |
| CORE_ADDR reg_position; |
| unsigned long mask; |
| alpha_extra_func_info_t proc_desc; |
| int returnreg; |
| |
| frame_saved_regs_zalloc (frame); |
| |
| /* If it is the frame for __sigtramp, the saved registers are located in a |
| sigcontext structure somewhere on the stack. __sigtramp passes a pointer |
| to the sigcontext structure on the stack. If the stack layout for |
| __sigtramp changes, or if sigcontext offsets change, we might have to |
| update this code. */ |
| |
| #ifndef SIGFRAME_PC_OFF |
| #define SIGFRAME_PC_OFF (2 * 8) |
| #define SIGFRAME_REGSAVE_OFF (4 * 8) |
| #define SIGFRAME_FPREGSAVE_OFF (SIGFRAME_REGSAVE_OFF + 32 * 8 + 8) |
| #endif |
| |
| proc_desc = frame->proc_desc; |
| if (proc_desc == NULL) |
| /* I'm not sure how/whether this can happen. Normally when we can't |
| find a proc_desc, we "synthesize" one using heuristic_proc_desc |
| and set the saved_regs right away. */ |
| return; |
| |
| /* Fill in the offsets for the registers which gen_mask says |
| were saved. */ |
| |
| reg_position = frame->frame + PROC_REG_OFFSET (proc_desc); |
| mask = PROC_REG_MASK (proc_desc); |
| |
| returnreg = PROC_PC_REG (proc_desc); |
| |
| /* Note that RA is always saved first, regardless of its actual |
| register number. */ |
| if (mask & (1 << returnreg)) |
| { |
| frame->saved_regs[returnreg] = reg_position; |
| reg_position += 8; |
| mask &= ~(1 << returnreg); /* Clear bit for RA so we |
| don't save again later. */ |
| } |
| |
| for (ireg = 0; ireg <= 31; ireg++) |
| if (mask & (1 << ireg)) |
| { |
| frame->saved_regs[ireg] = reg_position; |
| reg_position += 8; |
| } |
| |
| /* Fill in the offsets for the registers which float_mask says |
| were saved. */ |
| |
| reg_position = frame->frame + PROC_FREG_OFFSET (proc_desc); |
| mask = PROC_FREG_MASK (proc_desc); |
| |
| for (ireg = 0; ireg <= 31; ireg++) |
| if (mask & (1 << ireg)) |
| { |
| frame->saved_regs[FP0_REGNUM + ireg] = reg_position; |
| reg_position += 8; |
| } |
| |
| frame->saved_regs[PC_REGNUM] = frame->saved_regs[returnreg]; |
| } |
| |
| static CORE_ADDR |
| read_next_frame_reg (struct frame_info *fi, int regno) |
| { |
| CORE_ADDR result; |
| for (; fi; fi = fi->next) |
| { |
| /* We have to get the saved sp from the sigcontext |
| if it is a signal handler frame. */ |
| if (regno == SP_REGNUM) |
| return fi->frame; |
| else |
| { |
| if (fi->saved_regs == 0) |
| alpha_find_saved_regs (fi); |
| |
| if (fi->saved_regs[regno]) |
| { |
| if (read_memory_safe8 (fi->saved_regs[regno], &result) == 0) |
| return result; |
| else |
| return 0; |
| } |
| } |
| } |
| |
| return read_register (regno); |
| } |
| |
| static CORE_ADDR |
| alpha_frame_saved_pc (struct frame_info *frame) |
| { |
| return read_next_frame_reg (frame, frame->pc_reg); |
| } |
| |
| static struct alpha_extra_func_info temp_proc_desc; |
| |
| /* Nonzero if instruction at PC is a return instruction. "ret |
| $zero,($ra),1" on alpha. */ |
| |
| static int |
| alpha_about_to_return (CORE_ADDR pc) |
| { |
| int inst; |
| |
| read_memory_safe4 (pc, &inst); |
| return inst == 0x6bfa8001; |
| } |
| |
| /* A heuristically computed start address for the subprogram |
| containing address PC. Returns 0 if none detected. */ |
| |
| static CORE_ADDR |
| heuristic_proc_start (CORE_ADDR pc) |
| { |
| CORE_ADDR start_pc = pc; |
| CORE_ADDR fence = start_pc - heuristic_fence_post; |
| |
| if (start_pc == 0) |
| return 0; |
| |
| if (heuristic_fence_post == UINT_MAX |
| || fence < VM_MIN_ADDRESS) |
| fence = VM_MIN_ADDRESS; |
| |
| /* search back for previous return */ |
| for (start_pc -= 4; ; start_pc -= 4) |
| { |
| if (start_pc < fence) |
| return 0; |
| else if (alpha_about_to_return (start_pc)) |
| break; |
| } |
| |
| start_pc += 4; /* skip return */ |
| return start_pc; |
| } |
| |
| static alpha_extra_func_info_t |
| heuristic_proc_desc (CORE_ADDR start_pc, |
| CORE_ADDR limit_pc, |
| struct frame_info *next_frame, |
| struct frame_saved_regs *saved_regs_p) |
| { |
| CORE_ADDR sp = read_next_frame_reg (next_frame, SP_REGNUM); |
| CORE_ADDR cur_pc; |
| int frame_size; |
| int has_frame_reg = 0; |
| unsigned long reg_mask = 0; |
| int pcreg = -1; |
| |
| if (start_pc == 0) |
| return 0; |
| |
| memset (&temp_proc_desc, '\0', sizeof (temp_proc_desc)); |
| if (saved_regs_p != 0) |
| memset (saved_regs_p, '\0', sizeof (struct frame_saved_regs)); |
| |
| PROC_LOW_ADDR (&temp_proc_desc) = start_pc; |
| |
| if (start_pc + 200 < limit_pc) |
| limit_pc = start_pc + 200; |
| |
| frame_size = 0; |
| for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += 4) |
| { |
| unsigned int word; |
| int status; |
| |
| status = read_memory_safe4 (cur_pc, &word); |
| if (status) |
| return 0; |
| |
| if ((word & 0xffff0000) == 0x23de0000) /* lda $sp,n($sp) */ |
| { |
| if (word & 0x8000) |
| frame_size += (-word) & 0xffff; |
| else |
| /* Exit loop if a positive stack adjustment is found, which |
| usually means that the stack cleanup code in the function |
| epilogue is reached. */ |
| break; |
| } |
| else if ((word & 0xfc1f0000) == 0xb41e0000 /* stq reg,n($sp) */ |
| && (word & 0xffff0000) != 0xb7fe0000) /* reg != $zero */ |
| { |
| int reg = (word & 0x03e00000) >> 21; |
| |
| reg_mask |= 1 << reg; |
| if (saved_regs_p != 0) |
| saved_regs_p->regs[reg] = sp + (short) word; |
| |
| /* Starting with OSF/1-3.2C, the system libraries are shipped |
| without local symbols, but they still contain procedure |
| descriptors without a symbol reference. GDB is currently |
| unable to find these procedure descriptors and uses |
| heuristic_proc_desc instead. |
| As some low level compiler support routines (__div*, __add*) |
| use a non-standard return address register, we have to |
| add some heuristics to determine the return address register, |
| or stepping over these routines will fail. |
| Usually the return address register is the first register |
| saved on the stack, but assembler optimization might |
| rearrange the register saves. |
| So we recognize only a few registers (t7, t9, ra) within |
| the procedure prologue as valid return address registers. |
| If we encounter a return instruction, we extract the |
| the return address register from it. |
| |
| FIXME: Rewriting GDB to access the procedure descriptors, |
| e.g. via the minimal symbol table, might obviate this hack. */ |
| if (pcreg == -1 |
| && cur_pc < (start_pc + 80) |
| && (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM)) |
| pcreg = reg; |
| } |
| else if ((word & 0xffe0ffff) == 0x6be08001) /* ret zero,reg,1 */ |
| pcreg = (word >> 16) & 0x1f; |
| else if (word == 0x47de040f) /* bis sp,sp fp */ |
| has_frame_reg = 1; |
| } |
| |
| if (pcreg == -1) |
| { |
| /* If we haven't found a valid return address register yet, |
| keep searching in the procedure prologue. */ |
| while (cur_pc < (limit_pc + 80) && cur_pc < (start_pc + 80)) |
| { |
| unsigned int word; |
| |
| if (read_memory_safe4 (cur_pc, &word)) |
| break; |
| cur_pc += 4; |
| |
| if ((word & 0xfc1f0000) == 0xb41e0000 /* stq reg,n($sp) */ |
| && (word & 0xffff0000) != 0xb7fe0000) /* reg != $zero */ |
| { |
| int reg = (word & 0x03e00000) >> 21; |
| |
| if (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM) |
| { |
| pcreg = reg; |
| break; |
| } |
| } |
| else if ((word & 0xffe0ffff) == 0x6be08001) /* ret zero,reg,1 */ |
| { |
| pcreg = (word >> 16) & 0x1f; |
| break; |
| } |
| } |
| } |
| |
| if (has_frame_reg) |
| PROC_FRAME_REG (&temp_proc_desc) = GCC_FP_REGNUM; |
| else |
| PROC_FRAME_REG (&temp_proc_desc) = SP_REGNUM; |
| |
| PROC_FRAME_OFFSET (&temp_proc_desc) = frame_size; |
| PROC_REG_MASK (&temp_proc_desc) = reg_mask; |
| PROC_PC_REG (&temp_proc_desc) = (pcreg == -1) ? RA_REGNUM : pcreg; |
| PROC_LOCALOFF (&temp_proc_desc) = 0; /* XXX - bogus */ |
| |
| return &temp_proc_desc; |
| } |
| |
| static alpha_extra_func_info_t |
| find_proc_desc (CORE_ADDR pc, |
| struct frame_info *next_frame, |
| struct frame_saved_regs *saved_regs) |
| { |
| CORE_ADDR startaddr; |
| |
| /* If heuristic_fence_post is nonzero, determine the procedure |
| start address by examining the instructions. |
| This allows us to find the start address of static functions which |
| have no symbolic information, as startaddr would have been set to |
| the preceding global function start address by the |
| find_pc_partial_function call above. */ |
| startaddr = heuristic_proc_start (pc); |
| |
| return heuristic_proc_desc (startaddr, pc, next_frame, saved_regs); |
| } |
| |
| static CORE_ADDR |
| alpha_frame_chain (struct frame_info *frame) |
| { |
| alpha_extra_func_info_t proc_desc; |
| CORE_ADDR saved_pc = FRAME_SAVED_PC (frame); |
| |
| if (saved_pc == 0 || inside_entry_file (saved_pc)) |
| return 0; |
| |
| proc_desc = find_proc_desc (saved_pc, frame, NULL); |
| if (!proc_desc) |
| return 0; |
| |
| /* If no frame pointer and frame size is zero, we must be at end |
| of stack (or otherwise hosed). If we don't check frame size, |
| we loop forever if we see a zero size frame. */ |
| if (PROC_FRAME_REG (proc_desc) == SP_REGNUM |
| && PROC_FRAME_OFFSET (proc_desc) == 0) |
| return 0; |
| else |
| return read_next_frame_reg (frame, PROC_FRAME_REG (proc_desc)) |
| + PROC_FRAME_OFFSET (proc_desc); |
| } |
| |
| static void |
| init_extra_frame_info (struct frame_info *frame) |
| { |
| struct frame_saved_regs temp_saved_regs; |
| alpha_extra_func_info_t proc_desc = |
| find_proc_desc (frame->pc, frame->next, &temp_saved_regs); |
| |
| frame->saved_regs = NULL; |
| frame->localoff = 0; |
| frame->pc_reg = RA_REGNUM; |
| frame->proc_desc = proc_desc; |
| |
| if (proc_desc) |
| { |
| /* Get the locals offset and the saved pc register from the |
| procedure descriptor, they are valid even if we are in the |
| middle of the prologue. */ |
| frame->localoff = PROC_LOCALOFF (proc_desc); |
| frame->pc_reg = PROC_PC_REG (proc_desc); |
| |
| /* Fixup frame-pointer - only needed for top frame */ |
| |
| /* This may not be quite right, if proc has a real frame register. |
| Get the value of the frame relative sp, procedure might have been |
| interrupted by a signal at it's very start. */ |
| if (frame->pc == PROC_LOW_ADDR (proc_desc)) |
| frame->frame = read_next_frame_reg (frame->next, SP_REGNUM); |
| else |
| frame->frame |
| = (read_next_frame_reg (frame->next, PROC_FRAME_REG (proc_desc)) |
| + PROC_FRAME_OFFSET (proc_desc)); |
| |
| frame->saved_regs |
| = (CORE_ADDR *) frame_obstack_alloc (SIZEOF_FRAME_SAVED_REGS); |
| memcpy |
| (frame->saved_regs, temp_saved_regs.regs, SIZEOF_FRAME_SAVED_REGS); |
| frame->saved_regs[PC_REGNUM] = frame->saved_regs[RA_REGNUM]; |
| } |
| } |
| |
| /* Create an arbitrary (i.e. address specified by user) or innermost frame. |
| Always returns a non-NULL value. */ |
| |
| static struct frame_info * |
| create_new_frame (CORE_ADDR addr, CORE_ADDR pc) |
| { |
| struct frame_info *fi; |
| |
| fi = (struct frame_info *) |
| trace_alloc (sizeof (struct frame_info)); |
| |
| /* Arbitrary frame */ |
| fi->next = NULL; |
| fi->prev = NULL; |
| fi->frame = addr; |
| fi->pc = pc; |
| |
| #ifdef INIT_EXTRA_FRAME_INFO |
| INIT_EXTRA_FRAME_INFO (0, fi); |
| #endif |
| |
| return fi; |
| } |
| |
| static CORE_ADDR current_pc; |
| |
| static void |
| set_current_pc (void) |
| { |
| current_pc = (CORE_ADDR) __builtin_return_address (0); |
| } |
| |
| static CORE_ADDR |
| read_pc (void) |
| { |
| return current_pc; |
| } |
| |
| static struct frame_info * |
| get_current_frame (void) |
| { |
| return create_new_frame (0, read_pc ()); |
| } |
| |
| /* Return the frame that called FI. |
| If FI is the original frame (it has no caller), return 0. */ |
| |
| static struct frame_info * |
| get_prev_frame (struct frame_info *next_frame) |
| { |
| CORE_ADDR address = 0; |
| struct frame_info *prev; |
| int fromleaf = 0; |
| |
| /* If we have the prev one, return it */ |
| if (next_frame->prev) |
| return next_frame->prev; |
| |
| /* On some machines it is possible to call a function without |
| setting up a stack frame for it. On these machines, we |
| define this macro to take two args; a frameinfo pointer |
| identifying a frame and a variable to set or clear if it is |
| or isn't leafless. */ |
| |
| /* Two macros defined in tm.h specify the machine-dependent |
| actions to be performed here. |
| |
| First, get the frame's chain-pointer. If that is zero, the frame |
| is the outermost frame or a leaf called by the outermost frame. |
| This means that if start calls main without a frame, we'll return |
| 0 (which is fine anyway). |
| |
| Nope; there's a problem. This also returns when the current |
| routine is a leaf of main. This is unacceptable. We move |
| this to after the ffi test; I'd rather have backtraces from |
| start go curfluy than have an abort called from main not show |
| main. */ |
| |
| address = FRAME_CHAIN (next_frame); |
| if (!FRAME_CHAIN_VALID (address, next_frame)) |
| return 0; |
| address = FRAME_CHAIN_COMBINE (address, next_frame); |
| |
| if (address == 0) |
| return 0; |
| |
| prev = (struct frame_info *) trace_alloc (sizeof (struct frame_info)); |
| |
| prev->saved_regs = NULL; |
| if (next_frame) |
| next_frame->prev = prev; |
| |
| prev->next = next_frame; |
| prev->prev = (struct frame_info *) 0; |
| prev->frame = address; |
| |
| /* This change should not be needed, FIXME! We should |
| determine whether any targets *need* INIT_FRAME_PC to happen |
| after INIT_EXTRA_FRAME_INFO and come up with a simple way to |
| express what goes on here. |
| |
| INIT_EXTRA_FRAME_INFO is called from two places: create_new_frame |
| (where the PC is already set up) and here (where it isn't). |
| INIT_FRAME_PC is only called from here, always after |
| INIT_EXTRA_FRAME_INFO. |
| |
| The catch is the MIPS, where INIT_EXTRA_FRAME_INFO requires the PC |
| value (which hasn't been set yet). Some other machines appear to |
| require INIT_EXTRA_FRAME_INFO before they can do INIT_FRAME_PC. Phoo. |
| |
| We shouldn't need INIT_FRAME_PC_FIRST to add more complication to |
| an already overcomplicated part of GDB. gnu@cygnus.com, 15Sep92. |
| |
| Assuming that some machines need INIT_FRAME_PC after |
| INIT_EXTRA_FRAME_INFO, one possible scheme: |
| |
| SETUP_INNERMOST_FRAME() |
| Default version is just create_new_frame (read_fp ()), |
| read_pc ()). Machines with extra frame info would do that (or the |
| local equivalent) and then set the extra fields. |
| INIT_PREV_FRAME(fromleaf, prev) |
| Replace INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC. This should |
| also return a flag saying whether to keep the new frame, or |
| whether to discard it, because on some machines (e.g. mips) it |
| is really awkward to have FRAME_CHAIN_VALID called *before* |
| INIT_EXTRA_FRAME_INFO (there is no good way to get information |
| deduced in FRAME_CHAIN_VALID into the extra fields of the new frame). |
| std_frame_pc(fromleaf, prev) |
| This is the default setting for INIT_PREV_FRAME. It just does what |
| the default INIT_FRAME_PC does. Some machines will call it from |
| INIT_PREV_FRAME (either at the beginning, the end, or in the middle). |
| Some machines won't use it. |
| kingdon@cygnus.com, 13Apr93, 31Jan94, 14Dec94. */ |
| |
| #ifdef INIT_FRAME_PC_FIRST |
| INIT_FRAME_PC_FIRST (fromleaf, prev); |
| #endif |
| |
| #ifdef INIT_EXTRA_FRAME_INFO |
| INIT_EXTRA_FRAME_INFO (fromleaf, prev); |
| #endif |
| |
| /* This entry is in the frame queue now, which is good since |
| FRAME_SAVED_PC may use that queue to figure out its value |
| (see tm-sparc.h). We want the pc saved in the inferior frame. */ |
| INIT_FRAME_PC (fromleaf, prev); |
| |
| /* If ->frame and ->pc are unchanged, we are in the process of getting |
| ourselves into an infinite backtrace. Some architectures check this |
| in FRAME_CHAIN or thereabouts, but it seems like there is no reason |
| this can't be an architecture-independent check. */ |
| if (next_frame != NULL) |
| { |
| if (prev->frame == next_frame->frame |
| && prev->pc == next_frame->pc) |
| { |
| next_frame->prev = NULL; |
| free (prev); |
| return NULL; |
| } |
| } |
| |
| return prev; |
| } |
| |
| #define SAVE(regno,disp) \ |
| "stq $" #regno ", " #disp "(%0)\n" |
| |
| int |
| __gnat_backtrace (void **array, |
| int size, |
| void *exclude_min, |
| void *exclude_max, |
| int skip_frames) |
| { |
| struct frame_info* top; |
| struct frame_info* current; |
| int cnt; |
| |
| /* This function is not thread safe, protect it */ |
| (*Lock_Task) (); |
| asm volatile ( |
| SAVE (9,72) |
| SAVE (10,80) |
| SAVE (11,88) |
| SAVE (12,96) |
| SAVE (13,104) |
| SAVE (14,112) |
| SAVE (15,120) |
| SAVE (16,128) |
| SAVE (17,136) |
| SAVE (18,144) |
| SAVE (19,152) |
| SAVE (20,160) |
| SAVE (21,168) |
| SAVE (22,176) |
| SAVE (23,184) |
| SAVE (24,192) |
| SAVE (25,200) |
| SAVE (26,208) |
| SAVE (27,216) |
| SAVE (28,224) |
| SAVE (29,232) |
| SAVE (30,240) |
| : : "r" (&theRegisters)); |
| |
| trace_alloc_chain = NULL; |
| set_current_pc (); |
| |
| top = current = get_current_frame (); |
| cnt = 0; |
| |
| for (cnt = 0; cnt < skip_frames; cnt += 1) { |
| current = get_prev_frame (current); |
| } |
| |
| cnt = 0; |
| while (cnt < size) |
| { |
| if (STOP_FRAME) |
| break; |
| |
| if (current->pc < (CORE_ADDR) exclude_min |
| || current->pc > (CORE_ADDR) exclude_max) |
| array[cnt++] = (void*) (current->pc + PC_ADJUST); |
| |
| current = get_prev_frame (current); |
| } |
| |
| free_trace_alloc (); |
| (*Unlock_Task) (); |
| |
| return cnt; |
| } |