This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Other format: | [Raw text] |
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/
Attachment:
dummycall.diff
Description: Text document
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |