2004-04-22 Randolph Chung <tausq@debian.org>
* 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.
This commit is contained in:
parent
369aa52037
commit
abc485a155
@ -1,3 +1,27 @@
|
||||
2004-04-22 Randolph Chung <tausq@debian.org>
|
||||
|
||||
* 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 <tausq@debian.org>
|
||||
|
||||
* hppa-tdep.c (hppa_debug): New variable.
|
||||
|
@ -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
|
||||
|
625
gdb/hppa-tdep.c
625
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);
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user