Commit | Line | Data |
---|---|---|
faf5f7ad | 1 | /* GNU/Linux on ARM target support. |
0fd88904 | 2 | |
76a9d10f | 3 | Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 |
8e9d1a24 | 4 | Free Software Foundation, Inc. |
faf5f7ad SB |
5 | |
6 | This file is part of GDB. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
197e01b6 EZ |
20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 | Boston, MA 02110-1301, USA. */ | |
faf5f7ad SB |
22 | |
23 | #include "defs.h" | |
c20f6dea SB |
24 | #include "target.h" |
25 | #include "value.h" | |
faf5f7ad | 26 | #include "gdbtypes.h" |
134e61c4 | 27 | #include "floatformat.h" |
2a451106 KB |
28 | #include "gdbcore.h" |
29 | #include "frame.h" | |
4e052eda | 30 | #include "regcache.h" |
d16aafd8 | 31 | #include "doublest.h" |
7aa1783e | 32 | #include "solib-svr4.h" |
4be87837 | 33 | #include "osabi.h" |
cb587d83 | 34 | #include "regset.h" |
8e9d1a24 DJ |
35 | #include "trad-frame.h" |
36 | #include "tramp-frame.h" | |
faf5f7ad | 37 | |
34e8f22d | 38 | #include "arm-tdep.h" |
cb587d83 | 39 | #include "arm-linux-tdep.h" |
0670c0aa | 40 | #include "glibc-tdep.h" |
a52e6aac | 41 | |
8e9d1a24 DJ |
42 | #include "gdb_string.h" |
43 | ||
cb587d83 DJ |
44 | extern int arm_apcs_32; |
45 | ||
fdf39c9a RE |
46 | /* Under ARM GNU/Linux the traditional way of performing a breakpoint |
47 | is to execute a particular software interrupt, rather than use a | |
48 | particular undefined instruction to provoke a trap. Upon exection | |
49 | of the software interrupt the kernel stops the inferior with a | |
498b1f87 | 50 | SIGTRAP, and wakes the debugger. */ |
66e810cd | 51 | |
2ef47cd0 DJ |
52 | static const char arm_linux_arm_le_breakpoint[] = { 0x01, 0x00, 0x9f, 0xef }; |
53 | ||
54 | static const char arm_linux_arm_be_breakpoint[] = { 0xef, 0x9f, 0x00, 0x01 }; | |
66e810cd | 55 | |
c75a2cc8 DJ |
56 | /* However, the EABI syscall interface (new in Nov. 2005) does not look at |
57 | the operand of the swi if old-ABI compatibility is disabled. Therefore, | |
58 | use an undefined instruction instead. This is supported as of kernel | |
59 | version 2.5.70 (May 2003), so should be a safe assumption for EABI | |
60 | binaries. */ | |
61 | ||
62 | static const char eabi_linux_arm_le_breakpoint[] = { 0xf0, 0x01, 0xf0, 0xe7 }; | |
63 | ||
64 | static const char eabi_linux_arm_be_breakpoint[] = { 0xe7, 0xf0, 0x01, 0xf0 }; | |
65 | ||
66 | /* All the kernels which support Thumb support using a specific undefined | |
67 | instruction for the Thumb breakpoint. */ | |
68 | ||
498b1f87 DJ |
69 | static const char arm_linux_thumb_be_breakpoint[] = {0xde, 0x01}; |
70 | ||
71 | static const char arm_linux_thumb_le_breakpoint[] = {0x01, 0xde}; | |
72 | ||
9df628e0 | 73 | /* Description of the longjmp buffer. */ |
7a5ea0d4 | 74 | #define ARM_LINUX_JB_ELEMENT_SIZE INT_REGISTER_SIZE |
a6cdd8c5 | 75 | #define ARM_LINUX_JB_PC 21 |
faf5f7ad | 76 | |
faf5f7ad SB |
77 | /* Extract from an array REGBUF containing the (raw) register state |
78 | a function return value of type TYPE, and copy that, in virtual format, | |
79 | into VALBUF. */ | |
19d3fc80 RE |
80 | /* FIXME rearnsha/2002-02-23: This function shouldn't be necessary. |
81 | The ARM generic one should be able to handle the model used by | |
82 | linux and the low-level formatting of the registers should be | |
83 | hidden behind the regcache abstraction. */ | |
84 | static void | |
faf5f7ad | 85 | arm_linux_extract_return_value (struct type *type, |
03926e1f DJ |
86 | gdb_byte regbuf[], |
87 | gdb_byte *valbuf) | |
faf5f7ad SB |
88 | { |
89 | /* ScottB: This needs to be looked at to handle the different | |
fdf39c9a | 90 | floating point emulators on ARM GNU/Linux. Right now the code |
faf5f7ad SB |
91 | assumes that fetch inferior registers does the right thing for |
92 | GDB. I suspect this won't handle NWFPE registers correctly, nor | |
93 | will the default ARM version (arm_extract_return_value()). */ | |
94 | ||
34e8f22d RE |
95 | int regnum = ((TYPE_CODE_FLT == TYPE_CODE (type)) |
96 | ? ARM_F0_REGNUM : ARM_A1_REGNUM); | |
62700349 | 97 | memcpy (valbuf, ®buf[DEPRECATED_REGISTER_BYTE (regnum)], TYPE_LENGTH (type)); |
faf5f7ad | 98 | } |
134e61c4 | 99 | |
f38e884d | 100 | /* |
fdf39c9a RE |
101 | Dynamic Linking on ARM GNU/Linux |
102 | -------------------------------- | |
f38e884d SB |
103 | |
104 | Note: PLT = procedure linkage table | |
105 | GOT = global offset table | |
106 | ||
107 | As much as possible, ELF dynamic linking defers the resolution of | |
108 | jump/call addresses until the last minute. The technique used is | |
109 | inspired by the i386 ELF design, and is based on the following | |
110 | constraints. | |
111 | ||
112 | 1) The calling technique should not force a change in the assembly | |
113 | code produced for apps; it MAY cause changes in the way assembly | |
114 | code is produced for position independent code (i.e. shared | |
115 | libraries). | |
116 | ||
117 | 2) The technique must be such that all executable areas must not be | |
118 | modified; and any modified areas must not be executed. | |
119 | ||
120 | To do this, there are three steps involved in a typical jump: | |
121 | ||
122 | 1) in the code | |
123 | 2) through the PLT | |
124 | 3) using a pointer from the GOT | |
125 | ||
126 | When the executable or library is first loaded, each GOT entry is | |
127 | initialized to point to the code which implements dynamic name | |
128 | resolution and code finding. This is normally a function in the | |
fdf39c9a RE |
129 | program interpreter (on ARM GNU/Linux this is usually |
130 | ld-linux.so.2, but it does not have to be). On the first | |
131 | invocation, the function is located and the GOT entry is replaced | |
132 | with the real function address. Subsequent calls go through steps | |
133 | 1, 2 and 3 and end up calling the real code. | |
f38e884d SB |
134 | |
135 | 1) In the code: | |
136 | ||
137 | b function_call | |
138 | bl function_call | |
139 | ||
140 | This is typical ARM code using the 26 bit relative branch or branch | |
141 | and link instructions. The target of the instruction | |
142 | (function_call is usually the address of the function to be called. | |
143 | In position independent code, the target of the instruction is | |
144 | actually an entry in the PLT when calling functions in a shared | |
145 | library. Note that this call is identical to a normal function | |
146 | call, only the target differs. | |
147 | ||
148 | 2) In the PLT: | |
149 | ||
150 | The PLT is a synthetic area, created by the linker. It exists in | |
151 | both executables and libraries. It is an array of stubs, one per | |
152 | imported function call. It looks like this: | |
153 | ||
154 | PLT[0]: | |
155 | str lr, [sp, #-4]! @push the return address (lr) | |
156 | ldr lr, [pc, #16] @load from 6 words ahead | |
157 | add lr, pc, lr @form an address for GOT[0] | |
158 | ldr pc, [lr, #8]! @jump to the contents of that addr | |
159 | ||
160 | The return address (lr) is pushed on the stack and used for | |
161 | calculations. The load on the second line loads the lr with | |
162 | &GOT[3] - . - 20. The addition on the third leaves: | |
163 | ||
164 | lr = (&GOT[3] - . - 20) + (. + 8) | |
165 | lr = (&GOT[3] - 12) | |
166 | lr = &GOT[0] | |
167 | ||
168 | On the fourth line, the pc and lr are both updated, so that: | |
169 | ||
170 | pc = GOT[2] | |
171 | lr = &GOT[0] + 8 | |
172 | = &GOT[2] | |
173 | ||
174 | NOTE: PLT[0] borrows an offset .word from PLT[1]. This is a little | |
175 | "tight", but allows us to keep all the PLT entries the same size. | |
176 | ||
177 | PLT[n+1]: | |
178 | ldr ip, [pc, #4] @load offset from gotoff | |
179 | add ip, pc, ip @add the offset to the pc | |
180 | ldr pc, [ip] @jump to that address | |
181 | gotoff: .word GOT[n+3] - . | |
182 | ||
183 | The load on the first line, gets an offset from the fourth word of | |
184 | the PLT entry. The add on the second line makes ip = &GOT[n+3], | |
185 | which contains either a pointer to PLT[0] (the fixup trampoline) or | |
186 | a pointer to the actual code. | |
187 | ||
188 | 3) In the GOT: | |
189 | ||
190 | The GOT contains helper pointers for both code (PLT) fixups and | |
191 | data fixups. The first 3 entries of the GOT are special. The next | |
192 | M entries (where M is the number of entries in the PLT) belong to | |
193 | the PLT fixups. The next D (all remaining) entries belong to | |
194 | various data fixups. The actual size of the GOT is 3 + M + D. | |
195 | ||
196 | The GOT is also a synthetic area, created by the linker. It exists | |
197 | in both executables and libraries. When the GOT is first | |
198 | initialized , all the GOT entries relating to PLT fixups are | |
199 | pointing to code back at PLT[0]. | |
200 | ||
201 | The special entries in the GOT are: | |
202 | ||
203 | GOT[0] = linked list pointer used by the dynamic loader | |
204 | GOT[1] = pointer to the reloc table for this module | |
205 | GOT[2] = pointer to the fixup/resolver code | |
206 | ||
207 | The first invocation of function call comes through and uses the | |
208 | fixup/resolver code. On the entry to the fixup/resolver code: | |
209 | ||
210 | ip = &GOT[n+3] | |
211 | lr = &GOT[2] | |
212 | stack[0] = return address (lr) of the function call | |
213 | [r0, r1, r2, r3] are still the arguments to the function call | |
214 | ||
215 | This is enough information for the fixup/resolver code to work | |
216 | with. Before the fixup/resolver code returns, it actually calls | |
217 | the requested function and repairs &GOT[n+3]. */ | |
218 | ||
2a451106 KB |
219 | /* The constants below were determined by examining the following files |
220 | in the linux kernel sources: | |
221 | ||
222 | arch/arm/kernel/signal.c | |
223 | - see SWI_SYS_SIGRETURN and SWI_SYS_RT_SIGRETURN | |
224 | include/asm-arm/unistd.h | |
225 | - see __NR_sigreturn, __NR_rt_sigreturn, and __NR_SYSCALL_BASE */ | |
226 | ||
227 | #define ARM_LINUX_SIGRETURN_INSTR 0xef900077 | |
228 | #define ARM_LINUX_RT_SIGRETURN_INSTR 0xef9000ad | |
229 | ||
edfb1a26 DJ |
230 | /* For ARM EABI, the syscall number is not in the SWI instruction |
231 | (instead it is loaded into r7). We recognize the pattern that | |
232 | glibc uses... alternatively, we could arrange to do this by | |
233 | function name, but they are not always exported. */ | |
8e9d1a24 DJ |
234 | #define ARM_SET_R7_SIGRETURN 0xe3a07077 |
235 | #define ARM_SET_R7_RT_SIGRETURN 0xe3a070ad | |
236 | #define ARM_EABI_SYSCALL 0xef000000 | |
2a451106 | 237 | |
8e9d1a24 DJ |
238 | static void |
239 | arm_linux_sigtramp_cache (struct frame_info *next_frame, | |
240 | struct trad_frame_cache *this_cache, | |
241 | CORE_ADDR func, int regs_offset) | |
2a451106 | 242 | { |
8e9d1a24 DJ |
243 | CORE_ADDR sp = frame_unwind_register_unsigned (next_frame, ARM_SP_REGNUM); |
244 | CORE_ADDR base = sp + regs_offset; | |
245 | int i; | |
2a451106 | 246 | |
8e9d1a24 DJ |
247 | for (i = 0; i < 16; i++) |
248 | trad_frame_set_reg_addr (this_cache, i, base + i * 4); | |
2a451106 | 249 | |
8e9d1a24 | 250 | trad_frame_set_reg_addr (this_cache, ARM_PS_REGNUM, base + 16 * 4); |
2a451106 | 251 | |
8e9d1a24 DJ |
252 | /* The VFP or iWMMXt registers may be saved on the stack, but there's |
253 | no reliable way to restore them (yet). */ | |
2a451106 | 254 | |
8e9d1a24 DJ |
255 | /* Save a frame ID. */ |
256 | trad_frame_set_id (this_cache, frame_id_build (sp, func)); | |
257 | } | |
2a451106 | 258 | |
edfb1a26 DJ |
259 | /* There are a couple of different possible stack layouts that |
260 | we need to support. | |
261 | ||
262 | Before version 2.6.18, the kernel used completely independent | |
263 | layouts for non-RT and RT signals. For non-RT signals the stack | |
264 | began directly with a struct sigcontext. For RT signals the stack | |
265 | began with two redundant pointers (to the siginfo and ucontext), | |
266 | and then the siginfo and ucontext. | |
267 | ||
268 | As of version 2.6.18, the non-RT signal frame layout starts with | |
269 | a ucontext and the RT signal frame starts with a siginfo and then | |
270 | a ucontext. Also, the ucontext now has a designated save area | |
271 | for coprocessor registers. | |
272 | ||
273 | For RT signals, it's easy to tell the difference: we look for | |
274 | pinfo, the pointer to the siginfo. If it has the expected | |
275 | value, we have an old layout. If it doesn't, we have the new | |
276 | layout. | |
277 | ||
278 | For non-RT signals, it's a bit harder. We need something in one | |
279 | layout or the other with a recognizable offset and value. We can't | |
280 | use the return trampoline, because ARM usually uses SA_RESTORER, | |
281 | in which case the stack return trampoline is not filled in. | |
282 | We can't use the saved stack pointer, because sigaltstack might | |
283 | be in use. So for now we guess the new layout... */ | |
284 | ||
285 | /* There are three words (trap_no, error_code, oldmask) in | |
286 | struct sigcontext before r0. */ | |
287 | #define ARM_SIGCONTEXT_R0 0xc | |
288 | ||
289 | /* There are five words (uc_flags, uc_link, and three for uc_stack) | |
290 | in the ucontext_t before the sigcontext. */ | |
291 | #define ARM_UCONTEXT_SIGCONTEXT 0x14 | |
292 | ||
293 | /* There are three elements in an rt_sigframe before the ucontext: | |
294 | pinfo, puc, and info. The first two are pointers and the third | |
295 | is a struct siginfo, with size 128 bytes. We could follow puc | |
296 | to the ucontext, but it's simpler to skip the whole thing. */ | |
297 | #define ARM_OLD_RT_SIGFRAME_SIGINFO 0x8 | |
298 | #define ARM_OLD_RT_SIGFRAME_UCONTEXT 0x88 | |
299 | ||
300 | #define ARM_NEW_RT_SIGFRAME_UCONTEXT 0x80 | |
301 | ||
302 | #define ARM_NEW_SIGFRAME_MAGIC 0x5ac3c35a | |
303 | ||
8e9d1a24 DJ |
304 | static void |
305 | arm_linux_sigreturn_init (const struct tramp_frame *self, | |
306 | struct frame_info *next_frame, | |
307 | struct trad_frame_cache *this_cache, | |
308 | CORE_ADDR func) | |
2a451106 | 309 | { |
edfb1a26 DJ |
310 | CORE_ADDR sp = frame_unwind_register_unsigned (next_frame, ARM_SP_REGNUM); |
311 | ULONGEST uc_flags = read_memory_unsigned_integer (sp, 4); | |
312 | ||
313 | if (uc_flags == ARM_NEW_SIGFRAME_MAGIC) | |
314 | arm_linux_sigtramp_cache (next_frame, this_cache, func, | |
315 | ARM_UCONTEXT_SIGCONTEXT | |
316 | + ARM_SIGCONTEXT_R0); | |
317 | else | |
318 | arm_linux_sigtramp_cache (next_frame, this_cache, func, | |
319 | ARM_SIGCONTEXT_R0); | |
8e9d1a24 | 320 | } |
2a451106 | 321 | |
8e9d1a24 DJ |
322 | static void |
323 | arm_linux_rt_sigreturn_init (const struct tramp_frame *self, | |
324 | struct frame_info *next_frame, | |
325 | struct trad_frame_cache *this_cache, | |
326 | CORE_ADDR func) | |
327 | { | |
edfb1a26 DJ |
328 | CORE_ADDR sp = frame_unwind_register_unsigned (next_frame, ARM_SP_REGNUM); |
329 | ULONGEST pinfo = read_memory_unsigned_integer (sp, 4); | |
330 | ||
331 | if (pinfo == sp + ARM_OLD_RT_SIGFRAME_SIGINFO) | |
332 | arm_linux_sigtramp_cache (next_frame, this_cache, func, | |
333 | ARM_OLD_RT_SIGFRAME_UCONTEXT | |
334 | + ARM_UCONTEXT_SIGCONTEXT | |
335 | + ARM_SIGCONTEXT_R0); | |
336 | else | |
337 | arm_linux_sigtramp_cache (next_frame, this_cache, func, | |
338 | ARM_NEW_RT_SIGFRAME_UCONTEXT | |
339 | + ARM_UCONTEXT_SIGCONTEXT | |
340 | + ARM_SIGCONTEXT_R0); | |
2a451106 KB |
341 | } |
342 | ||
8e9d1a24 DJ |
343 | static struct tramp_frame arm_linux_sigreturn_tramp_frame = { |
344 | SIGTRAMP_FRAME, | |
345 | 4, | |
346 | { | |
347 | { ARM_LINUX_SIGRETURN_INSTR, -1 }, | |
348 | { TRAMP_SENTINEL_INSN } | |
349 | }, | |
350 | arm_linux_sigreturn_init | |
351 | }; | |
352 | ||
353 | static struct tramp_frame arm_linux_rt_sigreturn_tramp_frame = { | |
354 | SIGTRAMP_FRAME, | |
355 | 4, | |
356 | { | |
357 | { ARM_LINUX_RT_SIGRETURN_INSTR, -1 }, | |
358 | { TRAMP_SENTINEL_INSN } | |
359 | }, | |
360 | arm_linux_rt_sigreturn_init | |
361 | }; | |
362 | ||
363 | static struct tramp_frame arm_eabi_linux_sigreturn_tramp_frame = { | |
364 | SIGTRAMP_FRAME, | |
365 | 4, | |
366 | { | |
367 | { ARM_SET_R7_SIGRETURN, -1 }, | |
368 | { ARM_EABI_SYSCALL, -1 }, | |
369 | { TRAMP_SENTINEL_INSN } | |
370 | }, | |
371 | arm_linux_sigreturn_init | |
372 | }; | |
373 | ||
374 | static struct tramp_frame arm_eabi_linux_rt_sigreturn_tramp_frame = { | |
375 | SIGTRAMP_FRAME, | |
376 | 4, | |
377 | { | |
378 | { ARM_SET_R7_RT_SIGRETURN, -1 }, | |
379 | { ARM_EABI_SYSCALL, -1 }, | |
380 | { TRAMP_SENTINEL_INSN } | |
381 | }, | |
382 | arm_linux_rt_sigreturn_init | |
383 | }; | |
384 | ||
cb587d83 DJ |
385 | /* Core file and register set support. */ |
386 | ||
387 | #define ARM_LINUX_SIZEOF_GREGSET (18 * INT_REGISTER_SIZE) | |
388 | ||
389 | void | |
390 | arm_linux_supply_gregset (const struct regset *regset, | |
391 | struct regcache *regcache, | |
392 | int regnum, const void *gregs_buf, size_t len) | |
393 | { | |
394 | const gdb_byte *gregs = gregs_buf; | |
395 | int regno; | |
396 | CORE_ADDR reg_pc; | |
397 | gdb_byte pc_buf[INT_REGISTER_SIZE]; | |
398 | ||
399 | for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) | |
400 | if (regnum == -1 || regnum == regno) | |
401 | regcache_raw_supply (regcache, regno, | |
402 | gregs + INT_REGISTER_SIZE * regno); | |
403 | ||
404 | if (regnum == ARM_PS_REGNUM || regnum == -1) | |
405 | { | |
406 | if (arm_apcs_32) | |
407 | regcache_raw_supply (regcache, ARM_PS_REGNUM, | |
408 | gregs + INT_REGISTER_SIZE * ARM_CPSR_REGNUM); | |
409 | else | |
410 | regcache_raw_supply (regcache, ARM_PS_REGNUM, | |
411 | gregs + INT_REGISTER_SIZE * ARM_PC_REGNUM); | |
412 | } | |
413 | ||
414 | if (regnum == ARM_PC_REGNUM || regnum == -1) | |
415 | { | |
416 | reg_pc = extract_unsigned_integer (gregs | |
417 | + INT_REGISTER_SIZE * ARM_PC_REGNUM, | |
418 | INT_REGISTER_SIZE); | |
419 | reg_pc = ADDR_BITS_REMOVE (reg_pc); | |
420 | store_unsigned_integer (pc_buf, INT_REGISTER_SIZE, reg_pc); | |
421 | regcache_raw_supply (regcache, ARM_PC_REGNUM, pc_buf); | |
422 | } | |
423 | } | |
424 | ||
425 | void | |
426 | arm_linux_collect_gregset (const struct regset *regset, | |
427 | const struct regcache *regcache, | |
428 | int regnum, void *gregs_buf, size_t len) | |
429 | { | |
430 | gdb_byte *gregs = gregs_buf; | |
431 | int regno; | |
432 | ||
433 | for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) | |
434 | if (regnum == -1 || regnum == regno) | |
435 | regcache_raw_collect (regcache, regno, | |
436 | gregs + INT_REGISTER_SIZE * regno); | |
437 | ||
438 | if (regnum == ARM_PS_REGNUM || regnum == -1) | |
439 | { | |
440 | if (arm_apcs_32) | |
441 | regcache_raw_collect (regcache, ARM_PS_REGNUM, | |
442 | gregs + INT_REGISTER_SIZE * ARM_CPSR_REGNUM); | |
443 | else | |
444 | regcache_raw_collect (regcache, ARM_PS_REGNUM, | |
445 | gregs + INT_REGISTER_SIZE * ARM_PC_REGNUM); | |
446 | } | |
447 | ||
448 | if (regnum == ARM_PC_REGNUM || regnum == -1) | |
449 | regcache_raw_collect (regcache, ARM_PC_REGNUM, | |
450 | gregs + INT_REGISTER_SIZE * ARM_PC_REGNUM); | |
451 | } | |
452 | ||
453 | /* Support for register format used by the NWFPE FPA emulator. */ | |
454 | ||
455 | #define typeNone 0x00 | |
456 | #define typeSingle 0x01 | |
457 | #define typeDouble 0x02 | |
458 | #define typeExtended 0x03 | |
459 | ||
460 | void | |
461 | supply_nwfpe_register (struct regcache *regcache, int regno, | |
462 | const gdb_byte *regs) | |
463 | { | |
464 | const gdb_byte *reg_data; | |
465 | gdb_byte reg_tag; | |
466 | gdb_byte buf[FP_REGISTER_SIZE]; | |
467 | ||
468 | reg_data = regs + (regno - ARM_F0_REGNUM) * FP_REGISTER_SIZE; | |
469 | reg_tag = regs[(regno - ARM_F0_REGNUM) + NWFPE_TAGS_OFFSET]; | |
470 | memset (buf, 0, FP_REGISTER_SIZE); | |
471 | ||
472 | switch (reg_tag) | |
473 | { | |
474 | case typeSingle: | |
475 | memcpy (buf, reg_data, 4); | |
476 | break; | |
477 | case typeDouble: | |
478 | memcpy (buf, reg_data + 4, 4); | |
479 | memcpy (buf + 4, reg_data, 4); | |
480 | break; | |
481 | case typeExtended: | |
482 | /* We want sign and exponent, then least significant bits, | |
483 | then most significant. NWFPE does sign, most, least. */ | |
484 | memcpy (buf, reg_data, 4); | |
485 | memcpy (buf + 4, reg_data + 8, 4); | |
486 | memcpy (buf + 8, reg_data + 4, 4); | |
487 | break; | |
488 | default: | |
489 | break; | |
490 | } | |
491 | ||
492 | regcache_raw_supply (regcache, regno, buf); | |
493 | } | |
494 | ||
495 | void | |
496 | collect_nwfpe_register (const struct regcache *regcache, int regno, | |
497 | gdb_byte *regs) | |
498 | { | |
499 | gdb_byte *reg_data; | |
500 | gdb_byte reg_tag; | |
501 | gdb_byte buf[FP_REGISTER_SIZE]; | |
502 | ||
503 | regcache_raw_collect (regcache, regno, buf); | |
504 | ||
505 | /* NOTE drow/2006-06-07: This code uses the tag already in the | |
506 | register buffer. I've preserved that when moving the code | |
507 | from the native file to the target file. But this doesn't | |
508 | always make sense. */ | |
509 | ||
510 | reg_data = regs + (regno - ARM_F0_REGNUM) * FP_REGISTER_SIZE; | |
511 | reg_tag = regs[(regno - ARM_F0_REGNUM) + NWFPE_TAGS_OFFSET]; | |
512 | ||
513 | switch (reg_tag) | |
514 | { | |
515 | case typeSingle: | |
516 | memcpy (reg_data, buf, 4); | |
517 | break; | |
518 | case typeDouble: | |
519 | memcpy (reg_data, buf + 4, 4); | |
520 | memcpy (reg_data + 4, buf, 4); | |
521 | break; | |
522 | case typeExtended: | |
523 | memcpy (reg_data, buf, 4); | |
524 | memcpy (reg_data + 4, buf + 8, 4); | |
525 | memcpy (reg_data + 8, buf + 4, 4); | |
526 | break; | |
527 | default: | |
528 | break; | |
529 | } | |
530 | } | |
531 | ||
532 | void | |
533 | arm_linux_supply_nwfpe (const struct regset *regset, | |
534 | struct regcache *regcache, | |
535 | int regnum, const void *regs_buf, size_t len) | |
536 | { | |
537 | const gdb_byte *regs = regs_buf; | |
538 | int regno; | |
539 | ||
540 | if (regnum == ARM_FPS_REGNUM || regnum == -1) | |
541 | regcache_raw_supply (regcache, ARM_FPS_REGNUM, | |
542 | regs + NWFPE_FPSR_OFFSET); | |
543 | ||
544 | for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) | |
545 | if (regnum == -1 || regnum == regno) | |
546 | supply_nwfpe_register (regcache, regno, regs); | |
547 | } | |
548 | ||
549 | void | |
550 | arm_linux_collect_nwfpe (const struct regset *regset, | |
551 | const struct regcache *regcache, | |
552 | int regnum, void *regs_buf, size_t len) | |
553 | { | |
554 | gdb_byte *regs = regs_buf; | |
555 | int regno; | |
556 | ||
557 | for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) | |
558 | if (regnum == -1 || regnum == regno) | |
559 | collect_nwfpe_register (regcache, regno, regs); | |
560 | ||
561 | if (regnum == ARM_FPS_REGNUM || regnum == -1) | |
562 | regcache_raw_collect (regcache, ARM_FPS_REGNUM, | |
563 | regs + INT_REGISTER_SIZE * ARM_FPS_REGNUM); | |
564 | } | |
565 | ||
566 | /* Return the appropriate register set for the core section identified | |
567 | by SECT_NAME and SECT_SIZE. */ | |
568 | ||
569 | static const struct regset * | |
570 | arm_linux_regset_from_core_section (struct gdbarch *gdbarch, | |
571 | const char *sect_name, size_t sect_size) | |
572 | { | |
573 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
574 | ||
575 | if (strcmp (sect_name, ".reg") == 0 | |
576 | && sect_size == ARM_LINUX_SIZEOF_GREGSET) | |
577 | { | |
578 | if (tdep->gregset == NULL) | |
579 | tdep->gregset = regset_alloc (gdbarch, arm_linux_supply_gregset, | |
580 | arm_linux_collect_gregset); | |
581 | return tdep->gregset; | |
582 | } | |
583 | ||
584 | if (strcmp (sect_name, ".reg2") == 0 | |
585 | && sect_size == ARM_LINUX_SIZEOF_NWFPE) | |
586 | { | |
587 | if (tdep->fpregset == NULL) | |
588 | tdep->fpregset = regset_alloc (gdbarch, arm_linux_supply_nwfpe, | |
589 | arm_linux_collect_nwfpe); | |
590 | return tdep->fpregset; | |
591 | } | |
592 | ||
593 | return NULL; | |
594 | } | |
595 | ||
97e03143 RE |
596 | static void |
597 | arm_linux_init_abi (struct gdbarch_info info, | |
598 | struct gdbarch *gdbarch) | |
599 | { | |
600 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
601 | ||
602 | tdep->lowest_pc = 0x8000; | |
2ef47cd0 | 603 | if (info.byte_order == BFD_ENDIAN_BIG) |
498b1f87 | 604 | { |
c75a2cc8 DJ |
605 | if (tdep->arm_abi == ARM_ABI_AAPCS) |
606 | tdep->arm_breakpoint = eabi_linux_arm_be_breakpoint; | |
607 | else | |
608 | tdep->arm_breakpoint = arm_linux_arm_be_breakpoint; | |
498b1f87 DJ |
609 | tdep->thumb_breakpoint = arm_linux_thumb_be_breakpoint; |
610 | } | |
2ef47cd0 | 611 | else |
498b1f87 | 612 | { |
c75a2cc8 DJ |
613 | if (tdep->arm_abi == ARM_ABI_AAPCS) |
614 | tdep->arm_breakpoint = eabi_linux_arm_le_breakpoint; | |
615 | else | |
616 | tdep->arm_breakpoint = arm_linux_arm_le_breakpoint; | |
498b1f87 DJ |
617 | tdep->thumb_breakpoint = arm_linux_thumb_le_breakpoint; |
618 | } | |
66e810cd | 619 | tdep->arm_breakpoint_size = sizeof (arm_linux_arm_le_breakpoint); |
498b1f87 | 620 | tdep->thumb_breakpoint_size = sizeof (arm_linux_thumb_le_breakpoint); |
9df628e0 | 621 | |
28e97307 DJ |
622 | if (tdep->fp_model == ARM_FLOAT_AUTO) |
623 | tdep->fp_model = ARM_FLOAT_FPA; | |
fd50bc42 | 624 | |
a6cdd8c5 RE |
625 | tdep->jb_pc = ARM_LINUX_JB_PC; |
626 | tdep->jb_elt_size = ARM_LINUX_JB_ELEMENT_SIZE; | |
19d3fc80 | 627 | |
7aa1783e | 628 | set_solib_svr4_fetch_link_map_offsets |
76a9d10f | 629 | (gdbarch, svr4_ilp32_fetch_link_map_offsets); |
7aa1783e | 630 | |
84320456 | 631 | /* The following override shouldn't be needed. */ |
26e9b323 | 632 | set_gdbarch_deprecated_extract_return_value (gdbarch, arm_linux_extract_return_value); |
0e18d038 RE |
633 | |
634 | /* Shared library handling. */ | |
0e18d038 | 635 | set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); |
bb41a796 | 636 | set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); |
b2756930 KB |
637 | |
638 | /* Enable TLS support. */ | |
639 | set_gdbarch_fetch_tls_load_module_address (gdbarch, | |
640 | svr4_fetch_objfile_link_map); | |
8e9d1a24 DJ |
641 | |
642 | tramp_frame_prepend_unwinder (gdbarch, | |
643 | &arm_linux_sigreturn_tramp_frame); | |
644 | tramp_frame_prepend_unwinder (gdbarch, | |
645 | &arm_linux_rt_sigreturn_tramp_frame); | |
646 | tramp_frame_prepend_unwinder (gdbarch, | |
647 | &arm_eabi_linux_sigreturn_tramp_frame); | |
648 | tramp_frame_prepend_unwinder (gdbarch, | |
649 | &arm_eabi_linux_rt_sigreturn_tramp_frame); | |
cb587d83 DJ |
650 | |
651 | /* Core file support. */ | |
652 | set_gdbarch_regset_from_core_section (gdbarch, | |
653 | arm_linux_regset_from_core_section); | |
97e03143 RE |
654 | } |
655 | ||
faf5f7ad SB |
656 | void |
657 | _initialize_arm_linux_tdep (void) | |
658 | { | |
05816f70 MK |
659 | gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_LINUX, |
660 | arm_linux_init_abi); | |
faf5f7ad | 661 | } |