1/* Internal interfaces for the NetBSD code.
2
3   Copyright (C) 2006-2023 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include "gdbsupport/common-defs.h"
21#include "nat/netbsd-nat.h"
22#include "gdbsupport/common-debug.h"
23
24#include <sys/types.h>
25#include <sys/ptrace.h>
26#include <sys/sysctl.h>
27
28#include <cstring>
29
30#include "gdbsupport/function-view.h"
31
32namespace netbsd_nat
33{
34
35/* See netbsd-nat.h.  */
36
37const char *
38pid_to_exec_file (pid_t pid)
39{
40  static char buf[PATH_MAX];
41  int mib[4] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_PATHNAME};
42  size_t buflen = sizeof (buf);
43  if (::sysctl (mib, ARRAY_SIZE (mib), buf, &buflen, NULL, 0) != 0)
44    return NULL;
45  return buf;
46}
47
48/* Generic thread (LWP) lister within a specified PID.  The CALLBACK
49   parameters is a C++ function that is called for each detected thread.
50   When the CALLBACK function returns true, the iteration is interrupted.
51
52   This function assumes internally that the queried process is stopped
53   and the number of threads does not change between two sysctl () calls.  */
54
55static bool
56netbsd_thread_lister (const pid_t pid,
57		      gdb::function_view<bool (const struct kinfo_lwp *)>
58		      callback)
59{
60  int mib[5] = {CTL_KERN, KERN_LWP, pid, sizeof (struct kinfo_lwp), 0};
61  size_t size;
62
63  if (sysctl (mib, ARRAY_SIZE (mib), NULL, &size, NULL, 0) == -1 || size == 0)
64    perror_with_name (("sysctl"));
65
66  mib[4] = size / sizeof (size_t);
67
68  gdb::unique_xmalloc_ptr<struct kinfo_lwp[]> kl
69    ((struct kinfo_lwp *) xcalloc (size, 1));
70
71  if (sysctl (mib, ARRAY_SIZE (mib), kl.get (), &size, NULL, 0) == -1
72      || size == 0)
73    perror_with_name (("sysctl"));
74
75  for (size_t i = 0; i < size / sizeof (struct kinfo_lwp); i++)
76    {
77      struct kinfo_lwp *l = &kl[i];
78
79      /* Return true if the specified thread is alive.  */
80      auto lwp_alive
81	= [] (struct kinfo_lwp *lwp)
82	  {
83	    switch (lwp->l_stat)
84	      {
85	      case LSSLEEP:
86	      case LSRUN:
87	      case LSONPROC:
88	      case LSSTOP:
89	      case LSSUSPENDED:
90		return true;
91	      default:
92		return false;
93	      }
94	  };
95
96      /* Ignore embryonic or demised threads.  */
97      if (!lwp_alive (l))
98	continue;
99
100      if (callback (l))
101	return true;
102    }
103
104  return false;
105}
106
107/* See netbsd-nat.h.  */
108
109bool
110thread_alive (ptid_t ptid)
111{
112  pid_t pid = ptid.pid ();
113  lwpid_t lwp = ptid.lwp ();
114
115  auto fn
116    = [=] (const struct kinfo_lwp *kl)
117      {
118	return kl->l_lid == lwp;
119      };
120
121  return netbsd_thread_lister (pid, fn);
122}
123
124/* See netbsd-nat.h.  */
125
126const char *
127thread_name (ptid_t ptid)
128{
129  pid_t pid = ptid.pid ();
130  lwpid_t lwp = ptid.lwp ();
131
132  static char buf[KI_LNAMELEN] = {};
133
134  auto fn
135    = [=] (const struct kinfo_lwp *kl)
136      {
137	if (kl->l_lid == lwp)
138	  {
139	    xsnprintf (buf, sizeof buf, "%s", kl->l_name);
140	    return true;
141	  }
142	return false;
143      };
144
145  if (netbsd_thread_lister (pid, fn))
146    return buf;
147  else
148    return NULL;
149}
150
151/* See netbsd-nat.h.  */
152
153void
154for_each_thread (pid_t pid, gdb::function_view<void (ptid_t)> callback)
155{
156  auto fn
157    = [=, &callback] (const struct kinfo_lwp *kl)
158      {
159	ptid_t ptid = ptid_t (pid, kl->l_lid, 0);
160	callback (ptid);
161	return false;
162      };
163
164  netbsd_thread_lister (pid, fn);
165}
166
167/* See netbsd-nat.h.  */
168
169void
170enable_proc_events (pid_t pid)
171{
172  int events;
173
174  if (ptrace (PT_GET_EVENT_MASK, pid, &events, sizeof (events)) == -1)
175    perror_with_name (("ptrace"));
176
177  events |= PTRACE_LWP_CREATE;
178  events |= PTRACE_LWP_EXIT;
179
180  if (ptrace (PT_SET_EVENT_MASK, pid, &events, sizeof (events)) == -1)
181    perror_with_name (("ptrace"));
182}
183
184/* See netbsd-nat.h.  */
185
186int
187qxfer_siginfo (pid_t pid, const char *annex, unsigned char *readbuf,
188	       unsigned const char *writebuf, CORE_ADDR offset, int len)
189{
190  ptrace_siginfo_t psi;
191
192  if (offset > sizeof (siginfo_t))
193    return -1;
194
195  if (ptrace (PT_GET_SIGINFO, pid, &psi, sizeof (psi)) == -1)
196    return -1;
197
198  if (offset + len > sizeof (siginfo_t))
199    len = sizeof (siginfo_t) - offset;
200
201  if (readbuf != NULL)
202    memcpy (readbuf, ((gdb_byte *) &psi.psi_siginfo) + offset, len);
203  else
204    {
205      memcpy (((gdb_byte *) &psi.psi_siginfo) + offset, writebuf, len);
206
207      if (ptrace (PT_SET_SIGINFO, pid, &psi, sizeof (psi)) == -1)
208	return -1;
209    }
210  return len;
211}
212
213/* See netbsd-nat.h.  */
214
215int
216write_memory (pid_t pid, unsigned const char *writebuf, CORE_ADDR offset,
217	      size_t len, size_t *xfered_len)
218{
219  struct ptrace_io_desc io;
220  io.piod_op = PIOD_WRITE_D;
221  io.piod_len = len;
222
223  size_t bytes_written = 0;
224
225  /* Zero length write always succeeds.  */
226  if (len > 0)
227    {
228      do
229	{
230	  io.piod_addr = (void *)(writebuf + bytes_written);
231	  io.piod_offs = (void *)(offset + bytes_written);
232
233	  errno = 0;
234	  int rv = ptrace (PT_IO, pid, &io, 0);
235	  if (rv == -1)
236	    {
237	      gdb_assert (errno != 0);
238	      return errno;
239	    }
240	  if (io.piod_len == 0)
241	    break;
242
243	  bytes_written += io.piod_len;
244	  io.piod_len = len - bytes_written;
245	}
246      while (bytes_written < len);
247    }
248
249  if (xfered_len != nullptr)
250    *xfered_len = bytes_written;
251
252  return 0;
253}
254
255/* See netbsd-nat.h.  */
256
257int
258read_memory (pid_t pid, unsigned char *readbuf, CORE_ADDR offset,
259	      size_t len, size_t *xfered_len)
260{
261  struct ptrace_io_desc io;
262  io.piod_op = PIOD_READ_D;
263  io.piod_len = len;
264
265  size_t bytes_read = 0;
266
267  /* Zero length read always succeeds.  */
268  if (len > 0)
269    {
270      do
271	{
272	  io.piod_offs = (void *)(offset + bytes_read);
273	  io.piod_addr = readbuf + bytes_read;
274
275	  int rv = ptrace (PT_IO, pid, &io, 0);
276	  if (rv == -1)
277	    return errno;
278	  if (io.piod_len == 0)
279	    break;
280
281	  bytes_read += io.piod_len;
282	  io.piod_len = len - bytes_read;
283	}
284      while (bytes_read < len);
285    }
286
287  if (xfered_len != nullptr)
288    *xfered_len = bytes_read;
289
290  return 0;
291}
292
293}
294