]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/arm-linux-nat.c
Copyright updates for 2007.
[thirdparty/binutils-gdb.git] / gdb / arm-linux-nat.c
CommitLineData
ed9a39eb 1/* GNU/Linux on ARM native support.
6aba47ca 2 Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
10d6c8cd 3 Free Software Foundation, Inc.
ed9a39eb
JM
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 2 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, write to the Free Software
197e01b6
EZ
19 Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA. */
ed9a39eb
JM
21
22#include "defs.h"
23#include "inferior.h"
24#include "gdbcore.h"
25#include "gdb_string.h"
4e052eda 26#include "regcache.h"
10d6c8cd
DJ
27#include "target.h"
28#include "linux-nat.h"
ed9a39eb 29
aeb98c60 30#include "arm-tdep.h"
cb587d83 31#include "arm-linux-tdep.h"
aeb98c60 32
ed9a39eb
JM
33#include <sys/user.h>
34#include <sys/ptrace.h>
35#include <sys/utsname.h>
41c49b06 36#include <sys/procfs.h>
ed9a39eb 37
c60c0f5f
MS
38/* Prototypes for supply_gregset etc. */
39#include "gregset.h"
40
9308fc88
DJ
41/* Defines ps_err_e, struct ps_prochandle. */
42#include "gdb_proc_service.h"
43
44#ifndef PTRACE_GET_THREAD_AREA
45#define PTRACE_GET_THREAD_AREA 22
46#endif
47
ed9a39eb
JM
48extern int arm_apcs_32;
49
ed9a39eb 50/* The following variables are used to determine the version of the
fdf39c9a 51 underlying GNU/Linux operating system. Examples:
ed9a39eb 52
fdf39c9a 53 GNU/Linux 2.0.35 GNU/Linux 2.2.12
ed9a39eb
JM
54 os_version = 0x00020023 os_version = 0x0002020c
55 os_major = 2 os_major = 2
56 os_minor = 0 os_minor = 2
57 os_release = 35 os_release = 12
58
59 Note: os_version = (os_major << 16) | (os_minor << 8) | os_release
60
61 These are initialized using get_linux_version() from
62 _initialize_arm_linux_nat(). */
63
64static unsigned int os_version, os_major, os_minor, os_release;
65
fdf39c9a 66/* On GNU/Linux, threads are implemented as pseudo-processes, in which
41c49b06 67 case we may be tracing more than one process at a time. In that
39f77062 68 case, inferior_ptid will contain the main process ID and the
fdf39c9a
RE
69 individual thread (process) ID. get_thread_id () is used to get
70 the thread id if it's available, and the process id otherwise. */
41c49b06
SB
71
72int
39f77062 73get_thread_id (ptid_t ptid)
41c49b06 74{
39f77062
KB
75 int tid = TIDGET (ptid);
76 if (0 == tid)
77 tid = PIDGET (ptid);
41c49b06
SB
78 return tid;
79}
39f77062 80#define GET_THREAD_ID(PTID) get_thread_id ((PTID));
41c49b06 81
41c49b06 82/* Get the value of a particular register from the floating point
c6b92abd 83 state of the process and store it into regcache. */
41c49b06
SB
84
85static void
86fetch_fpregister (int regno)
87{
88 int ret, tid;
cb587d83 89 gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE];
41c49b06
SB
90
91 /* Get the thread id for the ptrace call. */
39f77062 92 tid = GET_THREAD_ID (inferior_ptid);
41c49b06
SB
93
94 /* Read the floating point state. */
cb587d83 95 ret = ptrace (PT_GETFPREGS, tid, 0, fp);
41c49b06
SB
96 if (ret < 0)
97 {
edefbb7c 98 warning (_("Unable to fetch floating point register."));
41c49b06
SB
99 return;
100 }
101
102 /* Fetch fpsr. */
34e8f22d 103 if (ARM_FPS_REGNUM == regno)
cb587d83
DJ
104 regcache_raw_supply (current_regcache, ARM_FPS_REGNUM,
105 fp + NWFPE_FPSR_OFFSET);
41c49b06
SB
106
107 /* Fetch the floating point register. */
34e8f22d 108 if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM)
cb587d83 109 supply_nwfpe_register (current_regcache, regno, fp);
41c49b06
SB
110}
111
112/* Get the whole floating point state of the process and store it
c6b92abd 113 into regcache. */
ed9a39eb
JM
114
115static void
116fetch_fpregs (void)
117{
41c49b06 118 int ret, regno, tid;
cb587d83 119 gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE];
ed9a39eb 120
41c49b06 121 /* Get the thread id for the ptrace call. */
39f77062 122 tid = GET_THREAD_ID (inferior_ptid);
41c49b06 123
ed9a39eb 124 /* Read the floating point state. */
cb587d83 125 ret = ptrace (PT_GETFPREGS, tid, 0, fp);
ed9a39eb
JM
126 if (ret < 0)
127 {
edefbb7c 128 warning (_("Unable to fetch the floating point registers."));
ed9a39eb
JM
129 return;
130 }
131
132 /* Fetch fpsr. */
cb587d83
DJ
133 regcache_raw_supply (current_regcache, ARM_FPS_REGNUM,
134 fp + NWFPE_FPSR_OFFSET);
ed9a39eb
JM
135
136 /* Fetch the floating point registers. */
34e8f22d 137 for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++)
cb587d83 138 supply_nwfpe_register (current_regcache, regno, fp);
ed9a39eb
JM
139}
140
41c49b06 141/* Save a particular register into the floating point state of the
c6b92abd 142 process using the contents from regcache. */
41c49b06
SB
143
144static void
145store_fpregister (int regno)
146{
147 int ret, tid;
cb587d83 148 gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE];
41c49b06
SB
149
150 /* Get the thread id for the ptrace call. */
39f77062 151 tid = GET_THREAD_ID (inferior_ptid);
41c49b06
SB
152
153 /* Read the floating point state. */
cb587d83 154 ret = ptrace (PT_GETFPREGS, tid, 0, fp);
41c49b06
SB
155 if (ret < 0)
156 {
edefbb7c 157 warning (_("Unable to fetch the floating point registers."));
41c49b06
SB
158 return;
159 }
160
161 /* Store fpsr. */
34e8f22d 162 if (ARM_FPS_REGNUM == regno && register_cached (ARM_FPS_REGNUM))
cb587d83
DJ
163 regcache_raw_collect (current_regcache, ARM_FPS_REGNUM,
164 fp + NWFPE_FPSR_OFFSET);
41c49b06
SB
165
166 /* Store the floating point register. */
34e8f22d 167 if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM)
cb587d83 168 collect_nwfpe_register (current_regcache, regno, fp);
41c49b06 169
cb587d83 170 ret = ptrace (PTRACE_SETFPREGS, tid, 0, fp);
41c49b06
SB
171 if (ret < 0)
172 {
edefbb7c 173 warning (_("Unable to store floating point register."));
41c49b06
SB
174 return;
175 }
176}
177
ed9a39eb 178/* Save the whole floating point state of the process using
c6b92abd 179 the contents from regcache. */
ed9a39eb
JM
180
181static void
182store_fpregs (void)
183{
41c49b06 184 int ret, regno, tid;
cb587d83 185 gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE];
ed9a39eb 186
41c49b06 187 /* Get the thread id for the ptrace call. */
39f77062 188 tid = GET_THREAD_ID (inferior_ptid);
41c49b06
SB
189
190 /* Read the floating point state. */
cb587d83 191 ret = ptrace (PT_GETFPREGS, tid, 0, fp);
41c49b06
SB
192 if (ret < 0)
193 {
edefbb7c 194 warning (_("Unable to fetch the floating point registers."));
41c49b06
SB
195 return;
196 }
197
ed9a39eb 198 /* Store fpsr. */
34e8f22d 199 if (register_cached (ARM_FPS_REGNUM))
cb587d83
DJ
200 regcache_raw_collect (current_regcache, ARM_FPS_REGNUM,
201 fp + NWFPE_FPSR_OFFSET);
ed9a39eb
JM
202
203 /* Store the floating point registers. */
34e8f22d 204 for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++)
cb587d83
DJ
205 if (register_cached (regno))
206 collect_nwfpe_register (current_regcache, regno, fp);
ed9a39eb 207
cb587d83 208 ret = ptrace (PTRACE_SETFPREGS, tid, 0, fp);
ed9a39eb
JM
209 if (ret < 0)
210 {
edefbb7c 211 warning (_("Unable to store floating point registers."));
ed9a39eb
JM
212 return;
213 }
214}
215
41c49b06 216/* Fetch a general register of the process and store into
c6b92abd 217 regcache. */
41c49b06
SB
218
219static void
220fetch_register (int regno)
221{
222 int ret, tid;
c2152441 223 elf_gregset_t regs;
41c49b06
SB
224
225 /* Get the thread id for the ptrace call. */
39f77062 226 tid = GET_THREAD_ID (inferior_ptid);
41c49b06
SB
227
228 ret = ptrace (PTRACE_GETREGS, tid, 0, &regs);
229 if (ret < 0)
230 {
edefbb7c 231 warning (_("Unable to fetch general register."));
41c49b06
SB
232 return;
233 }
234
34e8f22d 235 if (regno >= ARM_A1_REGNUM && regno < ARM_PC_REGNUM)
23a6d369 236 regcache_raw_supply (current_regcache, regno, (char *) &regs[regno]);
41c49b06 237
34e8f22d 238 if (ARM_PS_REGNUM == regno)
41c49b06
SB
239 {
240 if (arm_apcs_32)
23a6d369
AC
241 regcache_raw_supply (current_regcache, ARM_PS_REGNUM,
242 (char *) &regs[ARM_CPSR_REGNUM]);
41c49b06 243 else
23a6d369
AC
244 regcache_raw_supply (current_regcache, ARM_PS_REGNUM,
245 (char *) &regs[ARM_PC_REGNUM]);
41c49b06
SB
246 }
247
34e8f22d 248 if (ARM_PC_REGNUM == regno)
41c49b06 249 {
34e8f22d 250 regs[ARM_PC_REGNUM] = ADDR_BITS_REMOVE (regs[ARM_PC_REGNUM]);
23a6d369
AC
251 regcache_raw_supply (current_regcache, ARM_PC_REGNUM,
252 (char *) &regs[ARM_PC_REGNUM]);
41c49b06
SB
253 }
254}
255
ed9a39eb 256/* Fetch all general registers of the process and store into
c6b92abd 257 regcache. */
ed9a39eb
JM
258
259static void
260fetch_regs (void)
261{
41c49b06 262 int ret, regno, tid;
c2152441 263 elf_gregset_t regs;
ed9a39eb 264
41c49b06 265 /* Get the thread id for the ptrace call. */
39f77062 266 tid = GET_THREAD_ID (inferior_ptid);
41c49b06
SB
267
268 ret = ptrace (PTRACE_GETREGS, tid, 0, &regs);
ed9a39eb
JM
269 if (ret < 0)
270 {
edefbb7c 271 warning (_("Unable to fetch general registers."));
ed9a39eb
JM
272 return;
273 }
274
34e8f22d 275 for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++)
23a6d369 276 regcache_raw_supply (current_regcache, regno, (char *) &regs[regno]);
ed9a39eb
JM
277
278 if (arm_apcs_32)
23a6d369
AC
279 regcache_raw_supply (current_regcache, ARM_PS_REGNUM,
280 (char *) &regs[ARM_CPSR_REGNUM]);
ed9a39eb 281 else
23a6d369
AC
282 regcache_raw_supply (current_regcache, ARM_PS_REGNUM,
283 (char *) &regs[ARM_PC_REGNUM]);
ed9a39eb 284
34e8f22d 285 regs[ARM_PC_REGNUM] = ADDR_BITS_REMOVE (regs[ARM_PC_REGNUM]);
23a6d369
AC
286 regcache_raw_supply (current_regcache, ARM_PC_REGNUM,
287 (char *) &regs[ARM_PC_REGNUM]);
ed9a39eb
JM
288}
289
290/* Store all general registers of the process from the values in
c6b92abd 291 regcache. */
ed9a39eb 292
41c49b06
SB
293static void
294store_register (int regno)
295{
296 int ret, tid;
c2152441 297 elf_gregset_t regs;
41c49b06 298
c6b92abd 299 if (!register_cached (regno))
41c49b06
SB
300 return;
301
302 /* Get the thread id for the ptrace call. */
39f77062 303 tid = GET_THREAD_ID (inferior_ptid);
41c49b06
SB
304
305 /* Get the general registers from the process. */
306 ret = ptrace (PTRACE_GETREGS, tid, 0, &regs);
307 if (ret < 0)
308 {
edefbb7c 309 warning (_("Unable to fetch general registers."));
41c49b06
SB
310 return;
311 }
312
34e8f22d 313 if (regno >= ARM_A1_REGNUM && regno <= ARM_PC_REGNUM)
822c9732 314 regcache_raw_collect (current_regcache, regno, (char *) &regs[regno]);
adb8a87c
DJ
315 else if (arm_apcs_32 && regno == ARM_PS_REGNUM)
316 regcache_raw_collect (current_regcache, regno,
317 (char *) &regs[ARM_CPSR_REGNUM]);
318 else if (!arm_apcs_32 && regno == ARM_PS_REGNUM)
319 regcache_raw_collect (current_regcache, ARM_PC_REGNUM,
320 (char *) &regs[ARM_PC_REGNUM]);
41c49b06
SB
321
322 ret = ptrace (PTRACE_SETREGS, tid, 0, &regs);
323 if (ret < 0)
324 {
edefbb7c 325 warning (_("Unable to store general register."));
41c49b06
SB
326 return;
327 }
328}
329
ed9a39eb
JM
330static void
331store_regs (void)
332{
41c49b06 333 int ret, regno, tid;
c2152441 334 elf_gregset_t regs;
ed9a39eb 335
41c49b06 336 /* Get the thread id for the ptrace call. */
39f77062 337 tid = GET_THREAD_ID (inferior_ptid);
41c49b06
SB
338
339 /* Fetch the general registers. */
340 ret = ptrace (PTRACE_GETREGS, tid, 0, &regs);
ed9a39eb
JM
341 if (ret < 0)
342 {
edefbb7c 343 warning (_("Unable to fetch general registers."));
ed9a39eb
JM
344 return;
345 }
346
34e8f22d 347 for (regno = ARM_A1_REGNUM; regno <= ARM_PC_REGNUM; regno++)
ed9a39eb 348 {
c6b92abd 349 if (register_cached (regno))
822c9732 350 regcache_raw_collect (current_regcache, regno, (char *) &regs[regno]);
ed9a39eb
JM
351 }
352
adb8a87c
DJ
353 if (arm_apcs_32 && register_cached (ARM_PS_REGNUM))
354 regcache_raw_collect (current_regcache, ARM_PS_REGNUM,
355 (char *) &regs[ARM_CPSR_REGNUM]);
356
41c49b06 357 ret = ptrace (PTRACE_SETREGS, tid, 0, &regs);
ed9a39eb
JM
358
359 if (ret < 0)
360 {
edefbb7c 361 warning (_("Unable to store general registers."));
ed9a39eb
JM
362 return;
363 }
364}
365
366/* Fetch registers from the child process. Fetch all registers if
367 regno == -1, otherwise fetch all general registers or all floating
368 point registers depending upon the value of regno. */
369
10d6c8cd
DJ
370static void
371arm_linux_fetch_inferior_registers (int regno)
ed9a39eb 372{
41c49b06
SB
373 if (-1 == regno)
374 {
375 fetch_regs ();
376 fetch_fpregs ();
377 }
378 else
379 {
34e8f22d 380 if (regno < ARM_F0_REGNUM || regno > ARM_FPS_REGNUM)
41c49b06 381 fetch_register (regno);
ed9a39eb 382
34e8f22d 383 if (regno >= ARM_F0_REGNUM && regno <= ARM_FPS_REGNUM)
41c49b06
SB
384 fetch_fpregister (regno);
385 }
ed9a39eb
JM
386}
387
388/* Store registers back into the inferior. Store all registers if
389 regno == -1, otherwise store all general registers or all floating
390 point registers depending upon the value of regno. */
391
10d6c8cd
DJ
392static void
393arm_linux_store_inferior_registers (int regno)
ed9a39eb 394{
41c49b06
SB
395 if (-1 == regno)
396 {
397 store_regs ();
398 store_fpregs ();
399 }
400 else
401 {
34e8f22d 402 if ((regno < ARM_F0_REGNUM) || (regno > ARM_FPS_REGNUM))
41c49b06 403 store_register (regno);
ed9a39eb 404
34e8f22d 405 if ((regno >= ARM_F0_REGNUM) && (regno <= ARM_FPS_REGNUM))
41c49b06
SB
406 store_fpregister (regno);
407 }
ed9a39eb
JM
408}
409
cb587d83
DJ
410/* Wrapper functions for the standard regset handling, used by
411 thread debugging. */
41c49b06
SB
412
413void
713f0374 414fill_gregset (gdb_gregset_t *gregsetp, int regno)
41c49b06 415{
cb587d83 416 arm_linux_collect_gregset (NULL, current_regcache, regno, gregsetp, 0);
41c49b06
SB
417}
418
41c49b06 419void
713f0374 420supply_gregset (gdb_gregset_t *gregsetp)
41c49b06 421{
cb587d83 422 arm_linux_supply_gregset (NULL, current_regcache, -1, gregsetp, 0);
41c49b06
SB
423}
424
41c49b06 425void
713f0374 426fill_fpregset (gdb_fpregset_t *fpregsetp, int regno)
41c49b06 427{
cb587d83 428 arm_linux_collect_nwfpe (NULL, current_regcache, regno, fpregsetp, 0);
41c49b06
SB
429}
430
431/* Fill GDB's register array with the floating-point register values
432 in *fpregsetp. */
433
434void
713f0374 435supply_fpregset (gdb_fpregset_t *fpregsetp)
ed9a39eb 436{
cb587d83 437 arm_linux_supply_nwfpe (NULL, current_regcache, -1, fpregsetp, 0);
ed9a39eb
JM
438}
439
440int
441arm_linux_kernel_u_size (void)
442{
443 return (sizeof (struct user));
444}
445
9308fc88
DJ
446/* Fetch the thread-local storage pointer for libthread_db. */
447
448ps_err_e
449ps_get_thread_area (const struct ps_prochandle *ph,
450 lwpid_t lwpid, int idx, void **base)
451{
452 if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
453 return PS_ERR;
454
455 /* IDX is the bias from the thread pointer to the beginning of the
456 thread descriptor. It has to be subtracted due to implementation
457 quirks in libthread_db. */
458 *base = (void *) ((char *)*base - idx);
459
460 return PS_OK;
461}
462
ed9a39eb
JM
463static unsigned int
464get_linux_version (unsigned int *vmajor,
465 unsigned int *vminor,
466 unsigned int *vrelease)
467{
468 struct utsname info;
469 char *pmajor, *pminor, *prelease, *tail;
470
471 if (-1 == uname (&info))
472 {
edefbb7c 473 warning (_("Unable to determine GNU/Linux version."));
ed9a39eb
JM
474 return -1;
475 }
476
477 pmajor = strtok (info.release, ".");
478 pminor = strtok (NULL, ".");
479 prelease = strtok (NULL, ".");
480
481 *vmajor = (unsigned int) strtoul (pmajor, &tail, 0);
482 *vminor = (unsigned int) strtoul (pminor, &tail, 0);
483 *vrelease = (unsigned int) strtoul (prelease, &tail, 0);
484
485 return ((*vmajor << 16) | (*vminor << 8) | *vrelease);
486}
487
10d6c8cd
DJ
488void _initialize_arm_linux_nat (void);
489
ed9a39eb
JM
490void
491_initialize_arm_linux_nat (void)
492{
10d6c8cd
DJ
493 struct target_ops *t;
494
ed9a39eb 495 os_version = get_linux_version (&os_major, &os_minor, &os_release);
10d6c8cd
DJ
496
497 /* Fill in the generic GNU/Linux methods. */
498 t = linux_target ();
499
500 /* Add our register access methods. */
501 t->to_fetch_registers = arm_linux_fetch_inferior_registers;
502 t->to_store_registers = arm_linux_store_inferior_registers;
503
504 /* Register the target. */
f973ed9c 505 linux_nat_add_target (t);
ed9a39eb 506}