/* GNU/Linux on ARM target support.
- Copyright (C) 1999-2020 Free Software Foundation, Inc.
+ Copyright (C) 1999-2024 Free Software Foundation, Inc.
This file is part of GDB.
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 "extract-store-integer.h"
#include "target.h"
#include "value.h"
#include "gdbtypes.h"
#include "breakpoint.h"
#include "auxv.h"
#include "xml-syscall.h"
+#include "expop.h"
#include "aarch32-tdep.h"
#include "arch/arm.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
+ particular undefined instruction to provoke a trap. Upon execution
of the software interrupt the kernel stops the inferior with a
SIGTRAP, and wakes the debugger. */
};
static void
-arm_linux_sigtramp_cache (struct frame_info *this_frame,
+arm_linux_sigtramp_cache (const frame_info_ptr &this_frame,
struct trad_frame_cache *this_cache,
CORE_ADDR func, int regs_offset)
{
/* See arm-linux.h for stack layout details. */
static void
arm_linux_sigreturn_init (const struct tramp_frame *self,
- struct frame_info *this_frame,
+ const frame_info_ptr &this_frame,
struct trad_frame_cache *this_cache,
CORE_ADDR func)
{
static void
arm_linux_rt_sigreturn_init (const struct tramp_frame *self,
- struct frame_info *this_frame,
+ const frame_info_ptr &this_frame,
struct trad_frame_cache *this_cache,
CORE_ADDR func)
{
static void
arm_linux_restart_syscall_init (const struct tramp_frame *self,
- struct frame_info *this_frame,
+ const frame_info_ptr &this_frame,
struct trad_frame_cache *this_cache,
CORE_ADDR func)
{
void *cb_data,
const struct regcache *regcache)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ 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);
struct target_ops *target,
bfd *abfd)
{
- CORE_ADDR arm_hwcap = linux_get_hwcap (target);
+ std::optional<gdb::byte_vector> auxv = target_read_auxv_raw (target);
+ CORE_ADDR arm_hwcap = linux_get_hwcap (auxv, target, gdbarch);
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 ();
+ return aarch32_read_description (false);
else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
- return arm_read_description (ARM_FP_TYPE_VFPV3);
+ return arm_read_description (ARM_FP_TYPE_VFPV3, false);
- return arm_read_description (ARM_FP_TYPE_VFPV2);
+ return arm_read_description (ARM_FP_TYPE_VFPV2, false);
}
return nullptr;
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,
+arm_linux_sigreturn_return_addr (const frame_info_ptr &frame,
unsigned long svc_number,
CORE_ADDR *pc, int *is_thumb)
{
return next_pc;
}
+/* Return true if we're at execve syscall-exit-stop. */
+
+static bool
+is_execve_syscall_exit (struct regcache *regs)
+{
+ ULONGEST reg = -1;
+
+ /* Check that lr is 0. */
+ regcache_cooked_read_unsigned (regs, ARM_LR_REGNUM, ®);
+ if (reg != 0)
+ return false;
+
+ /* Check that r0-r8 is 0. */
+ for (int i = 0; i <= 8; ++i)
+ {
+ reg = -1;
+ regcache_cooked_read_unsigned (regs, ARM_A1_REGNUM + i, ®);
+ if (reg != 0)
+ return false;
+ }
+
+ return true;
+}
+
+#define arm_sys_execve 11
+
/* At a ptrace syscall-stop, return the syscall number. This either
comes from the SWI instruction (OABI) or from r7 (EABI).
int is_thumb;
ULONGEST svc_number = -1;
+ if (is_execve_syscall_exit (regs))
+ return arm_sys_execve;
+
regcache_cooked_read_unsigned (regs, ARM_PC_REGNUM, &pc);
regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &cpsr);
is_thumb = (cpsr & t_bit) != 0;
/* 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 this_instr;
+ {
+ ULONGEST val;
+ if (!safe_read_memory_unsigned_integer (pc - 4, 4, byte_order_for_code,
+ &val))
+ return -1;
+ this_instr = val;
+ }
unsigned long svc_operand = (0x00ffffff & this_instr);
if (svc_operand)
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);
+ regcache *regcache
+ = gdb::checked_static_cast<struct regcache *> (self->regcache);
+ CORE_ADDR pc = regcache_read_pc (regcache);
+ int is_thumb = arm_is_thumb (regcache);
ULONGEST svc_number = 0;
if (is_thumb)
}
else
{
- struct gdbarch *gdbarch = self->regcache->arch ();
+ struct gdbarch *gdbarch = regcache->arch ();
enum bfd_endian byte_order_for_code =
gdbarch_byte_order_for_code (gdbarch);
unsigned long this_instr =
{
/* 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);
+ next_pc = arm_linux_sigreturn_next_pc (regcache, svc_number, &is_thumb);
}
/* Addresses for calling Thumb functions have the bit 0 set. */
static void
arm_linux_cleanup_svc (struct gdbarch *gdbarch,
struct regcache *regs,
- arm_displaced_step_closure *dsc)
+ arm_displaced_step_copy_insn_closure *dsc)
{
ULONGEST apparent_pc;
int within_scratch;
static int
arm_linux_copy_svc (struct gdbarch *gdbarch, struct regcache *regs,
- arm_displaced_step_closure *dsc)
+ arm_displaced_step_copy_insn_closure *dsc)
{
CORE_ADDR return_to = 0;
- struct frame_info *frame;
+ frame_info_ptr frame;
unsigned int svc_number = displaced_read_reg (regs, dsc, 7);
int is_sigreturn = 0;
int is_thumb;
= 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 ();
static void
cleanup_kernel_helper_return (struct gdbarch *gdbarch,
struct regcache *regs,
- arm_displaced_step_closure *dsc)
+ 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_closure *dsc)
+ arm_displaced_step_copy_insn_closure *dsc)
{
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
the program has stepped into a Linux kernel helper routine (which must be
handled as a special case). */
-static displaced_step_closure_up
+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_closure> dsc
- (new arm_displaced_step_closure);
+ 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. */
arm_displaced_init_closure (gdbarch, from, to, dsc.get ());
/* This is a work around for a problem with g++ 4.8. */
- return displaced_step_closure_up (dsc.release ());
+ return displaced_step_copy_insn_closure_up (dsc.release ());
}
/* Implementation of `gdbarch_stap_is_single_operand', as defined in
It returns one if the special token has been parsed successfully,
or zero if the current token is not considered special. */
-static int
+static expr::operation_up
arm_stap_parse_special_token (struct gdbarch *gdbarch,
struct stap_parse_info *p)
{
int len, offset;
int got_minus = 0;
long displacement;
- struct stoken str;
++tmp;
start = tmp;
++tmp;
if (*tmp != ',')
- return 0;
+ return {};
len = tmp - start;
regname = (char *) alloca (len + 2);
/* Skipping last `]'. */
if (*tmp++ != ']')
- return 0;
+ return {};
+ p->arg = tmp;
+
+ using namespace expr;
/* The displacement. */
- write_exp_elt_opcode (&p->pstate, OP_LONG);
- write_exp_elt_type (&p->pstate, builtin_type (gdbarch)->builtin_long);
- write_exp_elt_longcst (&p->pstate, displacement);
- write_exp_elt_opcode (&p->pstate, OP_LONG);
+ struct type *long_type = builtin_type (gdbarch)->builtin_long;
if (got_minus)
- write_exp_elt_opcode (&p->pstate, UNOP_NEG);
+ displacement = -displacement;
+ operation_up disp = make_operation<long_const_operation> (long_type,
+ displacement);
/* The register name. */
- write_exp_elt_opcode (&p->pstate, OP_REGISTER);
- str.ptr = regname;
- str.length = len;
- write_exp_string (&p->pstate, str);
- write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+ operation_up reg
+ = make_operation<register_operation> (regname);
- write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+ operation_up sum
+ = make_operation<add_operation> (std::move (reg), std::move (disp));
/* Casting to the expected type. */
- write_exp_elt_opcode (&p->pstate, UNOP_CAST);
- write_exp_elt_type (&p->pstate, lookup_pointer_type (p->arg_type));
- write_exp_elt_opcode (&p->pstate, UNOP_CAST);
-
- write_exp_elt_opcode (&p->pstate, UNOP_IND);
-
- p->arg = tmp;
+ 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));
}
- else
- return 0;
- return 1;
+ return {};
}
/* ARM process record-replay constructs: syscall, signal etc. */
-struct linux_record_tdep arm_linux_record_tdep;
+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
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 arm_sys_execve: return gdb_sys_execve;
case 12: return gdb_sys_chdir;
case 13: return gdb_sys_time;
case 14: return gdb_sys_mknod;
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;
if (syscall_gdb == gdb_sys_no_syscall)
{
- printf_unfiltered (_("Process record and replay target doesn't "
- "support syscall number %s\n"),
- plongest (svc_number));
+ gdb_printf (gdb_stderr,
+ _("Process record and replay target doesn't "
+ "support syscall number %s\n"),
+ plongest (svc_number));
return -1;
}
/* Implement the skip_trampoline_code gdbarch method. */
static CORE_ADDR
-arm_linux_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
+arm_linux_skip_trampoline_code (const frame_info_ptr &frame, CORE_ADDR pc)
{
CORE_ADDR target_pc = arm_skip_stub (frame, pc);
NULL };
static const char *const stap_register_indirection_suffixes[] = { "]",
NULL };
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch);
- linux_init_abi (info, gdbarch);
+ linux_init_abi (info, gdbarch, 1);
tdep->lowest_pc = 0x8000;
if (info.byte_order_for_code == BFD_ENDIAN_BIG)
break;
default:
internal_error
- (__FILE__, __LINE__,
- _("arm_linux_init_abi: Floating point model not supported"));
+ (_("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, svr4_ilp32_fetch_link_map_offsets);
+ (gdbarch, linux_ilp32_fetch_link_map_offsets);
/* Single stepping. */
set_gdbarch_software_single_step (gdbarch, arm_linux_software_single_step);
set_gdbarch_displaced_step_copy_insn (gdbarch,
arm_linux_displaced_step_copy_insn);
set_gdbarch_displaced_step_fixup (gdbarch, arm_displaced_step_fixup);
- set_gdbarch_displaced_step_location (gdbarch, linux_displaced_step_location);
/* Reversible debugging, process record. */
set_gdbarch_process_record (gdbarch, arm_process_record);