mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-02 20:53:06 +08:00

Implement a "generate-core-file" command in gdb, save target state. * gcore.c: New file. Implement new command 'generate-core-file'. Save a corefile image of the current state of the inferior. * linux-proc.c: Add linux-specific code for saving corefiles. * target.h (struct target_ops): Add new target vectors for saving corefiles; to_find_memory_regions and to_make_corefile_notes. (target_find_memory_regions): New macro. (target_make_corefile_notes): New macro. * target.c (update_current_target): Inherit new target methods. (dummy_find_memory_regions): New place-holder method. (dummy_make_corefile_notes): New place-holder method. (init_dummy_target): Initialize new dummy target vectors. * exec.c (exec_set_find_memory_regions): New function. Allow the exec_ops vector for memory regions to be taken over. (exec_make_note_section): New function, target vector method. * defs.h (exec_set_find_memory_regions): Export prototype. * procfs.c (proc_find_memory_regions): New function, corefile method. (procfs_make_note_section): New function, corefile method. (init_procfs_ops): Set new target vector pointers. (find_memory_regions_callback): New function. (procfs_do_thread_registers): New function. (procfs_corefile_thread_callback): New function. * sol-thread.c (sol_find_memory_regions): New function. (sol_make_note_section): New function. (init_sol_thread_ops): Initialize new target vectors. * inftarg.c (inftarg_set_find_memory_regions): New function. Allow to_find_memory_regions vector to be taken over. (inftarg_set_make_corefile_notes): New function. Allow to_make_corefile_notes vector to be taken over. * thread-db.c (thread_db_new_objfile): Don't activate thread-db interface layer if not target_has_execution (may be a corefile). * config/i386/linux.mh: Add gcore.o to NATDEPFILES. * config/sparc/sun4sol2.mh: Ditto. * config/alpha/alpha-linux.mh: Ditto. * config/arm/linux.mh: Ditto. * config/i386/x86-64linux.mh: Ditto. * config/ia64/linux.mh: Ditto. * config/m68k/linux.mh: Ditto. * config/mips/linux.mh: Ditto. * config/powerpc/linux.mh: Ditto. * config/sparc/linux.mh: Ditto.
272 lines
7.4 KiB
C
272 lines
7.4 KiB
C
/* Linux-specific methods for using the /proc file system.
|
|
Copyright 2001, 2002 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 "inferior.h"
|
|
#include <sys/param.h> /* for MAXPATHLEN */
|
|
#include <sys/procfs.h>
|
|
#include "gregset.h" /* for gregset */
|
|
#include "gdbcore.h" /* for get_exec_file */
|
|
#include "gdbthread.h" /* for struct thread_info etc. */
|
|
#include "elf-bfd.h"
|
|
|
|
/* Function: child_pid_to_exec_file
|
|
*
|
|
* Accepts an integer pid
|
|
* Returns a string representing a file that can be opened
|
|
* to get the symbols for the child process.
|
|
*/
|
|
|
|
char *
|
|
child_pid_to_exec_file (int pid)
|
|
{
|
|
static char fname[MAXPATHLEN];
|
|
|
|
sprintf (fname, "/proc/%d/exe", pid);
|
|
/* FIXME use readlink to get the real name. */
|
|
return fname;
|
|
}
|
|
|
|
/* Function: linux_find_memory_regions
|
|
*
|
|
* Fills the "to_find_memory_regions" target vector.
|
|
* Lists the memory regions in the inferior for a corefile.
|
|
*/
|
|
|
|
static int
|
|
linux_find_memory_regions (int (*func) (CORE_ADDR,
|
|
unsigned long,
|
|
int, int, int,
|
|
void *),
|
|
void *obfd)
|
|
{
|
|
long long pid = PIDGET (inferior_ptid);
|
|
char procfilename[MAXPATHLEN];
|
|
FILE *procfile;
|
|
long long addr, endaddr, size, offset, inode;
|
|
char perms[8], dev[8], filename[MAXPATHLEN];
|
|
int read, write, exec;
|
|
int ret;
|
|
|
|
/* Compose the filename for the /proc memory map, and open it. */
|
|
sprintf (procfilename, "/proc/%lld/maps", pid);
|
|
if ((procfile = fopen (procfilename, "r")) == NULL)
|
|
error ("Could not open %s\n", procfilename);
|
|
|
|
if (info_verbose)
|
|
fprintf_filtered (gdb_stdout,
|
|
"Reading memory regions from %s\n", procfilename);
|
|
|
|
/* Read the first memory segment descriptor from the maps file. */
|
|
ret = fscanf (procfile, "%llx-%llx %s %llx %s %llx",
|
|
&addr, &endaddr, perms, &offset, dev, &inode);
|
|
if (inode)
|
|
fscanf (procfile, " %s\n", filename);
|
|
else
|
|
{
|
|
filename[0] = '\0';
|
|
fscanf (procfile, "\n");
|
|
}
|
|
|
|
/* Now iterate until end-of-file. */
|
|
while (ret > 0 && ret != EOF)
|
|
{
|
|
size = endaddr - addr;
|
|
|
|
/* Get the segment's permissions. */
|
|
read = (strchr (perms, 'r') != 0);
|
|
write = (strchr (perms, 'w') != 0);
|
|
exec = (strchr (perms, 'x') != 0);
|
|
|
|
if (info_verbose)
|
|
{
|
|
fprintf_filtered (gdb_stdout,
|
|
"Save segment, %lld bytes at 0x%s (%c%c%c)",
|
|
size, paddr_nz (addr),
|
|
read ? 'r' : ' ',
|
|
write ? 'w' : ' ',
|
|
exec ? 'x' : ' ');
|
|
if (filename && filename[0])
|
|
fprintf_filtered (gdb_stdout,
|
|
" for %s", filename);
|
|
fprintf_filtered (gdb_stdout, "\n");
|
|
}
|
|
|
|
/* Invoke the callback function to create the corefile segment. */
|
|
func (addr, size, read, write, exec, obfd);
|
|
|
|
/* Read the next memory region. */
|
|
filename[0] = '\0';
|
|
ret = fscanf (procfile, "%llx-%llx %s %llx %s %llx",
|
|
&addr, &endaddr, perms, &offset, dev, &inode);
|
|
if (inode)
|
|
fscanf (procfile, " %s\n", filename);
|
|
else
|
|
{
|
|
filename[0] = '\0';
|
|
fscanf (procfile, "\n");
|
|
}
|
|
}
|
|
|
|
fclose (procfile);
|
|
return 0;
|
|
}
|
|
|
|
/* Function: linux_do_thread_registers
|
|
*
|
|
* Records the thread's register state for the corefile note section.
|
|
*/
|
|
|
|
static char *
|
|
linux_do_thread_registers (bfd *obfd, ptid_t ptid,
|
|
char *note_data, int *note_size)
|
|
{
|
|
gdb_gregset_t gregs;
|
|
gdb_fpregset_t fpregs;
|
|
#ifdef HAVE_PTRACE_GETFPXREGS
|
|
gdb_fpxregset_t fpxregs;
|
|
#endif
|
|
unsigned long merged_pid = ptid_get_tid (ptid) << 16 | ptid_get_pid (ptid);
|
|
|
|
fill_gregset (&gregs, -1);
|
|
note_data = (char *) elfcore_write_prstatus (obfd,
|
|
note_data,
|
|
note_size,
|
|
merged_pid,
|
|
stop_signal,
|
|
&gregs);
|
|
|
|
fill_fpregset (&fpregs, -1);
|
|
note_data = (char *) elfcore_write_prfpreg (obfd,
|
|
note_data,
|
|
note_size,
|
|
&fpregs,
|
|
sizeof (fpregs));
|
|
#ifdef HAVE_PTRACE_GETFPXREGS
|
|
fill_fpxregset (&fpxregs, -1);
|
|
note_data = (char *) elfcore_write_prxfpreg (obfd,
|
|
note_data,
|
|
note_size,
|
|
&fpxregs,
|
|
sizeof (fpxregs));
|
|
#endif
|
|
return note_data;
|
|
}
|
|
|
|
struct linux_corefile_thread_data {
|
|
bfd *obfd;
|
|
char *note_data;
|
|
int *note_size;
|
|
};
|
|
|
|
/* Function: linux_corefile_thread_callback
|
|
*
|
|
* Called by gdbthread.c once per thread.
|
|
* Records the thread's register state for the corefile note section.
|
|
*/
|
|
|
|
static int
|
|
linux_corefile_thread_callback (struct thread_info *ti, void *data)
|
|
{
|
|
struct linux_corefile_thread_data *args = data;
|
|
ptid_t saved_ptid = inferior_ptid;
|
|
|
|
inferior_ptid = ti->ptid;
|
|
registers_changed ();
|
|
target_fetch_registers (-1); /* FIXME should not be necessary;
|
|
fill_gregset should do it automatically. */
|
|
args->note_data = linux_do_thread_registers (args->obfd,
|
|
ti->ptid,
|
|
args->note_data,
|
|
args->note_size);
|
|
inferior_ptid = saved_ptid;
|
|
registers_changed ();
|
|
target_fetch_registers (-1); /* FIXME should not be necessary;
|
|
fill_gregset should do it automatically. */
|
|
return 0;
|
|
}
|
|
|
|
/* Function: linux_make_note_section
|
|
*
|
|
* Fills the "to_make_corefile_note" target vector.
|
|
* Builds the note section for a corefile, and returns it
|
|
* in a malloc buffer.
|
|
*/
|
|
|
|
static char *
|
|
linux_make_note_section (bfd *obfd, int *note_size)
|
|
{
|
|
struct linux_corefile_thread_data thread_args;
|
|
struct cleanup *old_chain;
|
|
char fname[16] = {'\0'};
|
|
char psargs[80] = {'\0'};
|
|
char *note_data = NULL;
|
|
ptid_t current_ptid = inferior_ptid;
|
|
|
|
if (get_exec_file (0))
|
|
{
|
|
strncpy (fname, strrchr (get_exec_file (0), '/') + 1, sizeof (fname));
|
|
strncpy (psargs, get_exec_file (0),
|
|
sizeof (psargs));
|
|
if (get_inferior_args ())
|
|
{
|
|
strncat (psargs, " ",
|
|
sizeof (psargs) - strlen (psargs));
|
|
strncat (psargs, get_inferior_args (),
|
|
sizeof (psargs) - strlen (psargs));
|
|
}
|
|
note_data = (char *) elfcore_write_prpsinfo (obfd,
|
|
note_data,
|
|
note_size,
|
|
fname,
|
|
psargs);
|
|
}
|
|
|
|
/* Dump information for threads. */
|
|
thread_args.obfd = obfd;
|
|
thread_args.note_data = note_data;
|
|
thread_args.note_size = note_size;
|
|
iterate_over_threads (linux_corefile_thread_callback, &thread_args);
|
|
if (thread_args.note_data == note_data)
|
|
{
|
|
/* iterate_over_threads didn't come up with any threads;
|
|
just use inferior_ptid. */
|
|
note_data = linux_do_thread_registers (obfd, inferior_ptid,
|
|
note_data, note_size);
|
|
}
|
|
else
|
|
{
|
|
note_data = thread_args.note_data;
|
|
}
|
|
|
|
make_cleanup (xfree, note_data);
|
|
return note_data;
|
|
}
|
|
|
|
void
|
|
_initialize_linux_proc (void)
|
|
{
|
|
extern void inftarg_set_find_memory_regions ();
|
|
extern void inftarg_set_make_corefile_notes ();
|
|
|
|
inftarg_set_find_memory_regions (linux_find_memory_regions);
|
|
inftarg_set_make_corefile_notes (linux_make_note_section);
|
|
}
|