From e4c6eabcf10a4db042e5ba35f7235abcd2f35dc2 Mon Sep 17 00:00:00 2001 From: nobody <> Date: Fri, 26 Mar 2004 23:15:42 +0000 Subject: This commit was manufactured by cvs2svn to create branch 'drow_intercu-20040221-branch'. Cherrypick from master 2004-03-26 23:15:40 UTC Joel Brobecker ' * amd64-tdep.c (amd64_classify): make RANGE_TYPE objects be part': gdb/amd64-linux-nat.c gdb/amd64-linux-tdep.c gdb/amd64-tdep.c gdb/amd64-tdep.h gdb/config/i386/linux64.mh gdb/config/i386/linux64.mt gdb/config/i386/nm-linux64.h gdb/config/i386/obsdaout.mh gdb/config/i386/tm-linux64.h gdb/config/nm-bsd.h gdb/frv-linux-tdep.c gdb/frv-tdep.h gdb/solib-frv.c gdb/testsuite/gdb.arch/gdb1431.s gdb/testsuite/gdb.base/auxv.c gdb/testsuite/gdb.base/auxv.exp gdb/testsuite/gdb.cp/classes.cc gdb/testsuite/gdb.cp/pr-1553.cc gdb/testsuite/gdb.cp/pr-1553.exp gdb/tramp-frame.c gdb/tramp-frame.h sim/frv/profile-fr450.c sim/testsuite/sim/frv/mqlclrhs.cgs sim/testsuite/sim/frv/mqlmths.cgs sim/testsuite/sim/frv/mqsllhi.cgs sim/testsuite/sim/frv/mqsrahi.cgs --- gdb/amd64-linux-nat.c | 385 +++++++++++ gdb/amd64-linux-tdep.c | 225 +++++++ gdb/amd64-tdep.c | 1191 ++++++++++++++++++++++++++++++++++ gdb/amd64-tdep.h | 86 +++ gdb/config/i386/linux64.mh | 10 + gdb/config/i386/linux64.mt | 5 + gdb/config/i386/nm-linux64.h | 69 ++ gdb/config/i386/obsdaout.mh | 5 + gdb/config/i386/tm-linux64.h | 36 ++ gdb/config/nm-bsd.h | 29 + gdb/frv-linux-tdep.c | 273 ++++++++ gdb/frv-tdep.h | 105 +++ gdb/solib-frv.c | 1230 ++++++++++++++++++++++++++++++++++++ gdb/testsuite/gdb.arch/gdb1431.s | 129 ++++ gdb/testsuite/gdb.base/auxv.c | 58 ++ gdb/testsuite/gdb.base/auxv.exp | 187 ++++++ gdb/testsuite/gdb.cp/classes.cc | 608 ++++++++++++++++++ gdb/testsuite/gdb.cp/pr-1553.cc | 53 ++ gdb/testsuite/gdb.cp/pr-1553.exp | 62 ++ gdb/tramp-frame.c | 173 +++++ gdb/tramp-frame.h | 69 ++ sim/frv/profile-fr450.c | 607 ++++++++++++++++++ sim/testsuite/sim/frv/mqlclrhs.cgs | 74 +++ sim/testsuite/sim/frv/mqlmths.cgs | 74 +++ sim/testsuite/sim/frv/mqsllhi.cgs | 40 ++ sim/testsuite/sim/frv/mqsrahi.cgs | 40 ++ 26 files changed, 5823 insertions(+) create mode 100644 gdb/amd64-linux-nat.c create mode 100644 gdb/amd64-linux-tdep.c create mode 100644 gdb/amd64-tdep.c create mode 100644 gdb/amd64-tdep.h create mode 100644 gdb/config/i386/linux64.mh create mode 100644 gdb/config/i386/linux64.mt create mode 100644 gdb/config/i386/nm-linux64.h create mode 100644 gdb/config/i386/obsdaout.mh create mode 100644 gdb/config/i386/tm-linux64.h create mode 100644 gdb/config/nm-bsd.h create mode 100644 gdb/frv-linux-tdep.c create mode 100644 gdb/frv-tdep.h create mode 100644 gdb/solib-frv.c create mode 100644 gdb/testsuite/gdb.arch/gdb1431.s create mode 100644 gdb/testsuite/gdb.base/auxv.c create mode 100644 gdb/testsuite/gdb.base/auxv.exp create mode 100644 gdb/testsuite/gdb.cp/classes.cc create mode 100644 gdb/testsuite/gdb.cp/pr-1553.cc create mode 100644 gdb/testsuite/gdb.cp/pr-1553.exp create mode 100644 gdb/tramp-frame.c create mode 100644 gdb/tramp-frame.h create mode 100644 sim/frv/profile-fr450.c create mode 100644 sim/testsuite/sim/frv/mqlclrhs.cgs create mode 100644 sim/testsuite/sim/frv/mqlmths.cgs create mode 100644 sim/testsuite/sim/frv/mqsllhi.cgs create mode 100644 sim/testsuite/sim/frv/mqsrahi.cgs diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c new file mode 100644 index 0000000..72aa73c --- /dev/null +++ b/gdb/amd64-linux-nat.c @@ -0,0 +1,385 @@ +/* Native-dependent code for GNU/Linux x86-64. + + Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Contributed by Jiri Smid, SuSE Labs. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "inferior.h" +#include "gdbcore.h" +#include "regcache.h" +#include "linux-nat.h" + +#include "gdb_assert.h" +#include "gdb_string.h" +#include +#include +#include +#include +#include +/* FIXME ezannoni-2003-07-09: we need to be included after + because the latter redefines FS and GS for no apparent + reason, and those definitions don't match the ones that libpthread_db + uses, which come from . */ +/* ezannoni-2003-07-09: I think this is fixed. The extraneous defs have + been removed from ptrace.h in the kernel. However, better safe than + sorry. */ +#include +#include +#include "gdb_proc_service.h" + +/* Prototypes for supply_gregset etc. */ +#include "gregset.h" + +#include "amd64-tdep.h" +#include "i386-linux-tdep.h" +#include "amd64-nat.h" + +/* Mapping between the general-purpose registers in GNU/Linux x86-64 + `struct user' format and GDB's register cache layout. */ + +static int amd64_linux_gregset64_reg_offset[] = +{ + RAX * 8, RBX * 8, /* %rax, %rbx */ + RCX * 8, RDX * 8, /* %rcx, %rdx */ + RSI * 8, RDI * 8, /* %rsi, %rdi */ + RBP * 8, RSP * 8, /* %rbp, %rsp */ + R8 * 8, R9 * 8, /* %r8 ... */ + R10 * 8, R11 * 8, + R12 * 8, R13 * 8, + R14 * 8, R15 * 8, /* ... %r15 */ + RIP * 8, EFLAGS * 8, /* %rip, %eflags */ + CS * 8, SS * 8, /* %cs, %ss */ + DS * 8, ES * 8, /* %ds, %es */ + FS * 8, GS * 8 /* %fs, %gs */ +}; + + +/* Mapping between the general-purpose registers in GNU/Linux x86-64 + `struct user' format and GDB's register cache layout for GNU/Linux + i386. + + Note that most GNU/Linux x86-64 registers are 64-bit, while the + GNU/Linux i386 registers are all 32-bit, but since we're + little-endian we get away with that. */ + +/* From on GNU/Linux i386. */ +static int amd64_linux_gregset32_reg_offset[] = +{ + RAX * 8, RCX * 8, /* %eax, %ecx */ + RDX * 8, RBX * 8, /* %edx, %ebx */ + RSP * 8, RBP * 8, /* %esp, %ebp */ + RSI * 8, RDI * 8, /* %esi, %edi */ + RIP * 8, EFLAGS * 8, /* %eip, %eflags */ + CS * 8, SS * 8, /* %cs, %ss */ + DS * 8, ES * 8, /* %ds, %es */ + FS * 8, GS * 8, /* %fs, %gs */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, + ORIG_RAX * 8 /* "orig_eax" */ +}; + + +/* Transfering the general-purpose registers between GDB, inferiors + and core files. */ + +/* Fill GDB's register cache with the general-purpose register values + in *GREGSETP. */ + +void +supply_gregset (elf_gregset_t *gregsetp) +{ + amd64_supply_native_gregset (current_regcache, gregsetp, -1); +} + +/* Fill register REGNUM (if it is a general-purpose register) in + *GREGSETP with the value in GDB's register cache. If REGNUM is -1, + do this for all registers. */ + +void +fill_gregset (elf_gregset_t *gregsetp, int regnum) +{ + amd64_collect_native_gregset (current_regcache, gregsetp, regnum); +} + +/* Transfering floating-point registers between GDB, inferiors and cores. */ + +/* Fill GDB's register cache with the floating-point and SSE register + values in *FPREGSETP. */ + +void +supply_fpregset (elf_fpregset_t *fpregsetp) +{ + amd64_supply_fxsave (current_regcache, -1, fpregsetp); +} + +/* Fill register REGNUM (if it is a floating-point or SSE register) in + *FPREGSETP with the value in GDB's register cache. If REGNUM is + -1, do this for all registers. */ + +void +fill_fpregset (elf_fpregset_t *fpregsetp, int regnum) +{ + amd64_collect_fxsave (current_regcache, regnum, fpregsetp); +} + + +/* Transferring arbitrary registers between GDB and inferior. */ + +/* Fetch register REGNUM from the child process. If REGNUM is -1, do + this for all registers (including the floating point and SSE + registers). */ + +void +fetch_inferior_registers (int regnum) +{ + int tid; + + /* GNU/Linux LWP ID's are process ID's. */ + tid = TIDGET (inferior_ptid); + if (tid == 0) + tid = PIDGET (inferior_ptid); /* Not a threaded program. */ + + if (regnum == -1 || amd64_native_gregset_supplies_p (regnum)) + { + elf_gregset_t regs; + + if (ptrace (PTRACE_GETREGS, tid, 0, (long) ®s) < 0) + perror_with_name ("Couldn't get registers"); + + amd64_supply_native_gregset (current_regcache, ®s, -1); + if (regnum != -1) + return; + } + + if (regnum == -1 || regnum >= AMD64_ST0_REGNUM) + { + elf_fpregset_t fpregs; + + if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0) + perror_with_name ("Couldn't get floating point status"); + + amd64_supply_fxsave (current_regcache, -1, &fpregs); + } +} + +/* Store register REGNUM back into the child process. If REGNUM is + -1, do this for all registers (including the floating-point and SSE + registers). */ + +void +store_inferior_registers (int regnum) +{ + int tid; + + /* GNU/Linux LWP ID's are process ID's. */ + tid = TIDGET (inferior_ptid); + if (tid == 0) + tid = PIDGET (inferior_ptid); /* Not a threaded program. */ + + if (regnum == -1 || amd64_native_gregset_supplies_p (regnum)) + { + elf_gregset_t regs; + + if (ptrace (PTRACE_GETREGS, tid, 0, (long) ®s) < 0) + perror_with_name ("Couldn't get registers"); + + amd64_collect_native_gregset (current_regcache, ®s, regnum); + + if (ptrace (PTRACE_SETREGS, tid, 0, (long) ®s) < 0) + perror_with_name ("Couldn't write registers"); + + if (regnum != -1) + return; + } + + if (regnum == -1 || regnum >= AMD64_ST0_REGNUM) + { + elf_fpregset_t fpregs; + + if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0) + perror_with_name ("Couldn't get floating point status"); + + amd64_collect_fxsave (current_regcache, regnum, &fpregs); + + if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0) + perror_with_name ("Couldn't write floating point status"); + + return; + } +} + + +static unsigned long +amd64_linux_dr_get (int regnum) +{ + int tid; + unsigned long value; + + /* FIXME: kettenis/2001-01-29: It's not clear what we should do with + multi-threaded processes here. For now, pretend there is just + one thread. */ + tid = PIDGET (inferior_ptid); + + /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the + ptrace call fails breaks debugging remote targets. The correct + way to fix this is to add the hardware breakpoint and watchpoint + stuff to the target vectore. For now, just return zero if the + ptrace call fails. */ + errno = 0; + value = ptrace (PT_READ_U, tid, + offsetof (struct user, u_debugreg[regnum]), 0); + if (errno != 0) +#if 0 + perror_with_name ("Couldn't read debug register"); +#else + return 0; +#endif + + return value; +} + +static void +amd64_linux_dr_set (int regnum, unsigned long value) +{ + int tid; + + /* FIXME: kettenis/2001-01-29: It's not clear what we should do with + multi-threaded processes here. For now, pretend there is just + one thread. */ + tid = PIDGET (inferior_ptid); + + errno = 0; + ptrace (PT_WRITE_U, tid, offsetof (struct user, u_debugreg[regnum]), value); + if (errno != 0) + perror_with_name ("Couldn't write debug register"); +} + +void +amd64_linux_dr_set_control (unsigned long control) +{ + amd64_linux_dr_set (DR_CONTROL, control); +} + +void +amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) +{ + gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); + + amd64_linux_dr_set (DR_FIRSTADDR + regnum, addr); +} + +void +amd64_linux_dr_reset_addr (int regnum) +{ + gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); + + amd64_linux_dr_set (DR_FIRSTADDR + regnum, 0L); +} + +unsigned long +amd64_linux_dr_get_status (void) +{ + return amd64_linux_dr_get (DR_STATUS); +} + + +/* This function is called by libthread_db as part of its handling of + a request for a thread's local storage address. */ + +ps_err_e +ps_get_thread_area (const struct ps_prochandle *ph, + lwpid_t lwpid, int idx, void **base) +{ + if (gdbarch_ptr_bit (current_gdbarch) == 32) + { + /* The full structure is found in . The second + integer is the LDT's base_address and that is used to locate + the thread's local storage. See i386-linux-nat.c more + info. */ + unsigned int desc[4]; + + /* This code assumes that "int" is 32 bits and that + GET_THREAD_AREA returns no more than 4 int values. */ + gdb_assert (sizeof (int) == 4); +#ifndef PTRACE_GET_THREAD_AREA +#define PTRACE_GET_THREAD_AREA 25 +#endif + if (ptrace (PTRACE_GET_THREAD_AREA, + lwpid, (void *) (long) idx, (unsigned long) &desc) < 0) + return PS_ERR; + + /* Extend the value to 64 bits. Here it's assumed that a "long" + and a "void *" are the same. */ + (*base) = (void *) (long) desc[1]; + return PS_OK; + } + else + { + /* This definition comes from prctl.h, but some kernels may not + have it. */ +#ifndef PTRACE_ARCH_PRCTL +#define PTRACE_ARCH_PRCTL 30 +#endif + /* FIXME: ezannoni-2003-07-09 see comment above about include + file order. We could be getting bogus values for these two. */ + gdb_assert (FS < ELF_NGREG); + gdb_assert (GS < ELF_NGREG); + switch (idx) + { + case FS: + if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0) + return PS_OK; + break; + case GS: + if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0) + return PS_OK; + break; + default: /* Should not happen. */ + return PS_BADADDR; + } + } + return PS_ERR; /* ptrace failed. */ +} + + +void +child_post_startup_inferior (ptid_t ptid) +{ + i386_cleanup_dregs (); + linux_child_post_startup_inferior (ptid); +} + + +/* Provide a prototype to silence -Wmissing-prototypes. */ +void _initialize_amd64_linux_nat (void); + +void +_initialize_amd64_linux_nat (void) +{ + amd64_native_gregset32_reg_offset = amd64_linux_gregset32_reg_offset; + amd64_native_gregset32_num_regs = I386_LINUX_NUM_REGS; + amd64_native_gregset64_reg_offset = amd64_linux_gregset64_reg_offset; + + gdb_assert (ARRAY_SIZE (amd64_linux_gregset32_reg_offset) + == amd64_native_gregset32_num_regs); + gdb_assert (ARRAY_SIZE (amd64_linux_gregset64_reg_offset) + == amd64_native_gregset64_num_regs); +} diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c new file mode 100644 index 0000000..843a84e --- /dev/null +++ b/gdb/amd64-linux-tdep.c @@ -0,0 +1,225 @@ +/* Target-dependent code for GNU/Linux x86-64. + + Copyright 2001, 2003, 2004 Free Software Foundation, Inc. + Contributed by Jiri Smid, SuSE Labs. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "frame.h" +#include "gdbcore.h" +#include "regcache.h" +#include "osabi.h" + +#include "gdb_string.h" + +#include "amd64-tdep.h" +#include "solib-svr4.h" + +/* Mapping between the general-purpose registers in `struct user' + format and GDB's register cache layout. */ + +/* From . */ +static int amd64_linux_gregset_reg_offset[] = +{ + 10 * 8, /* %rax */ + 5 * 8, /* %rbx */ + 11 * 8, /* %rcx */ + 12 * 8, /* %rdx */ + 13 * 8, /* %rsi */ + 14 * 8, /* %rdi */ + 4 * 8, /* %rbp */ + 19 * 8, /* %rsp */ + 9 * 8, /* %r8 ... */ + 8 * 8, + 7 * 8, + 6 * 8, + 3 * 8, + 2 * 8, + 1 * 8, + 0 * 8, /* ... %r15 */ + 16 * 8, /* %rip */ + 18 * 8, /* %eflags */ + 17 * 8, /* %cs */ + 20 * 8, /* %ss */ + 23 * 8, /* %ds */ + 24 * 8, /* %es */ + 25 * 8, /* %fs */ + 26 * 8 /* %gs */ +}; + + +/* Support for signal handlers. */ + +#define LINUX_SIGTRAMP_INSN0 0x48 /* mov $NNNNNNNN, %rax */ +#define LINUX_SIGTRAMP_OFFSET0 0 +#define LINUX_SIGTRAMP_INSN1 0x0f /* syscall */ +#define LINUX_SIGTRAMP_OFFSET1 7 + +static const unsigned char linux_sigtramp_code[] = +{ + /* mov $__NR_rt_sigreturn, %rax */ + LINUX_SIGTRAMP_INSN0, 0xc7, 0xc0, 0x0f, 0x00, 0x00, 0x00, + /* syscall */ + LINUX_SIGTRAMP_INSN1, 0x05 +}; + +#define LINUX_SIGTRAMP_LEN (sizeof linux_sigtramp_code) + +/* If PC is in a sigtramp routine, return the address of the start of + the routine. Otherwise, return 0. */ + +static CORE_ADDR +amd64_linux_sigtramp_start (CORE_ADDR pc) +{ + unsigned char buf[LINUX_SIGTRAMP_LEN]; + + /* We only recognize a signal trampoline if PC is at the start of + one of the two instructions. We optimize for finding the PC at + the start, as will be the case when the trampoline is not the + first frame on the stack. We assume that in the case where the + PC is not at the start of the instruction sequence, there will be + a few trailing readable bytes on the stack. */ + + if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0) + return 0; + + if (buf[0] != LINUX_SIGTRAMP_INSN0) + { + if (buf[0] != LINUX_SIGTRAMP_INSN1) + return 0; + + pc -= LINUX_SIGTRAMP_OFFSET1; + + if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0) + return 0; + } + + if (memcmp (buf, linux_sigtramp_code, LINUX_SIGTRAMP_LEN) != 0) + return 0; + + return pc; +} + +/* Return whether PC is in a GNU/Linux sigtramp routine. */ + +static int +amd64_linux_pc_in_sigtramp (CORE_ADDR pc, char *name) +{ + /* If we have NAME, we can optimize the search. The trampoline is + named __restore_rt. However, it isn't dynamically exported from + the shared C library, so the trampoline may appear to be part of + the preceding function. This should always be sigaction, + __sigaction, or __libc_sigaction (all aliases to the same + function). */ + if (name == NULL || strstr (name, "sigaction") != NULL) + return (amd64_linux_sigtramp_start (pc) != 0); + + return (strcmp ("__restore_rt", name) == 0); +} + +/* Offset to struct sigcontext in ucontext, from . */ +#define AMD64_LINUX_UCONTEXT_SIGCONTEXT_OFFSET 40 + +/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp + routine, return the address of the associated sigcontext structure. */ + +static CORE_ADDR +amd64_linux_sigcontext_addr (struct frame_info *next_frame) +{ + CORE_ADDR sp; + char buf[8]; + + frame_unwind_register (next_frame, SP_REGNUM, buf); + sp = extract_unsigned_integer (buf, 8); + + /* The sigcontext structure is part of the user context. A pointer + to the user context is passed as the third argument to the signal + handler, i.e. in %rdx. Unfortunately %rdx isn't preserved across + function calls so we can't use it. Fortunately the user context + is part of the signal frame and the unwound %rsp directly points + at it. */ + return sp + AMD64_LINUX_UCONTEXT_SIGCONTEXT_OFFSET; +} + + +/* From . */ +static int amd64_linux_sc_reg_offset[] = +{ + 13 * 8, /* %rax */ + 11 * 8, /* %rbx */ + 14 * 8, /* %rcx */ + 12 * 8, /* %rdx */ + 9 * 8, /* %rsi */ + 8 * 8, /* %rdi */ + 10 * 8, /* %rbp */ + 15 * 8, /* %rsp */ + 0 * 8, /* %r8 */ + 1 * 8, /* %r9 */ + 2 * 8, /* %r10 */ + 3 * 8, /* %r11 */ + 4 * 8, /* %r12 */ + 5 * 8, /* %r13 */ + 6 * 8, /* %r14 */ + 7 * 8, /* %r15 */ + 16 * 8, /* %rip */ + 17 * 8, /* %eflags */ + + /* FIXME: kettenis/2002030531: The registers %cs, %fs and %gs are + available in `struct sigcontext'. However, they only occupy two + bytes instead of four, which makes using them here rather + difficult. Leave them out for now. */ + -1, /* %cs */ + -1, /* %ss */ + -1, /* %ds */ + -1, /* %es */ + -1, /* %fs */ + -1 /* %gs */ +}; + +static void +amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + tdep->gregset_reg_offset = amd64_linux_gregset_reg_offset; + tdep->gregset_num_regs = ARRAY_SIZE (amd64_linux_gregset_reg_offset); + tdep->sizeof_gregset = 27 * 8; + + amd64_init_abi (info, gdbarch); + + set_gdbarch_deprecated_pc_in_sigtramp (gdbarch, amd64_linux_pc_in_sigtramp); + tdep->sigcontext_addr = amd64_linux_sigcontext_addr; + tdep->sc_reg_offset = amd64_linux_sc_reg_offset; + tdep->sc_num_regs = ARRAY_SIZE (amd64_linux_sc_reg_offset); + + /* GNU/Linux uses SVR4-style shared libraries. */ + set_solib_svr4_fetch_link_map_offsets + (gdbarch, svr4_lp64_fetch_link_map_offsets); +} + + +/* Provide a prototype to silence -Wmissing-prototypes. */ +extern void _initialize_amd64_linux_tdep (void); + +void +_initialize_amd64_linux_tdep (void) +{ + gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, + GDB_OSABI_LINUX, amd64_linux_init_abi); +} diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c new file mode 100644 index 0000000..1a66e45 --- /dev/null +++ b/gdb/amd64-tdep.c @@ -0,0 +1,1191 @@ +/* Target-dependent code for AMD64. + + Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Contributed by Jiri Smid, SuSE Labs. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "arch-utils.h" +#include "block.h" +#include "dummy-frame.h" +#include "frame.h" +#include "frame-base.h" +#include "frame-unwind.h" +#include "inferior.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "objfiles.h" +#include "regcache.h" +#include "regset.h" +#include "symfile.h" + +#include "gdb_assert.h" + +#include "amd64-tdep.h" +#include "i387-tdep.h" + +/* Note that the AMD64 architecture was previously known as x86-64. + The latter is (forever) engraved into the canonical system name as + returned by config.guess, and used as the name for the AMD64 port + of GNU/Linux. The BSD's have renamed their ports to amd64; they + don't like to shout. For GDB we prefer the amd64_-prefix over the + x86_64_-prefix since it's so much easier to type. */ + +/* Register information. */ + +struct amd64_register_info +{ + char *name; + struct type **type; +}; + +static struct amd64_register_info amd64_register_info[] = +{ + { "rax", &builtin_type_int64 }, + { "rbx", &builtin_type_int64 }, + { "rcx", &builtin_type_int64 }, + { "rdx", &builtin_type_int64 }, + { "rsi", &builtin_type_int64 }, + { "rdi", &builtin_type_int64 }, + { "rbp", &builtin_type_void_data_ptr }, + { "rsp", &builtin_type_void_data_ptr }, + + /* %r8 is indeed register number 8. */ + { "r8", &builtin_type_int64 }, + { "r9", &builtin_type_int64 }, + { "r10", &builtin_type_int64 }, + { "r11", &builtin_type_int64 }, + { "r12", &builtin_type_int64 }, + { "r13", &builtin_type_int64 }, + { "r14", &builtin_type_int64 }, + { "r15", &builtin_type_int64 }, + { "rip", &builtin_type_void_func_ptr }, + { "eflags", &builtin_type_int32 }, + { "cs", &builtin_type_int32 }, + { "ss", &builtin_type_int32 }, + { "ds", &builtin_type_int32 }, + { "es", &builtin_type_int32 }, + { "fs", &builtin_type_int32 }, + { "gs", &builtin_type_int32 }, + + /* %st0 is register number 24. */ + { "st0", &builtin_type_i387_ext }, + { "st1", &builtin_type_i387_ext }, + { "st2", &builtin_type_i387_ext }, + { "st3", &builtin_type_i387_ext }, + { "st4", &builtin_type_i387_ext }, + { "st5", &builtin_type_i387_ext }, + { "st6", &builtin_type_i387_ext }, + { "st7", &builtin_type_i387_ext }, + { "fctrl", &builtin_type_int32 }, + { "fstat", &builtin_type_int32 }, + { "ftag", &builtin_type_int32 }, + { "fiseg", &builtin_type_int32 }, + { "fioff", &builtin_type_int32 }, + { "foseg", &builtin_type_int32 }, + { "fooff", &builtin_type_int32 }, + { "fop", &builtin_type_int32 }, + + /* %xmm0 is register number 40. */ + { "xmm0", &builtin_type_v4sf }, + { "xmm1", &builtin_type_v4sf }, + { "xmm2", &builtin_type_v4sf }, + { "xmm3", &builtin_type_v4sf }, + { "xmm4", &builtin_type_v4sf }, + { "xmm5", &builtin_type_v4sf }, + { "xmm6", &builtin_type_v4sf }, + { "xmm7", &builtin_type_v4sf }, + { "xmm8", &builtin_type_v4sf }, + { "xmm9", &builtin_type_v4sf }, + { "xmm10", &builtin_type_v4sf }, + { "xmm11", &builtin_type_v4sf }, + { "xmm12", &builtin_type_v4sf }, + { "xmm13", &builtin_type_v4sf }, + { "xmm14", &builtin_type_v4sf }, + { "xmm15", &builtin_type_v4sf }, + { "mxcsr", &builtin_type_int32 } +}; + +/* Total number of registers. */ +#define AMD64_NUM_REGS \ + (sizeof (amd64_register_info) / sizeof (amd64_register_info[0])) + +/* Return the name of register REGNUM. */ + +static const char * +amd64_register_name (int regnum) +{ + if (regnum >= 0 && regnum < AMD64_NUM_REGS) + return amd64_register_info[regnum].name; + + return NULL; +} + +/* Return the GDB type object for the "standard" data type of data in + register REGNUM. */ + +static struct type * +amd64_register_type (struct gdbarch *gdbarch, int regnum) +{ + gdb_assert (regnum >= 0 && regnum < AMD64_NUM_REGS); + + return *amd64_register_info[regnum].type; +} + +/* DWARF Register Number Mapping as defined in the System V psABI, + section 3.6. */ + +static int amd64_dwarf_regmap[] = +{ + /* General Purpose Registers RAX, RDX, RCX, RBX, RSI, RDI. */ + AMD64_RAX_REGNUM, AMD64_RDX_REGNUM, + AMD64_RCX_REGNUM, AMD64_RBX_REGNUM, + AMD64_RSI_REGNUM, AMD64_RDI_REGNUM, + + /* Frame Pointer Register RBP. */ + AMD64_RBP_REGNUM, + + /* Stack Pointer Register RSP. */ + AMD64_RSP_REGNUM, + + /* Extended Integer Registers 8 - 15. */ + 8, 9, 10, 11, 12, 13, 14, 15, + + /* Return Address RA. Mapped to RIP. */ + AMD64_RIP_REGNUM, + + /* SSE Registers 0 - 7. */ + AMD64_XMM0_REGNUM + 0, AMD64_XMM1_REGNUM, + AMD64_XMM0_REGNUM + 2, AMD64_XMM0_REGNUM + 3, + AMD64_XMM0_REGNUM + 4, AMD64_XMM0_REGNUM + 5, + AMD64_XMM0_REGNUM + 6, AMD64_XMM0_REGNUM + 7, + + /* Extended SSE Registers 8 - 15. */ + AMD64_XMM0_REGNUM + 8, AMD64_XMM0_REGNUM + 9, + AMD64_XMM0_REGNUM + 10, AMD64_XMM0_REGNUM + 11, + AMD64_XMM0_REGNUM + 12, AMD64_XMM0_REGNUM + 13, + AMD64_XMM0_REGNUM + 14, AMD64_XMM0_REGNUM + 15, + + /* Floating Point Registers 0-7. */ + AMD64_ST0_REGNUM + 0, AMD64_ST0_REGNUM + 1, + AMD64_ST0_REGNUM + 2, AMD64_ST0_REGNUM + 3, + AMD64_ST0_REGNUM + 4, AMD64_ST0_REGNUM + 5, + AMD64_ST0_REGNUM + 6, AMD64_ST0_REGNUM + 7 +}; + +static const int amd64_dwarf_regmap_len = + (sizeof (amd64_dwarf_regmap) / sizeof (amd64_dwarf_regmap[0])); + +/* Convert DWARF register number REG to the appropriate register + number used by GDB. */ + +static int +amd64_dwarf_reg_to_regnum (int reg) +{ + int regnum = -1; + + if (reg >= 0 || reg < amd64_dwarf_regmap_len) + regnum = amd64_dwarf_regmap[reg]; + + if (regnum == -1) + warning ("Unmapped DWARF Register #%d encountered\n", reg); + + return regnum; +} + +/* Return nonzero if a value of type TYPE stored in register REGNUM + needs any special handling. */ + +static int +amd64_convert_register_p (int regnum, struct type *type) +{ + return i386_fp_regnum_p (regnum); +} + + +/* Register classes as defined in the psABI. */ + +enum amd64_reg_class +{ + AMD64_INTEGER, + AMD64_SSE, + AMD64_SSEUP, + AMD64_X87, + AMD64_X87UP, + AMD64_COMPLEX_X87, + AMD64_NO_CLASS, + AMD64_MEMORY +}; + +/* Return the union class of CLASS1 and CLASS2. See the psABI for + details. */ + +static enum amd64_reg_class +amd64_merge_classes (enum amd64_reg_class class1, enum amd64_reg_class class2) +{ + /* Rule (a): If both classes are equal, this is the resulting class. */ + if (class1 == class2) + return class1; + + /* Rule (b): If one of the classes is NO_CLASS, the resulting class + is the other class. */ + if (class1 == AMD64_NO_CLASS) + return class2; + if (class2 == AMD64_NO_CLASS) + return class1; + + /* Rule (c): If one of the classes is MEMORY, the result is MEMORY. */ + if (class1 == AMD64_MEMORY || class2 == AMD64_MEMORY) + return AMD64_MEMORY; + + /* Rule (d): If one of the classes is INTEGER, the result is INTEGER. */ + if (class1 == AMD64_INTEGER || class2 == AMD64_INTEGER) + return AMD64_INTEGER; + + /* Rule (e): If one of the classes is X87, X87UP, COMPLEX_X87 class, + MEMORY is used as class. */ + if (class1 == AMD64_X87 || class1 == AMD64_X87UP + || class1 == AMD64_COMPLEX_X87 || class2 == AMD64_X87 + || class2 == AMD64_X87UP || class2 == AMD64_COMPLEX_X87) + return AMD64_MEMORY; + + /* Rule (f): Otherwise class SSE is used. */ + return AMD64_SSE; +} + +static void amd64_classify (struct type *type, enum amd64_reg_class class[2]); + +/* Return non-zero if TYPE is a non-POD structure or union type. */ + +static int +amd64_non_pod_p (struct type *type) +{ + /* ??? A class with a base class certainly isn't POD, but does this + catch all non-POD structure types? */ + if (TYPE_CODE (type) == TYPE_CODE_STRUCT && TYPE_N_BASECLASSES (type) > 0) + return 1; + + return 0; +} + +/* Classify TYPE according to the rules for aggregate (structures and + arrays) and union types, and store the result in CLASS. */ + +static void +amd64_classify_aggregate (struct type *type, enum amd64_reg_class class[2]) +{ + int len = TYPE_LENGTH (type); + + /* 1. If the size of an object is larger than two eightbytes, or in + C++, is a non-POD structure or union type, or contains + unaligned fields, it has class memory. */ + if (len > 16 || amd64_non_pod_p (type)) + { + class[0] = class[1] = AMD64_MEMORY; + return; + } + + /* 2. Both eightbytes get initialized to class NO_CLASS. */ + class[0] = class[1] = AMD64_NO_CLASS; + + /* 3. Each field of an object is classified recursively so that + always two fields are considered. The resulting class is + calculated according to the classes of the fields in the + eightbyte: */ + + if (TYPE_CODE (type) == TYPE_CODE_ARRAY) + { + struct type *subtype = check_typedef (TYPE_TARGET_TYPE (type)); + + /* All fields in an array have the same type. */ + amd64_classify (subtype, class); + if (len > 8 && class[1] == AMD64_NO_CLASS) + class[1] = class[0]; + } + else + { + int i; + + /* Structure or union. */ + gdb_assert (TYPE_CODE (type) == TYPE_CODE_STRUCT + || TYPE_CODE (type) == TYPE_CODE_UNION); + + for (i = 0; i < TYPE_NFIELDS (type); i++) + { + struct type *subtype = check_typedef (TYPE_FIELD_TYPE (type, i)); + int pos = TYPE_FIELD_BITPOS (type, i) / 64; + enum amd64_reg_class subclass[2]; + + /* Ignore static fields. */ + if (TYPE_FIELD_STATIC (type, i)) + continue; + + gdb_assert (pos == 0 || pos == 1); + + amd64_classify (subtype, subclass); + class[pos] = amd64_merge_classes (class[pos], subclass[0]); + if (pos == 0) + class[1] = amd64_merge_classes (class[1], subclass[1]); + } + } + + /* 4. Then a post merger cleanup is done: */ + + /* Rule (a): If one of the classes is MEMORY, the whole argument is + passed in memory. */ + if (class[0] == AMD64_MEMORY || class[1] == AMD64_MEMORY) + class[0] = class[1] = AMD64_MEMORY; + + /* Rule (b): If SSEUP is not preceeded by SSE, it is converted to + SSE. */ + if (class[0] == AMD64_SSEUP) + class[0] = AMD64_SSE; + if (class[1] == AMD64_SSEUP && class[0] != AMD64_SSE) + class[1] = AMD64_SSE; +} + +/* Classify TYPE, and store the result in CLASS. */ + +static void +amd64_classify (struct type *type, enum amd64_reg_class class[2]) +{ + enum type_code code = TYPE_CODE (type); + int len = TYPE_LENGTH (type); + + class[0] = class[1] = AMD64_NO_CLASS; + + /* Arguments of types (signed and unsigned) _Bool, char, short, int, + long, long long, and pointers are in the INTEGER class. Similarly, + range types, used by languages such as Ada, are also in the INTEGER + class. */ + if ((code == TYPE_CODE_INT || code == TYPE_CODE_ENUM + || code == TYPE_CODE_RANGE + || code == TYPE_CODE_PTR || code == TYPE_CODE_REF) + && (len == 1 || len == 2 || len == 4 || len == 8)) + class[0] = AMD64_INTEGER; + + /* Arguments of types float, double and __m64 are in class SSE. */ + else if (code == TYPE_CODE_FLT && (len == 4 || len == 8)) + /* FIXME: __m64 . */ + class[0] = AMD64_SSE; + + /* Arguments of types __float128 and __m128 are split into two + halves. The least significant ones belong to class SSE, the most + significant one to class SSEUP. */ + /* FIXME: __float128, __m128. */ + + /* The 64-bit mantissa of arguments of type long double belongs to + class X87, the 16-bit exponent plus 6 bytes of padding belongs to + class X87UP. */ + else if (code == TYPE_CODE_FLT && len == 16) + /* Class X87 and X87UP. */ + class[0] = AMD64_X87, class[1] = AMD64_X87UP; + + /* Aggregates. */ + else if (code == TYPE_CODE_ARRAY || code == TYPE_CODE_STRUCT + || code == TYPE_CODE_UNION) + amd64_classify_aggregate (type, class); +} + +static enum return_value_convention +amd64_return_value (struct gdbarch *gdbarch, struct type *type, + struct regcache *regcache, + void *readbuf, const void *writebuf) +{ + enum amd64_reg_class class[2]; + int len = TYPE_LENGTH (type); + static int integer_regnum[] = { AMD64_RAX_REGNUM, AMD64_RDX_REGNUM }; + static int sse_regnum[] = { AMD64_XMM0_REGNUM, AMD64_XMM1_REGNUM }; + int integer_reg = 0; + int sse_reg = 0; + int i; + + gdb_assert (!(readbuf && writebuf)); + + /* 1. Classify the return type with the classification algorithm. */ + amd64_classify (type, class); + + /* 2. If the type has class MEMORY, then the caller provides space + for the return value and passes the address of this storage in + %rdi as if it were the first argument to the function. In + effect, this address becomes a hidden first argument. */ + if (class[0] == AMD64_MEMORY) + return RETURN_VALUE_STRUCT_CONVENTION; + + gdb_assert (class[1] != AMD64_MEMORY); + gdb_assert (len <= 16); + + for (i = 0; len > 0; i++, len -= 8) + { + int regnum = -1; + int offset = 0; + + switch (class[i]) + { + case AMD64_INTEGER: + /* 3. If the class is INTEGER, the next available register + of the sequence %rax, %rdx is used. */ + regnum = integer_regnum[integer_reg++]; + break; + + case AMD64_SSE: + /* 4. If the class is SSE, the next available SSE register + of the sequence %xmm0, %xmm1 is used. */ + regnum = sse_regnum[sse_reg++]; + break; + + case AMD64_SSEUP: + /* 5. If the class is SSEUP, the eightbyte is passed in the + upper half of the last used SSE register. */ + gdb_assert (sse_reg > 0); + regnum = sse_regnum[sse_reg - 1]; + offset = 8; + break; + + case AMD64_X87: + /* 6. If the class is X87, the value is returned on the X87 + stack in %st0 as 80-bit x87 number. */ + regnum = AMD64_ST0_REGNUM; + if (writebuf) + i387_return_value (gdbarch, regcache); + break; + + case AMD64_X87UP: + /* 7. If the class is X87UP, the value is returned together + with the previous X87 value in %st0. */ + gdb_assert (i > 0 && class[0] == AMD64_X87); + regnum = AMD64_ST0_REGNUM; + offset = 8; + len = 2; + break; + + case AMD64_NO_CLASS: + continue; + + default: + gdb_assert (!"Unexpected register class."); + } + + gdb_assert (regnum != -1); + + if (readbuf) + regcache_raw_read_part (regcache, regnum, offset, min (len, 8), + (char *) readbuf + i * 8); + if (writebuf) + regcache_raw_write_part (regcache, regnum, offset, min (len, 8), + (const char *) writebuf + i * 8); + } + + return RETURN_VALUE_REGISTER_CONVENTION; +} + + +static CORE_ADDR +amd64_push_arguments (struct regcache *regcache, int nargs, + struct value **args, CORE_ADDR sp, int struct_return) +{ + static int integer_regnum[] = + { + AMD64_RDI_REGNUM, /* %rdi */ + AMD64_RSI_REGNUM, /* %rsi */ + AMD64_RDX_REGNUM, /* %rdx */ + AMD64_RCX_REGNUM, /* %rcx */ + 8, /* %r8 */ + 9 /* %r9 */ + }; + static int sse_regnum[] = + { + /* %xmm0 ... %xmm7 */ + AMD64_XMM0_REGNUM + 0, AMD64_XMM1_REGNUM, + AMD64_XMM0_REGNUM + 2, AMD64_XMM0_REGNUM + 3, + AMD64_XMM0_REGNUM + 4, AMD64_XMM0_REGNUM + 5, + AMD64_XMM0_REGNUM + 6, AMD64_XMM0_REGNUM + 7, + }; + struct value **stack_args = alloca (nargs * sizeof (struct value *)); + int num_stack_args = 0; + int num_elements = 0; + int element = 0; + int integer_reg = 0; + int sse_reg = 0; + int i; + + /* Reserve a register for the "hidden" argument. */ + if (struct_return) + integer_reg++; + + for (i = 0; i < nargs; i++) + { + struct type *type = VALUE_TYPE (args[i]); + int len = TYPE_LENGTH (type); + enum amd64_reg_class class[2]; + int needed_integer_regs = 0; + int needed_sse_regs = 0; + int j; + + /* Classify argument. */ + amd64_classify (type, class); + + /* Calculate the number of integer and SSE registers needed for + this argument. */ + for (j = 0; j < 2; j++) + { + if (class[j] == AMD64_INTEGER) + needed_integer_regs++; + else if (class[j] == AMD64_SSE) + needed_sse_regs++; + } + + /* Check whether enough registers are available, and if the + argument should be passed in registers at all. */ + if (integer_reg + needed_integer_regs > ARRAY_SIZE (integer_regnum) + || sse_reg + needed_sse_regs > ARRAY_SIZE (sse_regnum) + || (needed_integer_regs == 0 && needed_sse_regs == 0)) + { + /* The argument will be passed on the stack. */ + num_elements += ((len + 7) / 8); + stack_args[num_stack_args++] = args[i]; + } + else + { + /* The argument will be passed in registers. */ + char *valbuf = VALUE_CONTENTS (args[i]); + char buf[8]; + + gdb_assert (len <= 16); + + for (j = 0; len > 0; j++, len -= 8) + { + int regnum = -1; + int offset = 0; + + switch (class[j]) + { + case AMD64_INTEGER: + regnum = integer_regnum[integer_reg++]; + break; + + case AMD64_SSE: + regnum = sse_regnum[sse_reg++]; + break; + + case AMD64_SSEUP: + gdb_assert (sse_reg > 0); + regnum = sse_regnum[sse_reg - 1]; + offset = 8; + break; + + default: + gdb_assert (!"Unexpected register class."); + } + + gdb_assert (regnum != -1); + memset (buf, 0, sizeof buf); + memcpy (buf, valbuf + j * 8, min (len, 8)); + regcache_raw_write_part (regcache, regnum, offset, 8, buf); + } + } + } + + /* Allocate space for the arguments on the stack. */ + sp -= num_elements * 8; + + /* The psABI says that "The end of the input argument area shall be + aligned on a 16 byte boundary." */ + sp &= ~0xf; + + /* Write out the arguments to the stack. */ + for (i = 0; i < num_stack_args; i++) + { + struct type *type = VALUE_TYPE (stack_args[i]); + char *valbuf = VALUE_CONTENTS (stack_args[i]); + int len = TYPE_LENGTH (type); + + write_memory (sp + element * 8, valbuf, len); + element += ((len + 7) / 8); + } + + /* The psABI says that "For calls that may call functions that use + varargs or stdargs (prototype-less calls or calls to functions + containing ellipsis (...) in the declaration) %al is used as + hidden argument to specify the number of SSE registers used. */ + regcache_raw_write_unsigned (regcache, AMD64_RAX_REGNUM, sse_reg); + return sp; +} + +static CORE_ADDR +amd64_push_dummy_call (struct gdbarch *gdbarch, CORE_ADDR func_addr, + struct regcache *regcache, CORE_ADDR bp_addr, + int nargs, struct value **args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr) +{ + char buf[8]; + + /* Pass arguments. */ + sp = amd64_push_arguments (regcache, nargs, args, sp, struct_return); + + /* Pass "hidden" argument". */ + if (struct_return) + { + store_unsigned_integer (buf, 8, struct_addr); + regcache_cooked_write (regcache, AMD64_RDI_REGNUM, buf); + } + + /* Store return address. */ + sp -= 8; + store_unsigned_integer (buf, 8, bp_addr); + write_memory (sp, buf, 8); + + /* Finally, update the stack pointer... */ + store_unsigned_integer (buf, 8, sp); + regcache_cooked_write (regcache, AMD64_RSP_REGNUM, buf); + + /* ...and fake a frame pointer. */ + regcache_cooked_write (regcache, AMD64_RBP_REGNUM, buf); + + return sp + 16; +} + + +/* The maximum number of saved registers. This should include %rip. */ +#define AMD64_NUM_SAVED_REGS AMD64_NUM_GREGS + +struct amd64_frame_cache +{ + /* Base address. */ + CORE_ADDR base; + CORE_ADDR sp_offset; + CORE_ADDR pc; + + /* Saved registers. */ + CORE_ADDR saved_regs[AMD64_NUM_SAVED_REGS]; + CORE_ADDR saved_sp; + + /* Do we have a frame? */ + int frameless_p; +}; + +/* Allocate and initialize a frame cache. */ + +static struct amd64_frame_cache * +amd64_alloc_frame_cache (void) +{ + struct amd64_frame_cache *cache; + int i; + + cache = FRAME_OBSTACK_ZALLOC (struct amd64_frame_cache); + + /* Base address. */ + cache->base = 0; + cache->sp_offset = -8; + cache->pc = 0; + + /* Saved registers. We initialize these to -1 since zero is a valid + offset (that's where %rbp is supposed to be stored). */ + for (i = 0; i < AMD64_NUM_SAVED_REGS; i++) + cache->saved_regs[i] = -1; + cache->saved_sp = 0; + + /* Frameless until proven otherwise. */ + cache->frameless_p = 1; + + return cache; +} + +/* Do a limited analysis of the prologue at PC and update CACHE + accordingly. Bail out early if CURRENT_PC is reached. Return the + address where the analysis stopped. + + We will handle only functions beginning with: + + pushq %rbp 0x55 + movq %rsp, %rbp 0x48 0x89 0xe5 + + Any function that doesn't start with this sequence will be assumed + to have no prologue and thus no valid frame pointer in %rbp. */ + +static CORE_ADDR +amd64_analyze_prologue (CORE_ADDR pc, CORE_ADDR current_pc, + struct amd64_frame_cache *cache) +{ + static unsigned char proto[3] = { 0x48, 0x89, 0xe5 }; + unsigned char buf[3]; + unsigned char op; + + if (current_pc <= pc) + return current_pc; + + op = read_memory_unsigned_integer (pc, 1); + + if (op == 0x55) /* pushq %rbp */ + { + /* Take into account that we've executed the `pushq %rbp' that + starts this instruction sequence. */ + cache->saved_regs[AMD64_RBP_REGNUM] = 0; + cache->sp_offset += 8; + + /* If that's all, return now. */ + if (current_pc <= pc + 1) + return current_pc; + + /* Check for `movq %rsp, %rbp'. */ + read_memory (pc + 1, buf, 3); + if (memcmp (buf, proto, 3) != 0) + return pc + 1; + + /* OK, we actually have a frame. */ + cache->frameless_p = 0; + return pc + 4; + } + + return pc; +} + +/* Return PC of first real instruction. */ + +static CORE_ADDR +amd64_skip_prologue (CORE_ADDR start_pc) +{ + struct amd64_frame_cache cache; + CORE_ADDR pc; + + pc = amd64_analyze_prologue (start_pc, 0xffffffffffffffff, &cache); + if (cache.frameless_p) + return start_pc; + + return pc; +} + + +/* Normal frames. */ + +static struct amd64_frame_cache * +amd64_frame_cache (struct frame_info *next_frame, void **this_cache) +{ + struct amd64_frame_cache *cache; + char buf[8]; + int i; + + if (*this_cache) + return *this_cache; + + cache = amd64_alloc_frame_cache (); + *this_cache = cache; + + cache->pc = frame_func_unwind (next_frame); + if (cache->pc != 0) + amd64_analyze_prologue (cache->pc, frame_pc_unwind (next_frame), cache); + + if (cache->frameless_p) + { + /* We didn't find a valid frame. If we're at the start of a + function, or somewhere half-way its prologue, the function's + frame probably hasn't been fully setup yet. Try to + reconstruct the base address for the stack frame by looking + at the stack pointer. For truly "frameless" functions this + might work too. */ + + frame_unwind_register (next_frame, AMD64_RSP_REGNUM, buf); + cache->base = extract_unsigned_integer (buf, 8) + cache->sp_offset; + } + else + { + frame_unwind_register (next_frame, AMD64_RBP_REGNUM, buf); + cache->base = extract_unsigned_integer (buf, 8); + } + + /* Now that we have the base address for the stack frame we can + calculate the value of %rsp in the calling frame. */ + cache->saved_sp = cache->base + 16; + + /* For normal frames, %rip is stored at 8(%rbp). If we don't have a + frame we find it at the same offset from the reconstructed base + address. */ + cache->saved_regs[AMD64_RIP_REGNUM] = 8; + + /* Adjust all the saved registers such that they contain addresses + instead of offsets. */ + for (i = 0; i < AMD64_NUM_SAVED_REGS; i++) + if (cache->saved_regs[i] != -1) + cache->saved_regs[i] += cache->base; + + return cache; +} + +static void +amd64_frame_this_id (struct frame_info *next_frame, void **this_cache, + struct frame_id *this_id) +{ + struct amd64_frame_cache *cache = + amd64_frame_cache (next_frame, this_cache); + + /* This marks the outermost frame. */ + if (cache->base == 0) + return; + + (*this_id) = frame_id_build (cache->base + 16, cache->pc); +} + +static void +amd64_frame_prev_register (struct frame_info *next_frame, void **this_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnump, void *valuep) +{ + struct amd64_frame_cache *cache = + amd64_frame_cache (next_frame, this_cache); + + gdb_assert (regnum >= 0); + + if (regnum == SP_REGNUM && cache->saved_sp) + { + *optimizedp = 0; + *lvalp = not_lval; + *addrp = 0; + *realnump = -1; + if (valuep) + { + /* Store the value. */ + store_unsigned_integer (valuep, 8, cache->saved_sp); + } + return; + } + + if (regnum < AMD64_NUM_SAVED_REGS && cache->saved_regs[regnum] != -1) + { + *optimizedp = 0; + *lvalp = lval_memory; + *addrp = cache->saved_regs[regnum]; + *realnump = -1; + if (valuep) + { + /* Read the value in from memory. */ + read_memory (*addrp, valuep, + register_size (current_gdbarch, regnum)); + } + return; + } + + frame_register_unwind (next_frame, regnum, + optimizedp, lvalp, addrp, realnump, valuep); +} + +static const struct frame_unwind amd64_frame_unwind = +{ + NORMAL_FRAME, + amd64_frame_this_id, + amd64_frame_prev_register +}; + +static const struct frame_unwind * +amd64_frame_sniffer (struct frame_info *next_frame) +{ + return &amd64_frame_unwind; +} + + +/* Signal trampolines. */ + +/* FIXME: kettenis/20030419: Perhaps, we can unify the 32-bit and + 64-bit variants. This would require using identical frame caches + on both platforms. */ + +static struct amd64_frame_cache * +amd64_sigtramp_frame_cache (struct frame_info *next_frame, void **this_cache) +{ + struct amd64_frame_cache *cache; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + CORE_ADDR addr; + char buf[8]; + int i; + + if (*this_cache) + return *this_cache; + + cache = amd64_alloc_frame_cache (); + + frame_unwind_register (next_frame, AMD64_RSP_REGNUM, buf); + cache->base = extract_unsigned_integer (buf, 8) - 8; + + addr = tdep->sigcontext_addr (next_frame); + gdb_assert (tdep->sc_reg_offset); + gdb_assert (tdep->sc_num_regs <= AMD64_NUM_SAVED_REGS); + for (i = 0; i < tdep->sc_num_regs; i++) + if (tdep->sc_reg_offset[i] != -1) + cache->saved_regs[i] = addr + tdep->sc_reg_offset[i]; + + *this_cache = cache; + return cache; +} + +static void +amd64_sigtramp_frame_this_id (struct frame_info *next_frame, + void **this_cache, struct frame_id *this_id) +{ + struct amd64_frame_cache *cache = + amd64_sigtramp_frame_cache (next_frame, this_cache); + + (*this_id) = frame_id_build (cache->base + 16, frame_pc_unwind (next_frame)); +} + +static void +amd64_sigtramp_frame_prev_register (struct frame_info *next_frame, + void **this_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnump, void *valuep) +{ + /* Make sure we've initialized the cache. */ + amd64_sigtramp_frame_cache (next_frame, this_cache); + + amd64_frame_prev_register (next_frame, this_cache, regnum, + optimizedp, lvalp, addrp, realnump, valuep); +} + +static const struct frame_unwind amd64_sigtramp_frame_unwind = +{ + SIGTRAMP_FRAME, + amd64_sigtramp_frame_this_id, + amd64_sigtramp_frame_prev_register +}; + +static const struct frame_unwind * +amd64_sigtramp_frame_sniffer (struct frame_info *next_frame) +{ + CORE_ADDR pc = frame_pc_unwind (next_frame); + char *name; + + find_pc_partial_function (pc, &name, NULL, NULL); + if (DEPRECATED_PC_IN_SIGTRAMP (pc, name)) + { + gdb_assert (gdbarch_tdep (current_gdbarch)->sigcontext_addr); + + return &amd64_sigtramp_frame_unwind; + } + + return NULL; +} + + +static CORE_ADDR +amd64_frame_base_address (struct frame_info *next_frame, void **this_cache) +{ + struct amd64_frame_cache *cache = + amd64_frame_cache (next_frame, this_cache); + + return cache->base; +} + +static const struct frame_base amd64_frame_base = +{ + &amd64_frame_unwind, + amd64_frame_base_address, + amd64_frame_base_address, + amd64_frame_base_address +}; + +static struct frame_id +amd64_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + char buf[8]; + CORE_ADDR fp; + + frame_unwind_register (next_frame, AMD64_RBP_REGNUM, buf); + fp = extract_unsigned_integer (buf, 8); + + return frame_id_build (fp + 16, frame_pc_unwind (next_frame)); +} + +/* 16 byte align the SP per frame requirements. */ + +static CORE_ADDR +amd64_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) +{ + return sp & -(CORE_ADDR)16; +} + + +/* Supply register REGNUM from the floating-point register set REGSET + to register cache REGCACHE. If REGNUM is -1, do this for all + registers in REGSET. */ + +static void +amd64_supply_fpregset (const struct regset *regset, struct regcache *regcache, + int regnum, const void *fpregs, size_t len) +{ + const struct gdbarch_tdep *tdep = regset->descr; + + gdb_assert (len == tdep->sizeof_fpregset); + amd64_supply_fxsave (regcache, regnum, fpregs); +} + +/* Return the appropriate register set for the core section identified + by SECT_NAME and SECT_SIZE. */ + +static const struct regset * +amd64_regset_from_core_section (struct gdbarch *gdbarch, + const char *sect_name, size_t sect_size) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (strcmp (sect_name, ".reg2") == 0 && sect_size == tdep->sizeof_fpregset) + { + if (tdep->fpregset == NULL) + { + tdep->fpregset = XMALLOC (struct regset); + tdep->fpregset->descr = tdep; + tdep->fpregset->supply_regset = amd64_supply_fpregset; + } + + return tdep->fpregset; + } + + return i386_regset_from_core_section (gdbarch, sect_name, sect_size); +} + + +void +amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + /* AMD64 generally uses `fxsave' instead of `fsave' for saving its + floating-point registers. */ + tdep->sizeof_fpregset = I387_SIZEOF_FXSAVE; + + /* AMD64 has an FPU and 16 SSE registers. */ + tdep->st0_regnum = AMD64_ST0_REGNUM; + tdep->num_xmm_regs = 16; + + /* This is what all the fuss is about. */ + set_gdbarch_long_bit (gdbarch, 64); + set_gdbarch_long_long_bit (gdbarch, 64); + set_gdbarch_ptr_bit (gdbarch, 64); + + /* In contrast to the i386, on AMD64 a `long double' actually takes + up 128 bits, even though it's still based on the i387 extended + floating-point format which has only 80 significant bits. */ + set_gdbarch_long_double_bit (gdbarch, 128); + + set_gdbarch_num_regs (gdbarch, AMD64_NUM_REGS); + set_gdbarch_register_name (gdbarch, amd64_register_name); + set_gdbarch_register_type (gdbarch, amd64_register_type); + + /* Register numbers of various important registers. */ + set_gdbarch_sp_regnum (gdbarch, AMD64_RSP_REGNUM); /* %rsp */ + set_gdbarch_pc_regnum (gdbarch, AMD64_RIP_REGNUM); /* %rip */ + set_gdbarch_ps_regnum (gdbarch, AMD64_EFLAGS_REGNUM); /* %eflags */ + set_gdbarch_fp0_regnum (gdbarch, AMD64_ST0_REGNUM); /* %st(0) */ + + /* The "default" register numbering scheme for AMD64 is referred to + as the "DWARF Register Number Mapping" in the System V psABI. + The preferred debugging format for all known AMD64 targets is + actually DWARF2, and GCC doesn't seem to support DWARF (that is + DWARF-1), but we provide the same mapping just in case. This + mapping is also used for stabs, which GCC does support. */ + set_gdbarch_stab_reg_to_regnum (gdbarch, amd64_dwarf_reg_to_regnum); + set_gdbarch_dwarf_reg_to_regnum (gdbarch, amd64_dwarf_reg_to_regnum); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, amd64_dwarf_reg_to_regnum); + + /* We don't override SDB_REG_RO_REGNUM, since COFF doesn't seem to + be in use on any of the supported AMD64 targets. */ + + /* Call dummy code. */ + set_gdbarch_push_dummy_call (gdbarch, amd64_push_dummy_call); + set_gdbarch_frame_align (gdbarch, amd64_frame_align); + set_gdbarch_frame_red_zone_size (gdbarch, 128); + + set_gdbarch_convert_register_p (gdbarch, amd64_convert_register_p); + set_gdbarch_register_to_value (gdbarch, i387_register_to_value); + set_gdbarch_value_to_register (gdbarch, i387_value_to_register); + + set_gdbarch_return_value (gdbarch, amd64_return_value); + + set_gdbarch_skip_prologue (gdbarch, amd64_skip_prologue); + + /* Avoid wiring in the MMX registers for now. */ + set_gdbarch_num_pseudo_regs (gdbarch, 0); + tdep->mm0_regnum = -1; + + set_gdbarch_unwind_dummy_id (gdbarch, amd64_unwind_dummy_id); + + /* FIXME: kettenis/20021026: This is ELF-specific. Fine for now, + since all supported AMD64 targets are ELF, but that might change + in the future. */ + set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section); + + frame_unwind_append_sniffer (gdbarch, amd64_sigtramp_frame_sniffer); + frame_unwind_append_sniffer (gdbarch, amd64_frame_sniffer); + frame_base_set_default (gdbarch, &amd64_frame_base); + + /* If we have a register mapping, enable the generic core file support. */ + if (tdep->gregset_reg_offset) + set_gdbarch_regset_from_core_section (gdbarch, + amd64_regset_from_core_section); +} + + +#define I387_ST0_REGNUM AMD64_ST0_REGNUM + +/* The 64-bit FXSAVE format differs from the 32-bit format in the + sense that the instruction pointer and data pointer are simply + 64-bit offsets into the code segment and the data segment instead + of a selector offset pair. The functions below store the upper 32 + bits of these pointers (instead of just the 16-bits of the segment + selector). */ + +/* Fill register REGNUM in REGCACHE with the appropriate + floating-point or SSE register value from *FXSAVE. If REGNUM is + -1, do this for all registers. This function masks off any of the + reserved bits in *FXSAVE. */ + +void +amd64_supply_fxsave (struct regcache *regcache, int regnum, + const void *fxsave) +{ + i387_supply_fxsave (regcache, regnum, fxsave); + + if (fxsave) + { + const char *regs = fxsave; + + if (regnum == -1 || regnum == I387_FISEG_REGNUM) + regcache_raw_supply (regcache, I387_FISEG_REGNUM, regs + 12); + if (regnum == -1 || regnum == I387_FOSEG_REGNUM) + regcache_raw_supply (regcache, I387_FOSEG_REGNUM, regs + 20); + } +} + +/* Fill register REGNUM (if it is a floating-point or SSE register) in + *FXSAVE with the value from REGCACHE. If REGNUM is -1, do this for + all registers. This function doesn't touch any of the reserved + bits in *FXSAVE. */ + +void +amd64_collect_fxsave (const struct regcache *regcache, int regnum, + void *fxsave) +{ + char *regs = fxsave; + + i387_collect_fxsave (regcache, regnum, fxsave); + + if (regnum == -1 || regnum == I387_FISEG_REGNUM) + regcache_raw_collect (regcache, I387_FISEG_REGNUM, regs + 12); + if (regnum == -1 || regnum == I387_FOSEG_REGNUM) + regcache_raw_collect (regcache, I387_FOSEG_REGNUM, regs + 20); +} diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h new file mode 100644 index 0000000..bfc549b --- /dev/null +++ b/gdb/amd64-tdep.h @@ -0,0 +1,86 @@ +/* Target-dependent definitions for AMD64. + + Copyright 2001, 2003, 2004 Free Software Foundation, Inc. + Contributed by Jiri Smid, SuSE Labs. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef AMD64_TDEP_H +#define AMD64_TDEP_H + +struct gdbarch; +struct frame_info; +struct regcache; + +#include "i386-tdep.h" + +/* Register numbers of various important registers. */ + +enum amd64_regnum +{ + AMD64_RAX_REGNUM, /* %rax */ + AMD64_RBX_REGNUM, /* %rbx */ + AMD64_RCX_REGNUM, /* %rcx */ + AMD64_RDX_REGNUM, /* %rdx */ + AMD64_RSI_REGNUM, /* %rsi */ + AMD64_RDI_REGNUM, /* %rdi */ + AMD64_RBP_REGNUM, /* %rbp */ + AMD64_RSP_REGNUM, /* %rsp */ + AMD64_R8_REGNUM = 8, /* %r8 */ + AMD64_R15_REGNUM = 15, /* %r15 */ + AMD64_RIP_REGNUM, /* %rip */ + AMD64_EFLAGS_REGNUM, /* %eflags */ + AMD64_ST0_REGNUM = 24, /* %st0 */ + AMD64_XMM0_REGNUM = 40, /* %xmm0 */ + AMD64_XMM1_REGNUM /* %xmm1 */ +}; + +/* Number of general purpose registers. */ +#define AMD64_NUM_GREGS 24 + +extern void amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch); + +/* Fill register REGNUM in REGCACHE with the appropriate + floating-point or SSE register value from *FXSAVE. If REGNUM is + -1, do this for all registers. This function masks off any of the + reserved bits in *FXSAVE. */ + +extern void amd64_supply_fxsave (struct regcache *regcache, int regnum, + const void *fxsave); + +/* Fill register REGNUM (if it is a floating-point or SSE register) in + *FXSAVE with the value from REGCACHE. If REGNUM is -1, do this for + all registers. This function doesn't touch any of the reserved + bits in *FXSAVE. */ + +extern void amd64_collect_fxsave (const struct regcache *regcache, int regnum, + void *fxsave); + + +/* Variables exported from amd64nbsd-tdep.c. */ +extern int amd64nbsd_r_reg_offset[]; + +/* Variables exported from amd64obsd-tdep.c. */ +extern int amd64obsd_r_reg_offset[]; + +/* Variables exported from amd64fbsd-tdep.c. */ +extern CORE_ADDR amd64fbsd_sigtramp_start_addr; +extern CORE_ADDR amd64fbsd_sigtramp_end_addr; +extern int amd64fbsd_sc_reg_offset[]; + +#endif /* amd64-tdep.h */ diff --git a/gdb/config/i386/linux64.mh b/gdb/config/i386/linux64.mh new file mode 100644 index 0000000..c02e864 --- /dev/null +++ b/gdb/config/i386/linux64.mh @@ -0,0 +1,10 @@ +# Host: GNU/Linux x86-64 +NATDEPFILES= infptrace.o inftarg.o fork-child.o \ + i386-nat.o amd64-nat.o amd64-linux-nat.o linux-nat.o \ + proc-service.o thread-db.o lin-lwp.o linux-proc.o gcore.o +NAT_FILE= nm-linux64.h +XM_FILE= xm-i386.h + +# The dynamically loaded libthread_db needs access to symbols in the +# gdb executable. +LOADLIBES = -ldl -rdynamic diff --git a/gdb/config/i386/linux64.mt b/gdb/config/i386/linux64.mt new file mode 100644 index 0000000..f642e31 --- /dev/null +++ b/gdb/config/i386/linux64.mt @@ -0,0 +1,5 @@ +# Target: GNU/Linux x86-64 +TDEPFILES= amd64-tdep.o amd64-linux-tdep.o \ + i386-tdep.o i387-tdep.o i386-linux-tdep.o glibc-tdep.o \ + solib.o solib-svr4.o solib-legacy.o corelow.o +TM_FILE=tm-linux64.h diff --git a/gdb/config/i386/nm-linux64.h b/gdb/config/i386/nm-linux64.h new file mode 100644 index 0000000..1764758 --- /dev/null +++ b/gdb/config/i386/nm-linux64.h @@ -0,0 +1,69 @@ +/* Native support for GNU/Linux x86-64. + + Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + + Contributed by Jiri Smid, SuSE Labs. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef NM_LINUX64_H +#define NM_LINUX64_H + +/* GNU/Linux supports the i386 hardware debugging registers. */ +#define I386_USE_GENERIC_WATCHPOINTS + +#include "i386/nm-i386.h" +#include "config/nm-linux.h" + +/* Support for 8-byte wide hardware watchpoints. */ +#define TARGET_HAS_DR_LEN_8 1 + +/* Provide access to the i386 hardware debugging registers. */ + +extern void amd64_linux_dr_set_control (unsigned long control); +#define I386_DR_LOW_SET_CONTROL(control) \ + amd64_linux_dr_set_control (control) + +extern void amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr); +#define I386_DR_LOW_SET_ADDR(regnum, addr) \ + amd64_linux_dr_set_addr (regnum, addr) + +extern void amd64_linux_dr_reset_addr (int regnum); +#define I386_DR_LOW_RESET_ADDR(regnum) \ + amd64_linux_dr_reset_addr (regnum) + +extern unsigned long amd64_linux_dr_get_status (void); +#define I386_DR_LOW_GET_STATUS() \ + amd64_linux_dr_get_status () + + +/* Type of the third argument to the `ptrace' system call. */ +#define PTRACE_ARG3_TYPE long + +/* Type of the fourth argument to the `ptrace' system call. */ +#define PTRACE_XFER_TYPE long + +/* Override copies of {fetch,store}_inferior_registers in `infptrace.c'. */ +#define FETCH_INFERIOR_REGISTERS + +/* `linux-nat.c' and `i386-nat.c' have their own versions of + child_post_startup_inferior. Define this to use the copy in + `x86-86-linux-nat.c' instead, which calls both. */ +#define LINUX_CHILD_POST_STARTUP_INFERIOR + +#endif /* nm-linux64.h */ diff --git a/gdb/config/i386/obsdaout.mh b/gdb/config/i386/obsdaout.mh new file mode 100644 index 0000000..3577178 --- /dev/null +++ b/gdb/config/i386/obsdaout.mh @@ -0,0 +1,5 @@ +# Host: OpenBSD/i386 a.out +NATDEPFILES= fork-child.o infptrace.o inftarg.o i386bsd-nat.o i386obsd-nat.o \ + solib.o solib-sunos.o +NAT_FILE= nm-obsd.h +XM_FILE= xm-i386.h diff --git a/gdb/config/i386/tm-linux64.h b/gdb/config/i386/tm-linux64.h new file mode 100644 index 0000000..c3ff352 --- /dev/null +++ b/gdb/config/i386/tm-linux64.h @@ -0,0 +1,36 @@ +/* Target-dependent definitions for GNU/Linux x86-64. + + Copyright 2002, 2003, 2004 Free Software Foundation, Inc. + + Contributed by Michal Ludvig, SuSE Labs. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef TM_LINUX64_H +#define TM_LINUX64_H + +/* We define SVR4_SHARED_LIBS unconditionally, on the assumption that + link.h is available on all linux platforms. For I386 and SH3/4, we + hard-code the information rather than use link.h anyway (for the + benefit of cross-debugging). We may move to doing that for other + architectures as well. */ + +#define SVR4_SHARED_LIBS +#include "solib.h" /* Support for shared libraries. */ + +#endif /* tm-linux64.h */ diff --git a/gdb/config/nm-bsd.h b/gdb/config/nm-bsd.h new file mode 100644 index 0000000..b44dd5e --- /dev/null +++ b/gdb/config/nm-bsd.h @@ -0,0 +1,29 @@ +/* Native-dependent definitions for *BSD. + + Copyright 2001, 2004 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* Type of the third argument to the `ptrace' system call. */ +#define PTRACE_ARG3_TYPE caddr_t + +/* Override copies of {fetch,store}_inferior_registers in `infptrace.c'. */ +#define FETCH_INFERIOR_REGISTERS + +/* We can attach and detach. */ +#define ATTACH_DETACH diff --git a/gdb/frv-linux-tdep.c b/gdb/frv-linux-tdep.c new file mode 100644 index 0000000..e862625 --- /dev/null +++ b/gdb/frv-linux-tdep.c @@ -0,0 +1,273 @@ +/* Target-dependent code for GNU/Linux running on the Fujitsu FR-V, + for GDB. + Copyright 2004 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "target.h" +#include "frame.h" +#include "osabi.h" +#include "elf-bfd.h" +#include "elf/frv.h" +#include "frv-tdep.h" + +/* Define the size (in bytes) of an FR-V instruction. */ +static const int frv_instr_size = 4; + +enum { + NORMAL_SIGTRAMP = 1, + RT_SIGTRAMP = 2 +}; + +static int +frv_linux_pc_in_sigtramp (CORE_ADDR pc, char *name) +{ + char buf[frv_instr_size]; + LONGEST instr; + int retval = 0; + + if (target_read_memory (pc, buf, sizeof buf) != 0) + return 0; + + instr = extract_unsigned_integer (buf, sizeof buf); + + if (instr == 0x8efc0077) /* setlos #__NR_sigreturn, gr7 */ + retval = NORMAL_SIGTRAMP; + else if (instr -= 0x8efc00ad) /* setlos #__NR_rt_sigreturn, gr7 */ + retval = RT_SIGTRAMP; + else + return 0; + + if (target_read_memory (pc + frv_instr_size, buf, sizeof buf) != 0) + return 0; + instr = extract_unsigned_integer (buf, sizeof buf); + if (instr != 0xc0700000) /* tira gr0, 0 */ + return 0; + + /* If we get this far, we'll return a non-zero value, either + NORMAL_SIGTRAMP (1) or RT_SIGTRAMP (2). */ + return retval; +} + +/* Given NEXT_FRAME, "callee" frame of the sigtramp frame that we + wish to decode, and REGNO, one of the frv register numbers defined + in frv-tdep.h, return the address of the saved register (corresponding + to REGNO) in the sigtramp frame. Return -1 if the register is not + found in the sigtramp frame. The magic numbers in the code below + were computed by examining the following kernel structs: + + From arch/frvnommu/signal.c: + + struct sigframe + { + void (*pretcode)(void); + int sig; + struct sigcontext sc; + unsigned long extramask[_NSIG_WORDS-1]; + uint32_t retcode[2]; + }; + + struct rt_sigframe + { + void (*pretcode)(void); + int sig; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; + uint32_t retcode[2]; + }; + + From include/asm-frvnommu/ucontext.h: + + struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; + }; + + From include/asm-frvnommu/sigcontext.h: + + struct sigcontext { + struct user_context sc_context; + unsigned long sc_oldmask; + } __attribute__((aligned(8))); + + From include/asm-frvnommu/registers.h: + struct user_int_regs + { + unsigned long psr; + unsigned long isr; + unsigned long ccr; + unsigned long cccr; + unsigned long lr; + unsigned long lcr; + unsigned long pc; + unsigned long __status; + unsigned long syscallno; + unsigned long orig_gr8; + unsigned long gner[2]; + unsigned long long iacc[1]; + + union { + unsigned long tbr; + unsigned long gr[64]; + }; + }; + + struct user_fpmedia_regs + { + unsigned long fr[64]; + unsigned long fner[2]; + unsigned long msr[2]; + unsigned long acc[8]; + unsigned char accg[8]; + unsigned long fsr[1]; + }; + + struct user_context + { + struct user_int_regs i; + struct user_fpmedia_regs f; + + void *extension; + } __attribute__((aligned(8))); */ + +static CORE_ADDR +frv_linux_sigcontext_reg_addr (struct frame_info *next_frame, int regno, + CORE_ADDR *sc_addr_cache_ptr) +{ + CORE_ADDR sc_addr; + + if (sc_addr_cache_ptr && *sc_addr_cache_ptr) + { + sc_addr = *sc_addr_cache_ptr; + } + else + { + CORE_ADDR pc, sp; + char buf[4]; + int tramp_type; + + pc = frame_pc_unwind (next_frame); + tramp_type = frv_linux_pc_in_sigtramp (pc, 0); + + frame_unwind_register (next_frame, sp_regnum, buf); + sp = extract_unsigned_integer (buf, sizeof buf); + + if (tramp_type == NORMAL_SIGTRAMP) + { + /* For a normal sigtramp frame, the sigcontext struct starts + at SP + 8. */ + sc_addr = sp + 8; + } + else if (tramp_type == RT_SIGTRAMP) + { + /* For a realtime sigtramp frame, SP + 12 contains a pointer + to the a ucontext struct. The ucontext struct contains + a sigcontext struct starting 12 bytes in. */ + if (target_read_memory (sp + 12, buf, sizeof buf) != 0) + { + warning ("Can't read realtime sigtramp frame."); + return 0; + } + sc_addr = extract_unsigned_integer (buf, sizeof buf); + sc_addr += 12; + } + else + internal_error (__FILE__, __LINE__, "not a signal trampoline"); + + if (sc_addr_cache_ptr) + *sc_addr_cache_ptr = sc_addr; + } + + switch (regno) + { + case psr_regnum : + return sc_addr + 0; + /* sc_addr + 4 has "isr", the Integer Status Register. */ + case ccr_regnum : + return sc_addr + 8; + case cccr_regnum : + return sc_addr + 12; + case lr_regnum : + return sc_addr + 16; + case lcr_regnum : + return sc_addr + 20; + case pc_regnum : + return sc_addr + 24; + /* sc_addr + 28 is __status, the exception status. + sc_addr + 32 is syscallno, the syscall number or -1. + sc_addr + 36 is orig_gr8, the original syscall arg #1. + sc_addr + 40 is gner[0]. + sc_addr + 44 is gner[1]. */ + case iacc0h_regnum : + return sc_addr + 48; + case iacc0l_regnum : + return sc_addr + 52; + default : + if (first_gpr_regnum <= regno && regno <= last_gpr_regnum) + return sc_addr + 56 + 4 * (regno - first_gpr_regnum); + else if (first_fpr_regnum <= regno && regno <= last_fpr_regnum) + return sc_addr + 312 + 4 * (regno - first_fpr_regnum); + else + return -1; /* not saved. */ + } +} + +static void +frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + /* When the FR-V Linux kernel calls a signal handler, the return + address points to a bit of code on the stack. This function is + used to identify this bit of code as a signal trampoline in order + to support backtracing through calls to signal handlers. */ + set_gdbarch_deprecated_pc_in_sigtramp (gdbarch, frv_linux_pc_in_sigtramp); + frv_set_sigcontext_reg_addr (gdbarch, frv_linux_sigcontext_reg_addr); +} + +static enum gdb_osabi +frv_linux_elf_osabi_sniffer (bfd *abfd) +{ + int elf_flags; + + elf_flags = elf_elfheader (abfd)->e_flags; + + /* Assume GNU/Linux if using the FDPIC ABI. If/when another OS shows + up that uses this ABI, we'll need to start using .note sections + or some such. */ + if (elf_flags & EF_FRV_FDPIC) + return GDB_OSABI_LINUX; + else + return GDB_OSABI_UNKNOWN; +} + +/* Provide a prototype to silence -Wmissing-prototypes. */ +void _initialize_frv_linux_tdep (void); + +void +_initialize_frv_linux_tdep (void) +{ + gdbarch_register_osabi (bfd_arch_frv, 0, GDB_OSABI_LINUX, frv_linux_init_abi); + gdbarch_register_osabi_sniffer (bfd_arch_frv, + bfd_target_elf_flavour, + frv_linux_elf_osabi_sniffer); +} diff --git a/gdb/frv-tdep.h b/gdb/frv-tdep.h new file mode 100644 index 0000000..5b9b88b --- /dev/null +++ b/gdb/frv-tdep.h @@ -0,0 +1,105 @@ +/* Architecture-dependent code for the Fujitsu FR-V, for GDB, the GNU Debugger. + Copyright 2004 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* Enumerate the possible ABIs for FR-V. */ +enum frv_abi + { + FRV_ABI_EABI, + FRV_ABI_FDPIC + }; + +/* Register numbers. The order in which these appear define the + remote protocol, so take care in changing them. */ +enum { + /* Register numbers 0 -- 63 are always reserved for general-purpose + registers. The chip at hand may have less. */ + first_gpr_regnum = 0, + sp_regnum = 1, + fp_regnum = 2, + struct_return_regnum = 3, + last_gpr_regnum = 63, + + /* Register numbers 64 -- 127 are always reserved for floating-point + registers. The chip at hand may have less. */ + first_fpr_regnum = 64, + last_fpr_regnum = 127, + + /* The PC register. */ + pc_regnum = 128, + + /* Register numbers 129 on up are always reserved for special-purpose + registers. */ + first_spr_regnum = 129, + psr_regnum = 129, + ccr_regnum = 130, + cccr_regnum = 131, + fdpic_loadmap_exec_regnum = 132, + fdpic_loadmap_interp_regnum = 133, + tbr_regnum = 135, + brr_regnum = 136, + dbar0_regnum = 137, + dbar1_regnum = 138, + dbar2_regnum = 139, + dbar3_regnum = 140, + scr0_regnum = 141, + scr1_regnum = 142, + scr2_regnum = 143, + scr3_regnum = 144, + lr_regnum = 145, + lcr_regnum = 146, + iacc0h_regnum = 147, + iacc0l_regnum = 148, + last_spr_regnum = 148, + + /* The total number of registers we know exist. */ + frv_num_regs = last_spr_regnum + 1, + + /* Pseudo registers */ + first_pseudo_regnum = frv_num_regs, + + /* iacc0 - the 64-bit concatenation of iacc0h and iacc0l. */ + iacc0_regnum = first_pseudo_regnum + 0, + + last_pseudo_regnum = iacc0_regnum, + frv_num_pseudo_regs = last_pseudo_regnum - first_pseudo_regnum + 1, +}; + +/* Return the FR-V ABI associated with GDBARCH. */ +enum frv_abi frv_abi (struct gdbarch *gdbarch); + +/* Associate a sigcontext address fetcher with GDBARCH. */ +void frv_set_sigcontext_reg_addr (struct gdbarch *gdbarch, + CORE_ADDR (*sigcontext_reg_addr) + (struct frame_info *, int, CORE_ADDR *)); + +/* Fetch the interpreter and executable loadmap addresses (for shared + library support) for the FDPIC ABI. Return 0 if successful, -1 if + not. (E.g, -1 will be returned if the ABI isn't the FDPIC ABI.) */ +int frv_fdpic_loadmap_addresses (struct gdbarch *gdbarch, + CORE_ADDR *interp_addr, CORE_ADDR *exec_addr); + +/* Given a function entry point, find and return the GOT address for the + containing load module. */ +CORE_ADDR frv_fdpic_find_global_pointer (CORE_ADDR addr); + +/* Given a function entry point, find and return the canonical descriptor + for that function, if one exists. If no canonical descriptor could + be found, return 0. */ +CORE_ADDR frv_fdpic_find_canonical_descriptor (CORE_ADDR entry_point); diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c new file mode 100644 index 0000000..5995b80 --- /dev/null +++ b/gdb/solib-frv.c @@ -0,0 +1,1230 @@ +/* Handle FR-V (FDPIC) shared libraries for GDB, the GNU Debugger. + Copyright 2004 + Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + + +#include "defs.h" +#include "gdb_string.h" +#include "inferior.h" +#include "gdbcore.h" +#include "solist.h" +#include "frv-tdep.h" +#include "objfiles.h" +#include "symtab.h" +#include "language.h" +#include "command.h" +#include "gdbcmd.h" +#include "elf/frv.h" + +/* Flag which indicates whether internal debug messages should be printed. */ +static int solib_frv_debug; + +/* FR-V pointers are four bytes wide. */ +enum { FRV_PTR_SIZE = 4 }; + +/* Representation of loadmap and related structs for the FR-V FDPIC ABI. */ + +/* External versions; the size and alignment of the fields should be + the same as those on the target. When loaded, the placement of + the bits in each field will be the same as on the target. */ +typedef unsigned char ext_Elf32_Half[2]; +typedef unsigned char ext_Elf32_Addr[4]; +typedef unsigned char ext_Elf32_Word[4]; + +struct ext_elf32_fdpic_loadseg +{ + /* Core address to which the segment is mapped. */ + ext_Elf32_Addr addr; + /* VMA recorded in the program header. */ + ext_Elf32_Addr p_vaddr; + /* Size of this segment in memory. */ + ext_Elf32_Word p_memsz; +}; + +struct ext_elf32_fdpic_loadmap { + /* Protocol version number, must be zero. */ + ext_Elf32_Half version; + /* Number of segments in this map. */ + ext_Elf32_Half nsegs; + /* The actual memory map. */ + struct ext_elf32_fdpic_loadseg segs[1 /* nsegs, actually */]; +}; + +/* Internal versions; the types are GDB types and the data in each + of the fields is (or will be) decoded from the external struct + for ease of consumption. */ +struct int_elf32_fdpic_loadseg +{ + /* Core address to which the segment is mapped. */ + CORE_ADDR addr; + /* VMA recorded in the program header. */ + CORE_ADDR p_vaddr; + /* Size of this segment in memory. */ + long p_memsz; +}; + +struct int_elf32_fdpic_loadmap { + /* Protocol version number, must be zero. */ + int version; + /* Number of segments in this map. */ + int nsegs; + /* The actual memory map. */ + struct int_elf32_fdpic_loadseg segs[1 /* nsegs, actually */]; +}; + +/* Given address LDMADDR, fetch and decode the loadmap at that address. + Return NULL if there is a problem reading the target memory or if + there doesn't appear to be a loadmap at the given address. The + allocated space (representing the loadmap) returned by this + function may be freed via a single call to xfree(). */ + +static struct int_elf32_fdpic_loadmap * +fetch_loadmap (CORE_ADDR ldmaddr) +{ + struct ext_elf32_fdpic_loadmap ext_ldmbuf_partial; + struct ext_elf32_fdpic_loadmap *ext_ldmbuf; + struct int_elf32_fdpic_loadmap *int_ldmbuf; + int ext_ldmbuf_size, int_ldmbuf_size; + int version, seg, nsegs; + + /* Fetch initial portion of the loadmap. */ + if (target_read_memory (ldmaddr, (char *) &ext_ldmbuf_partial, + sizeof ext_ldmbuf_partial)) + { + /* Problem reading the target's memory. */ + return NULL; + } + + /* Extract the version. */ + version = extract_unsigned_integer (&ext_ldmbuf_partial.version, + sizeof ext_ldmbuf_partial.version); + if (version != 0) + { + /* We only handle version 0. */ + return NULL; + } + + /* Extract the number of segments. */ + nsegs = extract_unsigned_integer (&ext_ldmbuf_partial.nsegs, + sizeof ext_ldmbuf_partial.nsegs); + + /* Allocate space for the complete (external) loadmap. */ + ext_ldmbuf_size = sizeof (struct ext_elf32_fdpic_loadmap) + + (nsegs - 1) * sizeof (struct ext_elf32_fdpic_loadseg); + ext_ldmbuf = xmalloc (ext_ldmbuf_size); + + /* Copy over the portion of the loadmap that's already been read. */ + memcpy (ext_ldmbuf, &ext_ldmbuf_partial, sizeof ext_ldmbuf_partial); + + /* Read the rest of the loadmap from the target. */ + if (target_read_memory (ldmaddr + sizeof ext_ldmbuf_partial, + (char *) ext_ldmbuf + sizeof ext_ldmbuf_partial, + ext_ldmbuf_size - sizeof ext_ldmbuf_partial)) + { + /* Couldn't read rest of the loadmap. */ + xfree (ext_ldmbuf); + return NULL; + } + + /* Allocate space into which to put information extract from the + external loadsegs. I.e, allocate the internal loadsegs. */ + int_ldmbuf_size = sizeof (struct int_elf32_fdpic_loadmap) + + (nsegs - 1) * sizeof (struct int_elf32_fdpic_loadseg); + int_ldmbuf = xmalloc (int_ldmbuf_size); + + /* Place extracted information in internal structs. */ + int_ldmbuf->version = version; + int_ldmbuf->nsegs = nsegs; + for (seg = 0; seg < nsegs; seg++) + { + int_ldmbuf->segs[seg].addr + = extract_unsigned_integer (&ext_ldmbuf->segs[seg].addr, + sizeof (ext_ldmbuf->segs[seg].addr)); + int_ldmbuf->segs[seg].p_vaddr + = extract_unsigned_integer (&ext_ldmbuf->segs[seg].p_vaddr, + sizeof (ext_ldmbuf->segs[seg].p_vaddr)); + int_ldmbuf->segs[seg].p_memsz + = extract_unsigned_integer (&ext_ldmbuf->segs[seg].p_memsz, + sizeof (ext_ldmbuf->segs[seg].p_memsz)); + } + + free (ext_ldmbuf); + return int_ldmbuf; +} + +/* External link_map and elf32_fdpic_loadaddr struct definitions. */ + +typedef unsigned char ext_ptr[4]; + +struct ext_elf32_fdpic_loadaddr +{ + ext_ptr map; /* struct elf32_fdpic_loadmap *map; */ + ext_ptr got_value; /* void *got_value; */ +}; + +struct ext_link_map +{ + struct ext_elf32_fdpic_loadaddr l_addr; + + /* Absolute file name object was found in. */ + ext_ptr l_name; /* char *l_name; */ + + /* Dynamic section of the shared object. */ + ext_ptr l_ld; /* ElfW(Dyn) *l_ld; */ + + /* Chain of loaded objects. */ + ext_ptr l_next, l_prev; /* struct link_map *l_next, *l_prev; */ +}; + +/* Link map info to include in an allocated so_list entry */ + +struct lm_info + { + /* The loadmap, digested into an easier to use form. */ + struct int_elf32_fdpic_loadmap *map; + /* The GOT address for this link map entry. */ + CORE_ADDR got_value; + + /* Cached dynamic symbol table and dynamic relocs initialized and + used only by find_canonical_descriptor_in_load_object(). + + Note: kevinb/2004-02-26: It appears that calls to + bfd_canonicalize_dynamic_reloc() will use the same symbols as + those supplied to the first call to this function. Therefore, + it's important to NOT free the asymbol ** data structure + supplied to the first call. Thus the caching of the dynamic + symbols (dyn_syms) is critical for correct operation. The + caching of the dynamic relocations could be dispensed with. */ + asymbol **dyn_syms; + arelent **dyn_relocs; + int dyn_reloc_count; /* number of dynamic relocs. */ + + }; + +/* The load map, got value, etc. are not available from the chain + of loaded shared objects. ``main_executable_lm_info'' provides + a way to get at this information so that it doesn't need to be + frequently recomputed. Initialized by frv_relocate_main_executable(). */ +static struct lm_info *main_executable_lm_info; + +static void frv_relocate_main_executable (void); +static CORE_ADDR main_got (void); +static int enable_break2 (void); + +/* + + LOCAL FUNCTION + + bfd_lookup_symbol -- lookup the value for a specific symbol + + SYNOPSIS + + CORE_ADDR bfd_lookup_symbol (bfd *abfd, char *symname) + + DESCRIPTION + + An expensive way to lookup the value of a single symbol for + bfd's that are only temporary anyway. This is used by the + shared library support to find the address of the debugger + interface structures in the shared library. + + Note that 0 is specifically allowed as an error return (no + such symbol). + */ + +static CORE_ADDR +bfd_lookup_symbol (bfd *abfd, char *symname) +{ + long storage_needed; + asymbol *sym; + asymbol **symbol_table; + unsigned int number_of_symbols; + unsigned int i; + struct cleanup *back_to; + CORE_ADDR symaddr = 0; + + storage_needed = bfd_get_symtab_upper_bound (abfd); + + if (storage_needed > 0) + { + symbol_table = (asymbol **) xmalloc (storage_needed); + back_to = make_cleanup (xfree, symbol_table); + number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table); + + for (i = 0; i < number_of_symbols; i++) + { + sym = *symbol_table++; + if (strcmp (sym->name, symname) == 0) + { + /* Bfd symbols are section relative. */ + symaddr = sym->value + sym->section->vma; + break; + } + } + do_cleanups (back_to); + } + + if (symaddr) + return symaddr; + + /* Look for the symbol in the dynamic string table too. */ + + storage_needed = bfd_get_dynamic_symtab_upper_bound (abfd); + + if (storage_needed > 0) + { + symbol_table = (asymbol **) xmalloc (storage_needed); + back_to = make_cleanup (xfree, symbol_table); + number_of_symbols = bfd_canonicalize_dynamic_symtab (abfd, symbol_table); + + for (i = 0; i < number_of_symbols; i++) + { + sym = *symbol_table++; + if (strcmp (sym->name, symname) == 0) + { + /* Bfd symbols are section relative. */ + symaddr = sym->value + sym->section->vma; + break; + } + } + do_cleanups (back_to); + } + + return symaddr; +} + + +/* + + LOCAL FUNCTION + + open_symbol_file_object + + SYNOPSIS + + void open_symbol_file_object (void *from_tty) + + DESCRIPTION + + If no open symbol file, attempt to locate and open the main symbol + file. + + If FROM_TTYP dereferences to a non-zero integer, allow messages to + be printed. This parameter is a pointer rather than an int because + open_symbol_file_object() is called via catch_errors() and + catch_errors() requires a pointer argument. */ + +static int +open_symbol_file_object (void *from_ttyp) +{ + /* Unimplemented. */ + return 0; +} + +/* Cached value for lm_base(), below. */ +static CORE_ADDR lm_base_cache = 0; + +/* Return the address from which the link map chain may be found. On + the FR-V, this may be found in a number of ways. Assuming that the + main executable has already been relocated, the easiest way to find + this value is to look up the address of _GLOBAL_OFFSET_TABLE_. A + pointer to the start of the link map will be located at the word found + at _GLOBAL_OFFSET_TABLE_ + 8. (This is part of the dynamic linker + reserve area mandated by the ABI.) */ + +static CORE_ADDR +lm_base (void) +{ + struct minimal_symbol *got_sym; + CORE_ADDR addr; + char buf[FRV_PTR_SIZE]; + + /* If we already have a cached value, return it. */ + if (lm_base_cache) + return lm_base_cache; + + got_sym = lookup_minimal_symbol ("_GLOBAL_OFFSET_TABLE_", NULL, + symfile_objfile); + if (got_sym == 0) + { + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "lm_base: _GLOBAL_OFFSET_TABLE_ not found.\n"); + return 0; + } + + addr = SYMBOL_VALUE_ADDRESS (got_sym) + 8; + + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "lm_base: _GLOBAL_OFFSET_TABLE_ + 8 = %s\n", + local_hex_string_custom (addr, "08l")); + + if (target_read_memory (addr, buf, sizeof buf) != 0) + return 0; + lm_base_cache = extract_unsigned_integer (buf, sizeof buf); + + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "lm_base: lm_base_cache = %s\n", + local_hex_string_custom (lm_base_cache, "08l")); + + return lm_base_cache; +} + + +/* LOCAL FUNCTION + + frv_current_sos -- build a list of currently loaded shared objects + + SYNOPSIS + + struct so_list *frv_current_sos () + + DESCRIPTION + + Build a list of `struct so_list' objects describing the shared + objects currently loaded in the inferior. This list does not + include an entry for the main executable file. + + Note that we only gather information directly available from the + inferior --- we don't examine any of the shared library files + themselves. The declaration of `struct so_list' says which fields + we provide values for. */ + +static struct so_list * +frv_current_sos (void) +{ + CORE_ADDR lm_addr, mgot; + struct so_list *sos_head = NULL; + struct so_list **sos_next_ptr = &sos_head; + + mgot = main_got (); + + /* Locate the address of the first link map struct. */ + lm_addr = lm_base (); + + /* We have at least one link map entry. Fetch the the lot of them, + building the solist chain. */ + while (lm_addr) + { + struct ext_link_map lm_buf; + CORE_ADDR got_addr; + + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "current_sos: reading link_map entry at %s\n", + local_hex_string_custom (lm_addr, "08l")); + + if (target_read_memory (lm_addr, (char *) &lm_buf, sizeof (lm_buf)) != 0) + { + warning ("frv_current_sos: Unable to read link map entry. Shared object chain may be incomplete."); + break; + } + + got_addr + = extract_unsigned_integer (&lm_buf.l_addr.got_value, + sizeof (lm_buf.l_addr.got_value)); + /* If the got_addr is the same as mgotr, then we're looking at the + entry for the main executable. By convention, we don't include + this in the list of shared objects. */ + if (got_addr != mgot) + { + int errcode; + char *name_buf; + struct int_elf32_fdpic_loadmap *loadmap; + struct so_list *sop; + CORE_ADDR addr; + + /* Fetch the load map address. */ + addr = extract_unsigned_integer (&lm_buf.l_addr.map, + sizeof lm_buf.l_addr.map); + loadmap = fetch_loadmap (addr); + if (loadmap == NULL) + { + warning ("frv_current_sos: Unable to fetch load map. Shared object chain may be incomplete."); + break; + } + + sop = xcalloc (1, sizeof (struct so_list)); + sop->lm_info = xcalloc (1, sizeof (struct lm_info)); + sop->lm_info->map = loadmap; + sop->lm_info->got_value = got_addr; + /* Fetch the name. */ + addr = extract_unsigned_integer (&lm_buf.l_name, + sizeof (lm_buf.l_name)); + target_read_string (addr, &name_buf, SO_NAME_MAX_PATH_SIZE - 1, + &errcode); + + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, "current_sos: name = %s\n", + name_buf); + + if (errcode != 0) + { + warning ("frv_current_sos: Can't read pathname for link map entry: %s\n", + safe_strerror (errcode)); + } + else + { + strncpy (sop->so_name, name_buf, SO_NAME_MAX_PATH_SIZE - 1); + sop->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; + xfree (name_buf); + strcpy (sop->so_original_name, sop->so_name); + } + + *sos_next_ptr = sop; + sos_next_ptr = &sop->next; + } + + lm_addr = extract_unsigned_integer (&lm_buf.l_next, sizeof (lm_buf.l_next)); + } + + enable_break2 (); + + return sos_head; +} + + +/* Return 1 if PC lies in the dynamic symbol resolution code of the + run time loader. */ + +static CORE_ADDR interp_text_sect_low; +static CORE_ADDR interp_text_sect_high; +static CORE_ADDR interp_plt_sect_low; +static CORE_ADDR interp_plt_sect_high; + +static int +frv_in_dynsym_resolve_code (CORE_ADDR pc) +{ + return ((pc >= interp_text_sect_low && pc < interp_text_sect_high) + || (pc >= interp_plt_sect_low && pc < interp_plt_sect_high) + || in_plt_section (pc, NULL)); +} + +/* Given a loadmap and an address, return the displacement needed + to relocate the address. */ + +CORE_ADDR +displacement_from_map (struct int_elf32_fdpic_loadmap *map, + CORE_ADDR addr) +{ + int seg; + + for (seg = 0; seg < map->nsegs; seg++) + { + if (map->segs[seg].p_vaddr <= addr + && addr < map->segs[seg].p_vaddr + map->segs[seg].p_memsz) + { + return map->segs[seg].addr - map->segs[seg].p_vaddr; + } + } + + return 0; +} + +/* Print a warning about being unable to set the dynamic linker + breakpoint. */ + +static void +enable_break_failure_warning (void) +{ + warning ("Unable to find dynamic linker breakpoint function.\n" + "GDB will be unable to debug shared library initializers\n" + "and track explicitly loaded dynamic code."); +} + +/* + + LOCAL FUNCTION + + enable_break -- arrange for dynamic linker to hit breakpoint + + SYNOPSIS + + int enable_break (void) + + DESCRIPTION + + The dynamic linkers has, as part of its debugger interface, support + for arranging for the inferior to hit a breakpoint after mapping in + the shared libraries. This function enables that breakpoint. + + On the FR-V, using the shared library (FDPIC) ABI, the symbol + _dl_debug_addr points to the r_debug struct which contains + a field called r_brk. r_brk is the address of the function + descriptor upon which a breakpoint must be placed. Being a + function descriptor, we must extract the entry point in order + to set the breakpoint. + + Our strategy will be to get the .interp section from the + executable. This section will provide us with the name of the + interpreter. We'll open the interpreter and then look up + the address of _dl_debug_addr. We then relocate this address + using the interpreter's loadmap. Once the relocated address + is known, we fetch the value (address) corresponding to r_brk + and then use that value to fetch the entry point of the function + we're interested in. + + */ + +static int enable_break1_done = 0; +static int enable_break2_done = 0; + +static int +enable_break2 (void) +{ + int success = 0; + char **bkpt_namep; + asection *interp_sect; + + if (!enable_break1_done || enable_break2_done) + return 1; + + enable_break2_done = 1; + + /* First, remove all the solib event breakpoints. Their addresses + may have changed since the last time we ran the program. */ + remove_solib_event_breakpoints (); + + interp_text_sect_low = interp_text_sect_high = 0; + interp_plt_sect_low = interp_plt_sect_high = 0; + + /* Find the .interp section; if not found, warn the user and drop + into the old breakpoint at symbol code. */ + interp_sect = bfd_get_section_by_name (exec_bfd, ".interp"); + if (interp_sect) + { + unsigned int interp_sect_size; + char *buf; + bfd *tmp_bfd = NULL; + int tmp_fd = -1; + char *tmp_pathname = NULL; + int status; + CORE_ADDR addr, interp_loadmap_addr; + char addr_buf[FRV_PTR_SIZE]; + struct int_elf32_fdpic_loadmap *ldm; + + /* Read the contents of the .interp section into a local buffer; + the contents specify the dynamic linker this program uses. */ + interp_sect_size = bfd_section_size (exec_bfd, interp_sect); + buf = alloca (interp_sect_size); + bfd_get_section_contents (exec_bfd, interp_sect, + buf, 0, interp_sect_size); + + /* Now we need to figure out where the dynamic linker was + loaded so that we can load its symbols and place a breakpoint + in the dynamic linker itself. + + This address is stored on the stack. However, I've been unable + to find any magic formula to find it for Solaris (appears to + be trivial on GNU/Linux). Therefore, we have to try an alternate + mechanism to find the dynamic linker's base address. */ + + tmp_fd = solib_open (buf, &tmp_pathname); + if (tmp_fd >= 0) + tmp_bfd = bfd_fdopenr (tmp_pathname, gnutarget, tmp_fd); + + if (tmp_bfd == NULL) + { + enable_break_failure_warning (); + return 0; + } + + /* Make sure the dynamic linker is really a useful object. */ + if (!bfd_check_format (tmp_bfd, bfd_object)) + { + warning ("Unable to grok dynamic linker %s as an object file", buf); + enable_break_failure_warning (); + bfd_close (tmp_bfd); + return 0; + } + + status = frv_fdpic_loadmap_addresses (current_gdbarch, + &interp_loadmap_addr, 0); + if (status < 0) + { + warning ("Unable to determine dynamic linker loadmap address\n"); + enable_break_failure_warning (); + bfd_close (tmp_bfd); + return 0; + } + + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "enable_break: interp_loadmap_addr = %s\n", + local_hex_string_custom (interp_loadmap_addr, + "08l")); + + ldm = fetch_loadmap (interp_loadmap_addr); + if (ldm == NULL) + { + warning ("Unable to load dynamic linker loadmap at address %s\n", + local_hex_string_custom (interp_loadmap_addr, "08l")); + enable_break_failure_warning (); + bfd_close (tmp_bfd); + return 0; + } + + /* Record the relocated start and end address of the dynamic linker + text and plt section for svr4_in_dynsym_resolve_code. */ + interp_sect = bfd_get_section_by_name (tmp_bfd, ".text"); + if (interp_sect) + { + interp_text_sect_low + = bfd_section_vma (tmp_bfd, interp_sect); + interp_text_sect_low + += displacement_from_map (ldm, interp_text_sect_low); + interp_text_sect_high + = interp_text_sect_low + bfd_section_size (tmp_bfd, interp_sect); + } + interp_sect = bfd_get_section_by_name (tmp_bfd, ".plt"); + if (interp_sect) + { + interp_plt_sect_low = + bfd_section_vma (tmp_bfd, interp_sect); + interp_plt_sect_low + += displacement_from_map (ldm, interp_plt_sect_low); + interp_plt_sect_high = + interp_plt_sect_low + bfd_section_size (tmp_bfd, interp_sect); + } + + addr = bfd_lookup_symbol (tmp_bfd, "_dl_debug_addr"); + if (addr == 0) + { + warning ("Could not find symbol _dl_debug_addr in dynamic linker"); + enable_break_failure_warning (); + bfd_close (tmp_bfd); + return 0; + } + + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "enable_break: _dl_debug_addr (prior to relocation) = %s\n", + local_hex_string_custom (addr, "08l")); + + addr += displacement_from_map (ldm, addr); + + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "enable_break: _dl_debug_addr (after relocation) = %s\n", + local_hex_string_custom (addr, "08l")); + + /* Fetch the address of the r_debug struct. */ + if (target_read_memory (addr, addr_buf, sizeof addr_buf) != 0) + { + warning ("Unable to fetch contents of _dl_debug_addr (at address %s) from dynamic linker", + local_hex_string_custom (addr, "08l")); + } + addr = extract_unsigned_integer (addr_buf, sizeof addr_buf); + + /* Fetch the r_brk field. It's 8 bytes from the start of + _dl_debug_addr. */ + if (target_read_memory (addr + 8, addr_buf, sizeof addr_buf) != 0) + { + warning ("Unable to fetch _dl_debug_addr->r_brk (at address %s) from dynamic linker", + local_hex_string_custom (addr + 8, "08l")); + enable_break_failure_warning (); + bfd_close (tmp_bfd); + return 0; + } + addr = extract_unsigned_integer (addr_buf, sizeof addr_buf); + + /* Now fetch the function entry point. */ + if (target_read_memory (addr, addr_buf, sizeof addr_buf) != 0) + { + warning ("Unable to fetch _dl_debug_addr->.r_brk entry point (at address %s) from dynamic linker", + local_hex_string_custom (addr, "08l")); + enable_break_failure_warning (); + bfd_close (tmp_bfd); + return 0; + } + addr = extract_unsigned_integer (addr_buf, sizeof addr_buf); + + /* We're done with the temporary bfd. */ + bfd_close (tmp_bfd); + + /* We're also done with the loadmap. */ + xfree (ldm); + + /* Now (finally!) create the solib breakpoint. */ + create_solib_event_breakpoint (addr); + + return 1; + } + + /* Tell the user we couldn't set a dynamic linker breakpoint. */ + enable_break_failure_warning (); + + /* Failure return. */ + return 0; +} + +static int +enable_break (void) +{ + asection *interp_sect; + + /* Remove all the solib event breakpoints. Their addresses + may have changed since the last time we ran the program. */ + remove_solib_event_breakpoints (); + + /* Check for the presence of a .interp section. If there is no + such section, the executable is statically linked. */ + + interp_sect = bfd_get_section_by_name (exec_bfd, ".interp"); + + if (interp_sect) + { + enable_break1_done = 1; + create_solib_event_breakpoint (symfile_objfile->ei.entry_point); + + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "enable_break: solib event breakpoint placed at entry point: %s\n", + local_hex_string_custom + (symfile_objfile->ei.entry_point, "08l")); + } + else + { + if (solib_frv_debug) + fprintf_unfiltered (gdb_stdlog, + "enable_break: No .interp section found.\n"); + } + + return 1; +} + +/* + + LOCAL FUNCTION + + special_symbol_handling -- additional shared library symbol handling + + SYNOPSIS + + void special_symbol_handling () + + DESCRIPTION + + Once the symbols from a shared object have been loaded in the usual + way, we are called to do any system specific symbol handling that + is needed. + + */ + +static void +frv_special_symbol_handling (void) +{ + /* Nothing needed (yet) for FRV. */ +} + +static void +frv_relocate_main_executable (void) +{ + int status; + CORE_ADDR exec_addr; + struct int_elf32_fdpic_loadmap *ldm; + struct cleanup *old_chain; + struct section_offsets *new_offsets; + int changed; + struct obj_section *osect; + + status = frv_fdpic_loadmap_addresses (current_gdbarch, 0, &exec_addr); + + if (status < 0) + { + /* Not using FDPIC ABI, so do nothing. */ + return; + } + + /* Fetch the loadmap located at ``exec_addr''. */ + ldm = fetch_loadmap (exec_addr); + if (ldm == NULL) + error ("Unable to load the executable's loadmap."); + + if (main_executable_lm_info) + xfree (main_executable_lm_info); + main_executable_lm_info = xcalloc (1, sizeof (struct lm_info)); + main_executable_lm_info->map = ldm; + + new_offsets = xcalloc (symfile_objfile->num_sections, + sizeof (struct section_offsets)); + old_chain = make_cleanup (xfree, new_offsets); + changed = 0; + + ALL_OBJFILE_OSECTIONS (symfile_objfile, osect) + { + CORE_ADDR orig_addr, addr, offset; + int osect_idx; + int seg; + + osect_idx = osect->the_bfd_section->index; + + /* Current address of section. */ + addr = osect->addr; + /* Offset from where this section started. */ + offset = ANOFFSET (symfile_objfile->section_offsets, osect_idx); + /* Original address prior to any past relocations. */ + orig_addr = addr - offset; + + for (seg = 0; seg < ldm->nsegs; seg++) + { + if (ldm->segs[seg].p_vaddr <= orig_addr + && orig_addr < ldm->segs[seg].p_vaddr + ldm->segs[seg].p_memsz) + { + new_offsets->offsets[osect_idx] + = ldm->segs[seg].addr - ldm->segs[seg].p_vaddr; + + if (new_offsets->offsets[osect_idx] != offset) + changed = 1; + break; + } + } + } + + if (changed) + objfile_relocate (symfile_objfile, new_offsets); + + do_cleanups (old_chain); + + /* Now that symfile_objfile has been relocated, we can compute the + GOT value and stash it away. */ + main_executable_lm_info->got_value = main_got (); +} + +/* + + GLOBAL FUNCTION + + frv_solib_create_inferior_hook -- shared library startup support + + SYNOPSIS + + void frv_solib_create_inferior_hook() + + DESCRIPTION + + When gdb starts up the inferior, it nurses it along (through the + shell) until it is ready to execute it's first instruction. At this + point, this function gets called via expansion of the macro + SOLIB_CREATE_INFERIOR_HOOK. + + For the FR-V shared library ABI (FDPIC), the main executable + needs to be relocated. The shared library breakpoints also need + to be enabled. + */ + +static void +frv_solib_create_inferior_hook (void) +{ + /* Relocate main executable. */ + frv_relocate_main_executable (); + + /* Enable shared library breakpoints. */ + if (!enable_break ()) + { + warning ("shared library handler failed to enable breakpoint"); + return; + } +} + +static void +frv_clear_solib (void) +{ + lm_base_cache = 0; + enable_break1_done = 0; + enable_break2_done = 0; +} + +static void +frv_free_so (struct so_list *so) +{ + xfree (so->lm_info->map); + xfree (so->lm_info->dyn_syms); + xfree (so->lm_info->dyn_relocs); + xfree (so->lm_info); +} + +static void +frv_relocate_section_addresses (struct so_list *so, + struct section_table *sec) +{ + int seg; + struct int_elf32_fdpic_loadmap *map; + + map = so->lm_info->map; + + for (seg = 0; seg < map->nsegs; seg++) + { + if (map->segs[seg].p_vaddr <= sec->addr + && sec->addr < map->segs[seg].p_vaddr + map->segs[seg].p_memsz) + { + CORE_ADDR displ = map->segs[seg].addr - map->segs[seg].p_vaddr; + sec->addr += displ; + sec->endaddr += displ; + break; + } + } +} + +/* Return the GOT address associated with the main executable. Return + 0 if it can't be found. */ + +static CORE_ADDR +main_got (void) +{ + struct minimal_symbol *got_sym; + + got_sym = lookup_minimal_symbol ("_GLOBAL_OFFSET_TABLE_", NULL, symfile_objfile); + if (got_sym == 0) + return 0; + + return SYMBOL_VALUE_ADDRESS (got_sym); +} + +/* Find the global pointer for the given function address ADDR. */ + +CORE_ADDR +frv_fdpic_find_global_pointer (CORE_ADDR addr) +{ + struct so_list *so; + + so = master_so_list (); + while (so) + { + int seg; + struct int_elf32_fdpic_loadmap *map; + + map = so->lm_info->map; + + for (seg = 0; seg < map->nsegs; seg++) + { + if (map->segs[seg].addr <= addr + && addr < map->segs[seg].addr + map->segs[seg].p_memsz) + return so->lm_info->got_value; + } + + so = so->next; + } + + /* Didn't find it it any of the shared objects. So assume it's in the + main executable. */ + return main_got (); +} + +/* Forward declarations for frv_fdpic_find_canonical_descriptor(). */ +static CORE_ADDR find_canonical_descriptor_in_load_object + (CORE_ADDR, CORE_ADDR, char *, bfd *, struct lm_info *); + +/* Given a function entry point, attempt to find the canonical descriptor + associated with that entry point. Return 0 if no canonical descriptor + could be found. */ + +CORE_ADDR +frv_fdpic_find_canonical_descriptor (CORE_ADDR entry_point) +{ + char *name; + CORE_ADDR addr; + CORE_ADDR got_value; + struct int_elf32_fdpic_loadmap *ldm = 0; + struct symbol *sym; + int status; + CORE_ADDR exec_loadmap_addr; + + /* Fetch the corresponding global pointer for the entry point. */ + got_value = frv_fdpic_find_global_pointer (entry_point); + + /* Attempt to find the name of the function. If the name is available, + it'll be used as an aid in finding matching functions in the dynamic + symbol table. */ + sym = find_pc_function (entry_point); + if (sym == 0) + name = 0; + else + name = SYMBOL_LINKAGE_NAME (sym); + + /* Check the main executable. */ + addr = find_canonical_descriptor_in_load_object + (entry_point, got_value, name, symfile_objfile->obfd, + main_executable_lm_info); + + /* If descriptor not found via main executable, check each load object + in list of shared objects. */ + if (addr == 0) + { + struct so_list *so; + + so = master_so_list (); + while (so) + { + addr = find_canonical_descriptor_in_load_object + (entry_point, got_value, name, so->abfd, so->lm_info); + + if (addr != 0) + break; + + so = so->next; + } + } + + return addr; +} + +static CORE_ADDR +find_canonical_descriptor_in_load_object + (CORE_ADDR entry_point, CORE_ADDR got_value, char *name, bfd *abfd, + struct lm_info *lm) +{ + arelent *rel; + unsigned int i; + CORE_ADDR addr = 0; + + /* Nothing to do if no bfd. */ + if (abfd == 0) + return 0; + + /* We want to scan the dynamic relocs for R_FRV_FUNCDESC relocations. + (More about this later.) But in order to fetch the relocs, we + need to first fetch the dynamic symbols. These symbols need to + be cached due to the way that bfd_canonicalize_dynamic_reloc() + works. (See the comments in the declaration of struct lm_info + for more information.) */ + if (lm->dyn_syms == NULL) + { + long storage_needed; + unsigned int number_of_symbols; + + /* Determine amount of space needed to hold the dynamic symbol table. */ + storage_needed = bfd_get_dynamic_symtab_upper_bound (abfd); + + /* If there are no dynamic symbols, there's nothing to do. */ + if (storage_needed <= 0) + return 0; + + /* Allocate space for the dynamic symbol table. */ + lm->dyn_syms = (asymbol **) xmalloc (storage_needed); + + /* Fetch the dynamic symbol table. */ + number_of_symbols = bfd_canonicalize_dynamic_symtab (abfd, lm->dyn_syms); + + if (number_of_symbols == 0) + return 0; + } + + /* Fetch the dynamic relocations if not already cached. */ + if (lm->dyn_relocs == NULL) + { + long storage_needed; + + /* Determine amount of space needed to hold the dynamic relocs. */ + storage_needed = bfd_get_dynamic_reloc_upper_bound (abfd); + + /* Bail out if there are no dynamic relocs. */ + if (storage_needed <= 0) + return 0; + + /* Allocate space for the relocs. */ + lm->dyn_relocs = (arelent **) xmalloc (storage_needed); + + /* Fetch the dynamic relocs. */ + lm->dyn_reloc_count + = bfd_canonicalize_dynamic_reloc (abfd, lm->dyn_relocs, lm->dyn_syms); + } + + /* Search the dynamic relocs. */ + for (i = 0; i < lm->dyn_reloc_count; i++) + { + rel = lm->dyn_relocs[i]; + + /* Relocs of interest are those which meet the following + criteria: + + - the names match (assuming the caller could provide + a name which matches ``entry_point''). + - the relocation type must be R_FRV_FUNCDESC. Relocs + of this type are used (by the dynamic linker) to + look up the address of a canonical descriptor (allocating + it if need be) and initializing the GOT entry referred + to by the offset to the address of the descriptor. + + These relocs of interest may be used to obtain a + candidate descriptor by first adjusting the reloc's + address according to the link map and then dereferencing + this address (which is a GOT entry) to obtain a descriptor + address. */ + if ((name == 0 || strcmp (name, (*rel->sym_ptr_ptr)->name) == 0) + && rel->howto->type == R_FRV_FUNCDESC) + { + char buf[FRV_PTR_SIZE]; + + /* Compute address of address of candidate descriptor. */ + addr = rel->address + displacement_from_map (lm->map, rel->address); + + /* Fetch address of candidate descriptor. */ + if (target_read_memory (addr, buf, sizeof buf) != 0) + continue; + addr = extract_unsigned_integer (buf, sizeof buf); + + /* Check for matching entry point. */ + if (target_read_memory (addr, buf, sizeof buf) != 0) + continue; + if (extract_unsigned_integer (buf, sizeof buf) != entry_point) + continue; + + /* Check for matching got value. */ + if (target_read_memory (addr + 4, buf, sizeof buf) != 0) + continue; + if (extract_unsigned_integer (buf, sizeof buf) != got_value) + continue; + + /* Match was successful! Exit loop. */ + break; + } + } + + return addr; +} + +static struct target_so_ops frv_so_ops; + +void +_initialize_frv_solib (void) +{ + frv_so_ops.relocate_section_addresses = frv_relocate_section_addresses; + frv_so_ops.free_so = frv_free_so; + frv_so_ops.clear_solib = frv_clear_solib; + frv_so_ops.solib_create_inferior_hook = frv_solib_create_inferior_hook; + frv_so_ops.special_symbol_handling = frv_special_symbol_handling; + frv_so_ops.current_sos = frv_current_sos; + frv_so_ops.open_symbol_file_object = open_symbol_file_object; + frv_so_ops.in_dynsym_resolve_code = frv_in_dynsym_resolve_code; + + /* FIXME: Don't do this here. *_gdbarch_init() should set so_ops. */ + current_target_so_ops = &frv_so_ops; + + /* Debug this file's internals. */ + add_show_from_set (add_set_cmd ("solib-frv", class_maintenance, var_zinteger, + &solib_frv_debug, +"Set internal debugging of shared library code for FR-V.\n" +"When non-zero, FR-V solib specific internal debugging is enabled.", + &setdebuglist), + &showdebuglist); +} diff --git a/gdb/testsuite/gdb.arch/gdb1431.s b/gdb/testsuite/gdb.arch/gdb1431.s new file mode 100644 index 0000000..36b43d2 --- /dev/null +++ b/gdb/testsuite/gdb.arch/gdb1431.s @@ -0,0 +1,129 @@ +! Copyright 2004 Free Software Foundation, Inc. +! +! This program is free software; you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation; either version 2 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program; if not, write to the Free Software +! Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +! +! Please email any bugs, comments, and/or additions to this file to: +! bug-gdb@gnu.org +! +! This file is part of the gdb testsuite. +! +! It was generated using "sh-elf-gcc -S gdb1431.c", using the following +! source file: +! +! #include +! +! main() +! { +! printf("hello world\n"); +! sub1(); +! sub2(); +! } +! sub1() +! { +! int buf[64]; +! +! } +! +! sub2() +! { +! int buf[65]; +! +! } +! +! We use a pregenerated assembly file as the test input to avoid possible +! problems with future versions of gcc generating different code. + + .file "gdb1431.c" + .text + .section .rodata + .align 2 +.LC0: + .string "hello world\n" + .text + .align 1 + .global _main + .type _main, @function +_main: + mov.l r14,@-r15 + sts.l pr,@-r15 + mov r15,r14 + mov.l .L2,r1 + mov r1,r4 + mov.l .L3,r1 + jsr @r1 + nop + mov.l .L4,r1 + jsr @r1 + nop + mov.l .L5,r1 + jsr @r1 + nop + mov r14,r15 + lds.l @r15+,pr + mov.l @r15+,r14 + rts + nop +.L6: + .align 2 +.L2: + .long .LC0 +.L3: + .long _printf +.L4: + .long _sub1 +.L5: + .long _sub2 + .size _main, .-_main + .align 1 + .global _sub1 + .type _sub1, @function +_sub1: + mov.l r14,@-r15 + sts.l pr,@-r15 + add #-128,r15 + add #-128,r15 + mov r15,r14 + mov.w .L8,r7 + add r7,r14 + mov r14,r15 + lds.l @r15+,pr + mov.l @r15+,r14 + rts + nop + .align 1 +.L8: + .short 256 + .size _sub1, .-_sub1 + .align 1 + .global _sub2 + .type _sub2, @function +_sub2: + mov.l r14,@-r15 + sts.l pr,@-r15 + mov.w .L11,r1 + sub r1,r15 + mov r15,r14 + mov.w .L11,r7 + add r7,r14 + mov r14,r15 + lds.l @r15+,pr + mov.l @r15+,r14 + rts + nop + .align 1 +.L11: + .short 260 + .size _sub2, .-_sub2 + .ident "GCC: (GNU) 3.5.0 20040204 (experimental)" diff --git a/gdb/testsuite/gdb.base/auxv.c b/gdb/testsuite/gdb.base/auxv.c new file mode 100644 index 0000000..94f9d00 --- /dev/null +++ b/gdb/testsuite/gdb.base/auxv.c @@ -0,0 +1,58 @@ +/* Simple little program that just generates a core dump from inside some + nested function calls. Keep this as self contained as possible, I.E. + use no environment resources other than possibly abort(). */ + +#ifndef __STDC__ +#define const /**/ +#endif + +#ifndef HAVE_ABORT +#define HAVE_ABORT 1 +#endif + +#if HAVE_ABORT +#define ABORT abort() +#else +#define ABORT {char *invalid = 0; *invalid = 0xFF;} +#endif + +/* Don't make these automatic vars or we will have to walk back up the + stack to access them. */ + +char *buf1; +char *buf2; + +int coremaker_data = 1; /* In Data section */ +int coremaker_bss; /* In BSS section */ + +const int coremaker_ro = 201; /* In Read-Only Data section */ + +void +func2 (int x) +{ + int coremaker_local[5]; + int i; + static int y; + + /* Make sure that coremaker_local doesn't get optimized away. */ + for (i = 0; i < 5; i++) + coremaker_local[i] = i; + coremaker_bss = 0; + for (i = 0; i < 5; i++) + coremaker_bss += coremaker_local[i]; + coremaker_data = coremaker_ro + 1; + y = 10 * x; + ABORT; +} + +void +func1 (int x) +{ + func2 (x * 2); +} + +int main () +{ + func1 (10); + return 0; +} diff --git a/gdb/testsuite/gdb.base/auxv.exp b/gdb/testsuite/gdb.base/auxv.exp new file mode 100644 index 0000000..3a509b9 --- /dev/null +++ b/gdb/testsuite/gdb.base/auxv.exp @@ -0,0 +1,187 @@ +# Test `info auxv' and related functionality. + +# Copyright 1992,1993,1994,1995,1996,1997,1998,1999,2000,2004 +# Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@prep.ai.mit.edu + +# This file is based on corefile.exp which was written by Fred +# Fish. (fnf@cygnus.com) + +if $tracelevel then { + strace $tracelevel +} + +set prms_id 0 +set bug_id 0 + +set testfile "auxv" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +set corefile ${objdir}/${subdir}/${testfile}.corefile +set gcorefile ${objdir}/${subdir}/${testfile}.gcore + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." +} + +# Use a fresh directory to confine the native core dumps. +# Make it the working directory for gdb and its child. +set coredir "${objdir}/${subdir}/coredir.[getpid]" +file mkdir $coredir +set core_works [isnative] + +# Run GDB on the test program up to where it will dump core. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} +gdb_test "set print sevenbit-strings" "" \ + "set print sevenbit-strings; ${testfile}" +gdb_test "set width 0" "" \ + "set width 0; ${testfile}" + +if {$core_works} { + if {[gdb_test "cd $coredir" ".*Working directory .*" \ + "cd to temporary directory for core dumps"]} { + set core_works 0 + } +} + +if { ![runto_main] } then { + gdb_suppress_tests; +} +set print_core_line [gdb_get_line_number "ABORT;"] +gdb_test "tbreak $print_core_line" +gdb_test continue ".*ABORT;.*" + +proc fetch_auxv {test} { + global gdb_prompt + + set auxv_lines {} + set bad -1 + if {[gdb_test_multiple "info auxv" $test { + -re "info auxv\[\r\n\]+" { + exp_continue + } + -ex "The program has no auxiliary information now" { + set bad 1 + } + -ex "Auxiliary vector is empty" { + set bad 1 + } + -ex "No auxiliary vector found" { + set bad 1 + } + -re "^\[0-9\]+\[ \t\]+(AT_\[^ \t\]+)\[^\r\n\]+\[\r\n\]+" { + lappend auxv_lines $expect_out(0,string) + exp_continue + } + -re "^\[0-9\]+\[ \t\]+\\?\\?\\?\[^\r\n\]+\[\r\n\]+" { + warning "Unrecognized tag value: $expect_out(0,string)" + set bad 1 + lappend auxv_lines $expect_out(0,string) + exp_continue + } + -re ".*$gdb_prompt $" { + incr bad + } + -re "^\[^\r\n\]+\[\r\n\]+" { + warning "Unrecognized output: $expect_out(0,string)" + set bad 1 + } + }] != 0} { + return {} + } + + if {$bad} { + fail $test + return {} + } + + pass $test + return $auxv_lines +} + +set live_data [fetch_auxv "info auxv on live process"] + +# Now try gcore. +set gcore_works 0 +set escapedfilename [string_to_regexp $gcorefile] +gdb_test_multiple "gcore $gcorefile" "gcore" { + -re "Saved corefile ${escapedfilename}\[\r\n\]+$gdb_prompt $" { + pass "gcore" + set gcore_works 1 + } + -re "Can't create a corefile\[\r\n\]+$gdb_prompt $" { + unsupported "gcore" + } +} + +# Let the program continue and die. +gdb_test continue ".*Program received signal.*" +gdb_test continue ".*Program terminated with signal.*" + +# Now collect the core dump it left. +set test "generate native core dump" +if {$core_works} { + # Find the + set names [glob -nocomplain -directory $coredir *core*] + if {[llength $names] == 1} { + set file [file join $coredir [lindex $names 0]] + remote_exec build "mv $file $corefile" + pass $test + } else { + set core_works 0 + warning "can't generate a core file - core tests suppressed - check ulimit -c" + fail $test + } +} else { + unsupported $test +} +remote_exec build "rm -rf $coredir" + +# Now we can examine the core files and check that their data matches what +# we saw in the process. Note that the exact data can vary between runs, +# so it's important that the native core dump file and the gcore-created dump +# both be from the same run of the program as we examined live. + +proc do_core_test {works corefile test1 test2} { + if {! $works} { + unsupported $test1 + unsupported $test2 + } else { + gdb_test "core $corefile" "Core was generated by.*" \ + "load core file for $test1" \ + "A program is being debugged already.*" "y" + set core_data [fetch_auxv $test1] + global live_data + if {$core_data == $live_data} { + pass $test2 + } else { + fail $test2 + } + } +} + +do_core_test $core_works $corefile \ + "info auxv on native core dump" "matching auxv data from live and core" + +do_core_test $gcore_works $gcorefile \ + "info auxv on gcore-created dump" "matching auxv data from live and gcore" diff --git a/gdb/testsuite/gdb.cp/classes.cc b/gdb/testsuite/gdb.cp/classes.cc new file mode 100644 index 0000000..d09f38f --- /dev/null +++ b/gdb/testsuite/gdb.cp/classes.cc @@ -0,0 +1,608 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2004 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// Test various -*- C++ -*- things. + +// ====================== basic C++ types ======================= +bool v_bool; +bool v_bool_array[2]; + +typedef struct fleep fleep; +struct fleep { int a; } s; + +// ====================== simple class structures ======================= + +struct default_public_struct { + // defaults to public: + int a; + int b; +}; + +struct explicit_public_struct { + public: + int a; + int b; +}; + +struct protected_struct { + protected: + int a; + int b; +}; + +struct private_struct { + private: + int a; + int b; +}; + +struct mixed_protection_struct { + public: + int a; + int b; + private: + int c; + int d; + protected: + int e; + int f; + public: + int g; + private: + int h; + protected: + int i; +}; + +class public_class { + public: + int a; + int b; +}; + +class protected_class { + protected: + int a; + int b; +}; + +class default_private_class { + // defaults to private: + int a; + int b; +}; + +class explicit_private_class { + private: + int a; + int b; +}; + +class mixed_protection_class { + public: + int a; + int b; + private: + int c; + int d; + protected: + int e; + int f; + public: + int g; + private: + int h; + protected: + int i; +}; + +class const_vol_method_class { +public: + int a; + int b; + int foo (int &) const; + int bar (int &) volatile; + int baz (int &) const volatile; +}; + +int const_vol_method_class::foo (int & ir) const +{ + return ir + 3; +} +int const_vol_method_class::bar (int & ir) volatile +{ + return ir + 4; +} +int const_vol_method_class::baz (int & ir) const volatile +{ + return ir + 5; +} + +// ========================= simple inheritance ========================== + +class A { + public: + int a; + int x; +}; + +A g_A; + +class B : public A { + public: + int b; + int x; +}; + +B g_B; + +class C : public A { + public: + int c; + int x; +}; + +C g_C; + +class D : public B, public C { + public: + int d; + int x; +}; + +D g_D; + +class E : public D { + public: + int e; + int x; +}; + +E g_E; + +class class_with_anon_union +{ + public: + int one; + union + { + int a; + long b; + }; +}; + +class_with_anon_union g_anon_union; + +void inheritance2 (void) +{ +} + +void inheritance1 (void) +{ + int ival; + int *intp; + + // {A::a, A::x} + + g_A.A::a = 1; + g_A.A::x = 2; + + // {{A::a,A::x},B::b,B::x} + + g_B.A::a = 3; + g_B.A::x = 4; + g_B.B::b = 5; + g_B.B::x = 6; + + // {{A::a,A::x},C::c,C::x} + + g_C.A::a = 7; + g_C.A::x = 8; + g_C.C::c = 9; + g_C.C::x = 10; + + // {{{A::a,A::x},B::b,B::x},{{A::a,A::x},C::c,C::x},D::d,D::x} + + // The following initialization code is non-portable, but allows us + // to initialize all members of g_D until we can fill in the missing + // initialization code with legal C++ code. + + for (intp = (int *) &g_D, ival = 11; + intp < ((int *) &g_D + sizeof (g_D) / sizeof (int)); + intp++, ival++) + { + *intp = ival; + } + + // Overlay the nonportable initialization with legal initialization. + + // ????? = 11; (g_D.A::a = 11; is ambiguous) + // ????? = 12; (g_D.A::x = 12; is ambiguous) +/* djb 6-3-2000 + + This should take care of it. Rather than try to initialize using an ambiguous + construct, use 2 unambiguous ones for each. Since the ambiguous a/x member is + coming from C, and B, initialize D's C::a, and B::a, and D's C::x and B::x. + */ + g_D.C::a = 15; + g_D.C::x = 12; + g_D.B::a = 11; + g_D.B::x = 12; + g_D.B::b = 13; + g_D.B::x = 14; + // ????? = 15; + // ????? = 16; + g_D.C::c = 17; + g_D.C::x = 18; + g_D.D::d = 19; + g_D.D::x = 20; + + + // {{{{A::a,A::x},B::b,B::x},{{A::a,A::x},C::c,C::x},D::d,D::x}},E::e,E::x} + + // The following initialization code is non-portable, but allows us + // to initialize all members of g_D until we can fill in the missing + // initialization code with legal C++ code. + + for (intp = (int *) &g_E, ival = 21; + intp < ((int *) &g_E + sizeof (g_E) / sizeof (int)); + intp++, ival++) + { + *intp = ival; + } + + // Overlay the nonportable initialization with legal initialization. + + // ????? = 21; (g_E.A::a = 21; is ambiguous) + // ????? = 22; (g_E.A::x = 22; is ambiguous) + g_E.B::b = 23; + g_E.B::x = 24; + // ????? = 25; + // ????? = 26; + g_E.C::c = 27; + g_E.C::x = 28; + g_E.D::d = 29; + g_E.D::x = 30; + g_E.E::e = 31; + g_E.E::x = 32; + + g_anon_union.one = 1; + g_anon_union.a = 2; + + inheritance2 (); +} + +// ======================== static member functions ===================== + +class Static { +public: + static void ii(int, int); +}; +void Static::ii (int, int) { } + +// ======================== virtual base classes========================= + +class vA { + public: + int va; + int vx; +}; + +vA g_vA; + +class vB : public virtual vA { + public: + int vb; + int vx; +}; + +vB g_vB; + +class vC : public virtual vA { + public: + int vc; + int vx; +}; + +vC g_vC; + +class vD : public virtual vB, public virtual vC { + public: + int vd; + int vx; +}; + +vD g_vD; + +class vE : public virtual vD { + public: + int ve; + int vx; +}; + +vE g_vE; + +void inheritance4 (void) +{ +} + +void inheritance3 (void) +{ + int ival; + int *intp; + + // {vA::va, vA::vx} + + g_vA.vA::va = 1; + g_vA.vA::vx = 2; + + // {{vA::va, vA::vx}, vB::vb, vB::vx} + + g_vB.vA::va = 3; + g_vB.vA::vx = 4; + g_vB.vB::vb = 5; + g_vB.vB::vx = 6; + + // {{vA::va, vA::vx}, vC::vc, vC::vx} + + g_vC.vA::va = 7; + g_vC.vA::vx = 8; + g_vC.vC::vc = 9; + g_vC.vC::vx = 10; + + // {{{{vA::va, vA::vx}, vB::vb, vB::vx}, vC::vc, vC::vx}, vD::vd,vD::vx} + + g_vD.vA::va = 11; + g_vD.vA::vx = 12; + g_vD.vB::vb = 13; + g_vD.vB::vx = 14; + g_vD.vC::vc = 15; + g_vD.vC::vx = 16; + g_vD.vD::vd = 17; + g_vD.vD::vx = 18; + + + // {{{{{vA::va,vA::vx},vB::vb,vB::vx},vC::vc,vC::vx},vD::vd,vD::vx},vE::ve,vE::vx} + + g_vD.vA::va = 19; + g_vD.vA::vx = 20; + g_vD.vB::vb = 21; + g_vD.vB::vx = 22; + g_vD.vC::vc = 23; + g_vD.vC::vx = 24; + g_vD.vD::vd = 25; + g_vD.vD::vx = 26; + g_vE.vE::ve = 27; + g_vE.vE::vx = 28; + + inheritance4 (); +} + +// ====================================================================== + +class Base1 { + public: + int x; + Base1(int i) { x = i; } +}; + +class Foo +{ + public: + int x; + int y; + static int st; + Foo (int i, int j) { x = i; y = j; } + int operator! (); + operator int (); + int times (int y); +}; + +class Bar : public Base1, public Foo { + public: + int z; + Bar (int i, int j, int k) : Base1 (10*k), Foo (i, j) { z = k; } +}; + +int Foo::operator! () { return !x; } + +int Foo::times (int y) { return x * y; } + +int Foo::st = 100; + +Foo::operator int() { return x; } + +Foo foo(10, 11); +Bar bar(20, 21, 22); + +class ClassWithEnum { +public: + enum PrivEnum { red, green, blue, yellow = 42 }; + PrivEnum priv_enum; + int x; +}; + +void enums2 (void) +{ +} + +/* classes.exp relies on statement order in this function for testing + enumeration fields. */ + +void enums1 () +{ + ClassWithEnum obj_with_enum; + obj_with_enum.priv_enum = ClassWithEnum::red; + obj_with_enum.x = 0; + enums2 (); + obj_with_enum.priv_enum = ClassWithEnum::green; + obj_with_enum.x = 1; +} + +class ClassParam { +public: + int Aptr_a (A *a) { return a->a; } + int Aptr_x (A *a) { return a->x; } + int Aref_a (A &a) { return a.a; } + int Aref_x (A &a) { return a.x; } + int Aval_a (A a) { return a.a; } + int Aval_x (A a) { return a.x; } +}; + +ClassParam class_param; + +class Contains_static_instance +{ + public: + int x; + int y; + Contains_static_instance (int i, int j) { x = i; y = j; } + static Contains_static_instance null; +}; + +Contains_static_instance Contains_static_instance::null(0,0); +Contains_static_instance csi(10,20); + +class Contains_nested_static_instance +{ + public: + class Nested + { + public: + Nested(int i) : z(i) {} + int z; + static Contains_nested_static_instance xx; + }; + + Contains_nested_static_instance(int i, int j) : x(i), y(j) {} + + int x; + int y; + + static Contains_nested_static_instance null; + static Nested yy; +}; + +Contains_nested_static_instance Contains_nested_static_instance::null(0, 0); +Contains_nested_static_instance::Nested Contains_nested_static_instance::yy(5); +Contains_nested_static_instance + Contains_nested_static_instance::Nested::xx(1,2); +Contains_nested_static_instance cnsi(30,40); + +typedef struct { + int one; + int two; +} tagless_struct; +tagless_struct v_tagless; + +/* Try to get the compiler to allocate a class in a register. */ +class small { + public: + int x; + int method (); +}; + +int +small::method () +{ + return x + 5; +} + +void marker_reg1 () {} + +int +register_class () +{ + /* We don't call any methods for v, so gcc version cygnus-2.3.3-930220 + might put this variable in a register. This is a lose, though, because + it means that GDB can't call any methods for that variable. */ + register small v; + + int i; + + /* Perform a computation sufficiently complicated that optimizing compilers + won't optimized out the variable. If some compiler constant-folds this + whole loop, maybe using a parameter to this function here would help. */ + v.x = 0; + for (i = 0; i < 13; ++i) + v.x += i; + --v.x; /* v.x is now 77 */ + marker_reg1 (); + return v.x + 5; +} + +void dummy() +{ + v_bool = true; + v_bool_array[0] = false; + v_bool_array[1] = v_bool; +} + +void use_methods () +{ + /* Refer to methods so that they don't get optimized away. */ + int i; + i = class_param.Aptr_a (&g_A); + i = class_param.Aptr_x (&g_A); + i = class_param.Aref_a (g_A); + i = class_param.Aref_x (g_A); + i = class_param.Aval_a (g_A); + i = class_param.Aval_x (g_A); +} + + +int +main() +{ +#ifdef usestubs + set_debug_traps(); + breakpoint(); +#endif + dummy(); + inheritance1 (); + inheritance3 (); + enums1 (); + register_class (); + + /* FIXME: pmi gets optimized out. Need to do some more computation with + it or something. (No one notices, because the test is xfail'd anyway, + but that probably won't always be true...). */ + int Foo::* pmi = &Foo::y; + + /* Make sure the AIX linker doesn't remove the variable. */ + v_tagless.one = 5; + + use_methods (); + + return foo.*pmi; +} + +/* Create an instance for some classes, otherwise they get optimized away. */ + +default_public_struct default_public_s; +explicit_public_struct explicit_public_s; +protected_struct protected_s; +private_struct private_s; +mixed_protection_struct mixed_protection_s; +public_class public_c; +protected_class protected_c; +default_private_class default_private_c; +explicit_private_class explicit_private_c; +mixed_protection_class mixed_protection_c; diff --git a/gdb/testsuite/gdb.cp/pr-1553.cc b/gdb/testsuite/gdb.cp/pr-1553.cc new file mode 100644 index 0000000..58441fd --- /dev/null +++ b/gdb/testsuite/gdb.cp/pr-1553.cc @@ -0,0 +1,53 @@ +class A { +public: + class B; + class C; +}; + +class A::B { + int a_b; + +public: + C* get_c(int i); +}; + +class A::C +{ + int a_c; +}; + +class E { +public: + class F; +}; + +class E::F { +public: + int e_f; + + F& operator=(const F &other); +}; + +void refer_to (E::F *f) { + // Do nothing. +} + +void refer_to (A::C **ref) { + // Do nothing. But, while we're at it, force out debug info for + // A::B and E::F. + + A::B b; + E::F f; + + refer_to (&f); +} + +int main () { + A::C* c_var; + A::B* b_var; + E *e_var; + + // Keep around a reference so that GCC 3.4 doesn't optimize the variable + // away. + refer_to (&c_var); +} diff --git a/gdb/testsuite/gdb.cp/pr-1553.exp b/gdb/testsuite/gdb.cp/pr-1553.exp new file mode 100644 index 0000000..fe9e2a2 --- /dev/null +++ b/gdb/testsuite/gdb.cp/pr-1553.exp @@ -0,0 +1,62 @@ +# Copyright 2004 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# Test for PR gdb/1553. + +# This file is part of the gdb testsuite. + +set ws "\[\r\n\t \]+" + +if $tracelevel then { + strace $tracelevel +} + +if { [skip_cplus_tests] } { continue } + +# +# test running programs +# +set prms_id 0 +set bug_id 0 + +set testfile "pr-1553" +set srcfile ${testfile}.cc +set binfile ${objdir}/${subdir}/${testfile} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } { + gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." +} + +if [get_compiler_info ${binfile} "c++"] { + return -1 +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if ![runto_main] then { + perror "couldn't run to breakpoint" + continue +} + +gdb_test "ptype c_var" "type = class A::C \{${ws}private:${ws}int a_c;${ws}\} \\*" + +gdb_test "ptype E::F" "type = class E::F \{${ws}public:${ws}int e_f;${ws}E::F & operator=\\(E::F const ?&\\);${ws}\}" + +gdb_exit +return 0 diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c new file mode 100644 index 0000000..ee3635f --- /dev/null +++ b/gdb/tramp-frame.c @@ -0,0 +1,173 @@ +/* Signal trampoline unwinder, for GDB the GNU Debugger. + + Copyright 2004 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "tramp-frame.h" +#include "frame-unwind.h" +#include "gdbcore.h" +#include "symtab.h" +#include "objfiles.h" +#include "target.h" +#include "trad-frame.h" +#include "frame-base.h" +#include "gdb_assert.h" + +struct frame_data +{ + const struct tramp_frame *tramp_frame; +}; + +struct tramp_frame_cache +{ + CORE_ADDR func; + const struct tramp_frame *tramp_frame; + struct trad_frame_cache *trad_cache; +}; + +static struct trad_frame_cache * +tramp_frame_cache (struct frame_info *next_frame, + void **this_cache) +{ + CORE_ADDR pc = frame_pc_unwind (next_frame); + struct tramp_frame_cache *tramp_cache = (*this_cache); + if (tramp_cache->trad_cache == NULL) + { + tramp_cache->trad_cache = trad_frame_cache_zalloc (next_frame); + tramp_cache->tramp_frame->init (tramp_cache->tramp_frame, + next_frame, + tramp_cache->trad_cache, + tramp_cache->func); + } + return tramp_cache->trad_cache; +} + +static void +tramp_frame_this_id (struct frame_info *next_frame, + void **this_cache, + struct frame_id *this_id) +{ + struct trad_frame_cache *trad_cache + = tramp_frame_cache (next_frame, this_cache); + trad_frame_get_id (trad_cache, this_id); +} + +static void +tramp_frame_prev_register (struct frame_info *next_frame, + void **this_cache, + int prev_regnum, + int *optimizedp, + enum lval_type * lvalp, + CORE_ADDR *addrp, + int *realnump, void *valuep) +{ + struct trad_frame_cache *trad_cache + = tramp_frame_cache (next_frame, this_cache); + trad_frame_get_register (trad_cache, next_frame, prev_regnum, optimizedp, + lvalp, addrp, realnump, valuep); +} + +static CORE_ADDR +tramp_frame_start (CORE_ADDR pc, const struct tramp_frame *tramp) +{ + int ti; + /* Search through the trampoline for one that matches the + instruction sequence around PC. */ + for (ti = 0; tramp->insn[ti] != TRAMP_SENTINEL_INSN; ti++) + { + CORE_ADDR func = pc - tramp->insn_size * ti; + int i; + for (i = 0; 1; i++) + { + bfd_byte buf[sizeof (tramp->insn[0])]; + ULONGEST insn; + if (tramp->insn[i] == TRAMP_SENTINEL_INSN) + return func; + if (target_read_memory (func + i * tramp->insn_size, buf, + tramp->insn_size) != 0) + break; + insn = extract_unsigned_integer (buf, tramp->insn_size); + if (tramp->insn[i] != insn) + break; + } + } + /* Trampoline doesn't match. */ + return 0; +} + +static int +tramp_frame_sniffer (const struct frame_unwind *self, + struct frame_info *next_frame, + void **this_cache) +{ + const struct tramp_frame *tramp = self->unwind_data->tramp_frame; + CORE_ADDR pc = frame_pc_unwind (next_frame); + CORE_ADDR func; + char *name; + struct tramp_frame_cache *tramp_cache; + + /* If the function has a valid symbol name, it isn't a + trampoline. */ + find_pc_partial_function (pc, &name, NULL, NULL); + if (name != NULL) + return 0; + /* If the function lives in a valid section (even without a starting + point) it isn't a trampoline. */ + if (find_pc_section (pc) != NULL) + return 0; + /* Finally, check that the trampoline matches at PC. */ + func = tramp_frame_start (pc, tramp); + if (func == 0) + return 0; + tramp_cache = FRAME_OBSTACK_ZALLOC (struct tramp_frame_cache); + tramp_cache->func = func; + tramp_cache->tramp_frame = tramp; + (*this_cache) = tramp_cache; + return 1; +} + +void +tramp_frame_append (struct gdbarch *gdbarch, + const struct tramp_frame *tramp_frame) +{ + struct frame_data *data; + struct frame_unwind *unwinder; + int i; + + /* Check that the instruction sequence contains a sentinel. */ + for (i = 0; i < ARRAY_SIZE (tramp_frame->insn); i++) + { + if (tramp_frame->insn[i] == TRAMP_SENTINEL_INSN) + break; + } + gdb_assert (i < ARRAY_SIZE (tramp_frame->insn)); + gdb_assert (tramp_frame->insn_size <= sizeof (tramp_frame->insn[0])); + + data = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_data); + unwinder = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind); + + data->tramp_frame = tramp_frame; + unwinder->type = SIGTRAMP_FRAME; + unwinder->unwind_data = data; + unwinder->sniffer = tramp_frame_sniffer; + unwinder->this_id = tramp_frame_this_id; + unwinder->prev_register = tramp_frame_prev_register; + frame_unwind_register_unwinder (gdbarch, unwinder); +} diff --git a/gdb/tramp-frame.h b/gdb/tramp-frame.h new file mode 100644 index 0000000..54f29bb --- /dev/null +++ b/gdb/tramp-frame.h @@ -0,0 +1,69 @@ +/* Signal trampoline unwinder, for GDB the GNU Debugger. + + Copyright 2004 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef TRAMP_FRAME_H +#define TRAMP_FRAME_H + +struct trad_frame; +struct frame_info; +struct trad_frame_cache; + +/* A trampoline consists of a small sequence of instructions placed at + an unspecified location in the inferior's address space. The only + identifying attribute of the trampoline's address is that it does + not fall inside an object file's section. + + The only way to identify a trampoline is to perform a brute force + examination of the instructions at and around the PC. + + This module provides a convent interface for performing that + operation. */ + +/* A trampoline descriptor. */ + +/* Magic instruction that to mark the end of the signal trampoline + instruction sequence. */ +#define TRAMP_SENTINEL_INSN ((LONGEST) -1) + +struct tramp_frame +{ + /* The trampoline's entire instruction sequence. Search for this in + the inferior at or around the frame's PC. It is assumed that the + PC is INSN_SIZE aligned, and that each element of TRAMP contains + one INSN_SIZE instruction. It is also assumed that TRAMP[0] + contains the first instruction of the trampoline and hence the + address of the instruction matching TRAMP[0] is the trampoline's + "func" address. The instruction sequence shall be terminated by + TRAMP_SENTINEL_INSN. */ + int insn_size; + ULONGEST insn[8]; + /* Initialize a trad-frame cache corresponding to the tramp-frame. + FUNC is the address of the instruction TRAMP[0] in memory. */ + void (*init) (const struct tramp_frame *self, + struct frame_info *next_frame, + struct trad_frame_cache *this_cache, + CORE_ADDR func); +}; + +void tramp_frame_append (struct gdbarch *gdbarch, + const struct tramp_frame *tramp); + +#endif diff --git a/sim/frv/profile-fr450.c b/sim/frv/profile-fr450.c new file mode 100644 index 0000000..27b9755 --- /dev/null +++ b/sim/frv/profile-fr450.c @@ -0,0 +1,607 @@ +/* frv simulator fr450 dependent profiling code. + + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + Contributed by Red Hat + +This file is part of the GNU simulators. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +#define WANT_CPU +#define WANT_CPU_FRVBF + +#include "sim-main.h" +#include "bfd.h" + +#if WITH_PROFILE_MODEL_P + +#include "profile.h" +#include "profile-fr400.h" + +int +frvbf_model_fr450_u_exec (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced) +{ + return idesc->timing->units[unit_num].done; +} + +int +frvbf_model_fr450_u_integer (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, INT out_GRk, + INT out_ICCi_1) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_integer (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, out_GRk, out_ICCi_1); +} + +int +frvbf_model_fr450_u_imul (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, INT out_GRk, INT out_ICCi_1) +{ + int cycles; + + if (model_insn == FRV_INSN_MODEL_PASS_1) + { + /* Pass 1 is the same as for fr500. */ + return frvbf_model_fr500_u_imul (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, out_GRk, out_ICCi_1); + } + + /* icc0-icc4 are the upper 4 fields of the CCR. */ + if (out_ICCi_1 >= 0) + out_ICCi_1 += 4; + + /* GRk and IACCi_1 have a latency of 1 cycle. */ + cycles = idesc->timing->units[unit_num].done; + update_GRdouble_latency (cpu, out_GRk, cycles + 1); + update_CCR_latency (cpu, out_ICCi_1, cycles + 1); + + return cycles; +} + +int +frvbf_model_fr450_u_idiv (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, INT out_GRk, INT out_ICCi_1) +{ + int cycles; + + if (model_insn == FRV_INSN_MODEL_PASS_1) + { + /* Pass 1 is the same as for fr500. */ + return frvbf_model_fr500_u_idiv (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, out_GRk, out_ICCi_1); + } + + /* icc0-icc4 are the upper 4 fields of the CCR. */ + if (out_ICCi_1 >= 0) + out_ICCi_1 += 4; + + /* GRk, ICCi_1 and the divider have a latency of 18 cycles */ + cycles = idesc->timing->units[unit_num].done; + update_GR_latency (cpu, out_GRk, cycles + 18); + update_CCR_latency (cpu, out_ICCi_1, cycles + 18); + update_idiv_resource_latency (cpu, 0, cycles + 18); + + return cycles; +} + +int +frvbf_model_fr450_u_branch (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, + INT in_ICCi_2, INT in_ICCi_3) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_branch (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, in_ICCi_2, in_ICCi_3); +} + +int +frvbf_model_fr450_u_trap (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, + INT in_ICCi_2, INT in_FCCi_2) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_trap (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, in_ICCi_2, in_FCCi_2); +} + +int +frvbf_model_fr450_u_check (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_ICCi_3, INT in_FCCi_3) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_check (cpu, idesc, unit_num, referenced, + in_ICCi_3, in_FCCi_3); +} + +int +frvbf_model_fr450_u_set_hilo (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT out_GRkhi, INT out_GRklo) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_set_hilo (cpu, idesc, unit_num, referenced, + out_GRkhi, out_GRklo); +} + +int +frvbf_model_fr450_u_gr_load (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, + INT out_GRk, INT out_GRdoublek) +{ + int cycles; + + if (model_insn == FRV_INSN_MODEL_PASS_1) + { + /* Pass 1 is the same as for fr500. */ + return frvbf_model_fr500_u_fr_load (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, out_GRk, + out_GRdoublek); + } + + cycles = idesc->timing->units[unit_num].done; + + /* The latency of GRk for a load will depend on how long it takes to retrieve + the the data from the cache or memory. */ + update_GR_latency_for_load (cpu, out_GRk, cycles); + update_GRdouble_latency_for_load (cpu, out_GRdoublek, cycles); + + if (CGEN_ATTR_VALUE(idesc, idesc->attrs, CGEN_INSN_NON_EXCEPTING)) + { + /* GNER has a latency of 2 cycles. */ + update_SPR_latency (cpu, GNER_FOR_GR (out_GRk), cycles + 2); + update_SPR_latency (cpu, GNER_FOR_GR (out_GRdoublek), cycles + 2); + } + + return cycles; +} + +int +frvbf_model_fr450_u_gr_store (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, + INT in_GRk, INT in_GRdoublek) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_gr_store (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, in_GRk, in_GRdoublek); +} + +int +frvbf_model_fr450_u_fr_load (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, + INT out_FRk, INT out_FRdoublek) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_fr_load (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, out_FRk, out_FRdoublek); +} + +int +frvbf_model_fr450_u_fr_store (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, + INT in_FRk, INT in_FRdoublek) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_fr_load (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, in_FRk, in_FRdoublek); +} + +int +frvbf_model_fr450_u_swap (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj, INT out_GRk) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_swap (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj, out_GRk); +} + +int +frvbf_model_fr450_u_fr2gr (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRk, INT out_GRj) +{ + int cycles; + + if (model_insn == FRV_INSN_MODEL_PASS_1) + { + /* Pass 1 is the same as for fr400. */ + return frvbf_model_fr500_u_fr2gr (cpu, idesc, unit_num, referenced, + in_FRk, out_GRj); + } + + /* The latency of GRj is 1 cycle. */ + cycles = idesc->timing->units[unit_num].done; + update_GR_latency (cpu, out_GRj, cycles + 1); + + return cycles; +} + +int +frvbf_model_fr450_u_spr2gr (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_spr, INT out_GRj) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_spr2gr (cpu, idesc, unit_num, referenced, + in_spr, out_GRj); +} + +int +frvbf_model_fr450_u_gr2fr (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRj, INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_gr2fr (cpu, idesc, unit_num, referenced, + in_GRj, out_FRk); +} + +int +frvbf_model_fr450_u_gr2spr (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRj, INT out_spr) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_gr2spr (cpu, idesc, unit_num, referenced, + in_GRj, out_spr); +} + +int +frvbf_model_fr450_u_media_1 (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, INT in_FRj, + INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_1 (cpu, idesc, unit_num, referenced, + in_FRi, in_FRj, out_FRk); +} + +int +frvbf_model_fr450_u_media_1_quad (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, INT in_FRj, + INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_1_quad (cpu, idesc, unit_num, referenced, + in_FRi, in_FRj, out_FRk); +} + +int +frvbf_model_fr450_u_media_hilo (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT out_FRkhi, INT out_FRklo) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_hilo (cpu, idesc, unit_num, referenced, + out_FRkhi, out_FRklo); +} + +int +frvbf_model_fr450_u_media_2 (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, INT in_FRj, + INT out_ACC40Sk, INT out_ACC40Uk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_2 (cpu, idesc, unit_num, referenced, + in_FRi, in_FRj, out_ACC40Sk, + out_ACC40Uk); +} + +int +frvbf_model_fr450_u_media_2_quad (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, INT in_FRj, + INT out_ACC40Sk, INT out_ACC40Uk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_2_quad (cpu, idesc, unit_num, referenced, + in_FRi, in_FRj, out_ACC40Sk, + out_ACC40Uk); +} + +int +frvbf_model_fr450_u_media_2_acc (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_ACC40Si, INT out_ACC40Sk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_2_acc (cpu, idesc, unit_num, referenced, + in_ACC40Si, out_ACC40Sk); +} + +int +frvbf_model_fr450_u_media_2_acc_dual (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_ACC40Si, INT out_ACC40Sk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_2_acc_dual (cpu, idesc, unit_num, + referenced, in_ACC40Si, + out_ACC40Sk); +} + +int +frvbf_model_fr450_u_media_2_add_sub (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_ACC40Si, INT out_ACC40Sk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_2_add_sub (cpu, idesc, unit_num, + referenced, in_ACC40Si, + out_ACC40Sk); +} + +int +frvbf_model_fr450_u_media_2_add_sub_dual (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_ACC40Si, INT out_ACC40Sk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_2_add_sub_dual (cpu, idesc, unit_num, + referenced, in_ACC40Si, + out_ACC40Sk); +} + +int +frvbf_model_fr450_u_media_3 (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, INT in_FRj, + INT out_FRk) +{ + /* Modelling is the same as media unit 1. */ + return frvbf_model_fr450_u_media_1 (cpu, idesc, unit_num, referenced, + in_FRi, in_FRj, out_FRk); +} + +int +frvbf_model_fr450_u_media_3_dual (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_3_dual (cpu, idesc, unit_num, referenced, + in_FRi, out_FRk); +} + +int +frvbf_model_fr450_u_media_3_quad (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, INT in_FRj, + INT out_FRk) +{ + /* Modelling is the same as media unit 1. */ + return frvbf_model_fr450_u_media_1_quad (cpu, idesc, unit_num, referenced, + in_FRi, in_FRj, out_FRk); +} + +int +frvbf_model_fr450_u_media_4 (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_ACC40Si, INT in_FRj, + INT out_ACC40Sk, INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_4 (cpu, idesc, unit_num, referenced, + in_ACC40Si, in_FRj, + out_ACC40Sk, out_FRk); +} + +int +frvbf_model_fr450_u_media_4_accg (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_ACCGi, INT in_FRinti, + INT out_ACCGk, INT out_FRintk) +{ + /* Modelling is the same as media-4 unit except use accumulator guards + as input instead of accumulators. */ + return frvbf_model_fr450_u_media_4 (cpu, idesc, unit_num, referenced, + in_ACCGi, in_FRinti, + out_ACCGk, out_FRintk); +} + +int +frvbf_model_fr450_u_media_4_acc_dual (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_ACC40Si, INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_4_acc_dual (cpu, idesc, unit_num, + referenced, in_ACC40Si, + out_FRk); +} + +int +frvbf_model_fr450_u_media_4_mclracca (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced) +{ + int cycles; + int acc; + FRV_PROFILE_STATE *ps; + + if (model_insn == FRV_INSN_MODEL_PASS_1) + return 0; + + /* The preprocessing can execute right away. */ + cycles = idesc->timing->units[unit_num].done; + + ps = CPU_PROFILE_STATE (cpu); + + /* The post processing must wait for any pending ACC writes. */ + ps->post_wait = cycles; + for (acc = 0; acc < 4; acc++) + post_wait_for_ACC (cpu, acc); + for (acc = 8; acc < 12; acc++) + post_wait_for_ACC (cpu, acc); + + for (acc = 0; acc < 4; acc++) + { + update_ACC_latency (cpu, acc, ps->post_wait); + update_ACC_ptime (cpu, acc, 2); + } + for (acc = 8; acc < 12; acc++) + { + update_ACC_latency (cpu, acc, ps->post_wait); + update_ACC_ptime (cpu, acc, 2); + } + + return cycles; +} + +int +frvbf_model_fr450_u_media_6 (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_6 (cpu, idesc, unit_num, referenced, + in_FRi, out_FRk); +} + +int +frvbf_model_fr450_u_media_7 (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRinti, INT in_FRintj, + INT out_FCCk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_7 (cpu, idesc, unit_num, referenced, + in_FRinti, in_FRintj, out_FCCk); +} + +int +frvbf_model_fr450_u_media_dual_expand (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRi, + INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_dual_expand (cpu, idesc, unit_num, + referenced, in_FRi, out_FRk); +} + +int +frvbf_model_fr450_u_media_dual_htob (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_FRj, + INT out_FRk) +{ + /* Modelling for this unit is the same as for fr400. */ + return frvbf_model_fr400_u_media_dual_htob (cpu, idesc, unit_num, + referenced, in_FRj, out_FRk); +} + +int +frvbf_model_fr450_u_ici (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_ici (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj); +} + +int +frvbf_model_fr450_u_dci (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_dci (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj); +} + +int +frvbf_model_fr450_u_dcf (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_dcf (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj); +} + +int +frvbf_model_fr450_u_icpl (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_icpl (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj); +} + +int +frvbf_model_fr450_u_dcpl (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_dcpl (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj); +} + +int +frvbf_model_fr450_u_icul (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_icul (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj); +} + +int +frvbf_model_fr450_u_dcul (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced, + INT in_GRi, INT in_GRj) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_dcul (cpu, idesc, unit_num, referenced, + in_GRi, in_GRj); +} + +int +frvbf_model_fr450_u_barrier (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_barrier (cpu, idesc, unit_num, referenced); +} + +int +frvbf_model_fr450_u_membar (SIM_CPU *cpu, const IDESC *idesc, + int unit_num, int referenced) +{ + /* Modelling for this unit is the same as for fr500. */ + return frvbf_model_fr500_u_membar (cpu, idesc, unit_num, referenced); +} + +#endif /* WITH_PROFILE_MODEL_P */ diff --git a/sim/testsuite/sim/frv/mqlclrhs.cgs b/sim/testsuite/sim/frv/mqlclrhs.cgs new file mode 100644 index 0000000..5e090b0 --- /dev/null +++ b/sim/testsuite/sim/frv/mqlclrhs.cgs @@ -0,0 +1,74 @@ +# frv testcase for mqlclrhs $FRi,$FRj,$FRj +# mach: fr450 + + .include "testutils.inc" + + start + + .global mqlclrhs +mqlclrhs: + set_fr_iimmed 0x1000,0x2000,fr4 + set_fr_iimmed 0xe800,0xd800,fr5 + set_fr_iimmed 0x0800,0x0800,fr6 + set_fr_iimmed 0x0800,0x0800,fr7 + mqlclrhs fr4,fr6,fr8 + test_fr_limmed 0x1000,0x2000,fr8 + test_fr_limmed 0xe800,0xd800,fr9 + + set_fr_iimmed 0x1000,0x2000,fr4 + set_fr_iimmed 0xe800,0xd800,fr5 + set_fr_iimmed 0xf800,0xf800,fr6 + set_fr_iimmed 0xf800,0xf800,fr7 + mqlclrhs fr4,fr6,fr8 + test_fr_limmed 0xf000,0xe000,fr8 + test_fr_limmed 0x1800,0x2800,fr9 + + set_fr_iimmed 0x1000,0x1000,fr4 + set_fr_iimmed 0x1000,0x1000,fr5 + set_fr_iimmed 0xf000,0xf800,fr6 + set_fr_iimmed 0x0800,0x1000,fr7 + mqlclrhs fr4,fr6,fr8 + test_fr_limmed 0x0000,0xf000,fr8 + test_fr_limmed 0x1000,0x0000,fr9 + + set_fr_iimmed 0xf000,0xf000,fr4 + set_fr_iimmed 0xf000,0xf000,fr5 + set_fr_iimmed 0xf000,0xf800,fr6 + set_fr_iimmed 0x0800,0x1000,fr7 + mqlclrhs fr4,fr6,fr8 + test_fr_limmed 0x0000,0x1000,fr8 + test_fr_limmed 0xf000,0x0000,fr9 + + set_fr_iimmed 0x8000,0x8000,fr4 + set_fr_iimmed 0x8000,0x8000,fr5 + set_fr_iimmed 0x8000,0x7fff,fr6 + set_fr_iimmed 0x8001,0x0000,fr7 + mqlclrhs fr4,fr6,fr8 + test_fr_limmed 0x0000,0x8000,fr8 + test_fr_limmed 0x7fff,0x8000,fr9 + + set_fr_iimmed 0x7fff,0x7fff,fr4 + set_fr_iimmed 0x7fff,0x7fff,fr5 + set_fr_iimmed 0x8000,0x7fff,fr6 + set_fr_iimmed 0x8001,0x0000,fr7 + mqlclrhs fr4,fr6,fr8 + test_fr_limmed 0x0000,0x0000,fr8 + test_fr_limmed 0x0000,0x7fff,fr9 + + set_fr_iimmed 0x8001,0x8001,fr4 + set_fr_iimmed 0x8001,0x8001,fr5 + set_fr_iimmed 0x8000,0x7fff,fr6 + set_fr_iimmed 0x8001,0x0000,fr7 + mqlclrhs fr4,fr6,fr8 + test_fr_limmed 0x0000,0x0000,fr8 + test_fr_limmed 0x0000,0x8001,fr9 + + set_fr_iimmed 0x8000,0x8000,fr4 + set_fr_iimmed 0x0001,0xffff,fr5 + set_fr_iimmed 0x0001,0xffff,fr6 + set_fr_iimmed 0x8000,0x8000,fr7 + mqlclrhs fr4,fr6,fr8 + test_fr_limmed 0x8000,0x7fff,fr8 + test_fr_limmed 0x0000,0x0000,fr9 + + pass diff --git a/sim/testsuite/sim/frv/mqlmths.cgs b/sim/testsuite/sim/frv/mqlmths.cgs new file mode 100644 index 0000000..d416d65 --- /dev/null +++ b/sim/testsuite/sim/frv/mqlmths.cgs @@ -0,0 +1,74 @@ +# frv testcase for mqlmths $FRi,$FRj,$FRj +# mach: fr450 + + .include "testutils.inc" + + start + + .global mqlmths +mqlmths: + set_fr_iimmed 0x1000,0x2000,fr4 + set_fr_iimmed 0xe800,0xd800,fr5 + set_fr_iimmed 0x0800,0x0800,fr6 + set_fr_iimmed 0x0800,0x0800,fr7 + mqlmths fr4,fr6,fr8 + test_fr_limmed 0x0800,0x0800,fr8 + test_fr_limmed 0xf800,0xf800,fr9 + + set_fr_iimmed 0x1000,0x2000,fr4 + set_fr_iimmed 0xe800,0xd800,fr5 + set_fr_iimmed 0xf800,0xf800,fr6 + set_fr_iimmed 0xf800,0xf800,fr7 + mqlmths fr4,fr6,fr8 + test_fr_limmed 0xf800,0xf800,fr8 + test_fr_limmed 0x0800,0x0800,fr9 + + set_fr_iimmed 0x1000,0x1000,fr4 + set_fr_iimmed 0x1000,0x1000,fr5 + set_fr_iimmed 0xe800,0xf800,fr6 + set_fr_iimmed 0x0800,0x1800,fr7 + mqlmths fr4,fr6,fr8 + test_fr_limmed 0x1000,0xf800,fr8 + test_fr_limmed 0x0800,0x1000,fr9 + + set_fr_iimmed 0xf000,0xf000,fr4 + set_fr_iimmed 0xf000,0xf000,fr5 + set_fr_iimmed 0xe800,0xf800,fr6 + set_fr_iimmed 0x0800,0x1800,fr7 + mqlmths fr4,fr6,fr8 + test_fr_limmed 0xf000,0x0800,fr8 + test_fr_limmed 0xf800,0xf000,fr9 + + set_fr_iimmed 0x8000,0x8000,fr4 + set_fr_iimmed 0x8000,0x8000,fr5 + set_fr_iimmed 0x8000,0x7fff,fr6 + set_fr_iimmed 0x8001,0x0000,fr7 + mqlmths fr4,fr6,fr8 + test_fr_limmed 0x7fff,0x8001,fr8 + test_fr_limmed 0x7fff,0x0000,fr9 + + set_fr_iimmed 0x7fff,0x7fff,fr4 + set_fr_iimmed 0x7fff,0x7fff,fr5 + set_fr_iimmed 0x8000,0x7fff,fr6 + set_fr_iimmed 0x8001,0x0000,fr7 + mqlmths fr4,fr6,fr8 + test_fr_limmed 0x7fff,0x7fff,fr8 + test_fr_limmed 0x8001,0x0000,fr9 + + set_fr_iimmed 0x8001,0x8001,fr4 + set_fr_iimmed 0x8001,0x8001,fr5 + set_fr_iimmed 0x8000,0x7fff,fr6 + set_fr_iimmed 0x8001,0x0000,fr7 + mqlmths fr4,fr6,fr8 + test_fr_limmed 0x8001,0x8001,fr8 + test_fr_limmed 0x7fff,0x0000,fr9 + + set_fr_iimmed 0x8000,0x8000,fr4 + set_fr_iimmed 0x0001,0xffff,fr5 + set_fr_iimmed 0x0001,0xffff,fr6 + set_fr_iimmed 0x8000,0x8000,fr7 + mqlmths fr4,fr6,fr8 + test_fr_limmed 0xffff,0x0001,fr8 + test_fr_limmed 0x0001,0xffff,fr9 + + pass diff --git a/sim/testsuite/sim/frv/mqsllhi.cgs b/sim/testsuite/sim/frv/mqsllhi.cgs new file mode 100644 index 0000000..21379f2 --- /dev/null +++ b/sim/testsuite/sim/frv/mqsllhi.cgs @@ -0,0 +1,40 @@ +# frv testcase for mqsllhi $FRi,#u6,$FRj +# mach: fr450 + + .include "testutils.inc" + + start + + .global mqsllhi +mqsllhi: + set_fr_iimmed 0x0001,0x0002,fr4 + set_fr_iimmed 0x0003,0x0004,fr5 + mqsllhi fr4,#1,fr6 + test_fr_limmed 0x0002,0x0004,fr6 + test_fr_limmed 0x0006,0x0008,fr7 + + set_fr_iimmed 0xffff,0xfffe,fr4 + set_fr_iimmed 0xfffc,0xfff8,fr5 + mqsllhi fr4,#1,fr6 + test_fr_limmed 0xfffe,0xfffc,fr6 + test_fr_limmed 0xfff8,0xfff0,fr7 + + set_fr_iimmed 0xffff,0xfffe,fr4 + set_fr_iimmed 0xfffc,0xfff8,fr5 + mqsllhi fr4,#12,fr6 + test_fr_limmed 0xf000,0xe000,fr6 + test_fr_limmed 0xc000,0x8000,fr7 + + set_fr_iimmed 0x1234,0x5678,fr4 + set_fr_iimmed 0x9abc,0xdef0,fr5 + mqsllhi fr4,#12,fr6 + test_fr_limmed 0x4000,0x8000,fr6 + test_fr_limmed 0xc000,0x0000,fr7 + + set_fr_iimmed 0x1234,0x5678,fr4 + set_fr_iimmed 0x9abc,0xdef0,fr5 + mqsllhi fr4,#16,fr6 + test_fr_limmed 0x1234,0x5678,fr6 + test_fr_limmed 0x9abc,0xdef0,fr7 + + pass diff --git a/sim/testsuite/sim/frv/mqsrahi.cgs b/sim/testsuite/sim/frv/mqsrahi.cgs new file mode 100644 index 0000000..1d30179 --- /dev/null +++ b/sim/testsuite/sim/frv/mqsrahi.cgs @@ -0,0 +1,40 @@ +# frv testcase for mqsrahi $FRi,#u6,$FRj +# mach: fr450 + + .include "testutils.inc" + + start + + .global mqsrahi +mqsrahi: + set_fr_iimmed 0x0001,0x0002,fr4 + set_fr_iimmed 0x0003,0x0004,fr5 + mqsrahi fr4,#1,fr6 + test_fr_limmed 0x0000,0x0001,fr6 + test_fr_limmed 0x0001,0x0002,fr7 + + set_fr_iimmed 0xffff,0xfffe,fr4 + set_fr_iimmed 0xfffc,0xfff8,fr5 + mqsrahi fr4,#1,fr6 + test_fr_limmed 0xffff,0xffff,fr6 + test_fr_limmed 0xfffe,0xfffc,fr7 + + set_fr_iimmed 0x8000,0xc000,fr4 + set_fr_iimmed 0xe000,0xf000,fr5 + mqsrahi fr4,#12,fr6 + test_fr_limmed 0xfff8,0xfffc,fr6 + test_fr_limmed 0xfffe,0xffff,fr7 + + set_fr_iimmed 0x1234,0x5678,fr4 + set_fr_iimmed 0x9abc,0xdef0,fr5 + mqsrahi fr4,#12,fr6 + test_fr_limmed 0x0001,0x0005,fr6 + test_fr_limmed 0xfff9,0xfffd,fr7 + + set_fr_iimmed 0x1234,0x5678,fr4 + set_fr_iimmed 0x9abc,0xdef0,fr5 + mqsrahi fr4,#16,fr6 + test_fr_limmed 0x1234,0x5678,fr6 + test_fr_limmed 0x9abc,0xdef0,fr7 + + pass -- cgit v1.1