aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorHeiko Schocher <hs@denx.de>2008-10-15 09:40:28 +0200
committerWolfgang Denk <wd@denx.de>2008-10-18 21:54:02 +0200
commit81473f67810c4c9b7efaed8dee258ed6bc4c7983 (patch)
tree8a700ac42b73d5ec6f01bd4466440d62465bd7f1 /common
parent67b23a322848d828a5e45c0567b72762bfde7abf (diff)
downloadu-boot-81473f67810c4c9b7efaed8dee258ed6bc4c7983.zip
u-boot-81473f67810c4c9b7efaed8dee258ed6bc4c7983.tar.gz
u-boot-81473f67810c4c9b7efaed8dee258ed6bc4c7983.tar.bz2
hush: add showvar command for hush shell.
This new command shows the local variables defined in the hush shell: => help showvar showvar - print values of all hushshell variables showvar name ... - print value of hushshell variable 'name' Also make the set_local_var() and unset_local_var () no longer static, so it is possible to define local hush shell variables at boot time. If CONFIG_HUSH_INIT_VAR is defined, u-boot calls hush_init_var (), where boardspecific code can define local hush shell variables at boottime. Signed-off-by: Heiko Schocher <hs@denx.de>
Diffstat (limited to 'common')
-rw-r--r--common/hush.c60
-rw-r--r--common/main.c4
2 files changed, 56 insertions, 8 deletions
diff --git a/common/hush.c b/common/hush.c
index 093c428..67bed39 100644
--- a/common/hush.c
+++ b/common/hush.c
@@ -501,10 +501,6 @@ static void remove_bg_job(struct pipe *pi);
static char **make_list_in(char **inp, char *name);
static char *insert_var_value(char *inp);
static char *get_local_var(const char *var);
-#ifndef __U_BOOT__
-static void unset_local_var(const char *name);
-#endif
-static int set_local_var(const char *s, int flg_export);
#ifndef __U_BOOT__
/* Table of built-in functions. They can be forked or not, depending on
@@ -2204,7 +2200,7 @@ static char *get_local_var(const char *s)
flg_export==0 if only local (not exporting) variable
flg_export==1 if "new" exporting environ
flg_export>1 if current startup environ (not call putenv()) */
-static int set_local_var(const char *s, int flg_export)
+int set_local_var(const char *s, int flg_export)
{
char *name, *value;
int result=0;
@@ -2295,8 +2291,7 @@ static int set_local_var(const char *s, int flg_export)
return result;
}
-#ifndef __U_BOOT__
-static void unset_local_var(const char *name)
+void unset_local_var(const char *name)
{
struct variables *cur;
@@ -2311,8 +2306,10 @@ static void unset_local_var(const char *name)
error_msg("%s: readonly variable", name);
return;
} else {
+#ifndef __U_BOOT__
if(cur->flg_export)
unsetenv(cur->name);
+#endif
free(cur->name);
free(cur->value);
while (next->next != cur)
@@ -2323,7 +2320,6 @@ static void unset_local_var(const char *name)
}
}
}
-#endif
static int is_assignment(const char *s)
{
@@ -3588,5 +3584,53 @@ static char * make_string(char ** inp)
return str;
}
+#ifdef __U_BOOT__
+int do_showvar (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ int i, k;
+ int rcode = 0;
+ struct variables *cur;
+
+ if (argc == 1) { /* Print all env variables */
+ for (cur = top_vars; cur; cur = cur->next) {
+ printf ("%s=%s\n", cur->name, cur->value);
+ if (ctrlc ()) {
+ puts ("\n ** Abort\n");
+ return 1;
+ }
+ }
+ return 0;
+ }
+ for (i = 1; i < argc; ++i) { /* print single env variables */
+ char *name = argv[i];
+
+ k = -1;
+ for (cur = top_vars; cur; cur = cur->next) {
+ if(strcmp (cur->name, name) == 0) {
+ k = 0;
+ printf ("%s=%s\n", cur->name, cur->value);
+ }
+ if (ctrlc ()) {
+ puts ("\n ** Abort\n");
+ return 1;
+ }
+ }
+ if (k < 0) {
+ printf ("## Error: \"%s\" not defined\n", name);
+ rcode ++;
+ }
+ }
+ return rcode;
+}
+
+U_BOOT_CMD(
+ showvar, CFG_MAXARGS, 1, do_showvar,
+ "showvar- print local hushshell variables\n",
+ "\n - print values of all hushshell variables\n"
+ "showvar name ...\n"
+ " - print value of hushshell variable 'name'\n"
+);
+
+#endif
#endif /* CFG_HUSH_PARSER */
/****************************************************************************/
diff --git a/common/main.c b/common/main.c
index c06ea07..9a9fc9d 100644
--- a/common/main.c
+++ b/common/main.c
@@ -341,6 +341,10 @@ void main_loop (void)
u_boot_hush_start ();
#endif
+#if defined(CONFIG_HUSH_INIT_VAR)
+ hush_init_var ();
+#endif
+
#ifdef CONFIG_AUTO_COMPLETE
install_auto_complete();
#endif
span class="hl opt">); } } void arm_linux_collect_gregset (const struct regset *regset, const struct regcache *regcache, int regnum, void *gregs_buf, size_t len) { gdb_byte *gregs = gregs_buf; int regno; for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) if (regnum == -1 || regnum == regno) regcache_raw_collect (regcache, regno, gregs + INT_REGISTER_SIZE * regno); if (regnum == ARM_PS_REGNUM || regnum == -1) { if (arm_apcs_32) regcache_raw_collect (regcache, ARM_PS_REGNUM, gregs + INT_REGISTER_SIZE * ARM_CPSR_GREGNUM); else regcache_raw_collect (regcache, ARM_PS_REGNUM, gregs + INT_REGISTER_SIZE * ARM_PC_REGNUM); } if (regnum == ARM_PC_REGNUM || regnum == -1) regcache_raw_collect (regcache, ARM_PC_REGNUM, gregs + 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[FP_REGISTER_SIZE]; reg_data = regs + (regno - ARM_F0_REGNUM) * FP_REGISTER_SIZE; reg_tag = regs[(regno - ARM_F0_REGNUM) + NWFPE_TAGS_OFFSET]; memset (buf, 0, 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 (regcache, 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[FP_REGISTER_SIZE]; regcache_raw_collect (regcache, 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) * 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 = regs_buf; int regno; if (regnum == ARM_FPS_REGNUM || regnum == -1) regcache_raw_supply (regcache, 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 = 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 (regcache, ARM_FPS_REGNUM, regs + 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 = regs_buf; int regno; if (regnum == ARM_FPSCR_REGNUM || regnum == -1) regcache_raw_supply (regcache, ARM_FPSCR_REGNUM, regs + 32 * 8); for (regno = ARM_D0_REGNUM; regno <= ARM_D31_REGNUM; regno++) if (regnum == -1 || regnum == regno) regcache_raw_supply (regcache, 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 = regs_buf; int regno; if (regnum == ARM_FPSCR_REGNUM || regnum == -1) regcache_raw_collect (regcache, ARM_FPSCR_REGNUM, regs + 32 * 8); for (regno = ARM_D0_REGNUM; regno <= ARM_D31_REGNUM; regno++) if (regnum == -1 || regnum == regno) regcache_raw_collect (regcache, regno, regs + (regno - ARM_D0_REGNUM) * 8); } /* Return the appropriate register set for the core section identified by SECT_NAME and SECT_SIZE. */ static const struct regset * arm_linux_regset_from_core_section (struct gdbarch *gdbarch, const char *sect_name, size_t sect_size) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); if (strcmp (sect_name, ".reg") == 0 && sect_size == ARM_LINUX_SIZEOF_GREGSET) { if (tdep->gregset == NULL) tdep->gregset = regset_alloc (gdbarch, arm_linux_supply_gregset, arm_linux_collect_gregset); return tdep->gregset; } if (strcmp (sect_name, ".reg2") == 0 && sect_size == ARM_LINUX_SIZEOF_NWFPE) { if (tdep->fpregset == NULL) tdep->fpregset = regset_alloc (gdbarch, arm_linux_supply_nwfpe, arm_linux_collect_nwfpe); return tdep->fpregset; } if (strcmp (sect_name, ".reg-arm-vfp") == 0 && sect_size == ARM_LINUX_SIZEOF_VFP) { if (tdep->vfpregset == NULL) tdep->vfpregset = regset_alloc (gdbarch, arm_linux_supply_vfp, arm_linux_collect_vfp); return tdep->vfpregset; } return NULL; } /* Core file register set sections. */ static struct core_regset_section arm_linux_fpa_regset_sections[] = { { ".reg", ARM_LINUX_SIZEOF_GREGSET, "general-purpose" }, { ".reg2", ARM_LINUX_SIZEOF_NWFPE, "FPA floating-point" }, { NULL, 0} }; static struct core_regset_section arm_linux_vfp_regset_sections[] = { { ".reg", ARM_LINUX_SIZEOF_GREGSET, "general-purpose" }, { ".reg-arm-vfp", ARM_LINUX_SIZEOF_VFP, "VFP floating-point" }, { NULL, 0} }; /* 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 = 0; if (target_auxv_search (target, AT_HWCAP, &arm_hwcap) != 1) return NULL; 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 tdesc_arm_with_neon; else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) return tdesc_arm_with_vfpv3; else return tdesc_arm_with_vfpv2; } return NULL; } /* 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; } /* When FRAME is at a syscall instruction, return the PC of the next instruction to be executed. */ static CORE_ADDR arm_linux_syscall_next_pc (struct frame_info *frame) { CORE_ADDR pc = get_frame_pc (frame); CORE_ADDR return_addr = 0; int is_thumb = arm_frame_is_thumb (frame); ULONGEST svc_number = 0; if (is_thumb) { svc_number = get_frame_register_unsigned (frame, 7); return_addr = pc + 2; } else { struct gdbarch *gdbarch = get_frame_arch (frame); 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 = get_frame_register_unsigned (frame, 7); } return_addr = pc + 4; } arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb); /* Addresses for calling Thumb functions have the bit 0 set. */ if (is_thumb) return_addr |= 1; return return_addr; } /* Insert a single step breakpoint at the next executed instruction. */ static int arm_linux_software_single_step (struct frame_info *frame) { struct gdbarch *gdbarch = get_frame_arch (frame); struct address_space *aspace = get_frame_address_space (frame); CORE_ADDR next_pc; if (arm_deal_with_atomic_sequence (frame)) return 1; next_pc = arm_get_next_pc (frame, get_frame_pc (frame)); /* The Linux kernel offers some user-mode helpers in a high page. We can not read this page (as of 2.6.23), and even if we could then we couldn't set breakpoints in it, and even if we could then the atomic operations would fail when interrupted. They are all called as functions and return to the address in LR, so step to there instead. */ if (next_pc > 0xffff0000) next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM); arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc); return 1; } /* Support for displaced stepping of Linux SVC instructions. */ static void arm_linux_cleanup_svc (struct gdbarch *gdbarch, struct regcache *regs, struct displaced_step_closure *dsc) { CORE_ADDR from = dsc->insn_addr; 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 + DISPLACED_MODIFIED_INSNS * 4 + 4)); if (debug_displaced) { fprintf_unfiltered (gdb_stdlog, "displaced: PC is apparently %.8lx after " "SVC step ", (unsigned long) apparent_pc); if (within_scratch) fprintf_unfiltered (gdb_stdlog, "(within scratch space)\n"); else fprintf_unfiltered (gdb_stdlog, "(outside scratch space)\n"); } if (within_scratch) displaced_write_reg (regs, dsc, ARM_PC_REGNUM, from + 4, BRANCH_WRITE_PC); } static int arm_linux_copy_svc (struct gdbarch *gdbarch, struct regcache *regs, struct displaced_step_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; if (debug_displaced) fprintf_unfiltered (gdb_stdlog, "displaced: found " "sigreturn/rt_sigreturn SVC call. PC in frame = %lx\n", (unsigned long) get_frame_pc (frame)); if (debug_displaced) fprintf_unfiltered (gdb_stdlog, "displaced: unwind pc = %lx. " "Setting momentary breakpoint.\n", (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); /* set_momentary_breakpoint invalidates FRAME. */ frame = NULL; /* We need to make sure we actually insert the momentary breakpoint set above. */ insert_breakpoints (); } else if (debug_displaced) fprintf_unfiltered (gdb_stderr, "displaced: couldn't find previous " "frame to set momentary breakpoint for " "sigreturn/rt_sigreturn\n"); } else if (debug_displaced) fprintf_unfiltered (gdb_stdlog, "displaced: sigreturn/rt_sigreturn " "SVC call not in signal trampoline frame\n"); /* 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 + 4 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, struct displaced_step_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, struct displaced_step_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), falling back to arm_displaced_step_copy_insn() if it hasn't. */ static struct displaced_step_closure * arm_linux_displaced_step_copy_insn (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs) { struct displaced_step_closure *dsc = xmalloc (sizeof (struct displaced_step_closure)); /* Detect when we enter an (inaccessible by GDB) Linux kernel helper, and stop at the return location. */ if (from > 0xffff0000) { if (debug_displaced) fprintf_unfiltered (gdb_stdlog, "displaced: detected kernel helper " "at %.8lx\n", (unsigned long) from); arm_catch_kernel_helper_return (gdbarch, from, to, regs, dsc); } 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); } arm_displaced_init_closure (gdbarch, from, to, dsc); return dsc; } static int arm_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) { return (*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 int 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; /* Used to save the register name. */ const char *start; char *regname; int len, offset; int got_minus = 0; long displacement; struct stoken str; ++tmp; start = tmp; /* Register name. */ while (isalnum (*tmp)) ++tmp; if (*tmp != ',') return 0; len = tmp - start; regname = 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_const (tmp); if (*tmp++ != '#') return 0; if (*tmp == '-') { ++tmp; got_minus = 1; } displacement = strtol (tmp, (char **) &tmp, 10); /* Skipping last `]'. */ if (*tmp++ != ']') return 0; /* The displacement. */ write_exp_elt_opcode (OP_LONG); write_exp_elt_type (builtin_type (gdbarch)->builtin_long); write_exp_elt_longcst (displacement); write_exp_elt_opcode (OP_LONG); if (got_minus) write_exp_elt_opcode (UNOP_NEG); /* The register name. */ write_exp_elt_opcode (OP_REGISTER); str.ptr = regname; str.length = len; write_exp_string (str); write_exp_elt_opcode (OP_REGISTER); write_exp_elt_opcode (BINOP_ADD); /* Casting to the expected type. */ write_exp_elt_opcode (UNOP_CAST); write_exp_elt_type (lookup_pointer_type (p->arg_type)); write_exp_elt_opcode (UNOP_CAST); write_exp_elt_opcode (UNOP_IND); p->arg = tmp; } else return 0; return 1; } static void arm_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); linux_init_abi (info, gdbarch); tdep->lowest_pc = 0x8000; if (info.byte_order == 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, svr4_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, find_solib_trampoline_target); 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, &arm_linux_restart_syscall_tramp_frame); tramp_frame_prepend_unwinder (gdbarch, &arm_kernel_linux_restart_syscall_tramp_frame); /* Core file support. */ set_gdbarch_regset_from_core_section (gdbarch, arm_linux_regset_from_core_section); set_gdbarch_core_read_description (gdbarch, arm_linux_core_read_description); if (tdep->have_vfp_registers) set_gdbarch_core_regset_sections (gdbarch, arm_linux_vfp_regset_sections); else if (tdep->have_fpa_registers) set_gdbarch_core_regset_sections (gdbarch, arm_linux_fpa_regset_sections); set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); /* 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); set_gdbarch_displaced_step_free_closure (gdbarch, simple_displaced_step_free_closure); set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point); /* Reversible debugging, process record. */ set_gdbarch_process_record (gdbarch, arm_process_record); /* SystemTap functions. */ set_gdbarch_stap_integer_prefix (gdbarch, "#"); set_gdbarch_stap_register_prefix (gdbarch, "r"); set_gdbarch_stap_register_indirection_prefix (gdbarch, "["); set_gdbarch_stap_register_indirection_suffix (gdbarch, "]"); 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); tdep->syscall_next_pc = arm_linux_syscall_next_pc; /* Syscall record. */ tdep->arm_swi_record = NULL; } /* Provide a prototype to silence -Wmissing-prototypes. */ extern initialize_file_ftype _initialize_arm_linux_tdep; void _initialize_arm_linux_tdep (void) { gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_LINUX, arm_linux_init_abi); }