#include "elf-bfd.h"
#include "coff/internal.h"
+#include "elf/arm.h"
/* Each OS has a different mechanism for accessing the various
registers stored in the sigcontext structure.
#define MSYMBOL_SIZE(msym) \
((long) MSYMBOL_INFO (msym) & 0x7fffffff)
+/* This table matches the indicees assigned to enum arm_abi. Keep
+ them in sync. */
+
+static const char * const arm_abi_names[] =
+{
+ "<unknown>",
+ "ARM EABI (version 1)",
+ "ARM EABI (version 2)",
+ "GNU/Linux",
+ "NetBSD (a.out)",
+ "NetBSD (ELF)",
+ "APCS",
+ "FreeBSD",
+ "Windows CE",
+ NULL
+};
+
/* Number of different reg name sets (options). */
static int num_flavor_options;
break;
default:
- fprintf (stderr, "Bad bit-field extraction\n");
+ fprintf_filtered (gdb_stderr, "Bad bit-field extraction\n");
return (pc);
}
}
return print_insn_little_arm (memaddr, info);
}
+/* The following define instruction sequences that will cause ARM
+ cpu's to take an undefined instruction trap. These are used to
+ signal a breakpoint to GDB.
+
+ The newer ARMv4T cpu's are capable of operating in ARM or Thumb
+ modes. A different instruction is required for each mode. The ARM
+ cpu's can also be big or little endian. Thus four different
+ instructions are needed to support all cases.
+
+ Note: ARMv4 defines several new instructions that will take the
+ undefined instruction trap. ARM7TDMI is nominally ARMv4T, but does
+ not in fact add the new instructions. The new undefined
+ instructions in ARMv4 are all instructions that had no defined
+ behaviour in earlier chips. There is no guarantee that they will
+ raise an exception, but may be treated as NOP's. In practice, it
+ may only safe to rely on instructions matching:
+
+ 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+ 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ C C C C 0 1 1 x x x x x x x x x x x x x x x x x x x x 1 x x x x
+
+ Even this may only true if the condition predicate is true. The
+ following use a condition predicate of ALWAYS so it is always TRUE.
+
+ There are other ways of forcing a breakpoint. GNU/Linux, RISC iX,
+ and NetBSD all use a software interrupt rather than an undefined
+ instruction to force a trap. This can be handled by by the
+ abi-specific code during establishment of the gdbarch vector. */
+
+
+/* XXX for now we allow a non-multi-arch gdb to override these
+ definitions. */
+#ifndef ARM_LE_BREAKPOINT
+#define ARM_LE_BREAKPOINT {0xFE,0xDE,0xFF,0xE7}
+#endif
+#ifndef ARM_BE_BREAKPOINT
+#define ARM_BE_BREAKPOINT {0xE7,0xFF,0xDE,0xFE}
+#endif
+#ifndef THUMB_LE_BREAKPOINT
+#define THUMB_LE_BREAKPOINT {0xfe,0xdf}
+#endif
+#ifndef THUMB_BE_BREAKPOINT
+#define THUMB_BE_BREAKPOINT {0xdf,0xfe}
+#endif
+
+static const char arm_default_arm_le_breakpoint[] = ARM_LE_BREAKPOINT;
+static const char arm_default_arm_be_breakpoint[] = ARM_BE_BREAKPOINT;
+static const char arm_default_thumb_le_breakpoint[] = THUMB_LE_BREAKPOINT;
+static const char arm_default_thumb_be_breakpoint[] = THUMB_BE_BREAKPOINT;
+
/* Determine the type and size of breakpoint to insert at PCPTR. Uses
the program counter value to determine whether a 16-bit or 32-bit
breakpoint should be used. It returns a pointer to a string of
unsigned char *
arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+
if (arm_pc_is_thumb (*pcptr) || arm_pc_is_thumb_dummy (*pcptr))
{
- if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
- {
- static char thumb_breakpoint[] = THUMB_BE_BREAKPOINT;
- *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
- *lenptr = sizeof (thumb_breakpoint);
- return thumb_breakpoint;
- }
- else
- {
- static char thumb_breakpoint[] = THUMB_LE_BREAKPOINT;
- *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
- *lenptr = sizeof (thumb_breakpoint);
- return thumb_breakpoint;
- }
+ *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+ *lenptr = tdep->thumb_breakpoint_size;
+ return tdep->thumb_breakpoint;
}
else
{
- if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
- {
- static char arm_breakpoint[] = ARM_BE_BREAKPOINT;
- *lenptr = sizeof (arm_breakpoint);
- return arm_breakpoint;
- }
- else
- {
- static char arm_breakpoint[] = ARM_LE_BREAKPOINT;
- *lenptr = sizeof (arm_breakpoint);
- return arm_breakpoint;
- }
+ *lenptr = tdep->arm_breakpoint_size;
+ return tdep->arm_breakpoint;
}
}
MSYMBOL_SET_SPECIAL (msym);
}
+\f
+static void
+process_note_abi_tag_sections (bfd *abfd, asection *sect, void *obj)
+{
+ enum arm_abi *os_ident_ptr = obj;
+ const char *name;
+ unsigned int sectsize;
+
+ name = bfd_get_section_name (abfd, sect);
+ sectsize = bfd_section_size (abfd, sect);
+
+ if (strcmp (name, ".note.ABI-tag") == 0 && sectsize > 0)
+ {
+ unsigned int name_length, data_length, note_type;
+ char *note;
+
+ /* If the section is larger than this, it's probably not what we are
+ looking for. */
+ if (sectsize > 128)
+ sectsize = 128;
+
+ note = alloca (sectsize);
+
+ bfd_get_section_contents (abfd, sect, note,
+ (file_ptr) 0, (bfd_size_type) sectsize);
+
+ name_length = bfd_h_get_32 (abfd, note);
+ data_length = bfd_h_get_32 (abfd, note + 4);
+ note_type = bfd_h_get_32 (abfd, note + 8);
+
+ if (name_length == 4 && data_length == 16 && note_type == 1
+ && strcmp (note + 12, "GNU") == 0)
+ {
+ int os_number = bfd_h_get_32 (abfd, note + 16);
+
+ /* The case numbers are from abi-tags in glibc */
+ switch (os_number)
+ {
+ case 0 :
+ *os_ident_ptr = ARM_ABI_LINUX;
+ break;
+
+ case 1 :
+ internal_error
+ (__FILE__, __LINE__,
+ "process_note_abi_sections: Hurd objects not supported");
+ break;
+
+ case 2 :
+ internal_error
+ (__FILE__, __LINE__,
+ "process_note_abi_sections: Solaris objects not supported");
+ break;
+
+ default :
+ internal_error
+ (__FILE__, __LINE__,
+ "process_note_abi_sections: unknown OS number %d",
+ os_number);
+ break;
+ }
+ }
+ }
+ /* NetBSD uses a similar trick. */
+ else if (strcmp (name, ".note.netbsd.ident") == 0 && sectsize > 0)
+ {
+ unsigned int name_length, desc_length, note_type;
+ char *note;
+
+ /* If the section is larger than this, it's probably not what we are
+ looking for. */
+ if (sectsize > 128)
+ sectsize = 128;
+
+ note = alloca (sectsize);
+
+ bfd_get_section_contents (abfd, sect, note,
+ (file_ptr) 0, (bfd_size_type) sectsize);
+
+ name_length = bfd_h_get_32 (abfd, note);
+ desc_length = bfd_h_get_32 (abfd, note + 4);
+ note_type = bfd_h_get_32 (abfd, note + 8);
+
+ if (name_length == 7 && desc_length == 4 && note_type == 1
+ && strcmp (note + 12, "NetBSD") == 0)
+ /* XXX Should we check the version here?
+ Probably not necessary yet. */
+ *os_ident_ptr = ARM_ABI_NETBSD_ELF;
+ }
+}
+
+/* Return one of the ELFOSABI_ constants for BFDs representing ELF
+ executables. If it's not an ELF executable or if the OS/ABI couldn't
+ be determined, simply return -1. */
+
+static int
+get_elfosabi (bfd *abfd)
+{
+ int elfosabi;
+ enum arm_abi arm_abi = ARM_ABI_UNKNOWN;
+
+ elfosabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
+
+ /* When elfosabi is 0 (ELFOSABI_NONE), this is supposed to indicate
+ that we're on a SYSV system. However, GNU/Linux uses a note section
+ to record OS/ABI info, but leaves e_ident[EI_OSABI] zero. So we
+ have to check the note sections too.
+
+ GNU/ARM tools set the EI_OSABI field to ELFOSABI_ARM, so handle that
+ as well.*/
+ if (elfosabi == 0 || elfosabi == ELFOSABI_ARM)
+ {
+ bfd_map_over_sections (abfd,
+ process_note_abi_tag_sections,
+ &arm_abi);
+ }
+
+ if (arm_abi != ARM_ABI_UNKNOWN)
+ return arm_abi;
+
+ switch (elfosabi)
+ {
+ case ELFOSABI_NONE:
+ /* Existing ARM Tools don't set this field, so look at the EI_FLAGS
+ field for more information. */
+
+ switch (EF_ARM_EABI_VERSION(elf_elfheader(abfd)->e_flags))
+ {
+ case EF_ARM_EABI_VER1:
+ return ARM_ABI_EABI_V1;
+
+ case EF_ARM_EABI_VER2:
+ return ARM_ABI_EABI_V2;
+
+ case EF_ARM_EABI_UNKNOWN:
+ /* Assume GNU tools. */
+ return ARM_ABI_APCS;
+
+ default:
+ internal_error (__FILE__, __LINE__,
+ "get_elfosabi: Unknown ARM EABI version 0x%lx",
+ EF_ARM_EABI_VERSION(elf_elfheader(abfd)->e_flags));
+
+ }
+ break;
+
+ case ELFOSABI_NETBSD:
+ return ARM_ABI_NETBSD_ELF;
+
+ case ELFOSABI_FREEBSD:
+ return ARM_ABI_FREEBSD;
+
+ case ELFOSABI_LINUX:
+ return ARM_ABI_LINUX;
+
+ case ELFOSABI_ARM:
+ /* Assume GNU tools with the old APCS abi. */
+ return ARM_ABI_APCS;
+
+ default:
+ }
+
+ return ARM_ABI_UNKNOWN;
+}
+
+struct arm_abi_handler
+{
+ struct arm_abi_handler *next;
+ enum arm_abi abi;
+ void (*init_abi)(struct gdbarch_info, struct gdbarch *);
+};
+
+struct arm_abi_handler *arm_abi_handler_list = NULL;
+
+void
+arm_gdbarch_register_os_abi (enum arm_abi abi,
+ void (*init_abi)(struct gdbarch_info,
+ struct gdbarch *))
+{
+ struct arm_abi_handler **handler_p;
+
+ for (handler_p = &arm_abi_handler_list; *handler_p != NULL;
+ handler_p = &(*handler_p)->next)
+ {
+ if ((*handler_p)->abi == abi)
+ {
+ internal_error
+ (__FILE__, __LINE__,
+ "arm_gdbarch_register_os_abi: A handler for this ABI variant (%d)"
+ " has already been registered", (int)abi);
+ /* If user wants to continue, override previous definition. */
+ (*handler_p)->init_abi = init_abi;
+ return;
+ }
+ }
+
+ (*handler_p)
+ = (struct arm_abi_handler *) xmalloc (sizeof (struct arm_abi_handler));
+ (*handler_p)->next = NULL;
+ (*handler_p)->abi = abi;
+ (*handler_p)->init_abi = init_abi;
+}
+
+/* Initialize the current architecture based on INFO. If possible, re-use an
+ architecture from ARCHES, which is a list of architectures already created
+ during this debugging session.
+
+ Called e.g. at program startup, when reading a core file, and when reading
+ a binary file. */
+
static struct gdbarch *
arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
+ struct gdbarch_tdep *tdep;
struct gdbarch *gdbarch;
+ enum arm_abi arm_abi = ARM_ABI_UNKNOWN;
+ struct arm_abi_handler *abi_handler;
+
+ /* Try to deterimine the ABI of the object we are loading. */
+
+ if (info.abfd != NULL)
+ {
+ switch (bfd_get_flavour (info.abfd))
+ {
+ case bfd_target_elf_flavour:
+ arm_abi = get_elfosabi (info.abfd);
+ break;
+
+ case bfd_target_aout_flavour:
+ if (strcmp (bfd_get_target(info.abfd), "a.out-arm-netbsd") == 0)
+ arm_abi = ARM_ABI_NETBSD_AOUT;
+ else
+ /* Assume it's an old APCS-style ABI. */
+ arm_abi = ARM_ABI_APCS;
+ break;
+
+ case bfd_target_coff_flavour:
+ /* Assume it's an old APCS-style ABI. */
+ /* XXX WinCE? */
+ arm_abi = ARM_ABI_APCS;
+ break;
+
+ default:
+ /* Not sure what to do here, leave the ABI as unknown. */
+ break;
+ }
+ }
+
+ /* Find a candidate among extant architectures. */
+ for (arches = gdbarch_list_lookup_by_info (arches, &info);
+ arches != NULL;
+ arches = gdbarch_list_lookup_by_info (arches->next, &info))
+ {
+ /* Make sure the ABI selection matches. */
+ tdep = gdbarch_tdep (arches->gdbarch);
+ if (tdep && tdep->arm_abi == arm_abi)
+ return arches->gdbarch;
+ }
- if (arches != NULL)
- return arches->gdbarch;
+ tdep = xmalloc (sizeof (struct gdbarch_tdep));
+ gdbarch = gdbarch_alloc (&info, tdep);
- /* XXX We'll probably need to set the tdep field soon. */
- gdbarch = gdbarch_alloc (&info, NULL);
+ tdep->arm_abi = arm_abi;
+ if (arm_abi < ARM_ABI_INVALID)
+ tdep->abi_name = arm_abi_names[arm_abi];
+ else
+ {
+ internal_error (__FILE__, __LINE__, "Invalid setting of arm_abi %d",
+ (int) arm_abi);
+ tdep->abi_name = "<invalid>";
+ }
- /* Floating point sizes and format. */
+ /* Breakpoints and floating point sizes and format. */
switch (info.byte_order)
{
case BFD_ENDIAN_BIG:
+ tdep->arm_breakpoint = arm_default_arm_be_breakpoint;
+ tdep->arm_breakpoint_size = sizeof (arm_default_arm_be_breakpoint);
+ tdep->thumb_breakpoint = arm_default_thumb_be_breakpoint;
+ tdep->thumb_breakpoint_size = sizeof (arm_default_thumb_be_breakpoint);
+
set_gdbarch_float_format (gdbarch, &floatformat_ieee_single_big);
set_gdbarch_double_format (gdbarch, &floatformat_ieee_double_big);
set_gdbarch_long_double_format (gdbarch, &floatformat_ieee_double_big);
+
break;
case BFD_ENDIAN_LITTLE:
+ tdep->arm_breakpoint = arm_default_arm_le_breakpoint;
+ tdep->arm_breakpoint_size = sizeof (arm_default_arm_le_breakpoint);
+ tdep->thumb_breakpoint = arm_default_thumb_le_breakpoint;
+ tdep->thumb_breakpoint_size = sizeof (arm_default_thumb_le_breakpoint);
+
set_gdbarch_float_format (gdbarch, &floatformat_ieee_single_little);
set_gdbarch_double_format (gdbarch,
&floatformat_ieee_double_littlebyte_bigword);
set_gdbarch_long_double_format (gdbarch,
&floatformat_ieee_double_littlebyte_bigword);
+
break;
default:
"arm_gdbarch_init: bad byte order for float format");
}
+ tdep->lowest_pc = 0x20;
+
set_gdbarch_use_generic_dummy_frames (gdbarch, 0);
/* Call dummy code. */
set_gdbarch_coff_make_msymbol_special (gdbarch,
arm_coff_make_msymbol_special);
- /* XXX We can't do this until NUM_REGS is set for the architecture.
- Even then, we can't use SIZEOF_FRAME_SAVED_REGS, since that still
+ /* Hook in the ABI-specific overrides, if they have been registered. */
+ if (arm_abi == ARM_ABI_UNKNOWN)
+ {
+ fprintf_filtered
+ (gdb_stderr, "GDB doesn't recognize the ABI of the inferior. "
+ "Attempting to continue with the default ARM settings");
+ }
+ else
+ {
+ for (abi_handler = arm_abi_handler_list; abi_handler != NULL;
+ abi_handler = abi_handler->next)
+ if (abi_handler->abi == arm_abi)
+ break;
+
+ if (abi_handler)
+ abi_handler->init_abi (info, gdbarch);
+ else
+ {
+ /* We assume that if GDB_MULTI_ARCH is less than
+ GDB_MULTI_ARCH_TM that an ABI variant can be supported by
+ overriding definitions in this file. */
+ if (GDB_MULTI_ARCH > GDB_MULTI_ARCH_PARTIAL)
+ fprintf_filtered
+ (gdb_stderr,
+ "A handler for the ABI variant \"%s\" is not built into this "
+ "configuration of GDB. "
+ "Attempting to continue with the default ARM settings",
+ arm_abi_names[arm_abi]);
+ }
+ }
+
+ /* Now we have tuned the configuration, set a few final things,
+ based on what the OS ABI has told us. */
+
+ /* We can't use SIZEOF_FRAME_SAVED_REGS here, since that still
references the old architecture vector, not the one we are
building here. */
if (prologue_cache.saved_regs != NULL)
prologue_cache.saved_regs = (CORE_ADDR *)
xcalloc (1, (sizeof (CORE_ADDR)
- * (NUM_GREGS + NUM_FREGS + NUM_SREGS + NUM_PSEUDO_REGS)));
+ * (gdbarch_num_regs (gdbarch) + NUM_PSEUDO_REGS)));
return gdbarch;
}
+static void
+arm_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+
+ if (tdep == NULL)
+ return;
+
+ if (tdep->abi_name != NULL)
+ fprintf_unfiltered (file, "arm_dump_tdep: ABI = %s\n", tdep->abi_name);
+ else
+ internal_error (__FILE__, __LINE__,
+ "arm_dump_tdep: illegal setting of tdep->arm_abi (%d)",
+ (int) tdep->arm_abi);
+
+ fprintf_unfiltered (file, "arm_dump_tdep: Lowest pc = 0x%lx",
+ (unsigned long) tdep->lowest_pc);
+}
+
+static void
+arm_init_abi_eabi_v1 (struct gdbarch_info info,
+ struct gdbarch *gdbarch)
+{
+ /* Place-holder. */
+}
+
+static void
+arm_init_abi_eabi_v2 (struct gdbarch_info info,
+ struct gdbarch *gdbarch)
+{
+ /* Place-holder. */
+}
+
+static void
+arm_init_abi_apcs (struct gdbarch_info info,
+ struct gdbarch *gdbarch)
+{
+ /* Place-holder. */
+}
+
void
_initialize_arm_tdep (void)
{
static char *helptext;
if (GDB_MULTI_ARCH)
- register_gdbarch_init (bfd_arch_arm, arm_gdbarch_init);
+ gdbarch_register (bfd_arch_arm, arm_gdbarch_init, arm_dump_tdep);
+
+ /* Register some ABI variants for embedded systems. */
+ arm_gdbarch_register_os_abi (ARM_ABI_EABI_V1, arm_init_abi_eabi_v1);
+ arm_gdbarch_register_os_abi (ARM_ABI_EABI_V2, arm_init_abi_eabi_v2);
+ arm_gdbarch_register_os_abi (ARM_ABI_APCS, arm_init_abi_apcs);
tm_print_insn = gdb_print_insn_arm;