| /* GNU/Linux on ARM target support. | |
| Copyright (C) 1999-2022 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| #include "defs.h" | |
| #include "target.h" | |
| #include "value.h" | |
| #include "gdbtypes.h" | |
| #include "gdbcore.h" | |
| #include "frame.h" | |
| #include "regcache.h" | |
| #include "solib-svr4.h" | |
| #include "osabi.h" | |
| #include "regset.h" | |
| #include "trad-frame.h" | |
| #include "tramp-frame.h" | |
| #include "breakpoint.h" | |
| #include "auxv.h" | |
| #include "xml-syscall.h" | |
| #include "expop.h" | |
| #include "aarch32-tdep.h" | |
| #include "arch/arm.h" | |
| #include "arch/arm-get-next-pcs.h" | |
| #include "arch/arm-linux.h" | |
| #include "arm-tdep.h" | |
| #include "arm-linux-tdep.h" | |
| #include "linux-tdep.h" | |
| #include "glibc-tdep.h" | |
| #include "arch-utils.h" | |
| #include "inferior.h" | |
| #include "infrun.h" | |
| #include "gdbthread.h" | |
| #include "symfile.h" | |
| #include "record-full.h" | |
| #include "linux-record.h" | |
| #include "cli/cli-utils.h" | |
| #include "stap-probe.h" | |
| #include "parser-defs.h" | |
| #include "user-regs.h" | |
| #include <ctype.h> | |
| #include "elf/common.h" | |
| /* Under ARM GNU/Linux the traditional way of performing a breakpoint | |
| is to execute a particular software interrupt, rather than use a | |
| particular undefined instruction to provoke a trap. Upon exection | |
| of the software interrupt the kernel stops the inferior with a | |
| SIGTRAP, and wakes the debugger. */ | |
| static const gdb_byte arm_linux_arm_le_breakpoint[] = { 0x01, 0x00, 0x9f, 0xef }; | |
| static const gdb_byte arm_linux_arm_be_breakpoint[] = { 0xef, 0x9f, 0x00, 0x01 }; | |
| /* However, the EABI syscall interface (new in Nov. 2005) does not look at | |
| the operand of the swi if old-ABI compatibility is disabled. Therefore, | |
| use an undefined instruction instead. This is supported as of kernel | |
| version 2.5.70 (May 2003), so should be a safe assumption for EABI | |
| binaries. */ | |
| static const gdb_byte eabi_linux_arm_le_breakpoint[] = { 0xf0, 0x01, 0xf0, 0xe7 }; | |
| static const gdb_byte eabi_linux_arm_be_breakpoint[] = { 0xe7, 0xf0, 0x01, 0xf0 }; | |
| /* All the kernels which support Thumb support using a specific undefined | |
| instruction for the Thumb breakpoint. */ | |
| static const gdb_byte arm_linux_thumb_be_breakpoint[] = {0xde, 0x01}; | |
| static const gdb_byte arm_linux_thumb_le_breakpoint[] = {0x01, 0xde}; | |
| /* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks, | |
| we must use a length-appropriate breakpoint for 32-bit Thumb | |
| instructions. See also thumb_get_next_pc. */ | |
| static const gdb_byte arm_linux_thumb2_be_breakpoint[] = { 0xf7, 0xf0, 0xa0, 0x00 }; | |
| static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 }; | |
| /* Description of the longjmp buffer. The buffer is treated as an array of | |
| elements of size ARM_LINUX_JB_ELEMENT_SIZE. | |
| The location of saved registers in this buffer (in particular the PC | |
| to use after longjmp is called) varies depending on the ABI (in | |
| particular the FP model) and also (possibly) the C Library. | |
| For glibc, eglibc, and uclibc the following holds: If the FP model is | |
| SoftVFP or VFP (which implies EABI) then the PC is at offset 9 in the | |
| buffer. This is also true for the SoftFPA model. However, for the FPA | |
| model the PC is at offset 21 in the buffer. */ | |
| #define ARM_LINUX_JB_ELEMENT_SIZE ARM_INT_REGISTER_SIZE | |
| #define ARM_LINUX_JB_PC_FPA 21 | |
| #define ARM_LINUX_JB_PC_EABI 9 | |
| /* | |
| Dynamic Linking on ARM GNU/Linux | |
| -------------------------------- | |
| Note: PLT = procedure linkage table | |
| GOT = global offset table | |
| As much as possible, ELF dynamic linking defers the resolution of | |
| jump/call addresses until the last minute. The technique used is | |
| inspired by the i386 ELF design, and is based on the following | |
| constraints. | |
| 1) The calling technique should not force a change in the assembly | |
| code produced for apps; it MAY cause changes in the way assembly | |
| code is produced for position independent code (i.e. shared | |
| libraries). | |
| 2) The technique must be such that all executable areas must not be | |
| modified; and any modified areas must not be executed. | |
| To do this, there are three steps involved in a typical jump: | |
| 1) in the code | |
| 2) through the PLT | |
| 3) using a pointer from the GOT | |
| When the executable or library is first loaded, each GOT entry is | |
| initialized to point to the code which implements dynamic name | |
| resolution and code finding. This is normally a function in the | |
| program interpreter (on ARM GNU/Linux this is usually | |
| ld-linux.so.2, but it does not have to be). On the first | |
| invocation, the function is located and the GOT entry is replaced | |
| with the real function address. Subsequent calls go through steps | |
| 1, 2 and 3 and end up calling the real code. | |
| 1) In the code: | |
| b function_call | |
| bl function_call | |
| This is typical ARM code using the 26 bit relative branch or branch | |
| and link instructions. The target of the instruction | |
| (function_call is usually the address of the function to be called. | |
| In position independent code, the target of the instruction is | |
| actually an entry in the PLT when calling functions in a shared | |
| library. Note that this call is identical to a normal function | |
| call, only the target differs. | |
| 2) In the PLT: | |
| The PLT is a synthetic area, created by the linker. It exists in | |
| both executables and libraries. It is an array of stubs, one per | |
| imported function call. It looks like this: | |
| PLT[0]: | |
| str lr, [sp, #-4]! @push the return address (lr) | |
| ldr lr, [pc, #16] @load from 6 words ahead | |
| add lr, pc, lr @form an address for GOT[0] | |
| ldr pc, [lr, #8]! @jump to the contents of that addr | |
| The return address (lr) is pushed on the stack and used for | |
| calculations. The load on the second line loads the lr with | |
| &GOT[3] - . - 20. The addition on the third leaves: | |
| lr = (&GOT[3] - . - 20) + (. + 8) | |
| lr = (&GOT[3] - 12) | |
| lr = &GOT[0] | |
| On the fourth line, the pc and lr are both updated, so that: | |
| pc = GOT[2] | |
| lr = &GOT[0] + 8 | |
| = &GOT[2] | |
| NOTE: PLT[0] borrows an offset .word from PLT[1]. This is a little | |
| "tight", but allows us to keep all the PLT entries the same size. | |
| PLT[n+1]: | |
| ldr ip, [pc, #4] @load offset from gotoff | |
| add ip, pc, ip @add the offset to the pc | |
| ldr pc, [ip] @jump to that address | |
| gotoff: .word GOT[n+3] - . | |
| The load on the first line, gets an offset from the fourth word of | |
| the PLT entry. The add on the second line makes ip = &GOT[n+3], | |
| which contains either a pointer to PLT[0] (the fixup trampoline) or | |
| a pointer to the actual code. | |
| 3) In the GOT: | |
| The GOT contains helper pointers for both code (PLT) fixups and | |
| data fixups. The first 3 entries of the GOT are special. The next | |
| M entries (where M is the number of entries in the PLT) belong to | |
| the PLT fixups. The next D (all remaining) entries belong to | |
| various data fixups. The actual size of the GOT is 3 + M + D. | |
| The GOT is also a synthetic area, created by the linker. It exists | |
| in both executables and libraries. When the GOT is first | |
| initialized , all the GOT entries relating to PLT fixups are | |
| pointing to code back at PLT[0]. | |
| The special entries in the GOT are: | |
| GOT[0] = linked list pointer used by the dynamic loader | |
| GOT[1] = pointer to the reloc table for this module | |
| GOT[2] = pointer to the fixup/resolver code | |
| The first invocation of function call comes through and uses the | |
| fixup/resolver code. On the entry to the fixup/resolver code: | |
| ip = &GOT[n+3] | |
| lr = &GOT[2] | |
| stack[0] = return address (lr) of the function call | |
| [r0, r1, r2, r3] are still the arguments to the function call | |
| This is enough information for the fixup/resolver code to work | |
| with. Before the fixup/resolver code returns, it actually calls | |
| the requested function and repairs &GOT[n+3]. */ | |
| /* The constants below were determined by examining the following files | |
| in the linux kernel sources: | |
| arch/arm/kernel/signal.c | |
| - see SWI_SYS_SIGRETURN and SWI_SYS_RT_SIGRETURN | |
| include/asm-arm/unistd.h | |
| - see __NR_sigreturn, __NR_rt_sigreturn, and __NR_SYSCALL_BASE */ | |
| #define ARM_LINUX_SIGRETURN_INSTR 0xef900077 | |
| #define ARM_LINUX_RT_SIGRETURN_INSTR 0xef9000ad | |
| /* For ARM EABI, the syscall number is not in the SWI instruction | |
| (instead it is loaded into r7). We recognize the pattern that | |
| glibc uses... alternatively, we could arrange to do this by | |
| function name, but they are not always exported. */ | |
| #define ARM_SET_R7_SIGRETURN 0xe3a07077 | |
| #define ARM_SET_R7_RT_SIGRETURN 0xe3a070ad | |
| #define ARM_EABI_SYSCALL 0xef000000 | |
| /* Equivalent patterns for Thumb2. */ | |
| #define THUMB2_SET_R7_SIGRETURN1 0xf04f | |
| #define THUMB2_SET_R7_SIGRETURN2 0x0777 | |
| #define THUMB2_SET_R7_RT_SIGRETURN1 0xf04f | |
| #define THUMB2_SET_R7_RT_SIGRETURN2 0x07ad | |
| #define THUMB2_EABI_SYSCALL 0xdf00 | |
| /* OABI syscall restart trampoline, used for EABI executables too | |
| whenever OABI support has been enabled in the kernel. */ | |
| #define ARM_OABI_SYSCALL_RESTART_SYSCALL 0xef900000 | |
| #define ARM_LDR_PC_SP_12 0xe49df00c | |
| #define ARM_LDR_PC_SP_4 0xe49df004 | |
| /* Syscall number for sigreturn. */ | |
| #define ARM_SIGRETURN 119 | |
| /* Syscall number for rt_sigreturn. */ | |
| #define ARM_RT_SIGRETURN 173 | |
| static CORE_ADDR | |
| arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self); | |
| /* Operation function pointers for get_next_pcs. */ | |
| static struct arm_get_next_pcs_ops arm_linux_get_next_pcs_ops = { | |
| arm_get_next_pcs_read_memory_unsigned_integer, | |
| arm_linux_get_next_pcs_syscall_next_pc, | |
| arm_get_next_pcs_addr_bits_remove, | |
| arm_get_next_pcs_is_thumb, | |
| arm_linux_get_next_pcs_fixup, | |
| }; | |
| static void | |
| arm_linux_sigtramp_cache (struct frame_info *this_frame, | |
| struct trad_frame_cache *this_cache, | |
| CORE_ADDR func, int regs_offset) | |
| { | |
| CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM); | |
| CORE_ADDR base = sp + regs_offset; | |
| int i; | |
| for (i = 0; i < 16; i++) | |
| trad_frame_set_reg_addr (this_cache, i, base + i * 4); | |
| trad_frame_set_reg_addr (this_cache, ARM_PS_REGNUM, base + 16 * 4); | |
| /* The VFP or iWMMXt registers may be saved on the stack, but there's | |
| no reliable way to restore them (yet). */ | |
| /* Save a frame ID. */ | |
| trad_frame_set_id (this_cache, frame_id_build (sp, func)); | |
| } | |
| /* See arm-linux.h for stack layout details. */ | |
| static void | |
| arm_linux_sigreturn_init (const struct tramp_frame *self, | |
| struct frame_info *this_frame, | |
| struct trad_frame_cache *this_cache, | |
| CORE_ADDR func) | |
| { | |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); | |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | |
| CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM); | |
| ULONGEST uc_flags = read_memory_unsigned_integer (sp, 4, byte_order); | |
| if (uc_flags == ARM_NEW_SIGFRAME_MAGIC) | |
| arm_linux_sigtramp_cache (this_frame, this_cache, func, | |
| ARM_UCONTEXT_SIGCONTEXT | |
| + ARM_SIGCONTEXT_R0); | |
| else | |
| arm_linux_sigtramp_cache (this_frame, this_cache, func, | |
| ARM_SIGCONTEXT_R0); | |
| } | |
| static void | |
| arm_linux_rt_sigreturn_init (const struct tramp_frame *self, | |
| struct frame_info *this_frame, | |
| struct trad_frame_cache *this_cache, | |
| CORE_ADDR func) | |
| { | |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); | |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | |
| CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM); | |
| ULONGEST pinfo = read_memory_unsigned_integer (sp, 4, byte_order); | |
| if (pinfo == sp + ARM_OLD_RT_SIGFRAME_SIGINFO) | |
| arm_linux_sigtramp_cache (this_frame, this_cache, func, | |
| ARM_OLD_RT_SIGFRAME_UCONTEXT | |
| + ARM_UCONTEXT_SIGCONTEXT | |
| + ARM_SIGCONTEXT_R0); | |
| else | |
| arm_linux_sigtramp_cache (this_frame, this_cache, func, | |
| ARM_NEW_RT_SIGFRAME_UCONTEXT | |
| + ARM_UCONTEXT_SIGCONTEXT | |
| + ARM_SIGCONTEXT_R0); | |
| } | |
| static void | |
| arm_linux_restart_syscall_init (const struct tramp_frame *self, | |
| struct frame_info *this_frame, | |
| struct trad_frame_cache *this_cache, | |
| CORE_ADDR func) | |
| { | |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); | |
| CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM); | |
| CORE_ADDR pc = get_frame_memory_unsigned (this_frame, sp, 4); | |
| CORE_ADDR cpsr = get_frame_register_unsigned (this_frame, ARM_PS_REGNUM); | |
| ULONGEST t_bit = arm_psr_thumb_bit (gdbarch); | |
| int sp_offset; | |
| /* There are two variants of this trampoline; with older kernels, the | |
| stub is placed on the stack, while newer kernels use the stub from | |
| the vector page. They are identical except that the older version | |
| increments SP by 12 (to skip stored PC and the stub itself), while | |
| the newer version increments SP only by 4 (just the stored PC). */ | |
| if (self->insn[1].bytes == ARM_LDR_PC_SP_4) | |
| sp_offset = 4; | |
| else | |
| sp_offset = 12; | |
| /* Update Thumb bit in CPSR. */ | |
| if (pc & 1) | |
| cpsr |= t_bit; | |
| else | |
| cpsr &= ~t_bit; | |
| /* Remove Thumb bit from PC. */ | |
| pc = gdbarch_addr_bits_remove (gdbarch, pc); | |
| /* Save previous register values. */ | |
| trad_frame_set_reg_value (this_cache, ARM_SP_REGNUM, sp + sp_offset); | |
| trad_frame_set_reg_value (this_cache, ARM_PC_REGNUM, pc); | |
| trad_frame_set_reg_value (this_cache, ARM_PS_REGNUM, cpsr); | |
| /* Save a frame ID. */ | |
| trad_frame_set_id (this_cache, frame_id_build (sp, func)); | |
| } | |
| static struct tramp_frame arm_linux_sigreturn_tramp_frame = { | |
| SIGTRAMP_FRAME, | |
| 4, | |
| { | |
| { ARM_LINUX_SIGRETURN_INSTR, ULONGEST_MAX }, | |
| { TRAMP_SENTINEL_INSN } | |
| }, | |
| arm_linux_sigreturn_init | |
| }; | |
| static struct tramp_frame arm_linux_rt_sigreturn_tramp_frame = { | |
| SIGTRAMP_FRAME, | |
| 4, | |
| { | |
| { ARM_LINUX_RT_SIGRETURN_INSTR, ULONGEST_MAX }, | |
| { TRAMP_SENTINEL_INSN } | |
| }, | |
| arm_linux_rt_sigreturn_init | |
| }; | |
| static struct tramp_frame arm_eabi_linux_sigreturn_tramp_frame = { | |
| SIGTRAMP_FRAME, | |
| 4, | |
| { | |
| { ARM_SET_R7_SIGRETURN, ULONGEST_MAX }, | |
| { ARM_EABI_SYSCALL, ULONGEST_MAX }, | |
| { TRAMP_SENTINEL_INSN } | |
| }, | |
| arm_linux_sigreturn_init | |
| }; | |
| static struct tramp_frame arm_eabi_linux_rt_sigreturn_tramp_frame = { | |
| SIGTRAMP_FRAME, | |
| 4, | |
| { | |
| { ARM_SET_R7_RT_SIGRETURN, ULONGEST_MAX }, | |
| { ARM_EABI_SYSCALL, ULONGEST_MAX }, | |
| { TRAMP_SENTINEL_INSN } | |
| }, | |
| arm_linux_rt_sigreturn_init | |
| }; | |
| static struct tramp_frame thumb2_eabi_linux_sigreturn_tramp_frame = { | |
| SIGTRAMP_FRAME, | |
| 2, | |
| { | |
| { THUMB2_SET_R7_SIGRETURN1, ULONGEST_MAX }, | |
| { THUMB2_SET_R7_SIGRETURN2, ULONGEST_MAX }, | |
| { THUMB2_EABI_SYSCALL, ULONGEST_MAX }, | |
| { TRAMP_SENTINEL_INSN } | |
| }, | |
| arm_linux_sigreturn_init | |
| }; | |
| static struct tramp_frame thumb2_eabi_linux_rt_sigreturn_tramp_frame = { | |
| SIGTRAMP_FRAME, | |
| 2, | |
| { | |
| { THUMB2_SET_R7_RT_SIGRETURN1, ULONGEST_MAX }, | |
| { THUMB2_SET_R7_RT_SIGRETURN2, ULONGEST_MAX }, | |
| { THUMB2_EABI_SYSCALL, ULONGEST_MAX }, | |
| { TRAMP_SENTINEL_INSN } | |
| }, | |
| arm_linux_rt_sigreturn_init | |
| }; | |
| static struct tramp_frame arm_linux_restart_syscall_tramp_frame = { | |
| NORMAL_FRAME, | |
| 4, | |
| { | |
| { ARM_OABI_SYSCALL_RESTART_SYSCALL, ULONGEST_MAX }, | |
| { ARM_LDR_PC_SP_12, ULONGEST_MAX }, | |
| { TRAMP_SENTINEL_INSN } | |
| }, | |
| arm_linux_restart_syscall_init | |
| }; | |
| static struct tramp_frame arm_kernel_linux_restart_syscall_tramp_frame = { | |
| NORMAL_FRAME, | |
| 4, | |
| { | |
| { ARM_OABI_SYSCALL_RESTART_SYSCALL, ULONGEST_MAX }, | |
| { ARM_LDR_PC_SP_4, ULONGEST_MAX }, | |
| { TRAMP_SENTINEL_INSN } | |
| }, | |
| arm_linux_restart_syscall_init | |
| }; | |
| /* Core file and register set support. */ | |
| #define ARM_LINUX_SIZEOF_GREGSET (18 * ARM_INT_REGISTER_SIZE) | |
| void | |
| arm_linux_supply_gregset (const struct regset *regset, | |
| struct regcache *regcache, | |
| int regnum, const void *gregs_buf, size_t len) | |
| { | |
| struct gdbarch *gdbarch = regcache->arch (); | |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | |
| const gdb_byte *gregs = (const gdb_byte *) gregs_buf; | |
| int regno; | |
| CORE_ADDR reg_pc; | |
| gdb_byte pc_buf[ARM_INT_REGISTER_SIZE]; | |
| for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) | |
| if (regnum == -1 || regnum == regno) | |
| regcache->raw_supply (regno, gregs + ARM_INT_REGISTER_SIZE * regno); | |
| if (regnum == ARM_PS_REGNUM || regnum == -1) | |
| { | |
| if (arm_apcs_32) | |
| regcache->raw_supply (ARM_PS_REGNUM, | |
| gregs + ARM_INT_REGISTER_SIZE * ARM_CPSR_GREGNUM); | |
| else | |
| regcache->raw_supply (ARM_PS_REGNUM, | |
| gregs + ARM_INT_REGISTER_SIZE * ARM_PC_REGNUM); | |
| } | |
| if (regnum == ARM_PC_REGNUM || regnum == -1) | |
| { | |
| reg_pc = extract_unsigned_integer ( | |
| gregs + ARM_INT_REGISTER_SIZE * ARM_PC_REGNUM, | |
| ARM_INT_REGISTER_SIZE, byte_order); | |
| reg_pc = gdbarch_addr_bits_remove (gdbarch, reg_pc); | |
| store_unsigned_integer (pc_buf, ARM_INT_REGISTER_SIZE, byte_order, | |
| reg_pc); | |
| regcache->raw_supply (ARM_PC_REGNUM, pc_buf); | |
| } | |
| } | |
| void | |
| arm_linux_collect_gregset (const struct regset *regset, | |
| const struct regcache *regcache, | |
| int regnum, void *gregs_buf, size_t len) | |
| { | |
| gdb_byte *gregs = (gdb_byte *) gregs_buf; | |
| int regno; | |
| for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) | |
| if (regnum == -1 || regnum == regno) | |
| regcache->raw_collect (regno, | |
| gregs + ARM_INT_REGISTER_SIZE * regno); | |
| if (regnum == ARM_PS_REGNUM || regnum == -1) | |
| { | |
| if (arm_apcs_32) | |
| regcache->raw_collect (ARM_PS_REGNUM, | |
| gregs + ARM_INT_REGISTER_SIZE * ARM_CPSR_GREGNUM); | |
| else | |
| regcache->raw_collect (ARM_PS_REGNUM, | |
| gregs + ARM_INT_REGISTER_SIZE * ARM_PC_REGNUM); | |
| } | |
| if (regnum == ARM_PC_REGNUM || regnum == -1) | |
| regcache->raw_collect (ARM_PC_REGNUM, | |
| gregs + ARM_INT_REGISTER_SIZE * ARM_PC_REGNUM); | |
| } | |
| /* Support for register format used by the NWFPE FPA emulator. */ | |
| #define typeNone 0x00 | |
| #define typeSingle 0x01 | |
| #define typeDouble 0x02 | |
| #define typeExtended 0x03 | |
| void | |
| supply_nwfpe_register (struct regcache *regcache, int regno, | |
| const gdb_byte *regs) | |
| { | |
| const gdb_byte *reg_data; | |
| gdb_byte reg_tag; | |
| gdb_byte buf[ARM_FP_REGISTER_SIZE]; | |
| reg_data = regs + (regno - ARM_F0_REGNUM) * ARM_FP_REGISTER_SIZE; | |
| reg_tag = regs[(regno - ARM_F0_REGNUM) + NWFPE_TAGS_OFFSET]; | |
| memset (buf, 0, ARM_FP_REGISTER_SIZE); | |
| switch (reg_tag) | |
| { | |
| case typeSingle: | |
| memcpy (buf, reg_data, 4); | |
| break; | |
| case typeDouble: | |
| memcpy (buf, reg_data + 4, 4); | |
| memcpy (buf + 4, reg_data, 4); | |
| break; | |
| case typeExtended: | |
| /* We want sign and exponent, then least significant bits, | |
| then most significant. NWFPE does sign, most, least. */ | |
| memcpy (buf, reg_data, 4); | |
| memcpy (buf + 4, reg_data + 8, 4); | |
| memcpy (buf + 8, reg_data + 4, 4); | |
| break; | |
| default: | |
| break; | |
| } | |
| regcache->raw_supply (regno, buf); | |
| } | |
| void | |
| collect_nwfpe_register (const struct regcache *regcache, int regno, | |
| gdb_byte *regs) | |
| { | |
| gdb_byte *reg_data; | |
| gdb_byte reg_tag; | |
| gdb_byte buf[ARM_FP_REGISTER_SIZE]; | |
| regcache->raw_collect (regno, buf); | |
| /* NOTE drow/2006-06-07: This code uses the tag already in the | |
| register buffer. I've preserved that when moving the code | |
| from the native file to the target file. But this doesn't | |
| always make sense. */ | |
| reg_data = regs + (regno - ARM_F0_REGNUM) * ARM_FP_REGISTER_SIZE; | |
| reg_tag = regs[(regno - ARM_F0_REGNUM) + NWFPE_TAGS_OFFSET]; | |
| switch (reg_tag) | |
| { | |
| case typeSingle: | |
| memcpy (reg_data, buf, 4); | |
| break; | |
| case typeDouble: | |
| memcpy (reg_data, buf + 4, 4); | |
| memcpy (reg_data + 4, buf, 4); | |
| break; | |
| case typeExtended: | |
| memcpy (reg_data, buf, 4); | |
| memcpy (reg_data + 4, buf + 8, 4); | |
| memcpy (reg_data + 8, buf + 4, 4); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| void | |
| arm_linux_supply_nwfpe (const struct regset *regset, | |
| struct regcache *regcache, | |
| int regnum, const void *regs_buf, size_t len) | |
| { | |
| const gdb_byte *regs = (const gdb_byte *) regs_buf; | |
| int regno; | |
| if (regnum == ARM_FPS_REGNUM || regnum == -1) | |
| regcache->raw_supply (ARM_FPS_REGNUM, | |
| regs + NWFPE_FPSR_OFFSET); | |
| for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) | |
| if (regnum == -1 || regnum == regno) | |
| supply_nwfpe_register (regcache, regno, regs); | |
| } | |
| void | |
| arm_linux_collect_nwfpe (const struct regset *regset, | |
| const struct regcache *regcache, | |
| int regnum, void *regs_buf, size_t len) | |
| { | |
| gdb_byte *regs = (gdb_byte *) regs_buf; | |
| int regno; | |
| for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) | |
| if (regnum == -1 || regnum == regno) | |
| collect_nwfpe_register (regcache, regno, regs); | |
| if (regnum == ARM_FPS_REGNUM || regnum == -1) | |
| regcache->raw_collect (ARM_FPS_REGNUM, | |
| regs + ARM_INT_REGISTER_SIZE * ARM_FPS_REGNUM); | |
| } | |
| /* Support VFP register format. */ | |
| #define ARM_LINUX_SIZEOF_VFP (32 * 8 + 4) | |
| static void | |
| arm_linux_supply_vfp (const struct regset *regset, | |
| struct regcache *regcache, | |
| int regnum, const void *regs_buf, size_t len) | |
| { | |
| const gdb_byte *regs = (const gdb_byte *) regs_buf; | |
| int regno; | |
| if (regnum == ARM_FPSCR_REGNUM || regnum == -1) | |
| regcache->raw_supply (ARM_FPSCR_REGNUM, regs + 32 * 8); | |
| for (regno = ARM_D0_REGNUM; regno <= ARM_D31_REGNUM; regno++) | |
| if (regnum == -1 || regnum == regno) | |
| regcache->raw_supply (regno, regs + (regno - ARM_D0_REGNUM) * 8); | |
| } | |
| static void | |
| arm_linux_collect_vfp (const struct regset *regset, | |
| const struct regcache *regcache, | |
| int regnum, void *regs_buf, size_t len) | |
| { | |
| gdb_byte *regs = (gdb_byte *) regs_buf; | |
| int regno; | |
| if (regnum == ARM_FPSCR_REGNUM || regnum == -1) | |
| regcache->raw_collect (ARM_FPSCR_REGNUM, regs + 32 * 8); | |
| for (regno = ARM_D0_REGNUM; regno <= ARM_D31_REGNUM; regno++) | |
| if (regnum == -1 || regnum == regno) | |
| regcache->raw_collect (regno, regs + (regno - ARM_D0_REGNUM) * 8); | |
| } | |
| static const struct regset arm_linux_gregset = | |
| { | |
| NULL, arm_linux_supply_gregset, arm_linux_collect_gregset | |
| }; | |
| static const struct regset arm_linux_fpregset = | |
| { | |
| NULL, arm_linux_supply_nwfpe, arm_linux_collect_nwfpe | |
| }; | |
| static const struct regset arm_linux_vfpregset = | |
| { | |
| NULL, arm_linux_supply_vfp, arm_linux_collect_vfp | |
| }; | |
| /* Iterate over core file register note sections. */ | |
| static void | |
| arm_linux_iterate_over_regset_sections (struct gdbarch *gdbarch, | |
| iterate_over_regset_sections_cb *cb, | |
| void *cb_data, | |
| const struct regcache *regcache) | |
| { | |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); | |
| cb (".reg", ARM_LINUX_SIZEOF_GREGSET, ARM_LINUX_SIZEOF_GREGSET, | |
| &arm_linux_gregset, NULL, cb_data); | |
| if (tdep->vfp_register_count > 0) | |
| cb (".reg-arm-vfp", ARM_LINUX_SIZEOF_VFP, ARM_LINUX_SIZEOF_VFP, | |
| &arm_linux_vfpregset, "VFP floating-point", cb_data); | |
| else if (tdep->have_fpa_registers) | |
| cb (".reg2", ARM_LINUX_SIZEOF_NWFPE, ARM_LINUX_SIZEOF_NWFPE, | |
| &arm_linux_fpregset, "FPA floating-point", cb_data); | |
| } | |
| /* Determine target description from core file. */ | |
| static const struct target_desc * | |
| arm_linux_core_read_description (struct gdbarch *gdbarch, | |
| struct target_ops *target, | |
| bfd *abfd) | |
| { | |
| CORE_ADDR arm_hwcap = linux_get_hwcap (target); | |
| if (arm_hwcap & HWCAP_VFP) | |
| { | |
| /* NEON implies VFPv3-D32 or no-VFP unit. Say that we only support | |
| Neon with VFPv3-D32. */ | |
| if (arm_hwcap & HWCAP_NEON) | |
| return aarch32_read_description (); | |
| else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) | |
| return arm_read_description (ARM_FP_TYPE_VFPV3, false); | |
| return arm_read_description (ARM_FP_TYPE_VFPV2, false); | |
| } | |
| return nullptr; | |
| } | |
| /* Copy the value of next pc of sigreturn and rt_sigrturn into PC, | |
| return 1. In addition, set IS_THUMB depending on whether we | |
| will return to ARM or Thumb code. Return 0 if it is not a | |
| rt_sigreturn/sigreturn syscall. */ | |
| static int | |
| arm_linux_sigreturn_return_addr (struct frame_info *frame, | |
| unsigned long svc_number, | |
| CORE_ADDR *pc, int *is_thumb) | |
| { | |
| /* Is this a sigreturn or rt_sigreturn syscall? */ | |
| if (svc_number == 119 || svc_number == 173) | |
| { | |
| if (get_frame_type (frame) == SIGTRAMP_FRAME) | |
| { | |
| ULONGEST t_bit = arm_psr_thumb_bit (frame_unwind_arch (frame)); | |
| CORE_ADDR cpsr | |
| = frame_unwind_register_unsigned (frame, ARM_PS_REGNUM); | |
| *is_thumb = (cpsr & t_bit) != 0; | |
| *pc = frame_unwind_caller_pc (frame); | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| /* Find the value of the next PC after a sigreturn or rt_sigreturn syscall | |
| based on current processor state. In addition, set IS_THUMB depending | |
| on whether we will return to ARM or Thumb code. */ | |
| static CORE_ADDR | |
| arm_linux_sigreturn_next_pc (struct regcache *regcache, | |
| unsigned long svc_number, int *is_thumb) | |
| { | |
| ULONGEST sp; | |
| unsigned long sp_data; | |
| CORE_ADDR next_pc = 0; | |
| struct gdbarch *gdbarch = regcache->arch (); | |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | |
| int pc_offset = 0; | |
| int is_sigreturn = 0; | |
| CORE_ADDR cpsr; | |
| gdb_assert (svc_number == ARM_SIGRETURN | |
| || svc_number == ARM_RT_SIGRETURN); | |
| is_sigreturn = (svc_number == ARM_SIGRETURN); | |
| regcache_cooked_read_unsigned (regcache, ARM_SP_REGNUM, &sp); | |
| sp_data = read_memory_unsigned_integer (sp, 4, byte_order); | |
| pc_offset = arm_linux_sigreturn_next_pc_offset (sp, sp_data, svc_number, | |
| is_sigreturn); | |
| next_pc = read_memory_unsigned_integer (sp + pc_offset, 4, byte_order); | |
| /* Set IS_THUMB according the CPSR saved on the stack. */ | |
| cpsr = read_memory_unsigned_integer (sp + pc_offset + 4, 4, byte_order); | |
| *is_thumb = ((cpsr & arm_psr_thumb_bit (gdbarch)) != 0); | |
| return next_pc; | |
| } | |
| /* At a ptrace syscall-stop, return the syscall number. This either | |
| comes from the SWI instruction (OABI) or from r7 (EABI). | |
| When the function fails, it should return -1. */ | |
| static LONGEST | |
| arm_linux_get_syscall_number (struct gdbarch *gdbarch, | |
| thread_info *thread) | |
| { | |
| struct regcache *regs = get_thread_regcache (thread); | |
| ULONGEST pc; | |
| ULONGEST cpsr; | |
| ULONGEST t_bit = arm_psr_thumb_bit (gdbarch); | |
| int is_thumb; | |
| ULONGEST svc_number = -1; | |
| regcache_cooked_read_unsigned (regs, ARM_PC_REGNUM, &pc); | |
| regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &cpsr); | |
| is_thumb = (cpsr & t_bit) != 0; | |
| if (is_thumb) | |
| { | |
| regcache_cooked_read_unsigned (regs, 7, &svc_number); | |
| } | |
| else | |
| { | |
| enum bfd_endian byte_order_for_code = | |
| gdbarch_byte_order_for_code (gdbarch); | |
| /* PC gets incremented before the syscall-stop, so read the | |
| previous instruction. */ | |
| unsigned long this_instr = | |
| read_memory_unsigned_integer (pc - 4, 4, byte_order_for_code); | |
| unsigned long svc_operand = (0x00ffffff & this_instr); | |
| if (svc_operand) | |
| { | |
| /* OABI */ | |
| svc_number = svc_operand - 0x900000; | |
| } | |
| else | |
| { | |
| /* EABI */ | |
| regcache_cooked_read_unsigned (regs, 7, &svc_number); | |
| } | |
| } | |
| return svc_number; | |
| } | |
| static CORE_ADDR | |
| arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self) | |
| { | |
| CORE_ADDR next_pc = 0; | |
| CORE_ADDR pc = regcache_read_pc (self->regcache); | |
| int is_thumb = arm_is_thumb (self->regcache); | |
| ULONGEST svc_number = 0; | |
| if (is_thumb) | |
| { | |
| svc_number = regcache_raw_get_unsigned (self->regcache, 7); | |
| next_pc = pc + 2; | |
| } | |
| else | |
| { | |
| struct gdbarch *gdbarch = self->regcache->arch (); | |
| enum bfd_endian byte_order_for_code = | |
| gdbarch_byte_order_for_code (gdbarch); | |
| unsigned long this_instr = | |
| read_memory_unsigned_integer (pc, 4, byte_order_for_code); | |
| unsigned long svc_operand = (0x00ffffff & this_instr); | |
| if (svc_operand) /* OABI. */ | |
| { | |
| svc_number = svc_operand - 0x900000; | |
| } | |
| else /* EABI. */ | |
| { | |
| svc_number = regcache_raw_get_unsigned (self->regcache, 7); | |
| } | |
| next_pc = pc + 4; | |
| } | |
| if (svc_number == ARM_SIGRETURN || svc_number == ARM_RT_SIGRETURN) | |
| { | |
| /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so | |
| update IS_THUMB. */ | |
| next_pc = arm_linux_sigreturn_next_pc (self->regcache, svc_number, | |
| &is_thumb); | |
| } | |
| /* Addresses for calling Thumb functions have the bit 0 set. */ | |
| if (is_thumb) | |
| next_pc = MAKE_THUMB_ADDR (next_pc); | |
| return next_pc; | |
| } | |
| /* Insert a single step breakpoint at the next executed instruction. */ | |
| static std::vector<CORE_ADDR> | |
| arm_linux_software_single_step (struct regcache *regcache) | |
| { | |
| struct gdbarch *gdbarch = regcache->arch (); | |
| struct arm_get_next_pcs next_pcs_ctx; | |
| /* If the target does have hardware single step, GDB doesn't have | |
| to bother software single step. */ | |
| if (target_can_do_single_step () == 1) | |
| return {}; | |
| arm_get_next_pcs_ctor (&next_pcs_ctx, | |
| &arm_linux_get_next_pcs_ops, | |
| gdbarch_byte_order (gdbarch), | |
| gdbarch_byte_order_for_code (gdbarch), | |
| 1, | |
| regcache); | |
| std::vector<CORE_ADDR> next_pcs = arm_get_next_pcs (&next_pcs_ctx); | |
| for (CORE_ADDR &pc_ref : next_pcs) | |
| pc_ref = gdbarch_addr_bits_remove (gdbarch, pc_ref); | |
| return next_pcs; | |
| } | |
| /* Support for displaced stepping of Linux SVC instructions. */ | |
| static void | |
| arm_linux_cleanup_svc (struct gdbarch *gdbarch, | |
| struct regcache *regs, | |
| arm_displaced_step_copy_insn_closure *dsc) | |
| { | |
| ULONGEST apparent_pc; | |
| int within_scratch; | |
| regcache_cooked_read_unsigned (regs, ARM_PC_REGNUM, &apparent_pc); | |
| within_scratch = (apparent_pc >= dsc->scratch_base | |
| && apparent_pc < (dsc->scratch_base | |
| + ARM_DISPLACED_MODIFIED_INSNS * 4 + 4)); | |
| displaced_debug_printf ("PC is apparently %.8lx after SVC step %s", | |
| (unsigned long) apparent_pc, | |
| (within_scratch | |
| ? "(within scratch space)" | |
| : "(outside scratch space)")); | |
| if (within_scratch) | |
| displaced_write_reg (regs, dsc, ARM_PC_REGNUM, | |
| dsc->insn_addr + dsc->insn_size, BRANCH_WRITE_PC); | |
| } | |
| static int | |
| arm_linux_copy_svc (struct gdbarch *gdbarch, struct regcache *regs, | |
| arm_displaced_step_copy_insn_closure *dsc) | |
| { | |
| CORE_ADDR return_to = 0; | |
| struct frame_info *frame; | |
| unsigned int svc_number = displaced_read_reg (regs, dsc, 7); | |
| int is_sigreturn = 0; | |
| int is_thumb; | |
| frame = get_current_frame (); | |
| is_sigreturn = arm_linux_sigreturn_return_addr(frame, svc_number, | |
| &return_to, &is_thumb); | |
| if (is_sigreturn) | |
| { | |
| struct symtab_and_line sal; | |
| displaced_debug_printf ("found sigreturn/rt_sigreturn SVC call. " | |
| "PC in frame = %lx", | |
| (unsigned long) get_frame_pc (frame)); | |
| displaced_debug_printf ("unwind pc = %lx. Setting momentary breakpoint.", | |
| (unsigned long) return_to); | |
| gdb_assert (inferior_thread ()->control.step_resume_breakpoint | |
| == NULL); | |
| sal = find_pc_line (return_to, 0); | |
| sal.pc = return_to; | |
| sal.section = find_pc_overlay (return_to); | |
| sal.explicit_pc = 1; | |
| frame = get_prev_frame (frame); | |
| if (frame) | |
| { | |
| inferior_thread ()->control.step_resume_breakpoint | |
| = set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame), | |
| bp_step_resume).release (); | |
| /* set_momentary_breakpoint invalidates FRAME. */ | |
| frame = NULL; | |
| /* We need to make sure we actually insert the momentary | |
| breakpoint set above. */ | |
| insert_breakpoints (); | |
| } | |
| else | |
| displaced_debug_printf ("couldn't find previous frame to set momentary " | |
| "breakpoint for sigreturn/rt_sigreturn"); | |
| } | |
| else | |
| displaced_debug_printf ("found SVC call"); | |
| /* Preparation: If we detect sigreturn, set momentary breakpoint at resume | |
| location, else nothing. | |
| Insn: unmodified svc. | |
| Cleanup: if pc lands in scratch space, pc <- insn_addr + insn_size | |
| else leave pc alone. */ | |
| dsc->cleanup = &arm_linux_cleanup_svc; | |
| /* Pretend we wrote to the PC, so cleanup doesn't set PC to the next | |
| instruction. */ | |
| dsc->wrote_to_pc = 1; | |
| return 0; | |
| } | |
| /* The following two functions implement single-stepping over calls to Linux | |
| kernel helper routines, which perform e.g. atomic operations on architecture | |
| variants which don't support them natively. | |
| When this function is called, the PC will be pointing at the kernel helper | |
| (at an address inaccessible to GDB), and r14 will point to the return | |
| address. Displaced stepping always executes code in the copy area: | |
| so, make the copy-area instruction branch back to the kernel helper (the | |
| "from" address), and make r14 point to the breakpoint in the copy area. In | |
| that way, we regain control once the kernel helper returns, and can clean | |
| up appropriately (as if we had just returned from the kernel helper as it | |
| would have been called from the non-displaced location). */ | |
| static void | |
| cleanup_kernel_helper_return (struct gdbarch *gdbarch, | |
| struct regcache *regs, | |
| arm_displaced_step_copy_insn_closure *dsc) | |
| { | |
| displaced_write_reg (regs, dsc, ARM_LR_REGNUM, dsc->tmp[0], CANNOT_WRITE_PC); | |
| displaced_write_reg (regs, dsc, ARM_PC_REGNUM, dsc->tmp[0], BRANCH_WRITE_PC); | |
| } | |
| static void | |
| arm_catch_kernel_helper_return (struct gdbarch *gdbarch, CORE_ADDR from, | |
| CORE_ADDR to, struct regcache *regs, | |
| arm_displaced_step_copy_insn_closure *dsc) | |
| { | |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | |
| dsc->numinsns = 1; | |
| dsc->insn_addr = from; | |
| dsc->cleanup = &cleanup_kernel_helper_return; | |
| /* Say we wrote to the PC, else cleanup will set PC to the next | |
| instruction in the helper, which isn't helpful. */ | |
| dsc->wrote_to_pc = 1; | |
| /* Preparation: tmp[0] <- r14 | |
| r14 <- <scratch space>+4 | |
| *(<scratch space>+8) <- from | |
| Insn: ldr pc, [r14, #4] | |
| Cleanup: r14 <- tmp[0], pc <- tmp[0]. */ | |
| dsc->tmp[0] = displaced_read_reg (regs, dsc, ARM_LR_REGNUM); | |
| displaced_write_reg (regs, dsc, ARM_LR_REGNUM, (ULONGEST) to + 4, | |
| CANNOT_WRITE_PC); | |
| write_memory_unsigned_integer (to + 8, 4, byte_order, from); | |
| dsc->modinsn[0] = 0xe59ef004; /* ldr pc, [lr, #4]. */ | |
| } | |
| /* Linux-specific displaced step instruction copying function. Detects when | |
| the program has stepped into a Linux kernel helper routine (which must be | |
| handled as a special case). */ | |
| static displaced_step_copy_insn_closure_up | |
| arm_linux_displaced_step_copy_insn (struct gdbarch *gdbarch, | |
| CORE_ADDR from, CORE_ADDR to, | |
| struct regcache *regs) | |
| { | |
| std::unique_ptr<arm_displaced_step_copy_insn_closure> dsc | |
| (new arm_displaced_step_copy_insn_closure); | |
| /* Detect when we enter an (inaccessible by GDB) Linux kernel helper, and | |
| stop at the return location. */ | |
| if (from > 0xffff0000) | |
| { | |
| displaced_debug_printf ("detected kernel helper at %.8lx", | |
| (unsigned long) from); | |
| arm_catch_kernel_helper_return (gdbarch, from, to, regs, dsc.get ()); | |
| } | |
| else | |
| { | |
| /* Override the default handling of SVC instructions. */ | |
| dsc->u.svc.copy_svc_os = arm_linux_copy_svc; | |
| arm_process_displaced_insn (gdbarch, from, to, regs, dsc.get ()); | |
| } | |
| arm_displaced_init_closure (gdbarch, from, to, dsc.get ()); | |
| /* This is a work around for a problem with g++ 4.8. */ | |
| return displaced_step_copy_insn_closure_up (dsc.release ()); | |
| } | |
| /* Implementation of `gdbarch_stap_is_single_operand', as defined in | |
| gdbarch.h. */ | |
| static int | |
| arm_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) | |
| { | |
| return (*s == '#' || *s == '$' || isdigit (*s) /* Literal number. */ | |
| || *s == '[' /* Register indirection or | |
| displacement. */ | |
| || isalpha (*s)); /* Register value. */ | |
| } | |
| /* This routine is used to parse a special token in ARM's assembly. | |
| The special tokens parsed by it are: | |
| - Register displacement (e.g, [fp, #-8]) | |
| It returns one if the special token has been parsed successfully, | |
| or zero if the current token is not considered special. */ | |
| static expr::operation_up | |
| arm_stap_parse_special_token (struct gdbarch *gdbarch, | |
| struct stap_parse_info *p) | |
| { | |
| if (*p->arg == '[') | |
| { | |
| /* Temporary holder for lookahead. */ | |
| const char *tmp = p->arg; | |
| char *endp; | |
| /* Used to save the register name. */ | |
| const char *start; | |
| char *regname; | |
| int len, offset; | |
| int got_minus = 0; | |
| long displacement; | |
| ++tmp; | |
| start = tmp; | |
| /* Register name. */ | |
| while (isalnum (*tmp)) | |
| ++tmp; | |
| if (*tmp != ',') | |
| return {}; | |
| len = tmp - start; | |
| regname = (char *) alloca (len + 2); | |
| offset = 0; | |
| if (isdigit (*start)) | |
| { | |
| /* If we are dealing with a register whose name begins with a | |
| digit, it means we should prefix the name with the letter | |
| `r', because GDB expects this name pattern. Otherwise (e.g., | |
| we are dealing with the register `fp'), we don't need to | |
| add such a prefix. */ | |
| regname[0] = 'r'; | |
| offset = 1; | |
| } | |
| strncpy (regname + offset, start, len); | |
| len += offset; | |
| regname[len] = '\0'; | |
| if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1) | |
| error (_("Invalid register name `%s' on expression `%s'."), | |
| regname, p->saved_arg); | |
| ++tmp; | |
| tmp = skip_spaces (tmp); | |
| if (*tmp == '#' || *tmp == '$') | |
| ++tmp; | |
| if (*tmp == '-') | |
| { | |
| ++tmp; | |
| got_minus = 1; | |
| } | |
| displacement = strtol (tmp, &endp, 10); | |
| tmp = endp; | |
| /* Skipping last `]'. */ | |
| if (*tmp++ != ']') | |
| return {}; | |
| p->arg = tmp; | |
| using namespace expr; | |
| /* The displacement. */ | |
| struct type *long_type = builtin_type (gdbarch)->builtin_long; | |
| if (got_minus) | |
| displacement = -displacement; | |
| operation_up disp = make_operation<long_const_operation> (long_type, | |
| displacement); | |
| /* The register name. */ | |
| operation_up reg | |
| = make_operation<register_operation> (regname); | |
| operation_up sum | |
| = make_operation<add_operation> (std::move (reg), std::move (disp)); | |
| /* Casting to the expected type. */ | |
| struct type *arg_ptr_type = lookup_pointer_type (p->arg_type); | |
| sum = make_operation<unop_cast_operation> (std::move (sum), | |
| arg_ptr_type); | |
| return make_operation<unop_ind_operation> (std::move (sum)); | |
| } | |
| return {}; | |
| } | |
| /* ARM process record-replay constructs: syscall, signal etc. */ | |
| static linux_record_tdep arm_linux_record_tdep; | |
| /* arm_canonicalize_syscall maps from the native arm Linux set | |
| of syscall ids into a canonical set of syscall ids used by | |
| process record. */ | |
| static enum gdb_syscall | |
| arm_canonicalize_syscall (int syscall) | |
| { | |
| switch (syscall) | |
| { | |
| case 0: return gdb_sys_restart_syscall; | |
| case 1: return gdb_sys_exit; | |
| case 2: return gdb_sys_fork; | |
| case 3: return gdb_sys_read; | |
| case 4: return gdb_sys_write; | |
| case 5: return gdb_sys_open; | |
| case 6: return gdb_sys_close; | |
| case 8: return gdb_sys_creat; | |
| case 9: return gdb_sys_link; | |
| case 10: return gdb_sys_unlink; | |
| case 11: return gdb_sys_execve; | |
| case 12: return gdb_sys_chdir; | |
| case 13: return gdb_sys_time; | |
| case 14: return gdb_sys_mknod; | |
| case 15: return gdb_sys_chmod; | |
| case 16: return gdb_sys_lchown16; | |
| case 19: return gdb_sys_lseek; | |
| case 20: return gdb_sys_getpid; | |
| case 21: return gdb_sys_mount; | |
| case 22: return gdb_sys_oldumount; | |
| case 23: return gdb_sys_setuid16; | |
| case 24: return gdb_sys_getuid16; | |
| case 25: return gdb_sys_stime; | |
| case 26: return gdb_sys_ptrace; | |
| case 27: return gdb_sys_alarm; | |
| case 29: return gdb_sys_pause; | |
| case 30: return gdb_sys_utime; | |
| case 33: return gdb_sys_access; | |
| case 34: return gdb_sys_nice; | |
| case 36: return gdb_sys_sync; | |
| case 37: return gdb_sys_kill; | |
| case 38: return gdb_sys_rename; | |
| case 39: return gdb_sys_mkdir; | |
| case 40: return gdb_sys_rmdir; | |
| case 41: return gdb_sys_dup; | |
| case 42: return gdb_sys_pipe; | |
| case 43: return gdb_sys_times; | |
| case 45: return gdb_sys_brk; | |
| case 46: return gdb_sys_setgid16; | |
| case 47: return gdb_sys_getgid16; | |
| case 49: return gdb_sys_geteuid16; | |
| case 50: return gdb_sys_getegid16; | |
| case 51: return gdb_sys_acct; | |
| case 52: return gdb_sys_umount; | |
| case 54: return gdb_sys_ioctl; | |
| case 55: return gdb_sys_fcntl; | |
| case 57: return gdb_sys_setpgid; | |
| case 60: return gdb_sys_umask; | |
| case 61: return gdb_sys_chroot; | |
| case 62: return gdb_sys_ustat; | |
| case 63: return gdb_sys_dup2; | |
| case 64: return gdb_sys_getppid; | |
| case 65: return gdb_sys_getpgrp; | |
| case 66: return gdb_sys_setsid; | |
| case 67: return gdb_sys_sigaction; | |
| case 70: return gdb_sys_setreuid16; | |
| case 71: return gdb_sys_setregid16; | |
| case 72: return gdb_sys_sigsuspend; | |
| case 73: return gdb_sys_sigpending; | |
| case 74: return gdb_sys_sethostname; | |
| case 75: return gdb_sys_setrlimit; | |
| case 76: return gdb_sys_getrlimit; | |
| case 77: return gdb_sys_getrusage; | |
| case 78: return gdb_sys_gettimeofday; | |
| case 79: return gdb_sys_settimeofday; | |
| case 80: return gdb_sys_getgroups16; | |
| case 81: return gdb_sys_setgroups16; | |
| case 82: return gdb_sys_select; | |
| case 83: return gdb_sys_symlink; | |
| case 85: return gdb_sys_readlink; | |
| case 86: return gdb_sys_uselib; | |
| case 87: return gdb_sys_swapon; | |
| case 88: return gdb_sys_reboot; | |
| case 89: return gdb_old_readdir; | |
| case 90: return gdb_old_mmap; | |
| case 91: return gdb_sys_munmap; | |
| case 92: return gdb_sys_truncate; | |
| case 93: return gdb_sys_ftruncate; | |
| case 94: return gdb_sys_fchmod; | |
| case 95: return gdb_sys_fchown16; | |
| case 96: return gdb_sys_getpriority; | |
| case 97: return gdb_sys_setpriority; | |
| case 99: return gdb_sys_statfs; | |
| case 100: return gdb_sys_fstatfs; | |
| case 102: return gdb_sys_socketcall; | |
| case 103: return gdb_sys_syslog; | |
| case 104: return gdb_sys_setitimer; | |
| case 105: return gdb_sys_getitimer; | |
| case 106: return gdb_sys_stat; | |
| case 107: return gdb_sys_lstat; | |
| case 108: return gdb_sys_fstat; | |
| case 111: return gdb_sys_vhangup; | |
| case 113: /* sys_syscall */ | |
| return gdb_sys_no_syscall; | |
| case 114: return gdb_sys_wait4; | |
| case 115: return gdb_sys_swapoff; | |
| case 116: return gdb_sys_sysinfo; | |
| case 117: return gdb_sys_ipc; | |
| case 118: return gdb_sys_fsync; | |
| case 119: return gdb_sys_sigreturn; | |
| case 120: return gdb_sys_clone; | |
| case 121: return gdb_sys_setdomainname; | |
| case 122: return gdb_sys_uname; | |
| case 124: return gdb_sys_adjtimex; | |
| case 125: return gdb_sys_mprotect; | |
| case 126: return gdb_sys_sigprocmask; | |
| case 128: return gdb_sys_init_module; | |
| case 129: return gdb_sys_delete_module; | |
| case 131: return gdb_sys_quotactl; | |
| case 132: return gdb_sys_getpgid; | |
| case 133: return gdb_sys_fchdir; | |
| case 134: return gdb_sys_bdflush; | |
| case 135: return gdb_sys_sysfs; | |
| case 136: return gdb_sys_personality; | |
| case 138: return gdb_sys_setfsuid16; | |
| case 139: return gdb_sys_setfsgid16; | |
| case 140: return gdb_sys_llseek; | |
| case 141: return gdb_sys_getdents; | |
| case 142: return gdb_sys_select; | |
| case 143: return gdb_sys_flock; | |
| case 144: return gdb_sys_msync; | |
| case 145: return gdb_sys_readv; | |
| case 146: return gdb_sys_writev; | |
| case 147: return gdb_sys_getsid; | |
| case 148: return gdb_sys_fdatasync; | |
| case 149: return gdb_sys_sysctl; | |
| case 150: return gdb_sys_mlock; | |
| case 151: return gdb_sys_munlock; | |
| case 152: return gdb_sys_mlockall; | |
| case 153: return gdb_sys_munlockall; | |
| case 154: return gdb_sys_sched_setparam; | |
| case 155: return gdb_sys_sched_getparam; | |
| case 156: return gdb_sys_sched_setscheduler; | |
| case 157: return gdb_sys_sched_getscheduler; | |
| case 158: return gdb_sys_sched_yield; | |
| case 159: return gdb_sys_sched_get_priority_max; | |
| case 160: return gdb_sys_sched_get_priority_min; | |
| case 161: return gdb_sys_sched_rr_get_interval; | |
| case 162: return gdb_sys_nanosleep; | |
| case 163: return gdb_sys_mremap; | |
| case 164: return gdb_sys_setresuid16; | |
| case 165: return gdb_sys_getresuid16; | |
| case 168: return gdb_sys_poll; | |
| case 169: return gdb_sys_nfsservctl; | |
| case 170: return gdb_sys_setresgid; | |
| case 171: return gdb_sys_getresgid; | |
| case 172: return gdb_sys_prctl; | |
| case 173: return gdb_sys_rt_sigreturn; | |
| case 174: return gdb_sys_rt_sigaction; | |
| case 175: return gdb_sys_rt_sigprocmask; | |
| case 176: return gdb_sys_rt_sigpending; | |
| case 177: return gdb_sys_rt_sigtimedwait; | |
| case 178: return gdb_sys_rt_sigqueueinfo; | |
| case 179: return gdb_sys_rt_sigsuspend; | |
| case 180: return gdb_sys_pread64; | |
| case 181: return gdb_sys_pwrite64; | |
| case 182: return gdb_sys_chown; | |
| case 183: return gdb_sys_getcwd; | |
| case 184: return gdb_sys_capget; | |
| case 185: return gdb_sys_capset; | |
| case 186: return gdb_sys_sigaltstack; | |
| case 187: return gdb_sys_sendfile; | |
| case 190: return gdb_sys_vfork; | |
| case 191: return gdb_sys_getrlimit; | |
| case 192: return gdb_sys_mmap2; | |
| case 193: return gdb_sys_truncate64; | |
| case 194: return gdb_sys_ftruncate64; | |
| case 195: return gdb_sys_stat64; | |
| case 196: return gdb_sys_lstat64; | |
| case 197: return gdb_sys_fstat64; | |
| case 198: return gdb_sys_lchown; | |
| case 199: return gdb_sys_getuid; | |
| case 200: return gdb_sys_getgid; | |
| case 201: return gdb_sys_geteuid; | |
| case 202: return gdb_sys_getegid; | |
| case 203: return gdb_sys_setreuid; | |
| case 204: return gdb_sys_setregid; | |
| case 205: return gdb_sys_getgroups; | |
| case 206: return gdb_sys_setgroups; | |
| case 207: return gdb_sys_fchown; | |
| case 208: return gdb_sys_setresuid; | |
| case 209: return gdb_sys_getresuid; | |
| case 210: return gdb_sys_setresgid; | |
| case 211: return gdb_sys_getresgid; | |
| case 212: return gdb_sys_chown; | |
| case 213: return gdb_sys_setuid; | |
| case 214: return gdb_sys_setgid; | |
| case 215: return gdb_sys_setfsuid; | |
| case 216: return gdb_sys_setfsgid; | |
| case 217: return gdb_sys_getdents64; | |
| case 218: return gdb_sys_pivot_root; | |
| case 219: return gdb_sys_mincore; | |
| case 220: return gdb_sys_madvise; | |
| case 221: return gdb_sys_fcntl64; | |
| case 224: return gdb_sys_gettid; | |
| case 225: return gdb_sys_readahead; | |
| case 226: return gdb_sys_setxattr; | |
| case 227: return gdb_sys_lsetxattr; | |
| case 228: return gdb_sys_fsetxattr; | |
| case 229: return gdb_sys_getxattr; | |
| case 230: return gdb_sys_lgetxattr; | |
| case 231: return gdb_sys_fgetxattr; | |
| case 232: return gdb_sys_listxattr; | |
| case 233: return gdb_sys_llistxattr; | |
| case 234: return gdb_sys_flistxattr; | |
| case 235: return gdb_sys_removexattr; | |
| case 236: return gdb_sys_lremovexattr; | |
| case 237: return gdb_sys_fremovexattr; | |
| case 238: return gdb_sys_tkill; | |
| case 239: return gdb_sys_sendfile64; | |
| case 240: return gdb_sys_futex; | |
| case 241: return gdb_sys_sched_setaffinity; | |
| case 242: return gdb_sys_sched_getaffinity; | |
| case 243: return gdb_sys_io_setup; | |
| case 244: return gdb_sys_io_destroy; | |
| case 245: return gdb_sys_io_getevents; | |
| case 246: return gdb_sys_io_submit; | |
| case 247: return gdb_sys_io_cancel; | |
| case 248: return gdb_sys_exit_group; | |
| case 249: return gdb_sys_lookup_dcookie; | |
| case 250: return gdb_sys_epoll_create; | |
| case 251: return gdb_sys_epoll_ctl; | |
| case 252: return gdb_sys_epoll_wait; | |
| case 253: return gdb_sys_remap_file_pages; | |
| case 256: return gdb_sys_set_tid_address; | |
| case 257: return gdb_sys_timer_create; | |
| case 258: return gdb_sys_timer_settime; | |
| case 259: return gdb_sys_timer_gettime; | |
| case 260: return gdb_sys_timer_getoverrun; | |
| case 261: return gdb_sys_timer_delete; | |
| case 262: return gdb_sys_clock_settime; | |
| case 263: return gdb_sys_clock_gettime; | |
| case 264: return gdb_sys_clock_getres; | |
| case 265: return gdb_sys_clock_nanosleep; | |
| case 266: return gdb_sys_statfs64; | |
| case 267: return gdb_sys_fstatfs64; | |
| case 268: return gdb_sys_tgkill; | |
| case 269: return gdb_sys_utimes; | |
| /* | |
| case 270: return gdb_sys_arm_fadvise64_64; | |
| case 271: return gdb_sys_pciconfig_iobase; | |
| case 272: return gdb_sys_pciconfig_read; | |
| case 273: return gdb_sys_pciconfig_write; | |
| */ | |
| case 274: return gdb_sys_mq_open; | |
| case 275: return gdb_sys_mq_unlink; | |
| case 276: return gdb_sys_mq_timedsend; | |
| case 277: return gdb_sys_mq_timedreceive; | |
| case 278: return gdb_sys_mq_notify; | |
| case 279: return gdb_sys_mq_getsetattr; | |
| case 280: return gdb_sys_waitid; | |
| case 281: return gdb_sys_socket; | |
| case 282: return gdb_sys_bind; | |
| case 283: return gdb_sys_connect; | |
| case 284: return gdb_sys_listen; | |
| case 285: return gdb_sys_accept; | |
| case 286: return gdb_sys_getsockname; | |
| case 287: return gdb_sys_getpeername; | |
| case 288: return gdb_sys_socketpair; | |
| case 289: /* send */ return gdb_sys_no_syscall; | |
| case 290: return gdb_sys_sendto; | |
| case 291: return gdb_sys_recv; | |
| case 292: return gdb_sys_recvfrom; | |
| case 293: return gdb_sys_shutdown; | |
| case 294: return gdb_sys_setsockopt; | |
| case 295: return gdb_sys_getsockopt; | |
| case 296: return gdb_sys_sendmsg; | |
| case 297: return gdb_sys_recvmsg; | |
| case 298: return gdb_sys_semop; | |
| case 299: return gdb_sys_semget; | |
| case 300: return gdb_sys_semctl; | |
| case 301: return gdb_sys_msgsnd; | |
| case 302: return gdb_sys_msgrcv; | |
| case 303: return gdb_sys_msgget; | |
| case 304: return gdb_sys_msgctl; | |
| case 305: return gdb_sys_shmat; | |
| case 306: return gdb_sys_shmdt; | |
| case 307: return gdb_sys_shmget; | |
| case 308: return gdb_sys_shmctl; | |
| case 309: return gdb_sys_add_key; | |
| case 310: return gdb_sys_request_key; | |
| case 311: return gdb_sys_keyctl; | |
| case 312: return gdb_sys_semtimedop; | |
| case 313: /* vserver */ return gdb_sys_no_syscall; | |
| case 314: return gdb_sys_ioprio_set; | |
| case 315: return gdb_sys_ioprio_get; | |
| case 316: return gdb_sys_inotify_init; | |
| case 317: return gdb_sys_inotify_add_watch; | |
| case 318: return gdb_sys_inotify_rm_watch; | |
| case 319: return gdb_sys_mbind; | |
| case 320: return gdb_sys_get_mempolicy; | |
| case 321: return gdb_sys_set_mempolicy; | |
| case 322: return gdb_sys_openat; | |
| case 323: return gdb_sys_mkdirat; | |
| case 324: return gdb_sys_mknodat; | |
| case 325: return gdb_sys_fchownat; | |
| case 326: return gdb_sys_futimesat; | |
| case 327: return gdb_sys_fstatat64; | |
| case 328: return gdb_sys_unlinkat; | |
| case 329: return gdb_sys_renameat; | |
| case 330: return gdb_sys_linkat; | |
| case 331: return gdb_sys_symlinkat; | |
| case 332: return gdb_sys_readlinkat; | |
| case 333: return gdb_sys_fchmodat; | |
| case 334: return gdb_sys_faccessat; | |
| case 335: return gdb_sys_pselect6; | |
| case 336: return gdb_sys_ppoll; | |
| case 337: return gdb_sys_unshare; | |
| case 338: return gdb_sys_set_robust_list; | |
| case 339: return gdb_sys_get_robust_list; | |
| case 340: return gdb_sys_splice; | |
| /*case 341: return gdb_sys_arm_sync_file_range;*/ | |
| case 342: return gdb_sys_tee; | |
| case 343: return gdb_sys_vmsplice; | |
| case 344: return gdb_sys_move_pages; | |
| case 345: return gdb_sys_getcpu; | |
| case 346: return gdb_sys_epoll_pwait; | |
| case 347: return gdb_sys_kexec_load; | |
| /* | |
| case 348: return gdb_sys_utimensat; | |
| case 349: return gdb_sys_signalfd; | |
| case 350: return gdb_sys_timerfd_create; | |
| case 351: return gdb_sys_eventfd; | |
| */ | |
| case 352: return gdb_sys_fallocate; | |
| /* | |
| case 353: return gdb_sys_timerfd_settime; | |
| case 354: return gdb_sys_timerfd_gettime; | |
| case 355: return gdb_sys_signalfd4; | |
| */ | |
| case 356: return gdb_sys_eventfd2; | |
| case 357: return gdb_sys_epoll_create1; | |
| case 358: return gdb_sys_dup3; | |
| case 359: return gdb_sys_pipe2; | |
| case 360: return gdb_sys_inotify_init1; | |
| /* | |
| case 361: return gdb_sys_preadv; | |
| case 362: return gdb_sys_pwritev; | |
| case 363: return gdb_sys_rt_tgsigqueueinfo; | |
| case 364: return gdb_sys_perf_event_open; | |
| case 365: return gdb_sys_recvmmsg; | |
| case 366: return gdb_sys_accept4; | |
| case 367: return gdb_sys_fanotify_init; | |
| case 368: return gdb_sys_fanotify_mark; | |
| case 369: return gdb_sys_prlimit64; | |
| case 370: return gdb_sys_name_to_handle_at; | |
| case 371: return gdb_sys_open_by_handle_at; | |
| case 372: return gdb_sys_clock_adjtime; | |
| case 373: return gdb_sys_syncfs; | |
| case 374: return gdb_sys_sendmmsg; | |
| case 375: return gdb_sys_setns; | |
| case 376: return gdb_sys_process_vm_readv; | |
| case 377: return gdb_sys_process_vm_writev; | |
| case 378: return gdb_sys_kcmp; | |
| case 379: return gdb_sys_finit_module; | |
| */ | |
| case 384: return gdb_sys_getrandom; | |
| case 983041: /* ARM_breakpoint */ return gdb_sys_no_syscall; | |
| case 983042: /* ARM_cacheflush */ return gdb_sys_no_syscall; | |
| case 983043: /* ARM_usr26 */ return gdb_sys_no_syscall; | |
| case 983044: /* ARM_usr32 */ return gdb_sys_no_syscall; | |
| case 983045: /* ARM_set_tls */ return gdb_sys_no_syscall; | |
| default: return gdb_sys_no_syscall; | |
| } | |
| } | |
| /* Record all registers but PC register for process-record. */ | |
| static int | |
| arm_all_but_pc_registers_record (struct regcache *regcache) | |
| { | |
| int i; | |
| for (i = 0; i < ARM_PC_REGNUM; i++) | |
| { | |
| if (record_full_arch_list_add_reg (regcache, ARM_A1_REGNUM + i)) | |
| return -1; | |
| } | |
| if (record_full_arch_list_add_reg (regcache, ARM_PS_REGNUM)) | |
| return -1; | |
| return 0; | |
| } | |
| /* Handler for arm system call instruction recording. */ | |
| static int | |
| arm_linux_syscall_record (struct regcache *regcache, unsigned long svc_number) | |
| { | |
| int ret = 0; | |
| enum gdb_syscall syscall_gdb; | |
| syscall_gdb = arm_canonicalize_syscall (svc_number); | |
| if (syscall_gdb == gdb_sys_no_syscall) | |
| { | |
| gdb_printf (gdb_stderr, | |
| _("Process record and replay target doesn't " | |
| "support syscall number %s\n"), | |
| plongest (svc_number)); | |
| return -1; | |
| } | |
| if (syscall_gdb == gdb_sys_sigreturn | |
| || syscall_gdb == gdb_sys_rt_sigreturn) | |
| { | |
| if (arm_all_but_pc_registers_record (regcache)) | |
| return -1; | |
| return 0; | |
| } | |
| ret = record_linux_system_call (syscall_gdb, regcache, | |
| &arm_linux_record_tdep); | |
| if (ret != 0) | |
| return ret; | |
| /* Record the return value of the system call. */ | |
| if (record_full_arch_list_add_reg (regcache, ARM_A1_REGNUM)) | |
| return -1; | |
| /* Record LR. */ | |
| if (record_full_arch_list_add_reg (regcache, ARM_LR_REGNUM)) | |
| return -1; | |
| /* Record CPSR. */ | |
| if (record_full_arch_list_add_reg (regcache, ARM_PS_REGNUM)) | |
| return -1; | |
| return 0; | |
| } | |
| /* Implement the skip_trampoline_code gdbarch method. */ | |
| static CORE_ADDR | |
| arm_linux_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc) | |
| { | |
| CORE_ADDR target_pc = arm_skip_stub (frame, pc); | |
| if (target_pc != 0) | |
| return target_pc; | |
| return find_solib_trampoline_target (frame, pc); | |
| } | |
| /* Implement the gcc_target_options gdbarch method. */ | |
| static std::string | |
| arm_linux_gcc_target_options (struct gdbarch *gdbarch) | |
| { | |
| /* GCC doesn't know "-m32". */ | |
| return {}; | |
| } | |
| static void | |
| arm_linux_init_abi (struct gdbarch_info info, | |
| struct gdbarch *gdbarch) | |
| { | |
| static const char *const stap_integer_prefixes[] = { "#", "$", "", NULL }; | |
| static const char *const stap_register_prefixes[] = { "r", NULL }; | |
| static const char *const stap_register_indirection_prefixes[] = { "[", | |
| NULL }; | |
| static const char *const stap_register_indirection_suffixes[] = { "]", | |
| NULL }; | |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); | |
| linux_init_abi (info, gdbarch, 1); | |
| tdep->lowest_pc = 0x8000; | |
| if (info.byte_order_for_code == BFD_ENDIAN_BIG) | |
| { | |
| if (tdep->arm_abi == ARM_ABI_AAPCS) | |
| tdep->arm_breakpoint = eabi_linux_arm_be_breakpoint; | |
| else | |
| tdep->arm_breakpoint = arm_linux_arm_be_breakpoint; | |
| tdep->thumb_breakpoint = arm_linux_thumb_be_breakpoint; | |
| tdep->thumb2_breakpoint = arm_linux_thumb2_be_breakpoint; | |
| } | |
| else | |
| { | |
| if (tdep->arm_abi == ARM_ABI_AAPCS) | |
| tdep->arm_breakpoint = eabi_linux_arm_le_breakpoint; | |
| else | |
| tdep->arm_breakpoint = arm_linux_arm_le_breakpoint; | |
| tdep->thumb_breakpoint = arm_linux_thumb_le_breakpoint; | |
| tdep->thumb2_breakpoint = arm_linux_thumb2_le_breakpoint; | |
| } | |
| tdep->arm_breakpoint_size = sizeof (arm_linux_arm_le_breakpoint); | |
| tdep->thumb_breakpoint_size = sizeof (arm_linux_thumb_le_breakpoint); | |
| tdep->thumb2_breakpoint_size = sizeof (arm_linux_thumb2_le_breakpoint); | |
| if (tdep->fp_model == ARM_FLOAT_AUTO) | |
| tdep->fp_model = ARM_FLOAT_FPA; | |
| switch (tdep->fp_model) | |
| { | |
| case ARM_FLOAT_FPA: | |
| tdep->jb_pc = ARM_LINUX_JB_PC_FPA; | |
| break; | |
| case ARM_FLOAT_SOFT_FPA: | |
| case ARM_FLOAT_SOFT_VFP: | |
| case ARM_FLOAT_VFP: | |
| tdep->jb_pc = ARM_LINUX_JB_PC_EABI; | |
| break; | |
| default: | |
| internal_error | |
| (__FILE__, __LINE__, | |
| _("arm_linux_init_abi: Floating point model not supported")); | |
| break; | |
| } | |
| tdep->jb_elt_size = ARM_LINUX_JB_ELEMENT_SIZE; | |
| set_solib_svr4_fetch_link_map_offsets | |
| (gdbarch, linux_ilp32_fetch_link_map_offsets); | |
| /* Single stepping. */ | |
| set_gdbarch_software_single_step (gdbarch, arm_linux_software_single_step); | |
| /* Shared library handling. */ | |
| set_gdbarch_skip_trampoline_code (gdbarch, arm_linux_skip_trampoline_code); | |
| set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); | |
| /* Enable TLS support. */ | |
| set_gdbarch_fetch_tls_load_module_address (gdbarch, | |
| svr4_fetch_objfile_link_map); | |
| tramp_frame_prepend_unwinder (gdbarch, | |
| &arm_linux_sigreturn_tramp_frame); | |
| tramp_frame_prepend_unwinder (gdbarch, | |
| &arm_linux_rt_sigreturn_tramp_frame); | |
| tramp_frame_prepend_unwinder (gdbarch, | |
| &arm_eabi_linux_sigreturn_tramp_frame); | |
| tramp_frame_prepend_unwinder (gdbarch, | |
| &arm_eabi_linux_rt_sigreturn_tramp_frame); | |
| tramp_frame_prepend_unwinder (gdbarch, | |
| &thumb2_eabi_linux_sigreturn_tramp_frame); | |
| tramp_frame_prepend_unwinder (gdbarch, | |
| &thumb2_eabi_linux_rt_sigreturn_tramp_frame); | |
| tramp_frame_prepend_unwinder (gdbarch, | |
| &arm_linux_restart_syscall_tramp_frame); | |
| tramp_frame_prepend_unwinder (gdbarch, | |
| &arm_kernel_linux_restart_syscall_tramp_frame); | |
| /* Core file support. */ | |
| set_gdbarch_iterate_over_regset_sections | |
| (gdbarch, arm_linux_iterate_over_regset_sections); | |
| set_gdbarch_core_read_description (gdbarch, arm_linux_core_read_description); | |
| /* Displaced stepping. */ | |
| set_gdbarch_displaced_step_copy_insn (gdbarch, | |
| arm_linux_displaced_step_copy_insn); | |
| set_gdbarch_displaced_step_fixup (gdbarch, arm_displaced_step_fixup); | |
| /* Reversible debugging, process record. */ | |
| set_gdbarch_process_record (gdbarch, arm_process_record); | |
| /* SystemTap functions. */ | |
| set_gdbarch_stap_integer_prefixes (gdbarch, stap_integer_prefixes); | |
| set_gdbarch_stap_register_prefixes (gdbarch, stap_register_prefixes); | |
| set_gdbarch_stap_register_indirection_prefixes (gdbarch, | |
| stap_register_indirection_prefixes); | |
| set_gdbarch_stap_register_indirection_suffixes (gdbarch, | |
| stap_register_indirection_suffixes); | |
| set_gdbarch_stap_gdb_register_prefix (gdbarch, "r"); | |
| set_gdbarch_stap_is_single_operand (gdbarch, arm_stap_is_single_operand); | |
| set_gdbarch_stap_parse_special_token (gdbarch, | |
| arm_stap_parse_special_token); | |
| /* `catch syscall' */ | |
| set_xml_syscall_file_name (gdbarch, "syscalls/arm-linux.xml"); | |
| set_gdbarch_get_syscall_number (gdbarch, arm_linux_get_syscall_number); | |
| /* Syscall record. */ | |
| tdep->arm_syscall_record = arm_linux_syscall_record; | |
| /* Initialize the arm_linux_record_tdep. */ | |
| /* These values are the size of the type that will be used in a system | |
| call. They are obtained from Linux Kernel source. */ | |
| arm_linux_record_tdep.size_pointer | |
| = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT; | |
| arm_linux_record_tdep.size__old_kernel_stat = 32; | |
| arm_linux_record_tdep.size_tms = 16; | |
| arm_linux_record_tdep.size_loff_t = 8; | |
| arm_linux_record_tdep.size_flock = 16; | |
| arm_linux_record_tdep.size_oldold_utsname = 45; | |
| arm_linux_record_tdep.size_ustat = 20; | |
| arm_linux_record_tdep.size_old_sigaction = 16; | |
| arm_linux_record_tdep.size_old_sigset_t = 4; | |
| arm_linux_record_tdep.size_rlimit = 8; | |
| arm_linux_record_tdep.size_rusage = 72; | |
| arm_linux_record_tdep.size_timeval = 8; | |
| arm_linux_record_tdep.size_timezone = 8; | |
| arm_linux_record_tdep.size_old_gid_t = 2; | |
| arm_linux_record_tdep.size_old_uid_t = 2; | |
| arm_linux_record_tdep.size_fd_set = 128; | |
| arm_linux_record_tdep.size_old_dirent = 268; | |
| arm_linux_record_tdep.size_statfs = 64; | |
| arm_linux_record_tdep.size_statfs64 = 84; | |
| arm_linux_record_tdep.size_sockaddr = 16; | |
| arm_linux_record_tdep.size_int | |
| = gdbarch_int_bit (gdbarch) / TARGET_CHAR_BIT; | |
| arm_linux_record_tdep.size_long | |
| = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT; | |
| arm_linux_record_tdep.size_ulong | |
| = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT; | |
| arm_linux_record_tdep.size_msghdr = 28; | |
| arm_linux_record_tdep.size_itimerval = 16; | |
| arm_linux_record_tdep.size_stat = 88; | |
| arm_linux_record_tdep.size_old_utsname = 325; | |
| arm_linux_record_tdep.size_sysinfo = 64; | |
| arm_linux_record_tdep.size_msqid_ds = 88; | |
| arm_linux_record_tdep.size_shmid_ds = 84; | |
| arm_linux_record_tdep.size_new_utsname = 390; | |
| arm_linux_record_tdep.size_timex = 128; | |
| arm_linux_record_tdep.size_mem_dqinfo = 24; | |
| arm_linux_record_tdep.size_if_dqblk = 68; | |
| arm_linux_record_tdep.size_fs_quota_stat = 68; | |
| arm_linux_record_tdep.size_timespec = 8; | |
| arm_linux_record_tdep.size_pollfd = 8; | |
| arm_linux_record_tdep.size_NFS_FHSIZE = 32; | |
| arm_linux_record_tdep.size_knfsd_fh = 132; | |
| arm_linux_record_tdep.size_TASK_COMM_LEN = 16; | |
| arm_linux_record_tdep.size_sigaction = 20; | |
| arm_linux_record_tdep.size_sigset_t = 8; | |
| arm_linux_record_tdep.size_siginfo_t = 128; | |
| arm_linux_record_tdep.size_cap_user_data_t = 12; | |
| arm_linux_record_tdep.size_stack_t = 12; | |
| arm_linux_record_tdep.size_off_t = arm_linux_record_tdep.size_long; | |
| arm_linux_record_tdep.size_stat64 = 96; | |
| arm_linux_record_tdep.size_gid_t = 4; | |
| arm_linux_record_tdep.size_uid_t = 4; | |
| arm_linux_record_tdep.size_PAGE_SIZE = 4096; | |
| arm_linux_record_tdep.size_flock64 = 24; | |
| arm_linux_record_tdep.size_user_desc = 16; | |
| arm_linux_record_tdep.size_io_event = 32; | |
| arm_linux_record_tdep.size_iocb = 64; | |
| arm_linux_record_tdep.size_epoll_event = 12; | |
| arm_linux_record_tdep.size_itimerspec | |
| = arm_linux_record_tdep.size_timespec * 2; | |
| arm_linux_record_tdep.size_mq_attr = 32; | |
| arm_linux_record_tdep.size_termios = 36; | |
| arm_linux_record_tdep.size_termios2 = 44; | |
| arm_linux_record_tdep.size_pid_t = 4; | |
| arm_linux_record_tdep.size_winsize = 8; | |
| arm_linux_record_tdep.size_serial_struct = 60; | |
| arm_linux_record_tdep.size_serial_icounter_struct = 80; | |
| arm_linux_record_tdep.size_hayes_esp_config = 12; | |
| arm_linux_record_tdep.size_size_t = 4; | |
| arm_linux_record_tdep.size_iovec = 8; | |
| arm_linux_record_tdep.size_time_t = 4; | |
| /* These values are the second argument of system call "sys_ioctl". | |
| They are obtained from Linux Kernel source. */ | |
| arm_linux_record_tdep.ioctl_TCGETS = 0x5401; | |
| arm_linux_record_tdep.ioctl_TCSETS = 0x5402; | |
| arm_linux_record_tdep.ioctl_TCSETSW = 0x5403; | |
| arm_linux_record_tdep.ioctl_TCSETSF = 0x5404; | |
| arm_linux_record_tdep.ioctl_TCGETA = 0x5405; | |
| arm_linux_record_tdep.ioctl_TCSETA = 0x5406; | |
| arm_linux_record_tdep.ioctl_TCSETAW = 0x5407; | |
| arm_linux_record_tdep.ioctl_TCSETAF = 0x5408; | |
| arm_linux_record_tdep.ioctl_TCSBRK = 0x5409; | |
| arm_linux_record_tdep.ioctl_TCXONC = 0x540a; | |
| arm_linux_record_tdep.ioctl_TCFLSH = 0x540b; | |
| arm_linux_record_tdep.ioctl_TIOCEXCL = 0x540c; | |
| arm_linux_record_tdep.ioctl_TIOCNXCL = 0x540d; | |
| arm_linux_record_tdep.ioctl_TIOCSCTTY = 0x540e; | |
| arm_linux_record_tdep.ioctl_TIOCGPGRP = 0x540f; | |
| arm_linux_record_tdep.ioctl_TIOCSPGRP = 0x5410; | |
| arm_linux_record_tdep.ioctl_TIOCOUTQ = 0x5411; | |
| arm_linux_record_tdep.ioctl_TIOCSTI = 0x5412; | |
| arm_linux_record_tdep.ioctl_TIOCGWINSZ = 0x5413; | |
| arm_linux_record_tdep.ioctl_TIOCSWINSZ = 0x5414; | |
| arm_linux_record_tdep.ioctl_TIOCMGET = 0x5415; | |
| arm_linux_record_tdep.ioctl_TIOCMBIS = 0x5416; | |
| arm_linux_record_tdep.ioctl_TIOCMBIC = 0x5417; | |
| arm_linux_record_tdep.ioctl_TIOCMSET = 0x5418; | |
| arm_linux_record_tdep.ioctl_TIOCGSOFTCAR = 0x5419; | |
| arm_linux_record_tdep.ioctl_TIOCSSOFTCAR = 0x541a; | |
| arm_linux_record_tdep.ioctl_FIONREAD = 0x541b; | |
| arm_linux_record_tdep.ioctl_TIOCINQ = arm_linux_record_tdep.ioctl_FIONREAD; | |
| arm_linux_record_tdep.ioctl_TIOCLINUX = 0x541c; | |
| arm_linux_record_tdep.ioctl_TIOCCONS = 0x541d; | |
| arm_linux_record_tdep.ioctl_TIOCGSERIAL = 0x541e; | |
| arm_linux_record_tdep.ioctl_TIOCSSERIAL = 0x541f; | |
| arm_linux_record_tdep.ioctl_TIOCPKT = 0x5420; | |
| arm_linux_record_tdep.ioctl_FIONBIO = 0x5421; | |
| arm_linux_record_tdep.ioctl_TIOCNOTTY = 0x5422; | |
| arm_linux_record_tdep.ioctl_TIOCSETD = 0x5423; | |
| arm_linux_record_tdep.ioctl_TIOCGETD = 0x5424; | |
| arm_linux_record_tdep.ioctl_TCSBRKP = 0x5425; | |
| arm_linux_record_tdep.ioctl_TIOCTTYGSTRUCT = 0x5426; | |
| arm_linux_record_tdep.ioctl_TIOCSBRK = 0x5427; | |
| arm_linux_record_tdep.ioctl_TIOCCBRK = 0x5428; | |
| arm_linux_record_tdep.ioctl_TIOCGSID = 0x5429; | |
| arm_linux_record_tdep.ioctl_TCGETS2 = 0x802c542a; | |
| arm_linux_record_tdep.ioctl_TCSETS2 = 0x402c542b; | |
| arm_linux_record_tdep.ioctl_TCSETSW2 = 0x402c542c; | |
| arm_linux_record_tdep.ioctl_TCSETSF2 = 0x402c542d; | |
| arm_linux_record_tdep.ioctl_TIOCGPTN = 0x80045430; | |
| arm_linux_record_tdep.ioctl_TIOCSPTLCK = 0x40045431; | |
| arm_linux_record_tdep.ioctl_FIONCLEX = 0x5450; | |
| arm_linux_record_tdep.ioctl_FIOCLEX = 0x5451; | |
| arm_linux_record_tdep.ioctl_FIOASYNC = 0x5452; | |
| arm_linux_record_tdep.ioctl_TIOCSERCONFIG = 0x5453; | |
| arm_linux_record_tdep.ioctl_TIOCSERGWILD = 0x5454; | |
| arm_linux_record_tdep.ioctl_TIOCSERSWILD = 0x5455; | |
| arm_linux_record_tdep.ioctl_TIOCGLCKTRMIOS = 0x5456; | |
| arm_linux_record_tdep.ioctl_TIOCSLCKTRMIOS = 0x5457; | |
| arm_linux_record_tdep.ioctl_TIOCSERGSTRUCT = 0x5458; | |
| arm_linux_record_tdep.ioctl_TIOCSERGETLSR = 0x5459; | |
| arm_linux_record_tdep.ioctl_TIOCSERGETMULTI = 0x545a; | |
| arm_linux_record_tdep.ioctl_TIOCSERSETMULTI = 0x545b; | |
| arm_linux_record_tdep.ioctl_TIOCMIWAIT = 0x545c; | |
| arm_linux_record_tdep.ioctl_TIOCGICOUNT = 0x545d; | |
| arm_linux_record_tdep.ioctl_TIOCGHAYESESP = 0x545e; | |
| arm_linux_record_tdep.ioctl_TIOCSHAYESESP = 0x545f; | |
| arm_linux_record_tdep.ioctl_FIOQSIZE = 0x5460; | |
| /* These values are the second argument of system call "sys_fcntl" | |
| and "sys_fcntl64". They are obtained from Linux Kernel source. */ | |
| arm_linux_record_tdep.fcntl_F_GETLK = 5; | |
| arm_linux_record_tdep.fcntl_F_GETLK64 = 12; | |
| arm_linux_record_tdep.fcntl_F_SETLK64 = 13; | |
| arm_linux_record_tdep.fcntl_F_SETLKW64 = 14; | |
| arm_linux_record_tdep.arg1 = ARM_A1_REGNUM; | |
| arm_linux_record_tdep.arg2 = ARM_A1_REGNUM + 1; | |
| arm_linux_record_tdep.arg3 = ARM_A1_REGNUM + 2; | |
| arm_linux_record_tdep.arg4 = ARM_A1_REGNUM + 3; | |
| arm_linux_record_tdep.arg5 = ARM_A1_REGNUM + 4; | |
| arm_linux_record_tdep.arg6 = ARM_A1_REGNUM + 5; | |
| arm_linux_record_tdep.arg7 = ARM_A1_REGNUM + 6; | |
| set_gdbarch_gcc_target_options (gdbarch, arm_linux_gcc_target_options); | |
| } | |
| void _initialize_arm_linux_tdep (); | |
| void | |
| _initialize_arm_linux_tdep () | |
| { | |
| gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_LINUX, | |
| arm_linux_init_abi); | |
| } |