#include "defs.h"
#include "inferior.h"
#include "target.h"
#include "gdbcore.h"
#include "xcoffsolib.h"
#include "symfile.h"
#include "objfiles.h"
#include "libbfd.h"
#include "bfd.h"
#include "gdb-stabs.h"
#include "regcache.h"
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <a.out.h>
#include <sys/file.h>
#include "gdb_stat.h"
#include <sys/core.h>
#define __LDINFO_PTRACE32__
#define __LDINFO_PTRACE64__
#include <sys/ldr.h>
#include <sys/systemcfg.h>
#ifdef __ld_info32
# define ARCH3264
#endif
#ifndef ARCH3264
# define ARCH64() 0
#else
# define ARCH64() (REGISTER_RAW_SIZE (0) == 8)
#endif
typedef union {
#ifdef ARCH3264
struct __context64 r64;
#else
struct mstsave r64;
#endif
struct mstsave r32;
} CoreRegs;
typedef union {
#ifndef ARCH3264
struct ld_info l32;
struct ld_info l64;
#else
struct __ld_info32 l32;
struct __ld_info64 l64;
#endif
} LdInfo;
#ifndef ARCH3264
# define ARCH64_DECL(var)
#else
# define ARCH64_DECL(var) int var = ARCH64 ()
#endif
#ifndef ARCH3264
# define LDI_FIELD(ldi, arch64, field) (ldi)->l32.ldinfo_##field
#else
# define LDI_FIELD(ldi, arch64, field) \
(arch64 ? (ldi)->l64.ldinfo_##field : (ldi)->l32.ldinfo_##field)
#endif
#define LDI_NEXT(ldi, arch64) LDI_FIELD(ldi, arch64, next)
#define LDI_FD(ldi, arch64) LDI_FIELD(ldi, arch64, fd)
#define LDI_FILENAME(ldi, arch64) LDI_FIELD(ldi, arch64, filename)
extern struct vmap *map_vmap (bfd * bf, bfd * arch);
extern struct target_ops exec_ops;
static void vmap_exec (void);
static void vmap_ldinfo (LdInfo *);
static struct vmap *add_vmap (LdInfo *);
static int objfile_symbol_add (void *);
static void vmap_symtab (struct vmap *);
static void fetch_core_registers (char *, unsigned int, int, CORE_ADDR);
static void exec_one_dummy_insn (void);
extern void
fixup_breakpoints (CORE_ADDR low, CORE_ADDR high, CORE_ADDR delta);
static int special_regs[] =
{
IAR,
MSR,
CR,
LR,
CTR,
XER,
MQ
};
static int
ptrace32 (int req, int id, int *addr, int data, int *buf)
{
int ret = ptrace (req, id, (int *)addr, data, buf);
#if 0
printf ("ptrace32 (%d, %d, 0x%x, %08x, 0x%x) = 0x%x\n",
req, id, (unsigned int)addr, data, (unsigned int)buf, ret);
#endif
return ret;
}
static int
ptrace64 (int req, int id, long long addr, int data, int *buf)
{
#ifdef ARCH3264
int ret = ptracex (req, id, addr, data, buf);
#else
int ret = 0;
#endif
#if 0
printf ("ptrace64 (%d, %d, 0x%llx, %08x, 0x%x) = 0x%x\n",
req, id, addr, data, (unsigned int)buf, ret);
#endif
return ret;
}
static void
fetch_register (int regno)
{
int *addr = (int *) ®isters[REGISTER_BYTE (regno)];
int nr;
errno = 0;
if (regno >= FP0_REGNUM && regno <= FPLAST_REGNUM)
{
nr = regno - FP0_REGNUM + FPR0;
ptrace32 (PT_READ_FPR, PIDGET (inferior_ptid), addr, nr, 0);
}
else if (regno > LAST_UISA_SP_REGNUM)
{
if (regno >= NUM_REGS)
fprintf_unfiltered (gdb_stderr,
"gdb error: register no %d not implemented.\n",
regno);
}
else
{
if (regno >= FIRST_UISA_SP_REGNUM)
nr = special_regs[regno - FIRST_UISA_SP_REGNUM];
else
nr = regno;
if (!ARCH64 ())
*addr = ptrace32 (PT_READ_GPR, PIDGET (inferior_ptid), (int *)nr, 0, 0);
else
{
long long buf;
ptrace64 (PT_READ_GPR, PIDGET (inferior_ptid), nr, 0, (int *)&buf);
if (REGISTER_RAW_SIZE (regno) == 8)
memcpy (addr, &buf, 8);
else
*addr = buf;
}
}
if (!errno)
register_valid[regno] = 1;
else
{
#if 0
perror ("ptrace read");
#endif
errno = 0;
}
}
static void
store_register (int regno)
{
int *addr = (int *) ®isters[REGISTER_BYTE (regno)];
int nr;
errno = 0;
if (regno >= FP0_REGNUM && regno <= FPLAST_REGNUM)
{
nr = regno - FP0_REGNUM + FPR0;
ptrace32 (PT_WRITE_FPR, PIDGET (inferior_ptid), addr, nr, 0);
}
else if (regno > LAST_UISA_SP_REGNUM)
{
if (regno >= NUM_REGS)
fprintf_unfiltered (gdb_stderr,
"gdb error: register no %d not implemented.\n",
regno);
}
else
{
if (regno == SP_REGNUM)
exec_one_dummy_insn ();
if (regno >= FIRST_UISA_SP_REGNUM)
nr = special_regs[regno - FIRST_UISA_SP_REGNUM];
else
nr = regno;
if (!ARCH64 ())
ptrace32 (PT_WRITE_GPR, PIDGET (inferior_ptid), (int *)nr, *addr, 0);
else
{
long long buf;
if (REGISTER_RAW_SIZE (regno) == 8)
memcpy (&buf, addr, 8);
else
buf = *addr;
ptrace64 (PT_WRITE_GPR, PIDGET (inferior_ptid), nr, 0, (int *)&buf);
}
}
if (errno)
{
perror ("ptrace write");
errno = 0;
}
}
void
fetch_inferior_registers (int regno)
{
if (regno != -1)
fetch_register (regno);
else
{
for (regno = 0; regno < 32; regno++)
fetch_register (regno);
for (regno = FP0_REGNUM; regno <= FPLAST_REGNUM; regno++)
fetch_register (regno);
for (regno = FIRST_UISA_SP_REGNUM; regno <= LAST_UISA_SP_REGNUM; regno++)
fetch_register (regno);
}
}
void
store_inferior_registers (int regno)
{
if (regno != -1)
store_register (regno);
else
{
for (regno = GPR0; regno <= GPR31; regno++)
store_register (regno);
for (regno = FP0_REGNUM; regno <= FPLAST_REGNUM; regno++)
store_register (regno);
for (regno = FIRST_UISA_SP_REGNUM; regno <= LAST_UISA_SP_REGNUM; regno++)
store_register (regno);
}
}
static int
read_word (CORE_ADDR from, int *to, int arch64)
{
errno = 0;
if (arch64)
*to = ptrace64 (PT_READ_I, PIDGET (inferior_ptid), from, 0, NULL);
else
*to = ptrace32 (PT_READ_I, PIDGET (inferior_ptid), (int *)(long) from,
0, NULL);
return !errno;
}
int
child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len,
int write, struct mem_attrib *attrib,
struct target_ops *target)
{
int mask = sizeof (int) - 1;
CORE_ADDR addr = memaddr & ~(CORE_ADDR)mask;
int count = ((memaddr + len - addr + mask) & ~(CORE_ADDR)mask)
/ sizeof (int);
int *buf = (int *) alloca (count * sizeof (int));
int arch64 = ARCH64 ();
int i;
if (!write)
{
for (i = 0; i < count; i++, addr += sizeof (int))
{
if (!read_word (addr, buf + i, arch64))
return 0;
QUIT;
}
addr -= count * sizeof (int);
memcpy (myaddr, (char *)buf + (memaddr - addr), len);
}
else
{
if (addr < memaddr)
if (!read_word (addr, buf, arch64))
return 0;
if (addr + count * sizeof (int) > memaddr + len)
if (!read_word (addr, buf + count - 1, arch64))
return 0;
memcpy ((char *)buf + (memaddr - addr), myaddr, len);
for (i = 0, errno = 0; i < count; i++, addr += sizeof (int))
{
if (arch64)
ptrace64 (PT_WRITE_D, PIDGET (inferior_ptid), addr, buf[i], NULL);
else
ptrace32 (PT_WRITE_D, PIDGET (inferior_ptid), (int *)(long) addr,
buf[i], NULL);
if (errno)
return 0;
QUIT;
}
}
return len;
}
static void
exec_one_dummy_insn (void)
{
#define DUMMY_INSN_ADDR (TEXT_SEGMENT_BASE)+0x200
char shadow_contents[BREAKPOINT_MAX];
int ret, status, pid;
CORE_ADDR prev_pc;
target_insert_breakpoint (DUMMY_INSN_ADDR, shadow_contents);
prev_pc = read_pc ();
write_pc (DUMMY_INSN_ADDR);
if (ARCH64 ())
ret = ptrace64 (PT_CONTINUE, PIDGET (inferior_ptid), 1, 0, NULL);
else
ret = ptrace32 (PT_CONTINUE, PIDGET (inferior_ptid), (int *)1, 0, NULL);
if (ret != 0)
perror ("pt_continue");
do
{
pid = wait (&status);
}
while (pid != PIDGET (inferior_ptid));
write_pc (prev_pc);
target_remove_breakpoint (DUMMY_INSN_ADDR, shadow_contents);
}
static void
fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
int which, CORE_ADDR reg_addr)
{
CoreRegs *regs;
double *fprs;
int arch64, i, size;
void *gprs, *sprs[7];
if (which != 0)
{
fprintf_unfiltered
(gdb_stderr,
"Gdb error: unknown parameter to fetch_core_registers().\n");
return;
}
arch64 = ARCH64 ();
regs = (CoreRegs *) core_reg_sect;
if (arch64)
{
gprs = regs->r64.gpr;
fprs = regs->r64.fpr;
sprs[0] = ®s->r64.iar;
sprs[1] = ®s->r64.msr;
sprs[2] = ®s->r64.cr;
sprs[3] = ®s->r64.lr;
sprs[4] = ®s->r64.ctr;
sprs[5] = ®s->r64.xer;
}
else
{
gprs = regs->r32.gpr;
fprs = regs->r32.fpr;
sprs[0] = ®s->r32.iar;
sprs[1] = ®s->r32.msr;
sprs[2] = ®s->r32.cr;
sprs[3] = ®s->r32.lr;
sprs[4] = ®s->r32.ctr;
sprs[5] = ®s->r32.xer;
sprs[6] = ®s->r32.mq;
}
memcpy (registers, gprs, 32 * (arch64 ? 8 : 4));
memcpy (registers + REGISTER_BYTE (FP0_REGNUM), fprs, 32 * 8);
for (i = FIRST_UISA_SP_REGNUM; i <= LAST_UISA_SP_REGNUM; i++)
{
size = REGISTER_RAW_SIZE (i);
if (size)
memcpy (registers + REGISTER_BYTE (i),
sprs[i - FIRST_UISA_SP_REGNUM], size);
}
}
static void
vmap_secs (struct vmap *vp, LdInfo *ldi, int arch64)
{
if (arch64)
{
vp->tstart = (CORE_ADDR) ldi->l64.ldinfo_textorg;
vp->tend = vp->tstart + ldi->l64.ldinfo_textsize;
vp->dstart = (CORE_ADDR) ldi->l64.ldinfo_dataorg;
vp->dend = vp->dstart + ldi->l64.ldinfo_datasize;
}
else
{
vp->tstart = (unsigned long) ldi->l32.ldinfo_textorg;
vp->tend = vp->tstart + ldi->l32.ldinfo_textsize;
vp->dstart = (unsigned long) ldi->l32.ldinfo_dataorg;
vp->dend = vp->dstart + ldi->l32.ldinfo_datasize;
}
vp->tstart += vp->toffs;
}
static void
vmap_symtab (struct vmap *vp)
{
register struct objfile *objfile;
struct section_offsets *new_offsets;
int i;
objfile = vp->objfile;
if (objfile == NULL)
{
if (symfile_objfile == NULL)
return;
objfile = symfile_objfile;
}
else if (!vp->loaded)
return;
new_offsets = (struct section_offsets *) alloca (SIZEOF_SECTION_OFFSETS);
for (i = 0; i < objfile->num_sections; ++i)
new_offsets->offsets[i] = ANOFFSET (objfile->section_offsets, i);
new_offsets->offsets[SECT_OFF_TEXT (objfile)] = vp->tstart - vp->tvma;
new_offsets->offsets[SECT_OFF_DATA (objfile)] = vp->dstart - vp->dvma;
new_offsets->offsets[SECT_OFF_BSS (objfile)] = vp->dstart - vp->dvma;
objfile_relocate (objfile, new_offsets);
}
static int
objfile_symbol_add (void *arg)
{
struct objfile *obj = (struct objfile *) arg;
syms_from_objfile (obj, NULL, 0, 0);
new_symfile_objfile (obj, 0, 0);
return 1;
}
int
vmap_add_symbols (struct vmap *vp)
{
if (catch_errors (objfile_symbol_add, vp->objfile,
"Error while reading shared library symbols:\n",
RETURN_MASK_ALL))
{
vp->loaded = 1;
vmap_symtab (vp);
return 1;
}
return 0;
}
static struct vmap *
add_vmap (LdInfo *ldi)
{
bfd *abfd, *last;
register char *mem, *objname, *filename;
struct objfile *obj;
struct vmap *vp;
int fd;
ARCH64_DECL (arch64);
filename = LDI_FILENAME (ldi, arch64);
mem = filename + strlen (filename) + 1;
mem = savestring (mem, strlen (mem));
objname = savestring (filename, strlen (filename));
fd = LDI_FD (ldi, arch64);
if (fd < 0)
abfd = bfd_openr (objname, gnutarget);
else
abfd = bfd_fdopenr (objname, gnutarget, fd);
if (!abfd)
{
warning ("Could not open `%s' as an executable file: %s",
objname, bfd_errmsg (bfd_get_error ()));
return NULL;
}
if (bfd_check_format (abfd, bfd_object))
vp = map_vmap (abfd, 0);
else if (bfd_check_format (abfd, bfd_archive))
{
last = 0;
while ((last = bfd_openr_next_archived_file (abfd, last)))
if (STREQ (mem, last->filename))
break;
if (!last)
{
warning ("\"%s\": member \"%s\" missing.", objname, mem);
bfd_close (abfd);
return NULL;
}
if (!bfd_check_format (last, bfd_object))
{
warning ("\"%s\": member \"%s\" not in executable format: %s.",
objname, mem, bfd_errmsg (bfd_get_error ()));
bfd_close (last);
bfd_close (abfd);
return NULL;
}
vp = map_vmap (last, abfd);
}
else
{
warning ("\"%s\": not in executable format: %s.",
objname, bfd_errmsg (bfd_get_error ()));
bfd_close (abfd);
return NULL;
}
obj = allocate_objfile (vp->bfd, 0);
vp->objfile = obj;
if (vp == vmap || auto_solib_add)
vmap_add_symbols (vp);
return vp;
}
static void
vmap_ldinfo (LdInfo *ldi)
{
struct stat ii, vi;
register struct vmap *vp;
int got_one, retried;
int got_exec_file = 0;
uint next;
int arch64 = ARCH64 ();
do
{
char *name = LDI_FILENAME (ldi, arch64);
char *memb = name + strlen (name) + 1;
int fd = LDI_FD (ldi, arch64);
retried = 0;
if (fstat (fd, &ii) < 0)
{
warning ("%s (fd=%d) has disappeared, keeping its symbols",
name, fd);
continue;
}
retry:
for (got_one = 0, vp = vmap; vp; vp = vp->nxt)
{
struct objfile *objfile;
if ((name[0] == '/' && !STREQ (name, vp->name))
|| (memb[0] && !STREQ (memb, vp->member)))
continue;
objfile = vp->objfile == NULL ? symfile_objfile : vp->objfile;
if (objfile == NULL
|| objfile->obfd == NULL
|| bfd_stat (objfile->obfd, &vi) < 0)
{
warning ("Unable to stat %s, keeping its symbols", name);
continue;
}
if (ii.st_dev != vi.st_dev || ii.st_ino != vi.st_ino)
continue;
if (!retried)
close (fd);
++got_one;
vmap_secs (vp, ldi, arch64);
if (vp->objfile == NULL)
got_exec_file = 1;
vmap_symtab (vp);
}
if (!got_one && !retried)
{
add_vmap (ldi);
++retried;
goto retry;
}
}
while ((next = LDI_NEXT (ldi, arch64))
&& (ldi = (void *) (next + (char *) ldi)));
if (symfile_objfile != NULL && !got_exec_file)
{
warning_begin ();
fputs_unfiltered ("Symbol file ", gdb_stderr);
fputs_unfiltered (symfile_objfile->name, gdb_stderr);
fputs_unfiltered ("\nis not mapped; discarding it.\n\
If in fact that file has symbols which the mapped files listed by\n\
\"info files\" lack, you can load symbols with the \"symbol-file\" or\n\
\"add-symbol-file\" commands (note that you must take care of relocating\n\
symbols to the proper address).\n", gdb_stderr);
free_objfile (symfile_objfile);
symfile_objfile = NULL;
}
breakpoint_re_set ();
}
static void
vmap_exec (void)
{
static bfd *execbfd;
int i;
if (execbfd == exec_bfd)
return;
execbfd = exec_bfd;
if (!vmap || !exec_ops.to_sections)
error ("vmap_exec: vmap or exec_ops.to_sections == 0\n");
for (i = 0; &exec_ops.to_sections[i] < exec_ops.to_sections_end; i++)
{
if (STREQ (".text", exec_ops.to_sections[i].the_bfd_section->name))
{
exec_ops.to_sections[i].addr += vmap->tstart - vmap->tvma;
exec_ops.to_sections[i].endaddr += vmap->tstart - vmap->tvma;
}
else if (STREQ (".data", exec_ops.to_sections[i].the_bfd_section->name))
{
exec_ops.to_sections[i].addr += vmap->dstart - vmap->dvma;
exec_ops.to_sections[i].endaddr += vmap->dstart - vmap->dvma;
}
else if (STREQ (".bss", exec_ops.to_sections[i].the_bfd_section->name))
{
exec_ops.to_sections[i].addr += vmap->dstart - vmap->dvma;
exec_ops.to_sections[i].endaddr += vmap->dstart - vmap->dvma;
}
}
}
static void
set_host_arch (int pid)
{
enum bfd_architecture arch;
unsigned long mach;
bfd abfd;
struct gdbarch_info info;
if (__power_rs ())
{
arch = bfd_arch_rs6000;
mach = bfd_mach_rs6k;
}
else
{
arch = bfd_arch_powerpc;
mach = bfd_mach_ppc;
}
bfd_default_set_arch_mach (&abfd, arch, mach);
gdbarch_info_init (&info);
info.bfd_arch_info = bfd_get_arch_info (&abfd);
if (!gdbarch_update_p (info))
{
internal_error (__FILE__, __LINE__,
"set_host_arch: failed to select architecture");
}
}
void
xcoff_relocate_symtab (unsigned int pid)
{
int load_segs = 64;
int rc;
LdInfo *ldi = NULL;
int arch64 = ARCH64 ();
int ldisize = arch64 ? sizeof (ldi->l64) : sizeof (ldi->l32);
int size;
do
{
size = load_segs * ldisize;
ldi = (void *) xrealloc (ldi, size);
#if 0
usleep (36000);
#endif
if (arch64)
rc = ptrace64 (PT_LDINFO, pid, (unsigned long) ldi, size, NULL);
else
rc = ptrace32 (PT_LDINFO, pid, (int *) ldi, size, NULL);
if (rc == -1)
{
if (errno == ENOMEM)
load_segs *= 2;
else
perror_with_name ("ptrace ldinfo");
}
else
{
vmap_ldinfo (ldi);
vmap_exec ();
}
} while (rc == -1);
if (ldi)
xfree (ldi);
}
void
xcoff_relocate_core (struct target_ops *target)
{
sec_ptr ldinfo_sec;
int offset = 0;
LdInfo *ldi;
struct vmap *vp;
int arch64 = ARCH64 ();
int nonfilesz = (int)LDI_FILENAME ((LdInfo *)0, arch64);
int buffer_size = nonfilesz;
char *buffer = xmalloc (buffer_size);
struct cleanup *old = make_cleanup (free_current_contents, &buffer);
ldinfo_sec = bfd_get_section_by_name (core_bfd, ".ldinfo");
if (ldinfo_sec == NULL)
{
bfd_err:
fprintf_filtered (gdb_stderr, "Couldn't get ldinfo from core file: %s\n",
bfd_errmsg (bfd_get_error ()));
do_cleanups (old);
return;
}
do
{
int i;
int names_found = 0;
if (bfd_get_section_contents (core_bfd, ldinfo_sec, buffer,
offset, nonfilesz) == 0)
goto bfd_err;
i = nonfilesz;
do
{
if (i == buffer_size)
{
buffer_size *= 2;
buffer = xrealloc (buffer, buffer_size);
}
if (bfd_get_section_contents (core_bfd, ldinfo_sec, &buffer[i],
offset + i, 1) == 0)
goto bfd_err;
if (buffer[i++] == '\0')
++names_found;
}
while (names_found < 2);
ldi = (LdInfo *) buffer;
if (arch64)
ldi->l64.ldinfo_fd = -1;
else
ldi->l32.ldinfo_fd = -1;
if (offset == 0 && vmap != NULL)
vp = vmap;
else
vp = add_vmap (ldi);
offset += LDI_NEXT (ldi, arch64);
if (vp == NULL)
continue;
vmap_secs (vp, ldi, arch64);
if (vp != vmap)
{
struct section_table *stp;
target_resize_to_sections (target, 2);
stp = target->to_sections_end - 2;
stp->bfd = vp->bfd;
stp->the_bfd_section = bfd_get_section_by_name (stp->bfd, ".text");
stp->addr = vp->tstart;
stp->endaddr = vp->tend;
stp++;
stp->bfd = vp->bfd;
stp->the_bfd_section = bfd_get_section_by_name (stp->bfd, ".data");
stp->addr = vp->dstart;
stp->endaddr = vp->dend;
}
vmap_symtab (vp);
}
while (LDI_NEXT (ldi, arch64) != 0);
vmap_exec ();
breakpoint_re_set ();
do_cleanups (old);
}
int
kernel_u_size (void)
{
return (sizeof (struct user));
}
static CORE_ADDR
find_toc_address (CORE_ADDR pc)
{
struct vmap *vp;
extern CORE_ADDR get_toc_offset (struct objfile *);
for (vp = vmap; vp; vp = vp->nxt)
{
if (pc >= vp->tstart && pc < vp->tend)
{
return vp->dstart + get_toc_offset (vp->objfile == NULL
? symfile_objfile
: vp->objfile);
}
}
error ("Unable to find TOC entry for pc 0x%x\n", pc);
}
static struct core_fns rs6000_core_fns =
{
bfd_target_xcoff_flavour,
default_check_format,
default_core_sniffer,
fetch_core_registers,
NULL
};
void
_initialize_core_rs6000 (void)
{
rs6000_find_toc_address_hook = find_toc_address;
rs6000_set_host_arch_hook = set_host_arch;
add_core_fns (&rs6000_core_fns);
}