diff options
author | Randolph Chung <tausq@debian.org> | 2004-07-11 06:51:41 +0000 |
---|---|---|
committer | Randolph Chung <tausq@debian.org> | 2004-07-11 06:51:41 +0000 |
commit | c268433a1abf1a57285b863dcc7e251293001747 (patch) | |
tree | 0235b53f084d7954496e9533cbe57306fad2f15e /gdb/hppa-hpux-tdep.c | |
parent | bbb268c36c132cff58c0c4f3ad1a9c6b48579025 (diff) | |
download | gdb-c268433a1abf1a57285b863dcc7e251293001747.zip gdb-c268433a1abf1a57285b863dcc7e251293001747.tar.gz gdb-c268433a1abf1a57285b863dcc7e251293001747.tar.bz2 |
2004-07-10 Randolph Chung <tausq@debian.org>
* hppa-hpux-tdep.c (hppa_hpux_som_find_global_pointer): New
function.
(hppa_hpux_push_dummy_code): New function.
(hppa_hpux_init_abi): Set push_dummy_code and call_dummy_location.
Set find_global_pointer method.
Diffstat (limited to 'gdb/hppa-hpux-tdep.c')
-rw-r--r-- | gdb/hppa-hpux-tdep.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/gdb/hppa-hpux-tdep.c b/gdb/hppa-hpux-tdep.c index 8e8765a..d01607a 100644 --- a/gdb/hppa-hpux-tdep.c +++ b/gdb/hppa-hpux-tdep.c @@ -1204,6 +1204,194 @@ hppa_hpux_sigtramp_unwind_sniffer (struct frame_info *next_frame) return NULL; } +static CORE_ADDR +hppa_hpux_som_find_global_pointer (struct value *function) +{ + CORE_ADDR faddr; + + faddr = value_as_address (function); + + /* Is this a plabel? If so, dereference it to get the gp value. */ + if (faddr & 2) + { + int status; + char buf[4]; + + faddr &= ~3; + + status = target_read_memory (faddr + 4, buf, sizeof (buf)); + if (status == 0) + return extract_unsigned_integer (buf, sizeof (buf)); + } + + return som_solib_get_got_by_pc (faddr); +} + +static CORE_ADDR +hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, + CORE_ADDR funcaddr, int using_gcc, + struct value **args, int nargs, + struct type *value_type, + CORE_ADDR *real_pc, CORE_ADDR *bp_addr) +{ + /* FIXME: tausq/2004-06-09: This needs much more testing. It is broken + for pa64, but we should be able to get it to work with a little bit + of work. gdb-6.1 has a lot of code to handle various cases; I've tried to + simplify it and avoid compile-time conditionals. */ + + /* On HPUX, functions in the main executable and in libraries can be located + in different spaces. In order for us to be able to select the right + space for the function call, we need to go through an instruction seqeunce + to select the right space for the target function, call it, and then + restore the space on return. + + There are two helper routines that can be used for this task -- if + an application is linked with gcc, it will contain a __gcc_plt_call + helper function. __gcc_plt_call, when passed the entry point of an + import stub, will do the necessary space setting/restoration for the + target function. + + For programs that are compiled/linked with the HP compiler, a similar + function called __d_plt_call exists; __d_plt_call expects a PLABEL instead + of an import stub as an argument. + + /* *INDENT-OFF* */ + To summarize, the call flow is: + current function -> dummy frame -> __gcc_plt_call (import stub) + -> target function + or + current function -> dummy frame -> __d_plt_call (plabel) + -> target function + /* *INDENT-ON* */ + + In general the "funcaddr" argument passed to push_dummy_code is the actual + entry point of the target function. For __gcc_plt_call, we need to + locate the import stub for the corresponding function. Failing that, + we construct a dummy "import stub" on the stack to pass as an argument. + For __d_plt_call, we similarly synthesize a PLABEL on the stack to + pass to the helper function. + + An additional twist is that, in order for us to restore the space register + to its starting state, we need __gcc_plt_call/__d_plt_call to return + to the instruction where we started the call. However, if we put + the breakpoint there, gdb will complain because it will find two + frames on the stack with the same (sp, pc) (with the dummy frame in + between). Currently, we set the return pointer to (pc - 4) of the + current function. FIXME: This is not an ideal solution; possibly if the + current pc is at the beginning of a page, this will cause a page fault. + Need to understand this better and figure out a better way to fix it. */ + + struct minimal_symbol *sym; + + /* Nonzero if we will use GCC's PLT call routine. This routine must be + passed an import stub, not a PLABEL. It is also necessary to get %r19 + before performing the call. (This is done by push_dummy_call.) */ + int use_gcc_plt_call = 1; + + /* See if __gcc_plt_call is available; if not we will use the HP version + instead. */ + sym = lookup_minimal_symbol ("__gcc_plt_call", NULL, NULL); + if (sym == NULL) + use_gcc_plt_call = 0; + + /* If using __gcc_plt_call, we need to make sure we pass in an import + stub. funcaddr can be pointing to an export stub or a real function, + so we try to resolve it to the import stub. */ + if (use_gcc_plt_call) + { + struct objfile *objfile; + struct minimal_symbol *funsym, *stubsym; + CORE_ADDR stubaddr = 0; + + funsym = lookup_minimal_symbol_by_pc (funcaddr); + if (!funsym) + error ("Unable to find symbol for target function.\n"); + + ALL_OBJFILES (objfile) + { + stubsym = lookup_minimal_symbol_solib_trampoline + (SYMBOL_LINKAGE_NAME (funsym), objfile); + + if (stubsym) + { + struct unwind_table_entry *u; + + u = find_unwind_entry (SYMBOL_VALUE (stubsym)); + if (u == NULL + || (u->stub_unwind.stub_type != IMPORT + && u->stub_unwind.stub_type != IMPORT_SHLIB)) + continue; + + stubaddr = SYMBOL_VALUE (stubsym); + + /* If we found an IMPORT stub, then we can stop searching; + if we found an IMPORT_SHLIB, we want to continue the search + in the hopes that we will find an IMPORT stub. */ + if (u->stub_unwind.stub_type == IMPORT) + break; + } + } + + if (stubaddr != 0) + { + /* Argument to __gcc_plt_call is passed in r22. */ + regcache_cooked_write_unsigned (current_regcache, 22, stubaddr); + } + else + { + /* No import stub found; let's synthesize one. */ + + /* ldsid %r21, %r1 */ + write_memory_unsigned_integer (sp, 4, 0x02a010a1); + /* mtsp %r1,%sr0 */ + write_memory_unsigned_integer (sp + 4, 4, 0x00011820); + /* be 0(%sr0, %r21) */ + write_memory_unsigned_integer (sp + 8, 4, 0xe2a00000); + /* nop */ + write_memory_unsigned_integer (sp + 12, 4, 0x08000240); + + regcache_cooked_write_unsigned (current_regcache, 21, funcaddr); + regcache_cooked_write_unsigned (current_regcache, 22, sp); + } + + /* We set the breakpoint address and r31 to (close to) where the current + pc is; when __gcc_plt_call returns, it will restore pcsqh to the + current value based on this. The -4 is needed for frame unwinding + to work properly -- we need to land in a different function than + the current function. */ + *bp_addr = (read_register (HPPA_PCOQ_HEAD_REGNUM) & ~3) - 4; + regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr); + + /* Continue from __gcc_plt_call. */ + *real_pc = SYMBOL_VALUE (sym); + } + else + { + unsigned int gp; + + /* Use __d_plt_call as a fallback; __d_plt_call expects to be called + with a plabel, so we need to build one. */ + + sym = lookup_minimal_symbol ("__d_plt_call", NULL, NULL); + if (sym == NULL) + error("Can't find an address for __d_plt_call or __gcc_plt_call " + "trampoline\nSuggest linking executable with -g or compiling " + "with gcc."); + + gp = gdbarch_tdep (gdbarch)->find_global_pointer (funcaddr); + write_memory_unsigned_integer (sp, 4, funcaddr); + write_memory_unsigned_integer (sp + 4, 4, gp); + + /* plabel is passed in r22 */ + regcache_cooked_write_unsigned (current_regcache, 22, sp); + } + + /* Pushed one stack frame, which has to be 64-byte aligned. */ + sp += 64; + + return sp; +} + static void hppa_hpux_inferior_created (struct target_ops *objfile, int from_tty) { @@ -1229,6 +1417,9 @@ hppa_hpux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) hppa_hpux_in_solib_return_trampoline); set_gdbarch_skip_trampoline_code (gdbarch, hppa_hpux_skip_trampoline_code); + set_gdbarch_push_dummy_code (gdbarch, hppa_hpux_push_dummy_code); + set_gdbarch_call_dummy_location (gdbarch, ON_STACK); + frame_unwind_append_sniffer (gdbarch, hppa_hpux_sigtramp_unwind_sniffer); observer_attach_inferior_created (hppa_hpux_inferior_created); @@ -1240,6 +1431,8 @@ hppa_hpux_som_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); tdep->is_elf = 0; + + tdep->find_global_pointer = hppa_hpux_som_find_global_pointer; hppa_hpux_init_abi (info, gdbarch); } |