From: Randolph Chung Date: Fri, 23 Apr 2004 02:54:21 +0000 (+0000) Subject: 2004-04-22 Randolph Chung X-Git-Url: http://git.efficios.com/?a=commitdiff_plain;h=abc485a155fa7a84d07dc3cf376f3be050602cc9;p=deliverable%2Fbinutils-gdb.git 2004-04-22 Randolph Chung * hppa-tdep.h (find_unwind_entry, hppa_get_field, hppa_extract_5_load) (hppa_extract_5R_store, hppa_extract_5r_store, hppa_extract_17) (hppa_extract_21, hppa_extract_14, hppa_low_sign_extend) (hppa_sign_extend): Add prototype. * hppa-tdep.c (get_field, extract_5_load, extract_5R_store) (extract_5r_store, extract_17, extract_21, extract_14, low_sign_extend) (sign_extend): Rename with hppa_ prefix and make non-static. Other hppa targets will also use these functions. (find_unwind_entry): Remove prototype (moved to hppa-tdep.h). (hppa_in_solib_call_trampoline, hppa_in_solib_return_trampoline) (hppa_skip_trampoline_code): Move to hppa-hpux-tdep.c (hppa_gdbarch_init): Remove gdbarch setting of skip_trampoline_code, in_solib_call_trampoline and in_solib_return_trampoline. * hppa-hpux-tdep.c (hppa32_hpux_in_solib_call_trampoline) (hppa64_hpux_in_solib_call_trampoline): New functions, split from hppa_in_solib_call_trampoline. (hppa_hpux_in_solib_return_trampoline, hppa_hpux_skip_trampoline_code): Moved from hppa-tdep.c. (hppa_hpux_init_abi): Set gdbarch for skip_trampoline_code, in_solib_call_trampoline and in_solib_return_trampoline. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 03c4ebdc4d..786b4fe914 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,27 @@ +2004-04-22 Randolph Chung + + * hppa-tdep.h (find_unwind_entry, hppa_get_field, hppa_extract_5_load) + (hppa_extract_5R_store, hppa_extract_5r_store, hppa_extract_17) + (hppa_extract_21, hppa_extract_14, hppa_low_sign_extend) + (hppa_sign_extend): Add prototype. + * hppa-tdep.c (get_field, extract_5_load, extract_5R_store) + (extract_5r_store, extract_17, extract_21, extract_14, low_sign_extend) + (sign_extend): Rename with hppa_ prefix and make non-static. Other + hppa targets will also use these functions. + (find_unwind_entry): Remove prototype (moved to hppa-tdep.h). + (hppa_in_solib_call_trampoline, hppa_in_solib_return_trampoline) + (hppa_skip_trampoline_code): Move to hppa-hpux-tdep.c + (hppa_gdbarch_init): Remove gdbarch setting of + skip_trampoline_code, in_solib_call_trampoline and + in_solib_return_trampoline. + * hppa-hpux-tdep.c (hppa32_hpux_in_solib_call_trampoline) + (hppa64_hpux_in_solib_call_trampoline): New functions, split from + hppa_in_solib_call_trampoline. + (hppa_hpux_in_solib_return_trampoline, hppa_hpux_skip_trampoline_code): + Moved from hppa-tdep.c. + (hppa_hpux_init_abi): Set gdbarch for skip_trampoline_code, + in_solib_call_trampoline and in_solib_return_trampoline. + 2004-04-22 Randolph Chung * hppa-tdep.c (hppa_debug): New variable. diff --git a/gdb/hppa-hpux-tdep.c b/gdb/hppa-hpux-tdep.c index cf5fccbb81..a047d42859 100644 --- a/gdb/hppa-hpux-tdep.c +++ b/gdb/hppa-hpux-tdep.c @@ -164,6 +164,515 @@ hppa64_hpux_frame_find_saved_regs_in_sigtramp (struct frame_info *fi, } } +/* Return one if PC is in the call path of a trampoline, else return zero. + + Note we return one for *any* call trampoline (long-call, arg-reloc), not + just shared library trampolines (import, export). */ + +static int +hppa32_hpux_in_solib_call_trampoline (CORE_ADDR pc, char *name) +{ + struct minimal_symbol *minsym; + struct unwind_table_entry *u; + static CORE_ADDR dyncall = 0; + static CORE_ADDR sr4export = 0; + + /* FIXME XXX - dyncall and sr4export must be initialized whenever we get a + new exec file */ + + /* First see if PC is in one of the two C-library trampolines. */ + if (!dyncall) + { + minsym = lookup_minimal_symbol ("$$dyncall", NULL, NULL); + if (minsym) + dyncall = SYMBOL_VALUE_ADDRESS (minsym); + else + dyncall = -1; + } + + if (!sr4export) + { + minsym = lookup_minimal_symbol ("_sr4export", NULL, NULL); + if (minsym) + sr4export = SYMBOL_VALUE_ADDRESS (minsym); + else + sr4export = -1; + } + + if (pc == dyncall || pc == sr4export) + return 1; + + minsym = lookup_minimal_symbol_by_pc (pc); + if (minsym && strcmp (DEPRECATED_SYMBOL_NAME (minsym), ".stub") == 0) + return 1; + + /* Get the unwind descriptor corresponding to PC, return zero + if no unwind was found. */ + u = find_unwind_entry (pc); + if (!u) + return 0; + + /* If this isn't a linker stub, then return now. */ + if (u->stub_unwind.stub_type == 0) + return 0; + + /* By definition a long-branch stub is a call stub. */ + if (u->stub_unwind.stub_type == LONG_BRANCH) + return 1; + + /* The call and return path execute the same instructions within + an IMPORT stub! So an IMPORT stub is both a call and return + trampoline. */ + if (u->stub_unwind.stub_type == IMPORT) + return 1; + + /* Parameter relocation stubs always have a call path and may have a + return path. */ + if (u->stub_unwind.stub_type == PARAMETER_RELOCATION + || u->stub_unwind.stub_type == EXPORT) + { + CORE_ADDR addr; + + /* Search forward from the current PC until we hit a branch + or the end of the stub. */ + for (addr = pc; addr <= u->region_end; addr += 4) + { + unsigned long insn; + + insn = read_memory_integer (addr, 4); + + /* Does it look like a bl? If so then it's the call path, if + we find a bv or be first, then we're on the return path. */ + if ((insn & 0xfc00e000) == 0xe8000000) + return 1; + else if ((insn & 0xfc00e001) == 0xe800c000 + || (insn & 0xfc000000) == 0xe0000000) + return 0; + } + + /* Should never happen. */ + warning ("Unable to find branch in parameter relocation stub.\n"); + return 0; + } + + /* Unknown stub type. For now, just return zero. */ + return 0; +} + +static int +hppa64_hpux_in_solib_call_trampoline (CORE_ADDR pc, char *name) +{ + /* PA64 has a completely different stub/trampoline scheme. Is it + better? Maybe. It's certainly harder to determine with any + certainty that we are in a stub because we can not refer to the + unwinders to help. + + The heuristic is simple. Try to lookup the current PC value in th + minimal symbol table. If that fails, then assume we are not in a + stub and return. + + Then see if the PC value falls within the section bounds for the + section containing the minimal symbol we found in the first + step. If it does, then assume we are not in a stub and return. + + Finally peek at the instructions to see if they look like a stub. */ + struct minimal_symbol *minsym; + asection *sec; + CORE_ADDR addr; + int insn, i; + + minsym = lookup_minimal_symbol_by_pc (pc); + if (! minsym) + return 0; + + sec = SYMBOL_BFD_SECTION (minsym); + + if (bfd_get_section_vma (sec->owner, sec) <= pc + && pc < (bfd_get_section_vma (sec->owner, sec) + + bfd_section_size (sec->owner, sec))) + return 0; + + /* We might be in a stub. Peek at the instructions. Stubs are 3 + instructions long. */ + insn = read_memory_integer (pc, 4); + + /* Find out where we think we are within the stub. */ + if ((insn & 0xffffc00e) == 0x53610000) + addr = pc; + else if ((insn & 0xffffffff) == 0xe820d000) + addr = pc - 4; + else if ((insn & 0xffffc00e) == 0x537b0000) + addr = pc - 8; + else + return 0; + + /* Now verify each insn in the range looks like a stub instruction. */ + insn = read_memory_integer (addr, 4); + if ((insn & 0xffffc00e) != 0x53610000) + return 0; + + /* Now verify each insn in the range looks like a stub instruction. */ + insn = read_memory_integer (addr + 4, 4); + if ((insn & 0xffffffff) != 0xe820d000) + return 0; + + /* Now verify each insn in the range looks like a stub instruction. */ + insn = read_memory_integer (addr + 8, 4); + if ((insn & 0xffffc00e) != 0x537b0000) + return 0; + + /* Looks like a stub. */ + return 1; +} + +/* Return one if PC is in the return path of a trampoline, else return zero. + + Note we return one for *any* call trampoline (long-call, arg-reloc), not + just shared library trampolines (import, export). */ + +static int +hppa_hpux_in_solib_return_trampoline (CORE_ADDR pc, char *name) +{ + struct unwind_table_entry *u; + + /* Get the unwind descriptor corresponding to PC, return zero + if no unwind was found. */ + u = find_unwind_entry (pc); + if (!u) + return 0; + + /* If this isn't a linker stub or it's just a long branch stub, then + return zero. */ + if (u->stub_unwind.stub_type == 0 || u->stub_unwind.stub_type == LONG_BRANCH) + return 0; + + /* The call and return path execute the same instructions within + an IMPORT stub! So an IMPORT stub is both a call and return + trampoline. */ + if (u->stub_unwind.stub_type == IMPORT) + return 1; + + /* Parameter relocation stubs always have a call path and may have a + return path. */ + if (u->stub_unwind.stub_type == PARAMETER_RELOCATION + || u->stub_unwind.stub_type == EXPORT) + { + CORE_ADDR addr; + + /* Search forward from the current PC until we hit a branch + or the end of the stub. */ + for (addr = pc; addr <= u->region_end; addr += 4) + { + unsigned long insn; + + insn = read_memory_integer (addr, 4); + + /* Does it look like a bl? If so then it's the call path, if + we find a bv or be first, then we're on the return path. */ + if ((insn & 0xfc00e000) == 0xe8000000) + return 0; + else if ((insn & 0xfc00e001) == 0xe800c000 + || (insn & 0xfc000000) == 0xe0000000) + return 1; + } + + /* Should never happen. */ + warning ("Unable to find branch in parameter relocation stub.\n"); + return 0; + } + + /* Unknown stub type. For now, just return zero. */ + return 0; + +} + +/* Figure out if PC is in a trampoline, and if so find out where + the trampoline will jump to. If not in a trampoline, return zero. + + Simple code examination probably is not a good idea since the code + sequences in trampolines can also appear in user code. + + We use unwinds and information from the minimal symbol table to + determine when we're in a trampoline. This won't work for ELF + (yet) since it doesn't create stub unwind entries. Whether or + not ELF will create stub unwinds or normal unwinds for linker + stubs is still being debated. + + This should handle simple calls through dyncall or sr4export, + long calls, argument relocation stubs, and dyncall/sr4export + calling an argument relocation stub. It even handles some stubs + used in dynamic executables. */ + +static CORE_ADDR +hppa_hpux_skip_trampoline_code (CORE_ADDR pc) +{ + long orig_pc = pc; + long prev_inst, curr_inst, loc; + static CORE_ADDR dyncall = 0; + static CORE_ADDR dyncall_external = 0; + static CORE_ADDR sr4export = 0; + struct minimal_symbol *msym; + struct unwind_table_entry *u; + + /* FIXME XXX - dyncall and sr4export must be initialized whenever we get a + new exec file */ + + if (!dyncall) + { + msym = lookup_minimal_symbol ("$$dyncall", NULL, NULL); + if (msym) + dyncall = SYMBOL_VALUE_ADDRESS (msym); + else + dyncall = -1; + } + + if (!dyncall_external) + { + msym = lookup_minimal_symbol ("$$dyncall_external", NULL, NULL); + if (msym) + dyncall_external = SYMBOL_VALUE_ADDRESS (msym); + else + dyncall_external = -1; + } + + if (!sr4export) + { + msym = lookup_minimal_symbol ("_sr4export", NULL, NULL); + if (msym) + sr4export = SYMBOL_VALUE_ADDRESS (msym); + else + sr4export = -1; + } + + /* Addresses passed to dyncall may *NOT* be the actual address + of the function. So we may have to do something special. */ + if (pc == dyncall) + { + pc = (CORE_ADDR) read_register (22); + + /* If bit 30 (counting from the left) is on, then pc is the address of + the PLT entry for this function, not the address of the function + itself. Bit 31 has meaning too, but only for MPE. */ + if (pc & 0x2) + pc = (CORE_ADDR) read_memory_integer (pc & ~0x3, TARGET_PTR_BIT / 8); + } + if (pc == dyncall_external) + { + pc = (CORE_ADDR) read_register (22); + pc = (CORE_ADDR) read_memory_integer (pc & ~0x3, TARGET_PTR_BIT / 8); + } + else if (pc == sr4export) + pc = (CORE_ADDR) (read_register (22)); + + /* Get the unwind descriptor corresponding to PC, return zero + if no unwind was found. */ + u = find_unwind_entry (pc); + if (!u) + return 0; + + /* If this isn't a linker stub, then return now. */ + /* elz: attention here! (FIXME) because of a compiler/linker + error, some stubs which should have a non zero stub_unwind.stub_type + have unfortunately a value of zero. So this function would return here + as if we were not in a trampoline. To fix this, we go look at the partial + symbol information, which reports this guy as a stub. + (FIXME): Unfortunately, we are not that lucky: it turns out that the + partial symbol information is also wrong sometimes. This is because + when it is entered (somread.c::som_symtab_read()) it can happen that + if the type of the symbol (from the som) is Entry, and the symbol is + in a shared library, then it can also be a trampoline. This would + be OK, except that I believe the way they decide if we are ina shared library + does not work. SOOOO..., even if we have a regular function w/o trampolines + its minimal symbol can be assigned type mst_solib_trampoline. + Also, if we find that the symbol is a real stub, then we fix the unwind + descriptor, and define the stub type to be EXPORT. + Hopefully this is correct most of the times. */ + if (u->stub_unwind.stub_type == 0) + { + +/* elz: NOTE (FIXME!) once the problem with the unwind information is fixed + we can delete all the code which appears between the lines */ +/*--------------------------------------------------------------------------*/ + msym = lookup_minimal_symbol_by_pc (pc); + + if (msym == NULL || MSYMBOL_TYPE (msym) != mst_solib_trampoline) + return orig_pc == pc ? 0 : pc & ~0x3; + + else if (msym != NULL && MSYMBOL_TYPE (msym) == mst_solib_trampoline) + { + struct objfile *objfile; + struct minimal_symbol *msymbol; + int function_found = 0; + + /* go look if there is another minimal symbol with the same name as + this one, but with type mst_text. This would happen if the msym + is an actual trampoline, in which case there would be another + symbol with the same name corresponding to the real function */ + + ALL_MSYMBOLS (objfile, msymbol) + { + if (MSYMBOL_TYPE (msymbol) == mst_text + && DEPRECATED_STREQ (DEPRECATED_SYMBOL_NAME (msymbol), DEPRECATED_SYMBOL_NAME (msym))) + { + function_found = 1; + break; + } + } + + if (function_found) + /* the type of msym is correct (mst_solib_trampoline), but + the unwind info is wrong, so set it to the correct value */ + u->stub_unwind.stub_type = EXPORT; + else + /* the stub type info in the unwind is correct (this is not a + trampoline), but the msym type information is wrong, it + should be mst_text. So we need to fix the msym, and also + get out of this function */ + { + MSYMBOL_TYPE (msym) = mst_text; + return orig_pc == pc ? 0 : pc & ~0x3; + } + } + +/*--------------------------------------------------------------------------*/ + } + + /* It's a stub. Search for a branch and figure out where it goes. + Note we have to handle multi insn branch sequences like ldil;ble. + Most (all?) other branches can be determined by examining the contents + of certain registers and the stack. */ + + loc = pc; + curr_inst = 0; + prev_inst = 0; + while (1) + { + /* Make sure we haven't walked outside the range of this stub. */ + if (u != find_unwind_entry (loc)) + { + warning ("Unable to find branch in linker stub"); + return orig_pc == pc ? 0 : pc & ~0x3; + } + + prev_inst = curr_inst; + curr_inst = read_memory_integer (loc, 4); + + /* Does it look like a branch external using %r1? Then it's the + branch from the stub to the actual function. */ + if ((curr_inst & 0xffe0e000) == 0xe0202000) + { + /* Yup. See if the previous instruction loaded + a value into %r1. If so compute and return the jump address. */ + if ((prev_inst & 0xffe00000) == 0x20200000) + return (hppa_extract_21 (prev_inst) + hppa_extract_17 (curr_inst)) & ~0x3; + else + { + warning ("Unable to find ldil X,%%r1 before ble Y(%%sr4,%%r1)."); + return orig_pc == pc ? 0 : pc & ~0x3; + } + } + + /* Does it look like a be 0(sr0,%r21)? OR + Does it look like a be, n 0(sr0,%r21)? OR + Does it look like a bve (r21)? (this is on PA2.0) + Does it look like a bve, n(r21)? (this is also on PA2.0) + That's the branch from an + import stub to an export stub. + + It is impossible to determine the target of the branch via + simple examination of instructions and/or data (consider + that the address in the plabel may be the address of the + bind-on-reference routine in the dynamic loader). + + So we have try an alternative approach. + + Get the name of the symbol at our current location; it should + be a stub symbol with the same name as the symbol in the + shared library. + + Then lookup a minimal symbol with the same name; we should + get the minimal symbol for the target routine in the shared + library as those take precedence of import/export stubs. */ + if ((curr_inst == 0xe2a00000) || + (curr_inst == 0xe2a00002) || + (curr_inst == 0xeaa0d000) || + (curr_inst == 0xeaa0d002)) + { + struct minimal_symbol *stubsym, *libsym; + + stubsym = lookup_minimal_symbol_by_pc (loc); + if (stubsym == NULL) + { + warning ("Unable to find symbol for 0x%lx", loc); + return orig_pc == pc ? 0 : pc & ~0x3; + } + + libsym = lookup_minimal_symbol (DEPRECATED_SYMBOL_NAME (stubsym), NULL, NULL); + if (libsym == NULL) + { + warning ("Unable to find library symbol for %s\n", + DEPRECATED_SYMBOL_NAME (stubsym)); + return orig_pc == pc ? 0 : pc & ~0x3; + } + + return SYMBOL_VALUE (libsym); + } + + /* Does it look like bl X,%rp or bl X,%r0? Another way to do a + branch from the stub to the actual function. */ + /*elz */ + else if ((curr_inst & 0xffe0e000) == 0xe8400000 + || (curr_inst & 0xffe0e000) == 0xe8000000 + || (curr_inst & 0xffe0e000) == 0xe800A000) + return (loc + hppa_extract_17 (curr_inst) + 8) & ~0x3; + + /* Does it look like bv (rp)? Note this depends on the + current stack pointer being the same as the stack + pointer in the stub itself! This is a branch on from the + stub back to the original caller. */ + /*else if ((curr_inst & 0xffe0e000) == 0xe840c000) */ + else if ((curr_inst & 0xffe0f000) == 0xe840c000) + { + /* Yup. See if the previous instruction loaded + rp from sp - 8. */ + if (prev_inst == 0x4bc23ff1) + return (read_memory_integer + (read_register (HPPA_SP_REGNUM) - 8, 4)) & ~0x3; + else + { + warning ("Unable to find restore of %%rp before bv (%%rp)."); + return orig_pc == pc ? 0 : pc & ~0x3; + } + } + + /* elz: added this case to capture the new instruction + at the end of the return part of an export stub used by + the PA2.0: BVE, n (rp) */ + else if ((curr_inst & 0xffe0f000) == 0xe840d000) + { + return (read_memory_integer + (read_register (HPPA_SP_REGNUM) - 24, TARGET_PTR_BIT / 8)) & ~0x3; + } + + /* What about be,n 0(sr0,%rp)? It's just another way we return to + the original caller from the stub. Used in dynamic executables. */ + else if (curr_inst == 0xe0400002) + { + /* The value we jump to is sitting in sp - 24. But that's + loaded several instructions before the be instruction. + I guess we could check for the previous instruction being + mtsp %r1,%sr0 if we want to do sanity checking. */ + return (read_memory_integer + (read_register (HPPA_SP_REGNUM) - 24, TARGET_PTR_BIT / 8)) & ~0x3; + } + + /* Haven't found the branch yet, but we're still in the stub. + Keep looking. */ + loc += 4; + } +} + + /* Exception handling support for the HP-UX ANSI C++ compiler. The compiler (aCC) provides a callback for exception events; GDB can set a breakpoint on this callback and find out what @@ -716,7 +1225,20 @@ child_get_current_exception_event (void) static void hppa_hpux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + set_gdbarch_deprecated_pc_in_sigtramp (gdbarch, hppa_hpux_pc_in_sigtramp); + + if (tdep->bytes_per_address == 4) + set_gdbarch_in_solib_call_trampoline (gdbarch, + hppa32_hpux_in_solib_call_trampoline); + else + set_gdbarch_in_solib_call_trampoline (gdbarch, + hppa64_hpux_in_solib_call_trampoline); + + set_gdbarch_in_solib_return_trampoline (gdbarch, + hppa_hpux_in_solib_return_trampoline); + set_gdbarch_skip_trampoline_code (gdbarch, hppa_hpux_skip_trampoline_code); } static void diff --git a/gdb/hppa-tdep.c b/gdb/hppa-tdep.c index 26b73dc551..1ac25c8418 100644 --- a/gdb/hppa-tdep.c +++ b/gdb/hppa-tdep.c @@ -88,28 +88,8 @@ const struct objfile_data *hppa_objfile_priv_data = NULL; #define UNWIND_ENTRY_SIZE 16 #define STUB_UNWIND_ENTRY_SIZE 8 -static int get_field (unsigned word, int from, int to); - -static int extract_5_load (unsigned int); - -static unsigned extract_5R_store (unsigned int); - -static unsigned extract_5r_store (unsigned int); - -struct unwind_table_entry *find_unwind_entry (CORE_ADDR); - -static int extract_17 (unsigned int); - -static int extract_21 (unsigned); - -static int extract_14 (unsigned); - static void unwind_command (char *, int); -static int low_sign_extend (unsigned int, unsigned int); - -static int sign_extend (unsigned int, unsigned int); - static int hppa_alignof (struct type *); static int prologue_inst_adjust_sp (unsigned long); @@ -246,16 +226,16 @@ hppa64_return_value (struct gdbarch *gdbarch, /* This assumes that no garbage lies outside of the lower bits of value. */ -static int -sign_extend (unsigned val, unsigned bits) +int +hppa_sign_extend (unsigned val, unsigned bits) { return (int) (val >> (bits - 1) ? (-1 << bits) | val : val); } /* For many immediate values the sign bit is the low bit! */ -static int -low_sign_extend (unsigned val, unsigned bits) +int +hppa_low_hppa_sign_extend (unsigned val, unsigned bits) { return (int) ((val & 0x1 ? (-1 << (bits - 1)) : 0) | val >> 1); } @@ -263,74 +243,74 @@ low_sign_extend (unsigned val, unsigned bits) /* Extract the bits at positions between FROM and TO, using HP's numbering (MSB = 0). */ -static int -get_field (unsigned word, int from, int to) +int +hppa_get_field (unsigned word, int from, int to) { return ((word) >> (31 - (to)) & ((1 << ((to) - (from) + 1)) - 1)); } /* extract the immediate field from a ld{bhw}s instruction */ -static int -extract_5_load (unsigned word) +int +hppa_extract_5_load (unsigned word) { - return low_sign_extend (word >> 16 & MASK_5, 5); + return hppa_low_hppa_sign_extend (word >> 16 & MASK_5, 5); } /* extract the immediate field from a break instruction */ -static unsigned -extract_5r_store (unsigned word) +unsigned +hppa_extract_5r_store (unsigned word) { return (word & MASK_5); } /* extract the immediate field from a {sr}sm instruction */ -static unsigned -extract_5R_store (unsigned word) +unsigned +hppa_extract_5R_store (unsigned word) { return (word >> 16 & MASK_5); } /* extract a 14 bit immediate field */ -static int -extract_14 (unsigned word) +int +hppa_extract_14 (unsigned word) { - return low_sign_extend (word & MASK_14, 14); + return hppa_low_hppa_sign_extend (word & MASK_14, 14); } /* extract a 21 bit constant */ -static int -extract_21 (unsigned word) +int +hppa_extract_21 (unsigned word) { int val; word &= MASK_21; word <<= 11; - val = get_field (word, 20, 20); + val = hppa_get_field (word, 20, 20); val <<= 11; - val |= get_field (word, 9, 19); + val |= hppa_get_field (word, 9, 19); val <<= 2; - val |= get_field (word, 5, 6); + val |= hppa_get_field (word, 5, 6); val <<= 5; - val |= get_field (word, 0, 4); + val |= hppa_get_field (word, 0, 4); val <<= 2; - val |= get_field (word, 7, 8); - return sign_extend (val, 21) << 11; + val |= hppa_get_field (word, 7, 8); + return hppa_sign_extend (val, 21) << 11; } /* extract a 17 bit constant from branch instructions, returning the 19 bit signed value. */ -static int -extract_17 (unsigned word) +int +hppa_extract_17 (unsigned word) { - return sign_extend (get_field (word, 19, 28) | - get_field (word, 29, 29) << 10 | - get_field (word, 11, 15) << 11 | + return hppa_sign_extend (hppa_get_field (word, 19, 28) | + hppa_get_field (word, 29, 29) << 10 | + hppa_get_field (word, 11, 15) << 11 | (word & 0x1) << 16, 17) << 2; } @@ -1099,515 +1079,6 @@ hppa_alignof (struct type *type) } } -/* Return one if PC is in the call path of a trampoline, else return zero. - - Note we return one for *any* call trampoline (long-call, arg-reloc), not - just shared library trampolines (import, export). */ - -static int -hppa_in_solib_call_trampoline (CORE_ADDR pc, char *name) -{ - struct minimal_symbol *minsym; - struct unwind_table_entry *u; - static CORE_ADDR dyncall = 0; - static CORE_ADDR sr4export = 0; - -#ifdef GDB_TARGET_IS_HPPA_20W - /* PA64 has a completely different stub/trampoline scheme. Is it - better? Maybe. It's certainly harder to determine with any - certainty that we are in a stub because we can not refer to the - unwinders to help. - - The heuristic is simple. Try to lookup the current PC value in th - minimal symbol table. If that fails, then assume we are not in a - stub and return. - - Then see if the PC value falls within the section bounds for the - section containing the minimal symbol we found in the first - step. If it does, then assume we are not in a stub and return. - - Finally peek at the instructions to see if they look like a stub. */ - { - struct minimal_symbol *minsym; - asection *sec; - CORE_ADDR addr; - int insn, i; - - minsym = lookup_minimal_symbol_by_pc (pc); - if (! minsym) - return 0; - - sec = SYMBOL_BFD_SECTION (minsym); - - if (bfd_get_section_vma (sec->owner, sec) <= pc - && pc < (bfd_get_section_vma (sec->owner, sec) - + bfd_section_size (sec->owner, sec))) - return 0; - - /* We might be in a stub. Peek at the instructions. Stubs are 3 - instructions long. */ - insn = read_memory_integer (pc, 4); - - /* Find out where we think we are within the stub. */ - if ((insn & 0xffffc00e) == 0x53610000) - addr = pc; - else if ((insn & 0xffffffff) == 0xe820d000) - addr = pc - 4; - else if ((insn & 0xffffc00e) == 0x537b0000) - addr = pc - 8; - else - return 0; - - /* Now verify each insn in the range looks like a stub instruction. */ - insn = read_memory_integer (addr, 4); - if ((insn & 0xffffc00e) != 0x53610000) - return 0; - - /* Now verify each insn in the range looks like a stub instruction. */ - insn = read_memory_integer (addr + 4, 4); - if ((insn & 0xffffffff) != 0xe820d000) - return 0; - - /* Now verify each insn in the range looks like a stub instruction. */ - insn = read_memory_integer (addr + 8, 4); - if ((insn & 0xffffc00e) != 0x537b0000) - return 0; - - /* Looks like a stub. */ - return 1; - } -#endif - - /* FIXME XXX - dyncall and sr4export must be initialized whenever we get a - new exec file */ - - /* First see if PC is in one of the two C-library trampolines. */ - if (!dyncall) - { - minsym = lookup_minimal_symbol ("$$dyncall", NULL, NULL); - if (minsym) - dyncall = SYMBOL_VALUE_ADDRESS (minsym); - else - dyncall = -1; - } - - if (!sr4export) - { - minsym = lookup_minimal_symbol ("_sr4export", NULL, NULL); - if (minsym) - sr4export = SYMBOL_VALUE_ADDRESS (minsym); - else - sr4export = -1; - } - - if (pc == dyncall || pc == sr4export) - return 1; - - minsym = lookup_minimal_symbol_by_pc (pc); - if (minsym && strcmp (DEPRECATED_SYMBOL_NAME (minsym), ".stub") == 0) - return 1; - - /* Get the unwind descriptor corresponding to PC, return zero - if no unwind was found. */ - u = find_unwind_entry (pc); - if (!u) - return 0; - - /* If this isn't a linker stub, then return now. */ - if (u->stub_unwind.stub_type == 0) - return 0; - - /* By definition a long-branch stub is a call stub. */ - if (u->stub_unwind.stub_type == LONG_BRANCH) - return 1; - - /* The call and return path execute the same instructions within - an IMPORT stub! So an IMPORT stub is both a call and return - trampoline. */ - if (u->stub_unwind.stub_type == IMPORT) - return 1; - - /* Parameter relocation stubs always have a call path and may have a - return path. */ - if (u->stub_unwind.stub_type == PARAMETER_RELOCATION - || u->stub_unwind.stub_type == EXPORT) - { - CORE_ADDR addr; - - /* Search forward from the current PC until we hit a branch - or the end of the stub. */ - for (addr = pc; addr <= u->region_end; addr += 4) - { - unsigned long insn; - - insn = read_memory_integer (addr, 4); - - /* Does it look like a bl? If so then it's the call path, if - we find a bv or be first, then we're on the return path. */ - if ((insn & 0xfc00e000) == 0xe8000000) - return 1; - else if ((insn & 0xfc00e001) == 0xe800c000 - || (insn & 0xfc000000) == 0xe0000000) - return 0; - } - - /* Should never happen. */ - warning ("Unable to find branch in parameter relocation stub.\n"); - return 0; - } - - /* Unknown stub type. For now, just return zero. */ - return 0; -} - -/* Return one if PC is in the return path of a trampoline, else return zero. - - Note we return one for *any* call trampoline (long-call, arg-reloc), not - just shared library trampolines (import, export). */ - -static int -hppa_in_solib_return_trampoline (CORE_ADDR pc, char *name) -{ - struct unwind_table_entry *u; - - /* Get the unwind descriptor corresponding to PC, return zero - if no unwind was found. */ - u = find_unwind_entry (pc); - if (!u) - return 0; - - /* If this isn't a linker stub or it's just a long branch stub, then - return zero. */ - if (u->stub_unwind.stub_type == 0 || u->stub_unwind.stub_type == LONG_BRANCH) - return 0; - - /* The call and return path execute the same instructions within - an IMPORT stub! So an IMPORT stub is both a call and return - trampoline. */ - if (u->stub_unwind.stub_type == IMPORT) - return 1; - - /* Parameter relocation stubs always have a call path and may have a - return path. */ - if (u->stub_unwind.stub_type == PARAMETER_RELOCATION - || u->stub_unwind.stub_type == EXPORT) - { - CORE_ADDR addr; - - /* Search forward from the current PC until we hit a branch - or the end of the stub. */ - for (addr = pc; addr <= u->region_end; addr += 4) - { - unsigned long insn; - - insn = read_memory_integer (addr, 4); - - /* Does it look like a bl? If so then it's the call path, if - we find a bv or be first, then we're on the return path. */ - if ((insn & 0xfc00e000) == 0xe8000000) - return 0; - else if ((insn & 0xfc00e001) == 0xe800c000 - || (insn & 0xfc000000) == 0xe0000000) - return 1; - } - - /* Should never happen. */ - warning ("Unable to find branch in parameter relocation stub.\n"); - return 0; - } - - /* Unknown stub type. For now, just return zero. */ - return 0; - -} - -/* Figure out if PC is in a trampoline, and if so find out where - the trampoline will jump to. If not in a trampoline, return zero. - - Simple code examination probably is not a good idea since the code - sequences in trampolines can also appear in user code. - - We use unwinds and information from the minimal symbol table to - determine when we're in a trampoline. This won't work for ELF - (yet) since it doesn't create stub unwind entries. Whether or - not ELF will create stub unwinds or normal unwinds for linker - stubs is still being debated. - - This should handle simple calls through dyncall or sr4export, - long calls, argument relocation stubs, and dyncall/sr4export - calling an argument relocation stub. It even handles some stubs - used in dynamic executables. */ - -static CORE_ADDR -hppa_skip_trampoline_code (CORE_ADDR pc) -{ - long orig_pc = pc; - long prev_inst, curr_inst, loc; - static CORE_ADDR dyncall = 0; - static CORE_ADDR dyncall_external = 0; - static CORE_ADDR sr4export = 0; - struct minimal_symbol *msym; - struct unwind_table_entry *u; - - /* FIXME XXX - dyncall and sr4export must be initialized whenever we get a - new exec file */ - - if (!dyncall) - { - msym = lookup_minimal_symbol ("$$dyncall", NULL, NULL); - if (msym) - dyncall = SYMBOL_VALUE_ADDRESS (msym); - else - dyncall = -1; - } - - if (!dyncall_external) - { - msym = lookup_minimal_symbol ("$$dyncall_external", NULL, NULL); - if (msym) - dyncall_external = SYMBOL_VALUE_ADDRESS (msym); - else - dyncall_external = -1; - } - - if (!sr4export) - { - msym = lookup_minimal_symbol ("_sr4export", NULL, NULL); - if (msym) - sr4export = SYMBOL_VALUE_ADDRESS (msym); - else - sr4export = -1; - } - - /* Addresses passed to dyncall may *NOT* be the actual address - of the function. So we may have to do something special. */ - if (pc == dyncall) - { - pc = (CORE_ADDR) read_register (22); - - /* If bit 30 (counting from the left) is on, then pc is the address of - the PLT entry for this function, not the address of the function - itself. Bit 31 has meaning too, but only for MPE. */ - if (pc & 0x2) - pc = (CORE_ADDR) read_memory_integer (pc & ~0x3, TARGET_PTR_BIT / 8); - } - if (pc == dyncall_external) - { - pc = (CORE_ADDR) read_register (22); - pc = (CORE_ADDR) read_memory_integer (pc & ~0x3, TARGET_PTR_BIT / 8); - } - else if (pc == sr4export) - pc = (CORE_ADDR) (read_register (22)); - - /* Get the unwind descriptor corresponding to PC, return zero - if no unwind was found. */ - u = find_unwind_entry (pc); - if (!u) - return 0; - - /* If this isn't a linker stub, then return now. */ - /* elz: attention here! (FIXME) because of a compiler/linker - error, some stubs which should have a non zero stub_unwind.stub_type - have unfortunately a value of zero. So this function would return here - as if we were not in a trampoline. To fix this, we go look at the partial - symbol information, which reports this guy as a stub. - (FIXME): Unfortunately, we are not that lucky: it turns out that the - partial symbol information is also wrong sometimes. This is because - when it is entered (somread.c::som_symtab_read()) it can happen that - if the type of the symbol (from the som) is Entry, and the symbol is - in a shared library, then it can also be a trampoline. This would - be OK, except that I believe the way they decide if we are ina shared library - does not work. SOOOO..., even if we have a regular function w/o trampolines - its minimal symbol can be assigned type mst_solib_trampoline. - Also, if we find that the symbol is a real stub, then we fix the unwind - descriptor, and define the stub type to be EXPORT. - Hopefully this is correct most of the times. */ - if (u->stub_unwind.stub_type == 0) - { - -/* elz: NOTE (FIXME!) once the problem with the unwind information is fixed - we can delete all the code which appears between the lines */ -/*--------------------------------------------------------------------------*/ - msym = lookup_minimal_symbol_by_pc (pc); - - if (msym == NULL || MSYMBOL_TYPE (msym) != mst_solib_trampoline) - return orig_pc == pc ? 0 : pc & ~0x3; - - else if (msym != NULL && MSYMBOL_TYPE (msym) == mst_solib_trampoline) - { - struct objfile *objfile; - struct minimal_symbol *msymbol; - int function_found = 0; - - /* go look if there is another minimal symbol with the same name as - this one, but with type mst_text. This would happen if the msym - is an actual trampoline, in which case there would be another - symbol with the same name corresponding to the real function */ - - ALL_MSYMBOLS (objfile, msymbol) - { - if (MSYMBOL_TYPE (msymbol) == mst_text - && DEPRECATED_STREQ (DEPRECATED_SYMBOL_NAME (msymbol), DEPRECATED_SYMBOL_NAME (msym))) - { - function_found = 1; - break; - } - } - - if (function_found) - /* the type of msym is correct (mst_solib_trampoline), but - the unwind info is wrong, so set it to the correct value */ - u->stub_unwind.stub_type = EXPORT; - else - /* the stub type info in the unwind is correct (this is not a - trampoline), but the msym type information is wrong, it - should be mst_text. So we need to fix the msym, and also - get out of this function */ - { - MSYMBOL_TYPE (msym) = mst_text; - return orig_pc == pc ? 0 : pc & ~0x3; - } - } - -/*--------------------------------------------------------------------------*/ - } - - /* It's a stub. Search for a branch and figure out where it goes. - Note we have to handle multi insn branch sequences like ldil;ble. - Most (all?) other branches can be determined by examining the contents - of certain registers and the stack. */ - - loc = pc; - curr_inst = 0; - prev_inst = 0; - while (1) - { - /* Make sure we haven't walked outside the range of this stub. */ - if (u != find_unwind_entry (loc)) - { - warning ("Unable to find branch in linker stub"); - return orig_pc == pc ? 0 : pc & ~0x3; - } - - prev_inst = curr_inst; - curr_inst = read_memory_integer (loc, 4); - - /* Does it look like a branch external using %r1? Then it's the - branch from the stub to the actual function. */ - if ((curr_inst & 0xffe0e000) == 0xe0202000) - { - /* Yup. See if the previous instruction loaded - a value into %r1. If so compute and return the jump address. */ - if ((prev_inst & 0xffe00000) == 0x20200000) - return (extract_21 (prev_inst) + extract_17 (curr_inst)) & ~0x3; - else - { - warning ("Unable to find ldil X,%%r1 before ble Y(%%sr4,%%r1)."); - return orig_pc == pc ? 0 : pc & ~0x3; - } - } - - /* Does it look like a be 0(sr0,%r21)? OR - Does it look like a be, n 0(sr0,%r21)? OR - Does it look like a bve (r21)? (this is on PA2.0) - Does it look like a bve, n(r21)? (this is also on PA2.0) - That's the branch from an - import stub to an export stub. - - It is impossible to determine the target of the branch via - simple examination of instructions and/or data (consider - that the address in the plabel may be the address of the - bind-on-reference routine in the dynamic loader). - - So we have try an alternative approach. - - Get the name of the symbol at our current location; it should - be a stub symbol with the same name as the symbol in the - shared library. - - Then lookup a minimal symbol with the same name; we should - get the minimal symbol for the target routine in the shared - library as those take precedence of import/export stubs. */ - if ((curr_inst == 0xe2a00000) || - (curr_inst == 0xe2a00002) || - (curr_inst == 0xeaa0d000) || - (curr_inst == 0xeaa0d002)) - { - struct minimal_symbol *stubsym, *libsym; - - stubsym = lookup_minimal_symbol_by_pc (loc); - if (stubsym == NULL) - { - warning ("Unable to find symbol for 0x%lx", loc); - return orig_pc == pc ? 0 : pc & ~0x3; - } - - libsym = lookup_minimal_symbol (DEPRECATED_SYMBOL_NAME (stubsym), NULL, NULL); - if (libsym == NULL) - { - warning ("Unable to find library symbol for %s\n", - DEPRECATED_SYMBOL_NAME (stubsym)); - return orig_pc == pc ? 0 : pc & ~0x3; - } - - return SYMBOL_VALUE (libsym); - } - - /* Does it look like bl X,%rp or bl X,%r0? Another way to do a - branch from the stub to the actual function. */ - /*elz */ - else if ((curr_inst & 0xffe0e000) == 0xe8400000 - || (curr_inst & 0xffe0e000) == 0xe8000000 - || (curr_inst & 0xffe0e000) == 0xe800A000) - return (loc + extract_17 (curr_inst) + 8) & ~0x3; - - /* Does it look like bv (rp)? Note this depends on the - current stack pointer being the same as the stack - pointer in the stub itself! This is a branch on from the - stub back to the original caller. */ - /*else if ((curr_inst & 0xffe0e000) == 0xe840c000) */ - else if ((curr_inst & 0xffe0f000) == 0xe840c000) - { - /* Yup. See if the previous instruction loaded - rp from sp - 8. */ - if (prev_inst == 0x4bc23ff1) - return (read_memory_integer - (read_register (HPPA_SP_REGNUM) - 8, 4)) & ~0x3; - else - { - warning ("Unable to find restore of %%rp before bv (%%rp)."); - return orig_pc == pc ? 0 : pc & ~0x3; - } - } - - /* elz: added this case to capture the new instruction - at the end of the return part of an export stub used by - the PA2.0: BVE, n (rp) */ - else if ((curr_inst & 0xffe0f000) == 0xe840d000) - { - return (read_memory_integer - (read_register (HPPA_SP_REGNUM) - 24, TARGET_PTR_BIT / 8)) & ~0x3; - } - - /* What about be,n 0(sr0,%rp)? It's just another way we return to - the original caller from the stub. Used in dynamic executables. */ - else if (curr_inst == 0xe0400002) - { - /* The value we jump to is sitting in sp - 24. But that's - loaded several instructions before the be instruction. - I guess we could check for the previous instruction being - mtsp %r1,%sr0 if we want to do sanity checking. */ - return (read_memory_integer - (read_register (HPPA_SP_REGNUM) - 24, TARGET_PTR_BIT / 8)) & ~0x3; - } - - /* Haven't found the branch yet, but we're still in the stub. - Keep looking. */ - loc += 4; - } -} - - /* For the given instruction (INST), return any adjustment it makes to the stack pointer or zero for no adjustment. @@ -1621,11 +1092,11 @@ prologue_inst_adjust_sp (unsigned long inst) /* The most common way to perform a stack adjustment ldo X(sp),sp */ if ((inst & 0xffffc000) == 0x37de0000) - return extract_14 (inst); + return hppa_extract_14 (inst); /* stwm X,D(sp) */ if ((inst & 0xffe00000) == 0x6fc00000) - return extract_14 (inst); + return hppa_extract_14 (inst); /* std,ma X,D(sp) */ if ((inst & 0xffe00008) == 0x73c00008) @@ -1635,16 +1106,16 @@ prologue_inst_adjust_sp (unsigned long inst) save high bits in save_high21 for later use. */ if ((inst & 0xffe00000) == 0x28200000) { - save_high21 = extract_21 (inst); + save_high21 = hppa_extract_21 (inst); return 0; } if ((inst & 0xffff0000) == 0x343e0000) - return save_high21 + extract_14 (inst); + return save_high21 + hppa_extract_14 (inst); /* fstws as used by the HP compilers. */ if ((inst & 0xffffffe0) == 0x2fd01220) - return extract_5_load (inst); + return hppa_extract_5_load (inst); /* No adjustment. */ return 0; @@ -1693,17 +1164,17 @@ inst_saves_gr (unsigned long inst) || (inst >> 26) == 0x1f || ((inst >> 26) == 0x1f && ((inst >> 6) == 0xa))) - return extract_5R_store (inst); + return hppa_extract_5R_store (inst); /* Does it look like a std? */ if ((inst >> 26) == 0x1c || ((inst >> 26) == 0x03 && ((inst >> 6) & 0xf) == 0xb)) - return extract_5R_store (inst); + return hppa_extract_5R_store (inst); /* Does it look like a stwm? GCC & HPC may use this in prologues. */ if ((inst >> 26) == 0x1b) - return extract_5R_store (inst); + return hppa_extract_5R_store (inst); /* Does it look like sth or stb? HPC versions 9.0 and later use these too. */ @@ -1711,7 +1182,7 @@ inst_saves_gr (unsigned long inst) || ((inst >> 26) == 0x3 && (((inst >> 6) & 0xf) == 0x8 || (inst >> 6) & 0xf) == 0x9)) - return extract_5R_store (inst); + return hppa_extract_5R_store (inst); return 0; } @@ -1729,14 +1200,14 @@ inst_saves_fr (unsigned long inst) { /* is this an FSTD ? */ if ((inst & 0xfc00dfc0) == 0x2c001200) - return extract_5r_store (inst); + return hppa_extract_5r_store (inst); if ((inst & 0xfc000002) == 0x70000002) - return extract_5R_store (inst); + return hppa_extract_5R_store (inst); /* is this an FSTW ? */ if ((inst & 0xfc00df80) == 0x24001200) - return extract_5r_store (inst); + return hppa_extract_5r_store (inst); if ((inst & 0xfc000002) == 0x7c000000) - return extract_5R_store (inst); + return hppa_extract_5R_store (inst); return 0; } @@ -2172,7 +1643,7 @@ hppa_frame_cache (struct frame_info *next_frame, void **this_cache) && (!u->Save_SP || reg != HPPA_FP_REGNUM)) { saved_gr_mask &= ~(1 << reg); - if ((inst >> 26) == 0x1b && extract_14 (inst) >= 0) + if ((inst >> 26) == 0x1b && hppa_extract_14 (inst) >= 0) /* stwm with a positive displacement is a _post_ _modify_. */ cache->saved_regs[reg].addr = 0; @@ -2186,9 +1657,9 @@ hppa_frame_cache (struct frame_info *next_frame, void **this_cache) if ((inst >> 26) == 0x1c) offset = (inst & 0x1 ? -1 << 13 : 0) | (((inst >> 4) & 0x3ff) << 3); else if ((inst >> 26) == 0x03) - offset = low_sign_extend (inst & 0x1f, 5); + offset = hppa_low_hppa_sign_extend (inst & 0x1f, 5); else - offset = extract_14 (inst); + offset = hppa_extract_14 (inst); /* Handle code with and without frame pointers. */ if (u->Save_SP) @@ -2210,7 +1681,7 @@ hppa_frame_cache (struct frame_info *next_frame, void **this_cache) /* ldo X(%r3),%r1 or ldo X(%r30),%r1. */ if ((inst & 0xffffc000) == 0x34610000 || (inst & 0xffffc000) == 0x37c10000) - fp_loc = extract_14 (inst); + fp_loc = hppa_extract_14 (inst); reg = inst_saves_fr (inst); if (reg >= 12 && reg <= 21) @@ -2690,10 +2161,6 @@ hppa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* The following gdbarch vector elements do not depend on the address size, or in any other gdbarch element previously set. */ set_gdbarch_skip_prologue (gdbarch, hppa_skip_prologue); - set_gdbarch_skip_trampoline_code (gdbarch, hppa_skip_trampoline_code); - set_gdbarch_in_solib_call_trampoline (gdbarch, hppa_in_solib_call_trampoline); - set_gdbarch_in_solib_return_trampoline (gdbarch, - hppa_in_solib_return_trampoline); set_gdbarch_inner_than (gdbarch, core_addr_greaterthan); set_gdbarch_sp_regnum (gdbarch, HPPA_SP_REGNUM); set_gdbarch_fp0_regnum (gdbarch, HPPA_FP0_REGNUM); diff --git a/gdb/hppa-tdep.h b/gdb/hppa-tdep.h index 80742f0f64..48ea353783 100644 --- a/gdb/hppa-tdep.h +++ b/gdb/hppa-tdep.h @@ -104,6 +104,8 @@ enum unwind_stub_types IMPORT_SHLIB = 12, }; +struct unwind_table_entry *find_unwind_entry (CORE_ADDR); + /* We use the objfile->obj_private pointer for two things: * 1. An unwind table; * @@ -134,4 +136,14 @@ struct hppa_objfile_private extern const struct objfile_data *hppa_objfile_priv_data; +int hppa_get_field (unsigned word, int from, int to); +int hppa_extract_5_load (unsigned int); +unsigned hppa_extract_5R_store (unsigned int); +unsigned hppa_extract_5r_store (unsigned int); +int hppa_extract_17 (unsigned int); +int hppa_extract_21 (unsigned); +int hppa_extract_14 (unsigned); +int hppa_low_sign_extend (unsigned int, unsigned int); +int hppa_sign_extend (unsigned int, unsigned int); + #endif /* HPPA_TDEP_H */