From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 20559 invoked by alias); 13 Dec 2004 17:30:31 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 19865 invoked from network); 13 Dec 2004 17:29:53 -0000 Received: from unknown (HELO arwen.tausq.org) (64.81.244.109) by sourceware.org with SMTP; 13 Dec 2004 17:29:53 -0000 Received: by arwen.tausq.org (Postfix, from userid 1000) id 5131F6BE6F; Mon, 13 Dec 2004 09:29:52 -0800 (PST) Date: Mon, 13 Dec 2004 17:39:00 -0000 From: Randolph Chung To: gdb-patches@sources.redhat.com Subject: [rfc/rfa] Dummy calls for hppa-hpux Message-ID: <20041213172952.GO29171@tausq.org> Reply-To: Randolph Chung Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="8GpibOaaTibBMecb" Content-Disposition: inline X-GPG: for GPG key, see http://www.tausq.org/gpg.txt User-Agent: Mutt/1.5.6+20040722i X-SW-Source: 2004-12/txt/msg00360.txt.bz2 --8GpibOaaTibBMecb Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-length: 15602 this is an implementation of the scheme discussed in: http://sources.redhat.com/ml/gdb-patches/2004-12/msg00269.html the attached patch adds support for hppa64-hpux dummy calls, as well as fixes some issues with the current hppa-hpux dummy call implementation. In particular, nested dummy calls will now work, as well as interspace calls (in common situations). the patch notes several FIXMEs for cases where the logic will fail. There are several corner cases which don't work yet. I will need to write some testcases for those cases. callfuncs.exp results are: hppa2.0w-hp-hpux11.11 # of expected passes 93 # of unexpected failures 9 # of unknown successes 3 hppa64-hp-hpux11.11 # of expected passes 35 # of unexpected failures 64 # of known failures 3 the large number of failures in hppa64-hp-hpux11.11 are due to a cascading failure early on in the test (we have some problems passing function pointers into functions); however, since basic infcalls work now, i suspect a much larger portion of gdb testcases will benefit. the patch is somewhat large and some parts of it are difficult to read because of what "diff" did to it. i've duplicated the interesting bits of code below for easier reviewing. comments? scary code? :-) ==== excerpt from hppa-hpux-tdep.c, read from bottom up ==== static unsigned int ldsid_pattern[] = { 0x000010a0, /* ldsid (rX),rY */ 0x00001820, /* mtsp rY,sr0 */ 0xe0000000 /* be,n (sr0,rX) */ }; static CORE_ADDR hppa_hpux_search_pattern (CORE_ADDR start, CORE_ADDR end, unsigned int *patterns, int count) { unsigned int *buf; int offset, i; int region, insns; region = end - start + 4; insns = region / 4; buf = (unsigned int *) alloca (region); read_memory (start, (char *) buf, region); for (i = 0; i < insns; i++) buf[i] = extract_unsigned_integer (&buf[i], 4); for (offset = 0; offset <= insns - count; offset++) { for (i = 0; i < count; i++) { if ((buf[offset + i] & patterns[i]) != patterns[i]) break; } if (i == count) break; } if (offset <= insns - count) return start + offset * 4; else return 0; } static CORE_ADDR hppa32_hpux_search_dummy_call_sequence (struct gdbarch *gdbarch, CORE_ADDR pc, int *argreg) { struct objfile *obj; struct obj_section *sec; struct hppa_objfile_private *priv; struct frame_info *frame; struct unwind_table_entry *u; CORE_ADDR addr, rp; char buf[4]; unsigned int insn; sec = find_pc_section (pc); obj = sec->objfile; priv = objfile_data (obj, hppa_objfile_priv_data); if (!priv) priv = hppa_init_objfile_priv_data (obj); if (!priv) error ("Internal error creating objfile private data.\n"); /* Use the cached value if we have one. */ if (priv->dummy_call_sequence_addr != 0) { *argreg = priv->dummy_call_sequence_reg; return priv->dummy_call_sequence_addr; } /* First try a heuristic; if we are in a shared library call, our return pointer is likely to point at an export stub. */ frame = get_current_frame (); rp = frame_unwind_register_unsigned (frame, 2); u = find_unwind_entry (rp); if (u && u->stub_unwind.stub_type == EXPORT) { addr = hppa_hpux_search_pattern (u->region_start, u->region_end, ldsid_pattern, ARRAY_SIZE (ldsid_pattern)); if (addr) goto found_pattern; } /* Next thing to try is to look for an export stub. */ if (priv->unwind_info) { int i; for (i = 0; i < priv->unwind_info->last; i++) { struct unwind_table_entry *u; u = &priv->unwind_info->table[i]; if (u->stub_unwind.stub_type == EXPORT) { addr = hppa_hpux_search_pattern (u->region_start, u->region_end, ldsid_pattern, ARRAY_SIZE (ldsid_pattern)); if (addr) { goto found_pattern; } } } } /* Finally, if this is the main executable, try to locate a sequence from noshlibs */ addr = hppa_symbol_address ("noshlibs"); sec = find_pc_section (addr); if (sec && sec->objfile == obj) { CORE_ADDR start, end; find_pc_partial_function (addr, NULL, &start, &end); if (start != 0 && end != 0) { addr = hppa_hpux_search_pattern (start, end, ldsid_pattern, ARRAY_SIZE (ldsid_pattern)); if (addr) goto found_pattern; } } /* Can't find a suitable sequence. */ return 0; found_pattern: target_read_memory (addr, buf, sizeof (buf)); insn = extract_unsigned_integer (buf, sizeof (buf)); priv->dummy_call_sequence_addr = addr; priv->dummy_call_sequence_reg = (insn >> 21) & 0x1f; *argreg = priv->dummy_call_sequence_reg; return priv->dummy_call_sequence_addr; } static CORE_ADDR hppa64_hpux_search_dummy_call_sequence (struct gdbarch *gdbarch, CORE_ADDR pc, int *argreg) { struct objfile *obj; struct obj_section *sec; struct hppa_objfile_private *priv; CORE_ADDR addr; struct minimal_symbol *msym; int i; sec = find_pc_section (pc); obj = sec->objfile; priv = objfile_data (obj, hppa_objfile_priv_data); if (!priv) priv = hppa_init_objfile_priv_data (obj); if (!priv) error ("Internal error creating objfile private data.\n"); /* Use the cached value if we have one. */ if (priv->dummy_call_sequence_addr != 0) { *argreg = priv->dummy_call_sequence_reg; return priv->dummy_call_sequence_addr; } /* FIXME: Without stub unwind information, locating a suitable sequence is fairly difficult. For now, we implement a very naive and inefficient scheme; try to read in blocks of code, and look for a "bve,n (rp)" instruction. These are likely to occur at the end of functions, so we only look at the last two instructions of each function. */ for (i = 0, msym = obj->msymbols; i < obj->minimal_symbol_count; i++, msym++) { CORE_ADDR begin, end; char *name; unsigned int insns[2]; int offset; find_pc_partial_function (SYMBOL_VALUE_ADDRESS (msym), &name, &begin, &end); if (*name == 0 || begin == 0 || end == 0) continue; if (target_read_memory (end - sizeof (insns), (char *)insns, sizeof (insns)) == 0) { for (offset = 0; offset < ARRAY_SIZE (insns); offset++) { unsigned int insn; insn = extract_unsigned_integer (&insns[offset], 4); if (insn == 0xe840d002) /* bve,n (rp) */ { addr = (end - sizeof (insns)) + (offset * 4); goto found_pattern; } } } } /* Can't find a suitable sequence. */ return 0; found_pattern: priv->dummy_call_sequence_addr = addr; /* Right now we only look for a "bve,l (rp)" sequence, so the register is always HPPA_RP_REGNUM. */ priv->dummy_call_sequence_reg = HPPA_RP_REGNUM; *argreg = priv->dummy_call_sequence_reg; return priv->dummy_call_sequence_addr; } static CORE_ADDR hppa_hpux_find_import_stub_for_addr (CORE_ADDR funcaddr) { struct objfile *objfile; struct minimal_symbol *funsym, *stubsym; CORE_ADDR stubaddr; funsym = lookup_minimal_symbol_by_pc (funcaddr); stubaddr = 0; 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; } } return stubaddr; } static int hppa_hpux_sr_for_addr (CORE_ADDR addr) { int sr; /* The space register to use is encoded in the top 2 bits of the address. */ sr = addr >> (gdbarch_tdep (current_gdbarch)->bytes_per_address * 8 - 2); return sr + 4; } static CORE_ADDR hppa_hpux_find_dummy_bpaddr (CORE_ADDR addr) { /* In order for us to restore the space register to its starting state, we need the dummy trampoline to return to the an instruction address in the same space as where we started the call. We used to place the breakpoint near the current pc, however, this breaks nested dummy calls as the nested call will hit the breakpoint address and terminate prematurely. Instead, we try to look for an address in the same space to put the breakpoint. This is similar in spirit to putting the breakpoint at the "entry point" of an executable. */ struct obj_section *sec; struct unwind_table_entry *u; struct minimal_symbol *msym; CORE_ADDR func; int i; sec = find_pc_section (addr); if (sec) { /* First try the lowest address in the section; we can use it as long as it is "regular" code (i.e. not a stub) */ u = find_unwind_entry (sec->addr); if (!u || u->stub_unwind.stub_type == 0) return sec->addr; /* Otherwise, we need to find a symbol for a regular function. We do this by walking the list of msymbols in the objfile. The symbol we find should not be the same as the function that was passed in. */ /* FIXME: this is broken, because we can find a function that will be called by the dummy call target function, which will still not work. */ find_pc_partial_function (addr, NULL, &func, NULL); for (i = 0, msym = sec->objfile->msymbols; i < sec->objfile->minimal_symbol_count; i++, msym++) { u = find_unwind_entry (SYMBOL_VALUE_ADDRESS (msym)); if (func != SYMBOL_VALUE_ADDRESS (msym) && (!u || u->stub_unwind.stub_type == 0)) return SYMBOL_VALUE_ADDRESS (msym); } } warning ("Cannot find suitable address to place dummy breakpoint; nested " "calls may fail.\n"); return addr - 4; } 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) { CORE_ADDR pc, stubaddr; int argreg; pc = read_pc (); /* Note: we don't want to pass a function descriptor here; push_dummy_call fills in the PIC register for us. */ funcaddr = gdbarch_convert_from_func_ptr_addr (gdbarch, funcaddr, NULL); /* The simple case is where we call a function in the same space that we are currently in; in that case we don't really need to do anything. */ if (hppa_hpux_sr_for_addr (pc) == hppa_hpux_sr_for_addr (funcaddr)) { /* Intraspace call. */ *bp_addr = hppa_hpux_find_dummy_bpaddr (pc); *real_pc = funcaddr; regcache_cooked_write_unsigned (current_regcache, HPPA_RP_REGNUM, *bp_addr); return sp; } /* In order to make an interspace call, we need to go through a stub. gcc supplies an appropriate stub called "__gcc_plt_call", however, if an application is compiled with HP compilers then this stub is not available. We used to fallback to "__d_plt_call", however that stub is not entirely useful for us because it doesn't do an interspace return back to the caller. Also, on hppa64-hpux, there is no __gcc_plt_call available. In order to keep the code uniform, we instead don't use either of these stubs, but instead write our own onto the stack. A problem arises since the stack is located in a different space than code, so in order to branch to a stack stub, we will need to do an interspace branch. Previous versions of gdb did this by modifying code at the current pc and doing single-stepping to set the pcsq. Since this is highly undesirable, we use a different scheme: All we really need to do the branch to the stub is a short instruction sequence like this: PA1.1: ldsid (rX),r1 mtsp r1,sr0 be,n (sr0,rX) PA2.0: bve,n (sr0,rX) Instead of writing these sequences ourselves, we can find it in the instruction stream that belongs to the current space. While this seems difficult at first, we are actually guaranteed to find the sequences in several places: For 32-bit code: - in export stubs for shared libraries - in the "noshlibs" routine in the main module For 64-bit code: - at the end of each "regular" function We cache the address of these sequences in the objfile's private data since these operations can potentially be quite expensive. So, what we do is: - write a stack trampoline - look for a suitable instruction sequence in the current space - point the sequence at the trampoline - set the return address of the trampoline to the current space (see hppa_hpux_find_dummy_call_bpaddr) - set the continuing address of the "dummy code" as the sequence. */ if (IS_32BIT_TARGET (gdbarch)) { static unsigned int hppa32_tramp[] = { 0x0fdf1291, /* stw r31,-8(,sp) */ 0x02c010a1, /* ldsid (,r22),r1 */ 0x00011820, /* mtsp r1,sr0 */ 0xe6c00000, /* be,l 0(sr0,r22),%sr0,%r31 */ 0x081f0242, /* copy r31,rp */ 0x0fd11082, /* ldw -8(,sp),rp */ 0x004010a1, /* ldsid (,rp),r1 */ 0x00011820, /* mtsp r1,sr0 */ 0xe0400000, /* be 0(sr0,rp) */ 0x08000240 /* nop */ }; /* for hppa32, we must call the function through a stub so that on return it can return to the space of our trampoline. */ stubaddr = hppa_hpux_find_import_stub_for_addr (funcaddr); if (stubaddr == 0) error ("Cannot call external function not referenced by application " "(no import stub).\n"); regcache_cooked_write_unsigned (current_regcache, 22, stubaddr); write_memory (sp, (char *)&hppa32_tramp, sizeof (hppa32_tramp)); *bp_addr = hppa_hpux_find_dummy_bpaddr (pc); regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr); *real_pc = hppa32_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg); if (*real_pc == 0) error ("Cannot make interspace call from here.\n"); regcache_cooked_write_unsigned (current_regcache, argreg, sp); sp += sizeof (hppa32_tramp); } else { static unsigned int hppa64_tramp[] = { 0xeac0f000, /* bve,l (r22),%r2 */ 0x0fdf12d1, /* std r31,-8(,sp) */ 0x0fd110c2, /* ldd -8(,sp),rp */ 0xe840d002, /* bve,n (rp) */ 0x08000240 /* nop */ }; /* for hppa64, we don't need to call through a stub; all functions return via a bve. */ regcache_cooked_write_unsigned (current_regcache, 22, funcaddr); write_memory (sp, (char *)&hppa64_tramp, sizeof (hppa64_tramp)); *bp_addr = pc - 4; regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr); *real_pc = hppa64_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg); if (*real_pc == 0) error ("Cannot make interspace call from here.\n"); regcache_cooked_write_unsigned (current_regcache, argreg, sp); sp += sizeof (hppa64_tramp); } sp = gdbarch_frame_align (gdbarch, sp); return sp; } ==== end of excerpt ==== complete patch is attached. randolph -- Randolph Chung Debian GNU/Linux Developer, hppa/ia64 ports http://www.tausq.org/ --8GpibOaaTibBMecb Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="dummycall.diff" Content-length: 28855 2004-12-13 Randolph Chung * hppa-hpux-tdep.c (IS_32BIT_TARGET): New. (in_opd_section): New. (hppa32_hpux_find_global_pointer): Rename from hppa_hpux_som_find_global_pointer. (hppa64_hpux_find_global_pointer): New. (ldsid_pattern): New. (hppa_hpux_search_pattern): New. (hppa32_hpux_search_dummy_call_sequence): New. (hppa64_hpux_search_dummy_call_sequence): New. (hppa_hpux_find_import_stub_for_addr): New. (hppa_hpux_sr_for_addr): New. (hppa_hpux_find_dummy_bpaddr): New. (hppa_hpux_init_abi): Use IS_32BIT_TARGET predicate. (hppa_hpux_som_init_abi): Set find_global_pointer method to hppa32_hpux_find_global_pointer instead of hppa_hpux_som_find_global_pointer. (hppa_hpux_elf_init_abi): Set find_global_pointer method. * hppa-tdep.c (hppa_init_objfile_priv_data): New. (read_unwind_info): Use function to initialize objfile-private data. (hppa32_push_dummy_call): Don't automatically set the RP if there is a push_dummy_code method. (hppa64_push_dummy_call): Retrieve and set the global pointer. Don't automatically set the RP if there is a push_dummy_code method. * hppa-tdep.h (hppa_objfile_private): Add dummy_call_sequence_reg and dummy_call_sequence_addr members. (hppa_init_objfile_priv_data): New prototype. Index: hppa-hpux-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/hppa-hpux-tdep.c,v retrieving revision 1.35 diff -u -p -r1.35 hppa-hpux-tdep.c --- hppa-hpux-tdep.c 13 Dec 2004 08:47:07 -0000 1.35 +++ hppa-hpux-tdep.c 13 Dec 2004 17:07:00 -0000 @@ -45,6 +45,9 @@ #define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER) #endif +#define IS_32BIT_TARGET(_gdbarch) \ + ((gdbarch_tdep (_gdbarch))->bytes_per_address == 4) + /* Forward declarations. */ extern void _initialize_hppa_hpux_tdep (void); extern initialize_file_ftype _initialize_hppa_hpux_tdep; @@ -57,6 +60,20 @@ typedef struct } args_for_find_stub; +static int +in_opd_section (CORE_ADDR pc) +{ + struct obj_section *s; + int retval = 0; + + s = find_pc_section (pc); + + retval = (s != NULL + && s->the_bfd_section->name != NULL + && strcmp (s->the_bfd_section->name, ".opd") == 0); + return (retval); +} + /* 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 @@ -1229,7 +1248,7 @@ hppa_hpux_sigtramp_unwind_sniffer (struc } static CORE_ADDR -hppa_hpux_som_find_global_pointer (struct value *function) +hppa32_hpux_find_global_pointer (struct value *function) { CORE_ADDR faddr; @@ -1252,169 +1271,484 @@ hppa_hpux_som_find_global_pointer (struc } 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) +hppa64_hpux_find_global_pointer (struct value *function) { - /* 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"); + CORE_ADDR faddr; + char buf[32]; - ALL_OBJFILES (objfile) + faddr = value_as_address (function); + + if (in_opd_section (faddr)) + { + target_read_memory (faddr, buf, sizeof (buf)); + return extract_unsigned_integer (&buf[24], 8); + } + else + { + return gdbarch_tdep (current_gdbarch)->solib_get_got_by_pc (faddr); + } +} + +static unsigned int ldsid_pattern[] = { + 0x000010a0, /* ldsid (rX),rY */ + 0x00001820, /* mtsp rY,sr0 */ + 0xe0000000 /* be,n (sr0,rX) */ +}; + +static CORE_ADDR +hppa_hpux_search_pattern (CORE_ADDR start, CORE_ADDR end, + unsigned int *patterns, int count) +{ + unsigned int *buf; + int offset, i; + int region, insns; + + region = end - start + 4; + insns = region / 4; + buf = (unsigned int *) alloca (region); + + read_memory (start, (char *) buf, region); + + for (i = 0; i < insns; i++) + buf[i] = extract_unsigned_integer (&buf[i], 4); + + for (offset = 0; offset <= insns - count; offset++) + { + for (i = 0; i < count; i++) { - stubsym = lookup_minimal_symbol_solib_trampoline - (SYMBOL_LINKAGE_NAME (funsym), objfile); + if ((buf[offset + i] & patterns[i]) != patterns[i]) + break; + } + if (i == count) + break; + } + + if (offset <= insns - count) + return start + offset * 4; + else + return 0; +} - if (stubsym) - { - struct unwind_table_entry *u; +static CORE_ADDR +hppa32_hpux_search_dummy_call_sequence (struct gdbarch *gdbarch, CORE_ADDR pc, + int *argreg) +{ + struct objfile *obj; + struct obj_section *sec; + struct hppa_objfile_private *priv; + struct frame_info *frame; + struct unwind_table_entry *u; + CORE_ADDR addr, rp; + char buf[4]; + unsigned int insn; + + sec = find_pc_section (pc); + obj = sec->objfile; + priv = objfile_data (obj, hppa_objfile_priv_data); - 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 (!priv) + priv = hppa_init_objfile_priv_data (obj); + if (!priv) + error ("Internal error creating objfile private data.\n"); + + /* Use the cached value if we have one. */ + if (priv->dummy_call_sequence_addr != 0) + { + *argreg = priv->dummy_call_sequence_reg; + return priv->dummy_call_sequence_addr; + } + + /* First try a heuristic; if we are in a shared library call, our return + pointer is likely to point at an export stub. */ + frame = get_current_frame (); + rp = frame_unwind_register_unsigned (frame, 2); + u = find_unwind_entry (rp); + if (u && u->stub_unwind.stub_type == EXPORT) + { + addr = hppa_hpux_search_pattern (u->region_start, u->region_end, + ldsid_pattern, + ARRAY_SIZE (ldsid_pattern)); + if (addr) + goto found_pattern; + } + + /* Next thing to try is to look for an export stub. */ + if (priv->unwind_info) + { + int i; + + for (i = 0; i < priv->unwind_info->last; i++) + { + struct unwind_table_entry *u; + u = &priv->unwind_info->table[i]; + if (u->stub_unwind.stub_type == EXPORT) + { + addr = hppa_hpux_search_pattern (u->region_start, u->region_end, + ldsid_pattern, + ARRAY_SIZE (ldsid_pattern)); + if (addr) + { + goto found_pattern; + } } } + } + + /* Finally, if this is the main executable, try to locate a sequence + from noshlibs */ + addr = hppa_symbol_address ("noshlibs"); + sec = find_pc_section (addr); + + if (sec && sec->objfile == obj) + { + CORE_ADDR start, end; - if (stubaddr != 0) + find_pc_partial_function (addr, NULL, &start, &end); + if (start != 0 && end != 0) { - /* Argument to __gcc_plt_call is passed in r22. */ - regcache_cooked_write_unsigned (current_regcache, 22, stubaddr); + addr = hppa_hpux_search_pattern (start, end, ldsid_pattern, + ARRAY_SIZE (ldsid_pattern)); + if (addr) + goto found_pattern; } - else + } + + /* Can't find a suitable sequence. */ + return 0; + +found_pattern: + target_read_memory (addr, buf, sizeof (buf)); + insn = extract_unsigned_integer (buf, sizeof (buf)); + priv->dummy_call_sequence_addr = addr; + priv->dummy_call_sequence_reg = (insn >> 21) & 0x1f; + + *argreg = priv->dummy_call_sequence_reg; + return priv->dummy_call_sequence_addr; +} + +static CORE_ADDR +hppa64_hpux_search_dummy_call_sequence (struct gdbarch *gdbarch, CORE_ADDR pc, + int *argreg) +{ + struct objfile *obj; + struct obj_section *sec; + struct hppa_objfile_private *priv; + CORE_ADDR addr; + struct minimal_symbol *msym; + int i; + + sec = find_pc_section (pc); + obj = sec->objfile; + priv = objfile_data (obj, hppa_objfile_priv_data); + + if (!priv) + priv = hppa_init_objfile_priv_data (obj); + if (!priv) + error ("Internal error creating objfile private data.\n"); + + /* Use the cached value if we have one. */ + if (priv->dummy_call_sequence_addr != 0) + { + *argreg = priv->dummy_call_sequence_reg; + return priv->dummy_call_sequence_addr; + } + + /* FIXME: Without stub unwind information, locating a suitable sequence is + fairly difficult. For now, we implement a very naive and inefficient + scheme; try to read in blocks of code, and look for a "bve,n (rp)" + instruction. These are likely to occur at the end of functions, so + we only look at the last two instructions of each function. */ + for (i = 0, msym = obj->msymbols; i < obj->minimal_symbol_count; i++, msym++) + { + CORE_ADDR begin, end; + char *name; + unsigned int insns[2]; + int offset; + + find_pc_partial_function (SYMBOL_VALUE_ADDRESS (msym), &name, + &begin, &end); + + if (*name == 0 || begin == 0 || end == 0) + continue; + + if (target_read_memory (end - sizeof (insns), (char *)insns, sizeof (insns)) == 0) { - /* No import stub found; let's synthesize one. */ + for (offset = 0; offset < ARRAY_SIZE (insns); offset++) + { + unsigned int insn; + + insn = extract_unsigned_integer (&insns[offset], 4); + if (insn == 0xe840d002) /* bve,n (rp) */ + { + addr = (end - sizeof (insns)) + (offset * 4); + goto found_pattern; + } + } + } + } + + /* Can't find a suitable sequence. */ + return 0; + +found_pattern: + priv->dummy_call_sequence_addr = addr; + /* Right now we only look for a "bve,l (rp)" sequence, so the register is + always HPPA_RP_REGNUM. */ + priv->dummy_call_sequence_reg = HPPA_RP_REGNUM; + + *argreg = priv->dummy_call_sequence_reg; + return priv->dummy_call_sequence_addr; +} + +static CORE_ADDR +hppa_hpux_find_import_stub_for_addr (CORE_ADDR funcaddr) +{ + struct objfile *objfile; + struct minimal_symbol *funsym, *stubsym; + CORE_ADDR stubaddr; + + funsym = lookup_minimal_symbol_by_pc (funcaddr); + stubaddr = 0; + + 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; + } + } + + return stubaddr; +} + +static int +hppa_hpux_sr_for_addr (CORE_ADDR addr) +{ + int sr; + /* The space register to use is encoded in the top 2 bits of the address. */ + sr = addr >> (gdbarch_tdep (current_gdbarch)->bytes_per_address * 8 - 2); + return sr + 4; +} + +static CORE_ADDR +hppa_hpux_find_dummy_bpaddr (CORE_ADDR addr) +{ + /* In order for us to restore the space register to its starting state, + we need the dummy trampoline to return to the an instruction address in + the same space as where we started the call. We used to place the + breakpoint near the current pc, however, this breaks nested dummy calls + as the nested call will hit the breakpoint address and terminate + prematurely. Instead, we try to look for an address in the same space to + put the breakpoint. + + This is similar in spirit to putting the breakpoint at the "entry point" + of an executable. */ + + struct obj_section *sec; + struct unwind_table_entry *u; + struct minimal_symbol *msym; + CORE_ADDR func; + int i; + + sec = find_pc_section (addr); + if (sec) + { + /* First try the lowest address in the section; we can use it as long + as it is "regular" code (i.e. not a stub) */ + u = find_unwind_entry (sec->addr); + if (!u || u->stub_unwind.stub_type == 0) + return sec->addr; + + /* Otherwise, we need to find a symbol for a regular function. We + do this by walking the list of msymbols in the objfile. The symbol + we find should not be the same as the function that was passed in. */ + + /* FIXME: this is broken, because we can find a function that will be + called by the dummy call target function, which will still not + work. */ + + find_pc_partial_function (addr, NULL, &func, NULL); + for (i = 0, msym = sec->objfile->msymbols; + i < sec->objfile->minimal_symbol_count; + i++, msym++) + { + u = find_unwind_entry (SYMBOL_VALUE_ADDRESS (msym)); + if (func != SYMBOL_VALUE_ADDRESS (msym) + && (!u || u->stub_unwind.stub_type == 0)) + return SYMBOL_VALUE_ADDRESS (msym); + } + } + + warning ("Cannot find suitable address to place dummy breakpoint; nested " + "calls may fail.\n"); + return addr - 4; +} + +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) +{ + CORE_ADDR pc, stubaddr; + int argreg; + + pc = read_pc (); - /* 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; + /* Note: we don't want to pass a function descriptor here; push_dummy_call + fills in the PIC register for us. */ + funcaddr = gdbarch_convert_from_func_ptr_addr (gdbarch, funcaddr, NULL); + + /* The simple case is where we call a function in the same space that we are + currently in; in that case we don't really need to do anything. */ + if (hppa_hpux_sr_for_addr (pc) == hppa_hpux_sr_for_addr (funcaddr)) + { + /* Intraspace call. */ + *bp_addr = hppa_hpux_find_dummy_bpaddr (pc); + *real_pc = funcaddr; + regcache_cooked_write_unsigned (current_regcache, HPPA_RP_REGNUM, *bp_addr); + + return sp; + } + + /* In order to make an interspace call, we need to go through a stub. + gcc supplies an appropriate stub called "__gcc_plt_call", however, if + an application is compiled with HP compilers then this stub is not + available. We used to fallback to "__d_plt_call", however that stub + is not entirely useful for us because it doesn't do an interspace + return back to the caller. Also, on hppa64-hpux, there is no + __gcc_plt_call available. In order to keep the code uniform, we + instead don't use either of these stubs, but instead write our own + onto the stack. + + A problem arises since the stack is located in a different space than + code, so in order to branch to a stack stub, we will need to do an + interspace branch. Previous versions of gdb did this by modifying code + at the current pc and doing single-stepping to set the pcsq. Since this + is highly undesirable, we use a different scheme: + + All we really need to do the branch to the stub is a short instruction + sequence like this: + + PA1.1: + ldsid (rX),r1 + mtsp r1,sr0 + be,n (sr0,rX) + + PA2.0: + bve,n (sr0,rX) + + Instead of writing these sequences ourselves, we can find it in + the instruction stream that belongs to the current space. While this + seems difficult at first, we are actually guaranteed to find the sequences + in several places: + + For 32-bit code: + - in export stubs for shared libraries + - in the "noshlibs" routine in the main module + + For 64-bit code: + - at the end of each "regular" function + + We cache the address of these sequences in the objfile's private data + since these operations can potentially be quite expensive. + + So, what we do is: + - write a stack trampoline + - look for a suitable instruction sequence in the current space + - point the sequence at the trampoline + - set the return address of the trampoline to the current space + (see hppa_hpux_find_dummy_call_bpaddr) + - set the continuing address of the "dummy code" as the sequence. + +*/ + + if (IS_32BIT_TARGET (gdbarch)) + { + static unsigned int hppa32_tramp[] = { + 0x0fdf1291, /* stw r31,-8(,sp) */ + 0x02c010a1, /* ldsid (,r22),r1 */ + 0x00011820, /* mtsp r1,sr0 */ + 0xe6c00000, /* be,l 0(sr0,r22),%sr0,%r31 */ + 0x081f0242, /* copy r31,rp */ + 0x0fd11082, /* ldw -8(,sp),rp */ + 0x004010a1, /* ldsid (,rp),r1 */ + 0x00011820, /* mtsp r1,sr0 */ + 0xe0400000, /* be 0(sr0,rp) */ + 0x08000240 /* nop */ + }; + + /* for hppa32, we must call the function through a stub so that on + return it can return to the space of our trampoline. */ + stubaddr = hppa_hpux_find_import_stub_for_addr (funcaddr); + if (stubaddr == 0) + error ("Cannot call external function not referenced by application " + "(no import stub).\n"); + regcache_cooked_write_unsigned (current_regcache, 22, stubaddr); + + write_memory (sp, (char *)&hppa32_tramp, sizeof (hppa32_tramp)); + + *bp_addr = hppa_hpux_find_dummy_bpaddr (pc); regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr); - /* Continue from __gcc_plt_call. */ - *real_pc = SYMBOL_VALUE (sym); + *real_pc = hppa32_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg); + if (*real_pc == 0) + error ("Cannot make interspace call from here.\n"); + + regcache_cooked_write_unsigned (current_regcache, argreg, sp); + + sp += sizeof (hppa32_tramp); } else { - ULONGEST gp; + static unsigned int hppa64_tramp[] = { + 0xeac0f000, /* bve,l (r22),%r2 */ + 0x0fdf12d1, /* std r31,-8(,sp) */ + 0x0fd110c2, /* ldd -8(,sp),rp */ + 0xe840d002, /* bve,n (rp) */ + 0x08000240 /* nop */ + }; + + /* for hppa64, we don't need to call through a stub; all functions + return via a bve. */ + regcache_cooked_write_unsigned (current_regcache, 22, funcaddr); + write_memory (sp, (char *)&hppa64_tramp, sizeof (hppa64_tramp)); - /* Use __d_plt_call as a fallback; __d_plt_call expects to be called - with a plabel, so we need to build one. */ + *bp_addr = pc - 4; + regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr); - 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."); + *real_pc = hppa64_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg); + if (*real_pc == 0) + error ("Cannot make interspace call from here.\n"); - gp = gdbarch_tdep (gdbarch)->find_global_pointer (funcaddr); - write_memory_unsigned_integer (sp, 4, funcaddr); - write_memory_unsigned_integer (sp + 4, 4, gp); + regcache_cooked_write_unsigned (current_regcache, argreg, sp); - /* plabel is passed in r22 */ - regcache_cooked_write_unsigned (current_regcache, 22, sp); + sp += sizeof (hppa64_tramp); } - /* Pushed one stack frame, which has to be 64-byte aligned. */ - sp += 64; + sp = gdbarch_frame_align (gdbarch, sp); return sp; } + /* Bit in the `ss_flag' member of `struct save_state' that indicates @@ -1656,7 +1991,7 @@ hppa_hpux_init_abi (struct gdbarch_info { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - if (tdep->bytes_per_address == 4) + if (IS_32BIT_TARGET (gdbarch)) tdep->in_solib_call_trampoline = hppa32_hpux_in_solib_call_trampoline; else tdep->in_solib_call_trampoline = hppa64_hpux_in_solib_call_trampoline; @@ -1689,7 +2023,8 @@ hppa_hpux_som_init_abi (struct gdbarch_i tdep->is_elf = 0; - tdep->find_global_pointer = hppa_hpux_som_find_global_pointer; + tdep->find_global_pointer = hppa32_hpux_find_global_pointer; + hppa_hpux_init_abi (info, gdbarch); som_solib_select (tdep); } @@ -1700,6 +2035,8 @@ hppa_hpux_elf_init_abi (struct gdbarch_i struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); tdep->is_elf = 1; + tdep->find_global_pointer = hppa64_hpux_find_global_pointer; + hppa_hpux_init_abi (info, gdbarch); pa64_solib_select (tdep); } Index: hppa-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/hppa-tdep.c,v retrieving revision 1.191 diff -u -p -r1.191 hppa-tdep.c --- hppa-tdep.c 13 Dec 2004 04:06:15 -0000 1.191 +++ hppa-tdep.c 13 Dec 2004 17:07:00 -0000 @@ -273,6 +273,20 @@ hppa_symbol_address(const char *sym) else return (CORE_ADDR)-1; } + +struct hppa_objfile_private * +hppa_init_objfile_priv_data (struct objfile *objfile) +{ + struct hppa_objfile_private *priv; + + priv = (struct hppa_objfile_private *) + obstack_alloc (&objfile->objfile_obstack, + sizeof (struct hppa_objfile_private)); + set_objfile_data (objfile, hppa_objfile_priv_data, priv); + memset (priv, 0, sizeof (*priv)); + + return priv; +} /* Compare the start address for two unwind entries returning 1 if @@ -529,15 +544,8 @@ read_unwind_info (struct objfile *objfil obj_private = (struct hppa_objfile_private *) objfile_data (objfile, hppa_objfile_priv_data); if (obj_private == NULL) - { - obj_private = (struct hppa_objfile_private *) - obstack_alloc (&objfile->objfile_obstack, - sizeof (struct hppa_objfile_private)); - set_objfile_data (objfile, hppa_objfile_priv_data, obj_private); - obj_private->unwind_info = NULL; - obj_private->so_info = NULL; - obj_private->dp = 0; - } + obj_private = hppa_init_objfile_priv_data (objfile); + obj_private->unwind_info = ui; } @@ -874,7 +882,8 @@ hppa32_push_dummy_call (struct gdbarch * write_register (19, gp); /* Set the return address. */ - regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, bp_addr); + if (!gdbarch_push_dummy_code_p (gdbarch)) + regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, bp_addr); /* Update the Stack Pointer. */ regcache_cooked_write_unsigned (regcache, HPPA_SP_REGNUM, param_end); @@ -914,6 +923,10 @@ hppa64_push_dummy_call (struct gdbarch * been pushed. */ CORE_ADDR new_sp = 0; + /* Global pointer (r27) of the function we are trying to call. */ + CORE_ADDR gp; + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + /* Two passes. First pass computes the location of everything, second pass writes the bytes out. */ int write_pass; @@ -995,8 +1008,14 @@ hppa64_push_dummy_call (struct gdbarch * if (struct_return) write_register (28, struct_addr); + gp = tdep->find_global_pointer (function); + + if (gp != 0) + write_register (27, gp); + /* Set the return address. */ - regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, bp_addr); + if (!gdbarch_push_dummy_code_p (gdbarch)) + regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, bp_addr); /* Update the Stack Pointer. */ regcache_cooked_write_unsigned (regcache, HPPA_SP_REGNUM, param_end + 64); Index: hppa-tdep.h =================================================================== RCS file: /cvs/src/src/gdb/hppa-tdep.h,v retrieving revision 1.17 diff -u -p -r1.17 hppa-tdep.h --- hppa-tdep.h 13 Dec 2004 04:06:15 -0000 1.17 +++ hppa-tdep.h 13 Dec 2004 17:07:00 -0000 @@ -204,6 +204,9 @@ struct hppa_objfile_private struct hppa_unwind_info *unwind_info; /* a pointer */ struct so_list *so_info; /* a pointer */ CORE_ADDR dp; + + int dummy_call_sequence_reg; + CORE_ADDR dummy_call_sequence_addr; }; extern const struct objfile_data *hppa_objfile_priv_data; @@ -231,4 +234,7 @@ extern void hppa_write_pc (CORE_ADDR pc, extern CORE_ADDR hppa_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame); +extern struct hppa_objfile_private * +hppa_init_objfile_priv_data (struct objfile *objfile); + #endif /* HPPA_TDEP_H */ --8GpibOaaTibBMecb--