/* Low level interface to ptrace, for the remote server for GDB.
- Copyright (C) 1995-2022 Free Software Foundation, Inc.
+ Copyright (C) 1995-2023 Free Software Foundation, Inc.
This file is part of GDB.
/* Does the current host support PTRACE_GETREGSET? */
int have_ptrace_getregset = -1;
+/* Return TRUE if THREAD is the leader thread of the process. */
+
+static bool
+is_leader (thread_info *thread)
+{
+ ptid_t ptid = ptid_of (thread);
+ return ptid.pid () == ptid.lwp ();
+}
+
/* LWP accessors. */
/* See nat/linux-nat.h. */
gdb_assert (info == nullptr);
}
+/* Open the /proc/PID/mem file for PROC. */
+
+static void
+open_proc_mem_file (process_info *proc)
+{
+ gdb_assert (proc->priv->mem_fd == -1);
+
+ char filename[64];
+ xsnprintf (filename, sizeof filename, "/proc/%d/mem", proc->pid);
+
+ proc->priv->mem_fd
+ = gdb_open_cloexec (filename, O_RDWR | O_LARGEFILE, 0).release ();
+}
+
process_info *
-linux_process_target::add_linux_process (int pid, int attached)
+linux_process_target::add_linux_process_no_mem_file (int pid, int attached)
{
struct process_info *proc;
proc->priv = XCNEW (struct process_info_private);
proc->priv->arch_private = low_new_process ();
+ proc->priv->mem_fd = -1;
+
+ return proc;
+}
+
+process_info *
+linux_process_target::add_linux_process (int pid, int attached)
+{
+ process_info *proc = add_linux_process_no_mem_file (pid, attached);
+ open_proc_mem_file (proc);
return proc;
}
+void
+linux_process_target::remove_linux_process (process_info *proc)
+{
+ if (proc->priv->mem_fd >= 0)
+ close (proc->priv->mem_fd);
+
+ this->low_delete_process (proc->priv->arch_private);
+
+ xfree (proc->priv);
+ proc->priv = nullptr;
+
+ remove_process (proc);
+}
+
arch_process_info *
linux_process_target::low_new_process ()
{
return 0;
}
- internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
+ internal_error (_("unknown ptrace event %d"), event);
}
CORE_ADDR
linux_process_target::get_pc (lwp_info *lwp)
{
- struct regcache *regcache;
- CORE_ADDR pc;
+ process_info *proc = get_thread_process (get_lwp_thread (lwp));
+ gdb_assert (!proc->starting_up);
if (!low_supports_breakpoints ())
return 0;
scoped_restore_current_thread restore_thread;
switch_to_thread (get_lwp_thread (lwp));
- regcache = get_thread_regcache (current_thread, 1);
- pc = low_get_pc (regcache);
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
+ CORE_ADDR pc = low_get_pc (regcache);
threads_debug_printf ("pc is 0x%lx", (long) pc);
if (!low_supports_breakpoints ())
return false;
+ process_info *proc = get_thread_process (get_lwp_thread (lwp));
+ if (proc->starting_up)
+ {
+ /* Claim we have the stop PC so that the caller doesn't try to
+ fetch it itself. */
+ return true;
+ }
+
pc = get_pc (lwp);
sw_breakpoint_pc = pc - low_decr_pc_after_break ();
NULL, NULL, NULL, NULL);
}
- add_linux_process (pid, 0);
+ /* When spawning a new process, we can't open the mem file yet. We
+ still have to nurse the process through the shell, and that execs
+ a couple times. The address space a /proc/PID/mem file is
+ accessing is destroyed on exec. */
+ process_info *proc = add_linux_process_no_mem_file (pid, 0);
ptid = ptid_t (pid, pid);
new_lwp = add_lwp (ptid);
post_fork_inferior (pid, program);
+ /* PROC is now past the shell running the program we want, so we can
+ open the /proc/PID/mem file. */
+ open_proc_mem_file (proc);
+
return pid;
}
ptid_t ptid = ptid_t (pid, pid);
int err;
- proc = add_linux_process (pid, 1);
+ /* Delay opening the /proc/PID/mem file until we've successfully
+ attached. */
+ proc = add_linux_process_no_mem_file (pid, 1);
/* Attach to PID. We will check for other threads
soon. */
err = attach_lwp (ptid);
if (err != 0)
{
- remove_process (proc);
+ this->remove_linux_process (proc);
std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
}
+ open_proc_mem_file (proc);
+
/* Don't ignore the initial SIGSTOP if we just attached to this
process. It will be collected by wait shortly. */
initial_thread = find_thread_ptid (ptid_t (pid, pid));
void
linux_process_target::mourn (process_info *process)
{
- struct process_info_private *priv;
-
#ifdef USE_THREAD_DB
thread_db_mourn (process);
#endif
delete_lwp (get_thread_lwp (thread));
});
- /* Freeing all private data. */
- priv = process->priv;
- low_delete_process (priv->arch_private);
- free (priv);
- process->priv = NULL;
-
- remove_process (process);
+ this->remove_linux_process (process);
}
void
struct lwp_info *
find_lwp_pid (ptid_t ptid)
{
- thread_info *thread = find_thread ([&] (thread_info *thr_arg)
+ long lwp = ptid.lwp () != 0 ? ptid.lwp () : ptid.pid ();
+ thread_info *thread = find_thread ([lwp] (thread_info *thr_arg)
{
- int lwp = ptid.lwp () != 0 ? ptid.lwp () : ptid.pid ();
return thr_arg->id.lwp () == lwp;
});
void
linux_process_target::check_zombie_leaders ()
{
- for_each_process ([this] (process_info *proc) {
- pid_t leader_pid = pid_of (proc);
- struct lwp_info *leader_lp;
-
- leader_lp = find_lwp_pid (ptid_t (leader_pid));
-
- threads_debug_printf ("leader_pid=%d, leader_lp!=NULL=%d, "
- "num_lwps=%d, zombie=%d",
- leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
- linux_proc_pid_is_zombie (leader_pid));
-
- if (leader_lp != NULL && !leader_lp->stopped
- /* Check if there are other threads in the group, as we may
- have raced with the inferior simply exiting. */
- && !last_thread_of_process_p (leader_pid)
- && linux_proc_pid_is_zombie (leader_pid))
- {
- /* A leader zombie can mean one of two things:
-
- - It exited, and there's an exit status pending
- available, or only the leader exited (not the whole
- program). In the latter case, we can't waitpid the
- leader's exit status until all other threads are gone.
-
- - There are 3 or more threads in the group, and a thread
- other than the leader exec'd. On an exec, the Linux
- kernel destroys all other threads (except the execing
- one) in the thread group, and resets the execing thread's
- tid to the tgid. No exit notification is sent for the
- execing thread -- from the ptracer's perspective, it
- appears as though the execing thread just vanishes.
- Until we reap all other threads except the leader and the
- execing thread, the leader will be zombie, and the
- execing thread will be in `D (disc sleep)'. As soon as
- all other threads are reaped, the execing thread changes
- it's tid to the tgid, and the previous (zombie) leader
- vanishes, giving place to the "new" leader. We could try
- distinguishing the exit and exec cases, by waiting once
- more, and seeing if something comes out, but it doesn't
- sound useful. The previous leader _does_ go away, and
- we'll re-add the new one once we see the exec event
- (which is just the same as what would happen if the
- previous leader did exit voluntarily before some other
- thread execs). */
-
- threads_debug_printf ("Thread group leader %d zombie "
- "(it exited, or another thread execd).",
- leader_pid);
-
- delete_lwp (leader_lp);
- }
+ for_each_process ([this] (process_info *proc)
+ {
+ pid_t leader_pid = pid_of (proc);
+ lwp_info *leader_lp = find_lwp_pid (ptid_t (leader_pid));
+
+ threads_debug_printf ("leader_pid=%d, leader_lp!=NULL=%d, "
+ "num_lwps=%d, zombie=%d",
+ leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
+ linux_proc_pid_is_zombie (leader_pid));
+
+ if (leader_lp != NULL && !leader_lp->stopped
+ /* Check if there are other threads in the group, as we may
+ have raced with the inferior simply exiting. Note this
+ isn't a watertight check. If the inferior is
+ multi-threaded and is exiting, it may be we see the
+ leader as zombie before we reap all the non-leader
+ threads. See comments below. */
+ && !last_thread_of_process_p (leader_pid)
+ && linux_proc_pid_is_zombie (leader_pid))
+ {
+ /* A zombie leader in a multi-threaded program can mean one
+ of three things:
+
+ #1 - Only the leader exited, not the whole program, e.g.,
+ with pthread_exit. Since we can't reap the leader's exit
+ status until all other threads are gone and reaped too,
+ we want to delete the zombie leader right away, as it
+ can't be debugged, we can't read its registers, etc.
+ This is the main reason we check for zombie leaders
+ disappearing.
+
+ #2 - The whole thread-group/process exited (a group exit,
+ via e.g. exit(3), and there is (or will be shortly) an
+ exit reported for each thread in the process, and then
+ finally an exit for the leader once the non-leaders are
+ reaped.
+
+ #3 - There are 3 or more threads in the group, and a
+ thread other than the leader exec'd. See comments on
+ exec events at the top of the file.
+
+ Ideally we would never delete the leader for case #2.
+ Instead, we want to collect the exit status of each
+ non-leader thread, and then finally collect the exit
+ status of the leader as normal and use its exit code as
+ whole-process exit code. Unfortunately, there's no
+ race-free way to distinguish cases #1 and #2. We can't
+ assume the exit events for the non-leaders threads are
+ already pending in the kernel, nor can we assume the
+ non-leader threads are in zombie state already. Between
+ the leader becoming zombie and the non-leaders exiting
+ and becoming zombie themselves, there's a small time
+ window, so such a check would be racy. Temporarily
+ pausing all threads and checking to see if all threads
+ exit or not before re-resuming them would work in the
+ case that all threads are running right now, but it
+ wouldn't work if some thread is currently already
+ ptrace-stopped, e.g., due to scheduler-locking.
+
+ So what we do is we delete the leader anyhow, and then
+ later on when we see its exit status, we re-add it back.
+ We also make sure that we only report a whole-process
+ exit when we see the leader exiting, as opposed to when
+ the last LWP in the LWP list exits, which can be a
+ non-leader if we deleted the leader here. */
+ threads_debug_printf ("Thread group leader %d zombie "
+ "(it exited, or another thread execd), "
+ "deleting it.",
+ leader_pid);
+ delete_lwp (leader_lp);
+ }
});
}
{
struct thread_info *thread = get_lwp_thread (lwp);
- internal_error (__FILE__, __LINE__,
- "unsuspend LWP %ld, suspended=%d\n", lwpid_of (thread),
+ internal_error ("unsuspend LWP %ld, suspended=%d\n", lwpid_of (thread),
lwp->suspended);
}
}
child = find_lwp_pid (ptid_t (lwpid));
- /* Check for stop events reported by a process we didn't already
- know about - anything not already in our LWP list.
-
- If we're expecting to receive stopped processes after
- fork, vfork, and clone events, then we'll just add the
- new one to our list and go back to waiting for the event
- to be reported - the stopped process might be returned
- from waitpid before or after the event is.
-
- But note the case of a non-leader thread exec'ing after the
- leader having exited, and gone from our lists (because
- check_zombie_leaders deleted it). The non-leader thread
- changes its tid to the tgid. */
-
- if (WIFSTOPPED (wstat) && child == NULL && WSTOPSIG (wstat) == SIGTRAP
- && linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC)
+ /* Check for events reported by anything not in our LWP list. */
+ if (child == nullptr)
{
- ptid_t child_ptid;
+ if (WIFSTOPPED (wstat))
+ {
+ if (WSTOPSIG (wstat) == SIGTRAP
+ && linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC)
+ {
+ /* A non-leader thread exec'ed after we've seen the
+ leader zombie, and removed it from our lists (in
+ check_zombie_leaders). The non-leader thread changes
+ its tid to the tgid. */
+ threads_debug_printf
+ ("Re-adding thread group leader LWP %d after exec.",
+ lwpid);
- /* A multi-thread exec after we had seen the leader exiting. */
- threads_debug_printf ("Re-adding thread group leader LWP %d after exec.",
- lwpid);
+ child = add_lwp (ptid_t (lwpid, lwpid));
+ child->stopped = 1;
+ switch_to_thread (child->thread);
+ }
+ else
+ {
+ /* A process we are controlling has forked and the new
+ child's stop was reported to us by the kernel. Save
+ its PID and go back to waiting for the fork event to
+ be reported - the stopped process might be returned
+ from waitpid before or after the fork event is. */
+ threads_debug_printf
+ ("Saving LWP %d status %s in stopped_pids list",
+ lwpid, status_to_str (wstat).c_str ());
+ add_to_pid_list (&stopped_pids, lwpid, wstat);
+ }
+ }
+ else
+ {
+ /* Don't report an event for the exit of an LWP not in our
+ list, i.e. not part of any inferior we're debugging.
+ This can happen if we detach from a program we originally
+ forked and then it exits. However, note that we may have
+ earlier deleted a leader of an inferior we're debugging,
+ in check_zombie_leaders. Re-add it back here if so. */
+ find_process ([&] (process_info *proc)
+ {
+ if (proc->pid == lwpid)
+ {
+ threads_debug_printf
+ ("Re-adding thread group leader LWP %d after exit.",
+ lwpid);
- child_ptid = ptid_t (lwpid, lwpid);
- child = add_lwp (child_ptid);
- child->stopped = 1;
- switch_to_thread (child->thread);
- }
+ child = add_lwp (ptid_t (lwpid, lwpid));
+ return true;
+ }
+ return false;
+ });
+ }
- /* If we didn't find a process, one of two things presumably happened:
- - A process we started and then detached from has exited. Ignore it.
- - A process we are controlling has forked and the new child's stop
- was reported to us by the kernel. Save its PID. */
- if (child == NULL && WIFSTOPPED (wstat))
- {
- add_to_pid_list (&stopped_pids, lwpid, wstat);
- return;
+ if (child == nullptr)
+ return;
}
- else if (child == NULL)
- return;
thread = get_lwp_thread (child);
unsuspend_all_lwps (child);
}
- /* If there is at least one more LWP, then the exit signal was
- not the end of the debugged application and should be
- ignored, unless GDB wants to hear about thread exits. */
- if (cs.report_thread_events
- || last_thread_of_process_p (pid_of (thread)))
+ /* If this is not the leader LWP, then the exit signal was not
+ the end of the debugged application and should be ignored,
+ unless GDB wants to hear about thread exits. */
+ if (cs.report_thread_events || is_leader (thread))
{
/* Since events are serialized to GDB core, and we can't
report this one right now. Leave the status pending for
int step = 0;
if (thread->last_resume_kind == resume_step)
- step = maybe_hw_step (thread);
+ {
+ if (supports_software_single_step ())
+ install_software_single_step_breakpoints (lp);
+
+ step = maybe_hw_step (thread);
+ }
threads_debug_printf ("resuming stopped-resumed LWP %s at %s: step=%d",
target_pid_to_str (ptid_of (thread)).c_str (),
if (requested_child->suspended
&& requested_child->status_pending_p)
{
- internal_error (__FILE__, __LINE__,
- "requesting an event out of a"
+ internal_error ("requesting an event out of a"
" suspended child?");
}
struct thread_info *thread = get_lwp_thread (event_child);
ptid_t ptid = ptid_of (thread);
- if (!last_thread_of_process_p (pid_of (thread)))
+ if (!is_leader (thread))
{
if (cs.report_thread_events)
ourstatus->set_thread_exited (0);
unstop_all_lwps (1, event_child);
}
+ /* At this point, we haven't set OURSTATUS. This is where we do it. */
+ gdb_assert (ourstatus->kind () == TARGET_WAITKIND_IGNORE);
+
if (event_child->waitstatus.kind () != TARGET_WAITKIND_IGNORE)
{
/* If the reported event is an exit, fork, vfork or exec, let
}
else
{
- /* The actual stop signal is overwritten below. */
- ourstatus->set_stopped (GDB_SIGNAL_0);
+ /* The LWP stopped due to a plain signal or a syscall signal. Either way,
+ event_child->waitstatus wasn't filled in with the details, so look at
+ the wait status W. */
+ if (WSTOPSIG (w) == SYSCALL_SIGTRAP)
+ {
+ int syscall_number;
+
+ get_syscall_trapinfo (event_child, &syscall_number);
+ if (event_child->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY)
+ ourstatus->set_syscall_entry (syscall_number);
+ else if (event_child->syscall_state == TARGET_WAITKIND_SYSCALL_RETURN)
+ ourstatus->set_syscall_return (syscall_number);
+ else
+ gdb_assert_not_reached ("unexpected syscall state");
+ }
+ else if (current_thread->last_resume_kind == resume_stop
+ && WSTOPSIG (w) == SIGSTOP)
+ {
+ /* A thread that has been requested to stop by GDB with vCont;t,
+ and it stopped cleanly, so report as SIG0. The use of
+ SIGSTOP is an implementation detail. */
+ ourstatus->set_stopped (GDB_SIGNAL_0);
+ }
+ else
+ ourstatus->set_stopped (gdb_signal_from_host (WSTOPSIG (w)));
}
/* Now that we've selected our final event LWP, un-adjust its PC if
}
}
- if (WSTOPSIG (w) == SYSCALL_SIGTRAP)
- {
- int syscall_number;
-
- get_syscall_trapinfo (event_child, &syscall_number);
- if (event_child->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY)
- ourstatus->set_syscall_entry (syscall_number);
- else if (event_child->syscall_state == TARGET_WAITKIND_SYSCALL_RETURN)
- ourstatus->set_syscall_return (syscall_number);
- else
- gdb_assert_not_reached ("unexpected syscall state");
- }
- else if (current_thread->last_resume_kind == resume_stop
- && WSTOPSIG (w) == SIGSTOP)
- {
- /* A thread that has been requested to stop by GDB with vCont;t,
- and it stopped cleanly, so report as SIG0. The use of
- SIGSTOP is an implementation detail. */
- ourstatus->set_stopped (GDB_SIGNAL_0);
- }
- else if (current_thread->last_resume_kind == resume_stop
- && WSTOPSIG (w) != SIGSTOP)
- {
- /* A thread that has been requested to stop by GDB with vCont;t,
- but, it stopped for other reasons. */
- ourstatus->set_stopped (gdb_signal_from_host (WSTOPSIG (w)));
- }
- else if (ourstatus->kind () == TARGET_WAITKIND_STOPPED)
- ourstatus->set_stopped (gdb_signal_from_host (WSTOPSIG (w)));
-
gdb_assert (step_over_bkpt == null_ptid);
- threads_debug_printf ("ret = %s, %d, %d",
+ threads_debug_printf ("ret = %s, %s",
target_pid_to_str (ptid_of (current_thread)).c_str (),
- ourstatus->kind (), ourstatus->sig ());
+ ourstatus->to_string ().c_str ());
if (ourstatus->kind () == TARGET_WAITKIND_EXITED)
return filter_exit_event (event_child, ourstatus);
if (lwp->suspended != 0)
{
- internal_error (__FILE__, __LINE__,
- "LWP %ld is suspended, suspended=%d\n",
+ internal_error ("LWP %ld is suspended, suspended=%d\n",
lwpid_of (thread), lwp->suspended);
}
gdb_assert (lwp->stopped);
if (lwp->suspended != 0)
{
- internal_error (__FILE__, __LINE__,
- "LWP %ld is suspended, suspended=%d\n",
+ internal_error ("LWP %ld is suspended, suspended=%d\n",
lwpid_of (thread), lwp->suspended);
}
gdb_assert (lwp->stopped);
step = 1;
else
{
- internal_error (__FILE__, __LINE__,
- "moving out of jump pad single-stepping"
+ internal_error ("moving out of jump pad single-stepping"
" not implemented on this target");
}
}
(PTRACE_TYPE_ARG4) (uintptr_t) signal);
if (errno)
- perror_with_name ("resuming thread");
+ {
+ int saved_errno = errno;
+
+ threads_debug_printf ("ptrace errno = %d (%s)",
+ saved_errno, strerror (saved_errno));
+
+ errno = saved_errno;
+ perror_with_name ("resuming thread");
+ }
/* Successfully resumed. Clear state that no longer makes sense,
and mark the LWP as running. Must not do this before resuming
}
catch (const gdb_exception_error &ex)
{
- if (!check_ptrace_stopped_lwp_gone (lwp))
+ if (check_ptrace_stopped_lwp_gone (lwp))
+ {
+ /* This could because we tried to resume an LWP after its leader
+ exited. Mark it as resumed, so we can collect an exit event
+ from it. */
+ lwp->stopped = 0;
+ lwp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ }
+ else
throw;
}
}
if (lwp->suspended != 0)
{
- internal_error (__FILE__, __LINE__,
- "LWP %ld suspended=%d\n", lwpid_of (thread),
+ internal_error ("LWP %ld suspended=%d\n", lwpid_of (thread),
lwp->suspended);
}
return the_target->read_memory (memaddr, myaddr, len);
}
-/* Copy LEN bytes from inferior's memory starting at MEMADDR
- to debugger memory starting at MYADDR. */
-int
-linux_process_target::read_memory (CORE_ADDR memaddr,
- unsigned char *myaddr, int len)
+/* Helper for read_memory/write_memory using /proc/PID/mem. Because
+ we can use a single read/write call, this can be much more
+ efficient than banging away at PTRACE_PEEKTEXT. Also, unlike
+ PTRACE_PEEKTEXT/PTRACE_POKETEXT, this works with running threads.
+ One an only one of READBUF and WRITEBUF is non-null. If READBUF is
+ not null, then we're reading, otherwise we're writing. */
+
+static int
+proc_xfer_memory (CORE_ADDR memaddr, unsigned char *readbuf,
+ const gdb_byte *writebuf, int len)
{
- int pid = lwpid_of (current_thread);
- PTRACE_XFER_TYPE *buffer;
- CORE_ADDR addr;
- int count;
- char filename[64];
- int i;
- int ret;
- int fd;
+ gdb_assert ((readbuf == nullptr) != (writebuf == nullptr));
+
+ process_info *proc = current_process ();
+
+ int fd = proc->priv->mem_fd;
+ if (fd == -1)
+ return EIO;
- /* Try using /proc. Don't bother for one word. */
- if (len >= 3 * sizeof (long))
+ while (len > 0)
{
int bytes;
- /* We could keep this file open and cache it - possibly one per
- thread. That requires some juggling, but is even faster. */
- sprintf (filename, "/proc/%d/mem", pid);
- fd = open (filename, O_RDONLY | O_LARGEFILE);
- if (fd == -1)
- goto no_proc;
-
- /* If pread64 is available, use it. It's faster if the kernel
- supports it (only one syscall), and it's 64-bit safe even on
- 32-bit platforms (for instance, SPARC debugging a SPARC64
- application). */
+ /* Use pread64/pwrite64 if available, since they save a syscall
+ and can handle 64-bit offsets even on 32-bit platforms (for
+ instance, SPARC debugging a SPARC64 application). But only
+ use them if the offset isn't so high that when cast to off_t
+ it'd be negative, as seen on SPARC64. pread64/pwrite64
+ outright reject such offsets. lseek does not. */
#ifdef HAVE_PREAD64
- bytes = pread64 (fd, myaddr, len, memaddr);
-#else
- bytes = -1;
- if (lseek (fd, memaddr, SEEK_SET) != -1)
- bytes = read (fd, myaddr, len);
+ if ((off_t) memaddr >= 0)
+ bytes = (readbuf != nullptr
+ ? pread64 (fd, readbuf, len, memaddr)
+ : pwrite64 (fd, writebuf, len, memaddr));
+ else
#endif
-
- close (fd);
- if (bytes == len)
- return 0;
-
- /* Some data was read, we'll try to get the rest with ptrace. */
- if (bytes > 0)
{
- memaddr += bytes;
- myaddr += bytes;
- len -= bytes;
+ bytes = -1;
+ if (lseek (fd, memaddr, SEEK_SET) != -1)
+ bytes = (readbuf != nullptr
+ ? read (fd, readbuf, len)
+ : write (fd, writebuf, len));
}
- }
- no_proc:
- /* Round starting address down to longword boundary. */
- addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
- /* Round ending address up; get number of longwords that makes. */
- count = ((((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
- / sizeof (PTRACE_XFER_TYPE));
- /* Allocate buffer of that many longwords. */
- buffer = XALLOCAVEC (PTRACE_XFER_TYPE, count);
+ if (bytes < 0)
+ return errno;
+ else if (bytes == 0)
+ {
+ /* EOF means the address space is gone, the whole process
+ exited or execed. */
+ return EIO;
+ }
- /* Read all the longwords */
- errno = 0;
- for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
- {
- /* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- buffer[i] = ptrace (PTRACE_PEEKTEXT, pid,
- (PTRACE_TYPE_ARG3) (uintptr_t) addr,
- (PTRACE_TYPE_ARG4) 0);
- if (errno)
- break;
+ memaddr += bytes;
+ if (readbuf != nullptr)
+ readbuf += bytes;
+ else
+ writebuf += bytes;
+ len -= bytes;
}
- ret = errno;
- /* Copy appropriate bytes out of the buffer. */
- if (i > 0)
- {
- i *= sizeof (PTRACE_XFER_TYPE);
- i -= memaddr & (sizeof (PTRACE_XFER_TYPE) - 1);
- memcpy (myaddr,
- (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
- i < len ? i : len);
- }
+ return 0;
+}
- return ret;
+int
+linux_process_target::read_memory (CORE_ADDR memaddr,
+ unsigned char *myaddr, int len)
+{
+ return proc_xfer_memory (memaddr, myaddr, nullptr, len);
}
/* Copy LEN bytes of data from debugger memory at MYADDR to inferior's
linux_process_target::write_memory (CORE_ADDR memaddr,
const unsigned char *myaddr, int len)
{
- int i;
- /* Round starting address down to longword boundary. */
- CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
- /* Round ending address up; get number of longwords that makes. */
- int count
- = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
- / sizeof (PTRACE_XFER_TYPE);
-
- /* Allocate buffer of that many longwords. */
- PTRACE_XFER_TYPE *buffer = XALLOCAVEC (PTRACE_XFER_TYPE, count);
-
- int pid = lwpid_of (current_thread);
-
- if (len == 0)
- {
- /* Zero length write always succeeds. */
- return 0;
- }
-
if (debug_threads)
{
/* Dump up to four bytes. */
char *p = str;
int dump = len < 4 ? len : 4;
- for (i = 0; i < dump; i++)
+ for (int i = 0; i < dump; i++)
{
sprintf (p, "%02x", myaddr[i]);
p += 2;
*p = '\0';
threads_debug_printf ("Writing %s to 0x%08lx in process %d",
- str, (long) memaddr, pid);
+ str, (long) memaddr, current_process ()->pid);
}
- /* Fill start and end extra bytes of buffer with existing memory data. */
-
- errno = 0;
- /* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- buffer[0] = ptrace (PTRACE_PEEKTEXT, pid,
- (PTRACE_TYPE_ARG3) (uintptr_t) addr,
- (PTRACE_TYPE_ARG4) 0);
- if (errno)
- return errno;
-
- if (count > 1)
- {
- errno = 0;
- buffer[count - 1]
- = ptrace (PTRACE_PEEKTEXT, pid,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_TYPE_ARG3) (uintptr_t) (addr + (count - 1)
- * sizeof (PTRACE_XFER_TYPE)),
- (PTRACE_TYPE_ARG4) 0);
- if (errno)
- return errno;
- }
-
- /* Copy data to be written over corresponding part of buffer. */
-
- memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
- myaddr, len);
-
- /* Write the entire buffer. */
-
- for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
- {
- errno = 0;
- ptrace (PTRACE_POKETEXT, pid,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_TYPE_ARG3) (uintptr_t) addr,
- (PTRACE_TYPE_ARG4) buffer[i]);
- if (errno)
- return errno;
- }
-
- return 0;
+ return proc_xfer_memory (memaddr, nullptr, myaddr, len);
}
void
{
/* Send a SIGINT to the process group. This acts just like the user
typed a ^C on the controlling terminal. */
- ::kill (-signal_pid, SIGINT);
+ int res = ::kill (-signal_pid, SIGINT);
+ if (res == -1)
+ warning (_("Sending SIGINT to process group of pid %ld failed: %s"),
+ signal_pid, safe_strerror (errno));
}
bool
to debugger memory starting at MYADDR. */
int
-linux_process_target::read_auxv (CORE_ADDR offset, unsigned char *myaddr,
- unsigned int len)
+linux_process_target::read_auxv (int pid, CORE_ADDR offset,
+ unsigned char *myaddr, unsigned int len)
{
char filename[PATH_MAX];
int fd, n;
- int pid = lwpid_of (current_thread);
xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
unstop_all_lwps (unfreeze, NULL);
}
-int
-linux_process_target::prepare_to_access_memory ()
-{
- /* Neither ptrace nor /proc/PID/mem allow accessing memory through a
- running LWP. */
- if (non_stop)
- target_pause_all (true);
- return 0;
-}
-
-void
-linux_process_target::done_accessing_memory ()
-{
- /* Neither ptrace nor /proc/PID/mem allow accessing memory through a
- running LWP. */
- if (non_stop)
- target_unpause_all (true);
-}
-
/* Extract &phdr and num_phdr in the inferior. Return 0 on success. */
static int
/* Offset and size of r_debug.r_map. */
int r_map_offset;
+ /* Offset of r_debug_extended.r_next. */
+ int r_next_offset;
+
/* Offset to l_addr field in struct link_map. */
int l_addr_offset;
int l_prev_offset;
};
+static const link_map_offsets lmo_32bit_offsets =
+ {
+ 0, /* r_version offset. */
+ 4, /* r_debug.r_map offset. */
+ 20, /* r_debug_extended.r_next. */
+ 0, /* l_addr offset in link_map. */
+ 4, /* l_name offset in link_map. */
+ 8, /* l_ld offset in link_map. */
+ 12, /* l_next offset in link_map. */
+ 16 /* l_prev offset in link_map. */
+ };
+
+static const link_map_offsets lmo_64bit_offsets =
+ {
+ 0, /* r_version offset. */
+ 8, /* r_debug.r_map offset. */
+ 40, /* r_debug_extended.r_next. */
+ 0, /* l_addr offset in link_map. */
+ 8, /* l_name offset in link_map. */
+ 16, /* l_ld offset in link_map. */
+ 24, /* l_next offset in link_map. */
+ 32 /* l_prev offset in link_map. */
+ };
+
+/* Get the loaded shared libraries from one namespace. */
+
+static void
+read_link_map (std::string &document, CORE_ADDR lmid, CORE_ADDR lm_addr,
+ CORE_ADDR lm_prev, int ptr_size, const link_map_offsets *lmo)
+{
+ CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
+
+ while (lm_addr
+ && read_one_ptr (lm_addr + lmo->l_name_offset,
+ &l_name, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_addr_offset,
+ &l_addr, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_ld_offset,
+ &l_ld, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_prev_offset,
+ &l_prev, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_next_offset,
+ &l_next, ptr_size) == 0)
+ {
+ unsigned char libname[PATH_MAX];
+
+ if (lm_prev != l_prev)
+ {
+ warning ("Corrupted shared library list: 0x%s != 0x%s",
+ paddress (lm_prev), paddress (l_prev));
+ break;
+ }
+
+ /* Not checking for error because reading may stop before we've got
+ PATH_MAX worth of characters. */
+ libname[0] = '\0';
+ linux_read_memory (l_name, libname, sizeof (libname) - 1);
+ libname[sizeof (libname) - 1] = '\0';
+ if (libname[0] != '\0')
+ {
+ string_appendf (document, "<library name=\"");
+ xml_escape_text_append (document, (char *) libname);
+ string_appendf (document, "\" lm=\"0x%s\" l_addr=\"0x%s\" "
+ "l_ld=\"0x%s\" lmid=\"0x%s\"/>",
+ paddress (lm_addr), paddress (l_addr),
+ paddress (l_ld), paddress (lmid));
+ }
+
+ lm_prev = lm_addr;
+ lm_addr = l_next;
+ }
+}
+
/* Construct qXfer:libraries-svr4:read reply. */
int
struct process_info_private *const priv = current_process ()->priv;
char filename[PATH_MAX];
int pid, is_elf64;
-
- static const struct link_map_offsets lmo_32bit_offsets =
- {
- 0, /* r_version offset. */
- 4, /* r_debug.r_map offset. */
- 0, /* l_addr offset in link_map. */
- 4, /* l_name offset in link_map. */
- 8, /* l_ld offset in link_map. */
- 12, /* l_next offset in link_map. */
- 16 /* l_prev offset in link_map. */
- };
-
- static const struct link_map_offsets lmo_64bit_offsets =
- {
- 0, /* r_version offset. */
- 8, /* r_debug.r_map offset. */
- 0, /* l_addr offset in link_map. */
- 8, /* l_name offset in link_map. */
- 16, /* l_ld offset in link_map. */
- 24, /* l_next offset in link_map. */
- 32 /* l_prev offset in link_map. */
- };
- const struct link_map_offsets *lmo;
unsigned int machine;
- int ptr_size;
- CORE_ADDR lm_addr = 0, lm_prev = 0;
- CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
- int header_done = 0;
+ CORE_ADDR lmid = 0, lm_addr = 0, lm_prev = 0;
if (writebuf != NULL)
return -2;
pid = lwpid_of (current_thread);
xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
is_elf64 = elf_64_file_p (filename, &machine);
- lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
- ptr_size = is_elf64 ? 8 : 4;
+ const link_map_offsets *lmo;
+ int ptr_size;
+ if (is_elf64)
+ {
+ lmo = &lmo_64bit_offsets;
+ ptr_size = 8;
+ }
+ else
+ {
+ lmo = &lmo_32bit_offsets;
+ ptr_size = 4;
+ }
while (annex[0] != '\0')
{
break;
name_len = sep - annex;
- if (name_len == 5 && startswith (annex, "start"))
+ if (name_len == 4 && startswith (annex, "lmid"))
+ addrp = &lmid;
+ else if (name_len == 5 && startswith (annex, "start"))
addrp = &lm_addr;
else if (name_len == 4 && startswith (annex, "prev"))
addrp = &lm_prev;
annex = decode_address_to_semicolon (addrp, sep + 1);
}
- if (lm_addr == 0)
+ std::string document = "<library-list-svr4 version=\"1.0\"";
+
+ /* When the starting LM_ADDR is passed in the annex, only traverse that
+ namespace, which is assumed to be identified by LMID.
+
+ Otherwise, start with R_DEBUG and traverse all namespaces we find. */
+ if (lm_addr != 0)
+ {
+ document += ">";
+ read_link_map (document, lmid, lm_addr, lm_prev, ptr_size, lmo);
+ }
+ else
{
- int r_version = 0;
+ if (lm_prev != 0)
+ warning ("ignoring prev=0x%s without start", paddress (lm_prev));
+
+ /* We could interpret LMID as 'provide only the libraries for this
+ namespace' but GDB is currently only providing lmid, start, and
+ prev, or nothing. */
+ if (lmid != 0)
+ warning ("ignoring lmid=0x%s without start", paddress (lmid));
- if (priv->r_debug == 0)
- priv->r_debug = get_r_debug (pid, is_elf64);
+ CORE_ADDR r_debug = priv->r_debug;
+ if (r_debug == 0)
+ r_debug = priv->r_debug = get_r_debug (pid, is_elf64);
/* We failed to find DT_DEBUG. Such situation will not change
for this inferior - do not retry it. Report it to GDB as
E01, see for the reasons at the GDB solib-svr4.c side. */
- if (priv->r_debug == (CORE_ADDR) -1)
+ if (r_debug == (CORE_ADDR) -1)
return -1;
- if (priv->r_debug != 0)
+ /* Terminate the header if we end up with an empty list. */
+ if (r_debug == 0)
+ document += ">";
+
+ while (r_debug != 0)
{
- if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
+ int r_version = 0;
+ if (linux_read_memory (r_debug + lmo->r_version_offset,
(unsigned char *) &r_version,
- sizeof (r_version)) != 0
- || r_version < 1)
+ sizeof (r_version)) != 0)
{
- warning ("unexpected r_debug version %d", r_version);
+ warning ("unable to read r_version from 0x%s",
+ paddress (r_debug + lmo->r_version_offset));
+ break;
}
- else if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
- &lm_addr, ptr_size) != 0)
+
+ if (r_version < 1)
{
- warning ("unable to read r_map from 0x%lx",
- (long) priv->r_debug + lmo->r_map_offset);
+ warning ("unexpected r_debug version %d", r_version);
+ break;
}
- }
- }
-
- std::string document = "<library-list-svr4 version=\"1.0\"";
- while (lm_addr
- && read_one_ptr (lm_addr + lmo->l_name_offset,
- &l_name, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_addr_offset,
- &l_addr, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_ld_offset,
- &l_ld, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_prev_offset,
- &l_prev, ptr_size) == 0
- && read_one_ptr (lm_addr + lmo->l_next_offset,
- &l_next, ptr_size) == 0)
- {
- unsigned char libname[PATH_MAX];
+ if (read_one_ptr (r_debug + lmo->r_map_offset, &lm_addr,
+ ptr_size) != 0)
+ {
+ warning ("unable to read r_map from 0x%s",
+ paddress (r_debug + lmo->r_map_offset));
+ break;
+ }
- if (lm_prev != l_prev)
- {
- warning ("Corrupted shared library list: 0x%lx != 0x%lx",
- (long) lm_prev, (long) l_prev);
- break;
- }
+ /* We read the entire namespace. */
+ lm_prev = 0;
- /* Ignore the first entry even if it has valid name as the first entry
- corresponds to the main executable. The first entry should not be
- skipped if the dynamic loader was loaded late by a static executable
- (see solib-svr4.c parameter ignore_first). But in such case the main
- executable does not have PT_DYNAMIC present and this function already
- exited above due to failed get_r_debug. */
- if (lm_prev == 0)
- string_appendf (document, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
- else
- {
- /* Not checking for error because reading may stop before
- we've got PATH_MAX worth of characters. */
- libname[0] = '\0';
- linux_read_memory (l_name, libname, sizeof (libname) - 1);
- libname[sizeof (libname) - 1] = '\0';
- if (libname[0] != '\0')
+ /* The first entry corresponds to the main executable unless the
+ dynamic loader was loaded late by a static executable. But
+ in such case the main executable does not have PT_DYNAMIC
+ present and we would not have gotten here. */
+ if (r_debug == priv->r_debug)
{
- if (!header_done)
+ if (lm_addr != 0)
+ string_appendf (document, " main-lm=\"0x%s\">",
+ paddress (lm_addr));
+ else
+ document += ">";
+
+ lm_prev = lm_addr;
+ if (read_one_ptr (lm_addr + lmo->l_next_offset,
+ &lm_addr, ptr_size) != 0)
{
- /* Terminate `<library-list-svr4'. */
- document += '>';
- header_done = 1;
+ warning ("unable to read l_next from 0x%s",
+ paddress (lm_addr + lmo->l_next_offset));
+ break;
}
+ }
+
+ read_link_map (document, r_debug, lm_addr, lm_prev, ptr_size, lmo);
- string_appendf (document, "<library name=\"");
- xml_escape_text_append (&document, (char *) libname);
- string_appendf (document, "\" lm=\"0x%lx\" "
- "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
- (unsigned long) lm_addr, (unsigned long) l_addr,
- (unsigned long) l_ld);
+ if (r_version < 2)
+ break;
+
+ if (read_one_ptr (r_debug + lmo->r_next_offset, &r_debug,
+ ptr_size) != 0)
+ {
+ warning ("unable to read r_next from 0x%s",
+ paddress (r_debug + lmo->r_next_offset));
+ break;
}
}
-
- lm_prev = lm_addr;
- lm_addr = l_next;
}
- if (!header_done)
- {
- /* Empty list; terminate `<library-list-svr4'. */
- document += "/>";
- }
- else
- document += "</library-list-svr4>";
+ document += "</library-list-svr4>";
int document_len = document.length ();
if (offset < document_len)
#ifdef HAVE_LINUX_BTRACE
+bool
+linux_process_target::supports_btrace ()
+{
+ return true;
+}
+
btrace_target_info *
linux_process_target::enable_btrace (thread_info *tp,
const btrace_config *conf)
/* Encode an Intel Processor Trace configuration. */
static void
-linux_low_encode_pt_config (struct buffer *buffer,
+linux_low_encode_pt_config (std::string *buffer,
const struct btrace_data_pt_config *config)
{
- buffer_grow_str (buffer, "<pt-config>\n");
+ *buffer += "<pt-config>\n";
switch (config->cpu.vendor)
{
case CV_INTEL:
- buffer_xml_printf (buffer, "<cpu vendor=\"GenuineIntel\" family=\"%u\" "
- "model=\"%u\" stepping=\"%u\"/>\n",
- config->cpu.family, config->cpu.model,
- config->cpu.stepping);
+ string_xml_appendf (*buffer, "<cpu vendor=\"GenuineIntel\" family=\"%u\" "
+ "model=\"%u\" stepping=\"%u\"/>\n",
+ config->cpu.family, config->cpu.model,
+ config->cpu.stepping);
break;
default:
break;
}
- buffer_grow_str (buffer, "</pt-config>\n");
+ *buffer += "</pt-config>\n";
}
/* Encode a raw buffer. */
static void
-linux_low_encode_raw (struct buffer *buffer, const gdb_byte *data,
+linux_low_encode_raw (std::string *buffer, const gdb_byte *data,
unsigned int size)
{
if (size == 0)
return;
/* We use hex encoding - see gdbsupport/rsp-low.h. */
- buffer_grow_str (buffer, "<raw>\n");
+ *buffer += "<raw>\n";
while (size-- > 0)
{
elem[0] = tohex ((*data >> 4) & 0xf);
elem[1] = tohex (*data++ & 0xf);
- buffer_grow (buffer, elem, 2);
+ buffer->append (elem, 2);
}
- buffer_grow_str (buffer, "</raw>\n");
+ *buffer += "</raw>\n";
}
/* See to_read_btrace target method. */
int
linux_process_target::read_btrace (btrace_target_info *tinfo,
- buffer *buffer,
+ std::string *buffer,
enum btrace_read_type type)
{
struct btrace_data btrace;
if (err != BTRACE_ERR_NONE)
{
if (err == BTRACE_ERR_OVERFLOW)
- buffer_grow_str0 (buffer, "E.Overflow.");
+ *buffer += "E.Overflow.";
else
- buffer_grow_str0 (buffer, "E.Generic Error.");
+ *buffer += "E.Generic Error.";
return -1;
}
switch (btrace.format)
{
case BTRACE_FORMAT_NONE:
- buffer_grow_str0 (buffer, "E.No Trace.");
+ *buffer += "E.No Trace.";
return -1;
case BTRACE_FORMAT_BTS:
- buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
- buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
+ *buffer += "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n";
+ *buffer += "<btrace version=\"1.0\">\n";
for (const btrace_block &block : *btrace.variant.bts.blocks)
- buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
- paddress (block.begin), paddress (block.end));
+ string_xml_appendf (*buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
+ paddress (block.begin), paddress (block.end));
- buffer_grow_str0 (buffer, "</btrace>\n");
+ *buffer += "</btrace>\n";
break;
case BTRACE_FORMAT_PT:
- buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
- buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
- buffer_grow_str (buffer, "<pt>\n");
+ *buffer += "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n";
+ *buffer += "<btrace version=\"1.0\">\n";
+ *buffer += "<pt>\n";
linux_low_encode_pt_config (buffer, &btrace.variant.pt.config);
linux_low_encode_raw (buffer, btrace.variant.pt.data,
btrace.variant.pt.size);
- buffer_grow_str (buffer, "</pt>\n");
- buffer_grow_str0 (buffer, "</btrace>\n");
+ *buffer += "</pt>\n";
+ *buffer += "</btrace>\n";
break;
default:
- buffer_grow_str0 (buffer, "E.Unsupported Trace Format.");
+ *buffer += "E.Unsupported Trace Format.";
return -1;
}
int
linux_process_target::read_btrace_conf (const btrace_target_info *tinfo,
- buffer *buffer)
+ std::string *buffer)
{
const struct btrace_config *conf;
- buffer_grow_str (buffer, "<!DOCTYPE btrace-conf SYSTEM \"btrace-conf.dtd\">\n");
- buffer_grow_str (buffer, "<btrace-conf version=\"1.0\">\n");
+ *buffer += "<!DOCTYPE btrace-conf SYSTEM \"btrace-conf.dtd\">\n";
+ *buffer += "<btrace-conf version=\"1.0\">\n";
conf = linux_btrace_conf (tinfo);
if (conf != NULL)
break;
case BTRACE_FORMAT_BTS:
- buffer_xml_printf (buffer, "<bts");
- buffer_xml_printf (buffer, " size=\"0x%x\"", conf->bts.size);
- buffer_xml_printf (buffer, " />\n");
+ string_xml_appendf (*buffer, "<bts");
+ string_xml_appendf (*buffer, " size=\"0x%x\"", conf->bts.size);
+ string_xml_appendf (*buffer, " />\n");
break;
case BTRACE_FORMAT_PT:
- buffer_xml_printf (buffer, "<pt");
- buffer_xml_printf (buffer, " size=\"0x%x\"", conf->pt.size);
- buffer_xml_printf (buffer, "/>\n");
+ string_xml_appendf (*buffer, "<pt");
+ string_xml_appendf (*buffer, " size=\"0x%x\"", conf->pt.size);
+ string_xml_appendf (*buffer, "/>\n");
break;
}
}
- buffer_grow_str0 (buffer, "</btrace-conf>\n");
+ *buffer += "</btrace-conf>\n";
return 0;
}
#endif /* HAVE_LINUX_BTRACE */
/* See linux-low.h. */
int
-linux_get_auxv (int wordsize, CORE_ADDR match, CORE_ADDR *valp)
+linux_get_auxv (int pid, int wordsize, CORE_ADDR match, CORE_ADDR *valp)
{
gdb_byte *data = (gdb_byte *) alloca (2 * wordsize);
int offset = 0;
gdb_assert (wordsize == 4 || wordsize == 8);
- while (the_target->read_auxv (offset, data, 2 * wordsize) == 2 * wordsize)
+ while (the_target->read_auxv (pid, offset, data, 2 * wordsize)
+ == 2 * wordsize)
{
if (wordsize == 4)
{
/* See linux-low.h. */
CORE_ADDR
-linux_get_hwcap (int wordsize)
+linux_get_hwcap (int pid, int wordsize)
{
CORE_ADDR hwcap = 0;
- linux_get_auxv (wordsize, AT_HWCAP, &hwcap);
+ linux_get_auxv (pid, wordsize, AT_HWCAP, &hwcap);
return hwcap;
}
/* See linux-low.h. */
CORE_ADDR
-linux_get_hwcap2 (int wordsize)
+linux_get_hwcap2 (int pid, int wordsize)
{
CORE_ADDR hwcap2 = 0;
- linux_get_auxv (wordsize, AT_HWCAP2, &hwcap2);
+ linux_get_auxv (pid, wordsize, AT_HWCAP2, &hwcap2);
return hwcap2;
}