xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/nat/aarch64-linux.c (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
1 /* Copyright (C) 2009-2023 Free Software Foundation, Inc.
2    Contributed by ARM Ltd.
3 
4    This file is part of GDB.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18 
19 #include "gdbsupport/common-defs.h"
20 #include "gdbsupport/break-common.h"
21 #include "nat/linux-nat.h"
22 #include "nat/aarch64-linux-hw-point.h"
23 #include "nat/aarch64-linux.h"
24 
25 #include "elf/common.h"
26 #include "nat/gdb_ptrace.h"
27 #include <asm/ptrace.h>
28 #include <sys/uio.h>
29 
30 /* Called when resuming a thread LWP.
31    The hardware debug registers are updated when there is any change.  */
32 
33 void
34 aarch64_linux_prepare_to_resume (struct lwp_info *lwp)
35 {
36   struct arch_lwp_info *info = lwp_arch_private_info (lwp);
37 
38   /* NULL means this is the main thread still going through the shell,
39      or, no watchpoint has been set yet.  In that case, there's
40      nothing to do.  */
41   if (info == NULL)
42     return;
43 
44   if (DR_HAS_CHANGED (info->dr_changed_bp)
45       || DR_HAS_CHANGED (info->dr_changed_wp))
46     {
47       ptid_t ptid = ptid_of_lwp (lwp);
48       int tid = ptid.lwp ();
49       struct aarch64_debug_reg_state *state
50 	= aarch64_get_debug_reg_state (ptid.pid ());
51 
52       if (show_debug_regs)
53 	debug_printf ("prepare_to_resume thread %d\n", tid);
54 
55       /* Watchpoints.  */
56       if (DR_HAS_CHANGED (info->dr_changed_wp))
57 	{
58 	  aarch64_linux_set_debug_regs (state, tid, 1);
59 	  DR_CLEAR_CHANGED (info->dr_changed_wp);
60 	}
61 
62       /* Breakpoints.  */
63       if (DR_HAS_CHANGED (info->dr_changed_bp))
64 	{
65 	  aarch64_linux_set_debug_regs (state, tid, 0);
66 	  DR_CLEAR_CHANGED (info->dr_changed_bp);
67 	}
68     }
69 }
70 
71 /* Function to call when a new thread is detected.  */
72 
73 void
74 aarch64_linux_new_thread (struct lwp_info *lwp)
75 {
76   ptid_t ptid = ptid_of_lwp (lwp);
77   struct aarch64_debug_reg_state *state
78     = aarch64_get_debug_reg_state (ptid.pid ());
79   struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
80 
81   /* If there are hardware breakpoints/watchpoints in the process then mark that
82      all the hardware breakpoint/watchpoint register pairs for this thread need
83      to be initialized (with data from aarch_process_info.debug_reg_state).  */
84   if (aarch64_any_set_debug_regs_state (state, false))
85     DR_MARK_ALL_CHANGED (info->dr_changed_bp, aarch64_num_bp_regs);
86   if (aarch64_any_set_debug_regs_state (state, true))
87     DR_MARK_ALL_CHANGED (info->dr_changed_wp, aarch64_num_wp_regs);
88 
89   lwp_set_arch_private_info (lwp, info);
90 }
91 
92 /* See nat/aarch64-linux.h.  */
93 
94 void
95 aarch64_linux_delete_thread (struct arch_lwp_info *arch_lwp)
96 {
97   xfree (arch_lwp);
98 }
99 
100 /* Convert native siginfo FROM to the siginfo in the layout of the
101    inferior's architecture TO.  */
102 
103 void
104 aarch64_compat_siginfo_from_siginfo (compat_siginfo_t *to, siginfo_t *from)
105 {
106   memset (to, 0, sizeof (*to));
107 
108   to->si_signo = from->si_signo;
109   to->si_errno = from->si_errno;
110   to->si_code = from->si_code;
111 
112   if (to->si_code == SI_TIMER)
113     {
114       to->cpt_si_timerid = from->si_timerid;
115       to->cpt_si_overrun = from->si_overrun;
116       to->cpt_si_ptr = (intptr_t) from->si_ptr;
117     }
118   else if (to->si_code == SI_USER)
119     {
120       to->cpt_si_pid = from->si_pid;
121       to->cpt_si_uid = from->si_uid;
122     }
123   else if (to->si_code < 0)
124     {
125       to->cpt_si_pid = from->si_pid;
126       to->cpt_si_uid = from->si_uid;
127       to->cpt_si_ptr = (intptr_t) from->si_ptr;
128     }
129   else
130     {
131       switch (to->si_signo)
132 	{
133 	case SIGCHLD:
134 	  to->cpt_si_pid = from->si_pid;
135 	  to->cpt_si_uid = from->si_uid;
136 	  to->cpt_si_status = from->si_status;
137 	  to->cpt_si_utime = from->si_utime;
138 	  to->cpt_si_stime = from->si_stime;
139 	  break;
140 	case SIGILL:
141 	case SIGFPE:
142 	case SIGSEGV:
143 	case SIGBUS:
144 	  to->cpt_si_addr = (intptr_t) from->si_addr;
145 	  break;
146 	case SIGPOLL:
147 	  to->cpt_si_band = from->si_band;
148 	  to->cpt_si_fd = from->si_fd;
149 	  break;
150 	default:
151 	  to->cpt_si_pid = from->si_pid;
152 	  to->cpt_si_uid = from->si_uid;
153 	  to->cpt_si_ptr = (intptr_t) from->si_ptr;
154 	  break;
155 	}
156     }
157 }
158 
159 /* Convert inferior's architecture siginfo FROM to native siginfo TO.  */
160 
161 void
162 aarch64_siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from)
163 {
164   memset (to, 0, sizeof (*to));
165 
166   to->si_signo = from->si_signo;
167   to->si_errno = from->si_errno;
168   to->si_code = from->si_code;
169 
170   if (to->si_code == SI_TIMER)
171     {
172       to->si_timerid = from->cpt_si_timerid;
173       to->si_overrun = from->cpt_si_overrun;
174       to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr;
175     }
176   else if (to->si_code == SI_USER)
177     {
178       to->si_pid = from->cpt_si_pid;
179       to->si_uid = from->cpt_si_uid;
180     }
181   if (to->si_code < 0)
182     {
183       to->si_pid = from->cpt_si_pid;
184       to->si_uid = from->cpt_si_uid;
185       to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr;
186     }
187   else
188     {
189       switch (to->si_signo)
190 	{
191 	case SIGCHLD:
192 	  to->si_pid = from->cpt_si_pid;
193 	  to->si_uid = from->cpt_si_uid;
194 	  to->si_status = from->cpt_si_status;
195 	  to->si_utime = from->cpt_si_utime;
196 	  to->si_stime = from->cpt_si_stime;
197 	  break;
198 	case SIGILL:
199 	case SIGFPE:
200 	case SIGSEGV:
201 	case SIGBUS:
202 	  to->si_addr = (void *) (intptr_t) from->cpt_si_addr;
203 	  break;
204 	case SIGPOLL:
205 	  to->si_band = from->cpt_si_band;
206 	  to->si_fd = from->cpt_si_fd;
207 	  break;
208 	default:
209 	  to->si_pid = from->cpt_si_pid;
210 	  to->si_uid = from->cpt_si_uid;
211 	  to->si_ptr = (void* ) (intptr_t) from->cpt_si_ptr;
212 	  break;
213 	}
214     }
215 }
216 
217 /* Called by libthread_db.  Returns a pointer to the thread local
218    storage (or its descriptor).  */
219 
220 ps_err_e
221 aarch64_ps_get_thread_area (struct ps_prochandle *ph,
222 			    lwpid_t lwpid, int idx, void **base,
223 			    int is_64bit_p)
224 {
225   struct iovec iovec;
226   uint64_t reg64;
227   uint32_t reg32;
228 
229   if (is_64bit_p)
230     {
231       iovec.iov_base = &reg64;
232       iovec.iov_len = sizeof (reg64);
233     }
234   else
235     {
236       iovec.iov_base = &reg32;
237       iovec.iov_len = sizeof (reg32);
238     }
239 
240   if (ptrace (PTRACE_GETREGSET, lwpid, NT_ARM_TLS, &iovec) != 0)
241     return PS_ERR;
242 
243   /* IDX is the bias from the thread pointer to the beginning of the
244      thread descriptor.  It has to be subtracted due to implementation
245      quirks in libthread_db.  */
246   if (is_64bit_p)
247     *base = (void *) (reg64 - idx);
248   else
249     *base = (void *) (uintptr_t) (reg32 - idx);
250 
251   return PS_OK;
252 }
253 
254 /* See nat/aarch64-linux.h.  */
255 
256 int
257 aarch64_tls_register_count (int tid)
258 {
259   uint64_t tls_regs[2];
260   struct iovec iovec;
261   iovec.iov_base = tls_regs;
262   iovec.iov_len = sizeof (tls_regs);
263 
264   /* Attempt to read both TPIDR and TPIDR2.  If the request fails, it means
265      the Linux Kernel does not support TPIDR2.
266 
267      Otherwise the Linux Kernel supports both TPIDR and TPIDR2.  */
268   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_TLS, &iovec) != 0)
269     return 1;
270 
271   /* TPIDR2 is available as well.  */
272   return 2;
273 }
274