-
-/* The following instructions are used in the signal trampoline code
- on GNU/Linux PPC. The kernel used to use magic syscalls 0x6666 and
- 0x7777 but now uses the sigreturn syscalls. We check for both. */
-#define INSTR_LI_R0_0x6666 0x38006666
-#define INSTR_LI_R0_0x7777 0x38007777
-#define INSTR_LI_R0_NR_sigreturn 0x38000077
-#define INSTR_LI_R0_NR_rt_sigreturn 0x380000AC
-
-#define INSTR_SC 0x44000002
-
-/* Since the *-tdep.c files are platform independent (i.e, they may be
- used to build cross platform debuggers), we can't include system
- headers. Therefore, details concerning the sigcontext structure
- must be painstakingly rerecorded. What's worse, if these details
- ever change in the header files, they'll have to be changed here
- as well. */
-
-/* __SIGNAL_FRAMESIZE from <asm/ptrace.h> */
-#define PPC_LINUX_SIGNAL_FRAMESIZE 64
-
-/* From <asm/sigcontext.h>, offsetof(struct sigcontext_struct, regs) == 0x1c */
-#define PPC_LINUX_REGS_PTR_OFFSET (PPC_LINUX_SIGNAL_FRAMESIZE + 0x1c)
-
-/* From <asm/sigcontext.h>,
- offsetof(struct sigcontext_struct, handler) == 0x14 */
-#define PPC_LINUX_HANDLER_PTR_OFFSET (PPC_LINUX_SIGNAL_FRAMESIZE + 0x14)
-
-/* From <asm/ptrace.h>, values for PT_NIP, PT_R1, and PT_LNK */
-#define PPC_LINUX_PT_R0 0
-#define PPC_LINUX_PT_R1 1
-#define PPC_LINUX_PT_R2 2
-#define PPC_LINUX_PT_R3 3
-#define PPC_LINUX_PT_R4 4
-#define PPC_LINUX_PT_R5 5
-#define PPC_LINUX_PT_R6 6
-#define PPC_LINUX_PT_R7 7
-#define PPC_LINUX_PT_R8 8
-#define PPC_LINUX_PT_R9 9
-#define PPC_LINUX_PT_R10 10
-#define PPC_LINUX_PT_R11 11
-#define PPC_LINUX_PT_R12 12
-#define PPC_LINUX_PT_R13 13
-#define PPC_LINUX_PT_R14 14
-#define PPC_LINUX_PT_R15 15
-#define PPC_LINUX_PT_R16 16
-#define PPC_LINUX_PT_R17 17
-#define PPC_LINUX_PT_R18 18
-#define PPC_LINUX_PT_R19 19
-#define PPC_LINUX_PT_R20 20
-#define PPC_LINUX_PT_R21 21
-#define PPC_LINUX_PT_R22 22
-#define PPC_LINUX_PT_R23 23
-#define PPC_LINUX_PT_R24 24
-#define PPC_LINUX_PT_R25 25
-#define PPC_LINUX_PT_R26 26
-#define PPC_LINUX_PT_R27 27
-#define PPC_LINUX_PT_R28 28
-#define PPC_LINUX_PT_R29 29
-#define PPC_LINUX_PT_R30 30
-#define PPC_LINUX_PT_R31 31
-#define PPC_LINUX_PT_NIP 32
-#define PPC_LINUX_PT_MSR 33
-#define PPC_LINUX_PT_CTR 35
-#define PPC_LINUX_PT_LNK 36
-#define PPC_LINUX_PT_XER 37
-#define PPC_LINUX_PT_CCR 38
-#define PPC_LINUX_PT_MQ 39
-#define PPC_LINUX_PT_FPR0 48 /* each FP reg occupies 2 slots in this space */
-#define PPC_LINUX_PT_FPR31 (PPC_LINUX_PT_FPR0 + 2*31)
-#define PPC_LINUX_PT_FPSCR (PPC_LINUX_PT_FPR0 + 2*32 + 1)
-
-static int ppc_linux_at_sigtramp_return_path (CORE_ADDR pc);
-
-/* Determine if pc is in a signal trampoline...
-
- Ha! That's not what this does at all. wait_for_inferior in
- infrun.c calls PC_IN_SIGTRAMP in order to detect entry into a
- signal trampoline just after delivery of a signal. But on
- GNU/Linux, signal trampolines are used for the return path only.
- The kernel sets things up so that the signal handler is called
- directly.
-
- If we use in_sigtramp2() in place of in_sigtramp() (see below)
- we'll (often) end up with stop_pc in the trampoline and prev_pc in
- the (now exited) handler. The code there will cause a temporary
- breakpoint to be set on prev_pc which is not very likely to get hit
- again.
-
- If this is confusing, think of it this way... the code in
- wait_for_inferior() needs to be able to detect entry into a signal
- trampoline just after a signal is delivered, not after the handler
- has been run.
-
- So, we define in_sigtramp() below to return 1 if the following is
- true:
-
- 1) The previous frame is a real signal trampoline.
-
- - and -
-
- 2) pc is at the first or second instruction of the corresponding
- handler.
-
- Why the second instruction? It seems that wait_for_inferior()
- never sees the first instruction when single stepping. When a
- signal is delivered while stepping, the next instruction that
- would've been stepped over isn't, instead a signal is delivered and
- the first instruction of the handler is stepped over instead. That
- puts us on the second instruction. (I added the test for the
- first instruction long after the fact, just in case the observed
- behavior is ever fixed.)
-
- PC_IN_SIGTRAMP is called from blockframe.c as well in order to set
- the frame's type (if a SIGTRAMP_FRAME). Because of our strange
- definition of in_sigtramp below, we can't rely on the frame's type
- getting set correctly from within blockframe.c. This is why we
- take pains to set it in init_extra_frame_info().
-
- NOTE: cagney/2002-11-10: I suspect the real problem here is that
- the get_prev_frame() only initializes the frame's type after the
- call to INIT_FRAME_INFO. get_prev_frame() should be fixed, this
- code shouldn't be working its way around a bug :-(. */
-
-int
-ppc_linux_in_sigtramp (CORE_ADDR pc, char *func_name)
-{
- CORE_ADDR lr;
- CORE_ADDR sp;
- CORE_ADDR tramp_sp;
- char buf[4];
- CORE_ADDR handler;
-
- lr = read_register (gdbarch_tdep (current_gdbarch)->ppc_lr_regnum);
- if (!ppc_linux_at_sigtramp_return_path (lr))
- return 0;
-
- sp = read_register (SP_REGNUM);
-
- if (target_read_memory (sp, buf, sizeof (buf)) != 0)
- return 0;
-
- tramp_sp = extract_unsigned_integer (buf, 4);
-
- if (target_read_memory (tramp_sp + PPC_LINUX_HANDLER_PTR_OFFSET, buf,
- sizeof (buf)) != 0)
- return 0;
-
- handler = extract_unsigned_integer (buf, 4);
-
- return (pc == handler || pc == handler + 4);
-}
-
-static inline int
-insn_is_sigreturn (unsigned long pcinsn)
-{
- switch(pcinsn)
- {
- case INSTR_LI_R0_0x6666:
- case INSTR_LI_R0_0x7777:
- case INSTR_LI_R0_NR_sigreturn:
- case INSTR_LI_R0_NR_rt_sigreturn:
- return 1;
- default:
- return 0;
- }
-}
-
-/*
- * The signal handler trampoline is on the stack and consists of exactly
- * two instructions. The easiest and most accurate way of determining
- * whether the pc is in one of these trampolines is by inspecting the
- * instructions. It'd be faster though if we could find a way to do this
- * via some simple address comparisons.
- */
-static int
-ppc_linux_at_sigtramp_return_path (CORE_ADDR pc)
-{
- char buf[12];
- unsigned long pcinsn;
- if (target_read_memory (pc - 4, buf, sizeof (buf)) != 0)
- return 0;
-
- /* extract the instruction at the pc */
- pcinsn = extract_unsigned_integer (buf + 4, 4);
-
- return (
- (insn_is_sigreturn (pcinsn)
- && extract_unsigned_integer (buf + 8, 4) == INSTR_SC)
- ||
- (pcinsn == INSTR_SC
- && insn_is_sigreturn (extract_unsigned_integer (buf, 4))));
-}
-
-CORE_ADDR
-ppc_linux_skip_trampoline_code (CORE_ADDR pc)
-{
- char buf[4];
- struct obj_section *sect;
- struct objfile *objfile;
- unsigned long insn;
- CORE_ADDR plt_start = 0;
- CORE_ADDR symtab = 0;
- CORE_ADDR strtab = 0;
- int num_slots = -1;
- int reloc_index = -1;
- CORE_ADDR plt_table;
- CORE_ADDR reloc;
- CORE_ADDR sym;
- long symidx;
- char symname[1024];
- struct minimal_symbol *msymbol;
-
- /* Find the section pc is in; return if not in .plt */
- sect = find_pc_section (pc);
- if (!sect || strcmp (sect->the_bfd_section->name, ".plt") != 0)
- return 0;
-
- objfile = sect->objfile;
-
- /* Pick up the instruction at pc. It had better be of the
- form
- li r11, IDX
-
- where IDX is an index into the plt_table. */
-
- if (target_read_memory (pc, buf, 4) != 0)
- return 0;
- insn = extract_unsigned_integer (buf, 4);
-
- if ((insn & 0xffff0000) != 0x39600000 /* li r11, VAL */ )
- return 0;
-
- reloc_index = (insn << 16) >> 16;
-
- /* Find the objfile that pc is in and obtain the information
- necessary for finding the symbol name. */
- for (sect = objfile->sections; sect < objfile->sections_end; ++sect)
- {
- const char *secname = sect->the_bfd_section->name;
- if (strcmp (secname, ".plt") == 0)
- plt_start = sect->addr;
- else if (strcmp (secname, ".rela.plt") == 0)
- num_slots = ((int) sect->endaddr - (int) sect->addr) / 12;
- else if (strcmp (secname, ".dynsym") == 0)
- symtab = sect->addr;
- else if (strcmp (secname, ".dynstr") == 0)
- strtab = sect->addr;
- }
-
- /* Make sure we have all the information we need. */
- if (plt_start == 0 || num_slots == -1 || symtab == 0 || strtab == 0)
- return 0;
-
- /* Compute the value of the plt table */
- plt_table = plt_start + 72 + 8 * num_slots;
-
- /* Get address of the relocation entry (Elf32_Rela) */
- if (target_read_memory (plt_table + reloc_index, buf, 4) != 0)
- return 0;
- reloc = extract_address (buf, 4);
-
- sect = find_pc_section (reloc);
- if (!sect)
- return 0;
-
- if (strcmp (sect->the_bfd_section->name, ".text") == 0)
- return reloc;
-
- /* Now get the r_info field which is the relocation type and symbol
- index. */
- if (target_read_memory (reloc + 4, buf, 4) != 0)
- return 0;
- symidx = extract_unsigned_integer (buf, 4);
-
- /* Shift out the relocation type leaving just the symbol index */
- /* symidx = ELF32_R_SYM(symidx); */
- symidx = symidx >> 8;
-
- /* compute the address of the symbol */
- sym = symtab + symidx * 4;
-
- /* Fetch the string table index */
- if (target_read_memory (sym, buf, 4) != 0)
- return 0;
- symidx = extract_unsigned_integer (buf, 4);
-
- /* Fetch the string; we don't know how long it is. Is it possible
- that the following will fail because we're trying to fetch too
- much? */
- if (target_read_memory (strtab + symidx, symname, sizeof (symname)) != 0)
- return 0;
-
- /* This might not work right if we have multiple symbols with the
- same name; the only way to really get it right is to perform
- the same sort of lookup as the dynamic linker. */
- msymbol = lookup_minimal_symbol_text (symname, NULL, NULL);
- if (!msymbol)
- return 0;
-
- return SYMBOL_VALUE_ADDRESS (msymbol);
-}
-
-/* The rs6000 version of FRAME_SAVED_PC will almost work for us. The
- signal handler details are different, so we'll handle those here
- and call the rs6000 version to do the rest. */
-CORE_ADDR
-ppc_linux_frame_saved_pc (struct frame_info *fi)
-{
- if ((get_frame_type (fi) == SIGTRAMP_FRAME))
- {
- CORE_ADDR regs_addr =
- read_memory_integer (fi->frame + PPC_LINUX_REGS_PTR_OFFSET, 4);
- /* return the NIP in the regs array */
- return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_NIP, 4);
- }
- else if (fi->next && (get_frame_type (fi->next) == SIGTRAMP_FRAME))
- {
- CORE_ADDR regs_addr =
- read_memory_integer (fi->next->frame + PPC_LINUX_REGS_PTR_OFFSET, 4);
- /* return LNK in the regs array */
- return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_LNK, 4);
- }
- else
- return rs6000_frame_saved_pc (fi);
-}
-
-void
-ppc_linux_init_extra_frame_info (int fromleaf, struct frame_info *fi)
-{
- rs6000_init_extra_frame_info (fromleaf, fi);
-
- if (fi->next != 0)
- {
- /* We're called from get_prev_frame_info; check to see if
- this is a signal frame by looking to see if the pc points
- at trampoline code */
- if (ppc_linux_at_sigtramp_return_path (fi->pc))
- deprecated_set_frame_type (fi, SIGTRAMP_FRAME);
- else
- /* FIXME: cagney/2002-11-10: Is this double bogus? What
- happens if the frame has previously been marked as a dummy? */
- deprecated_set_frame_type (fi, NORMAL_FRAME);
- }
-}
-
-int
-ppc_linux_frameless_function_invocation (struct frame_info *fi)
-{
- /* We'll find the wrong thing if we let
- rs6000_frameless_function_invocation () search for a signal trampoline */
- if (ppc_linux_at_sigtramp_return_path (fi->pc))
- return 0;
- else
- return rs6000_frameless_function_invocation (fi);
-}
-
-void
-ppc_linux_frame_init_saved_regs (struct frame_info *fi)
-{
- if ((get_frame_type (fi) == SIGTRAMP_FRAME))
- {
- CORE_ADDR regs_addr;
- int i;
- if (fi->saved_regs)
- return;
-
- frame_saved_regs_zalloc (fi);
-
- regs_addr =
- read_memory_integer (fi->frame + PPC_LINUX_REGS_PTR_OFFSET, 4);
- fi->saved_regs[PC_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_NIP;
- fi->saved_regs[gdbarch_tdep (current_gdbarch)->ppc_ps_regnum] =
- regs_addr + 4 * PPC_LINUX_PT_MSR;
- fi->saved_regs[gdbarch_tdep (current_gdbarch)->ppc_cr_regnum] =
- regs_addr + 4 * PPC_LINUX_PT_CCR;
- fi->saved_regs[gdbarch_tdep (current_gdbarch)->ppc_lr_regnum] =
- regs_addr + 4 * PPC_LINUX_PT_LNK;
- fi->saved_regs[gdbarch_tdep (current_gdbarch)->ppc_ctr_regnum] =
- regs_addr + 4 * PPC_LINUX_PT_CTR;
- fi->saved_regs[gdbarch_tdep (current_gdbarch)->ppc_xer_regnum] =
- regs_addr + 4 * PPC_LINUX_PT_XER;
- fi->saved_regs[gdbarch_tdep (current_gdbarch)->ppc_mq_regnum] =
- regs_addr + 4 * PPC_LINUX_PT_MQ;
- for (i = 0; i < 32; i++)
- fi->saved_regs[gdbarch_tdep (current_gdbarch)->ppc_gp0_regnum + i] =
- regs_addr + 4 * PPC_LINUX_PT_R0 + 4 * i;
- for (i = 0; i < 32; i++)
- fi->saved_regs[FP0_REGNUM + i] = regs_addr + 4 * PPC_LINUX_PT_FPR0 + 8 * i;
- }
- else
- rs6000_frame_init_saved_regs (fi);
-}
-
-CORE_ADDR
-ppc_linux_frame_chain (struct frame_info *thisframe)
-{
- /* Kernel properly constructs the frame chain for the handler */
- if ((get_frame_type (thisframe) == SIGTRAMP_FRAME))
- return read_memory_integer ((thisframe)->frame, 4);
- else
- return rs6000_frame_chain (thisframe);
-}