Index: sh-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/sh-tdep.c,v retrieving revision 1.52 diff -u -r1.52 sh-tdep.c --- sh-tdep.c 12 Apr 2002 18:18:57 -0000 1.52 +++ sh-tdep.c 7 May 2002 08:21:59 -0000 @@ -41,6 +41,8 @@ #include "regcache.h" #include "doublest.h" +#include "elf-bfd.h" + #include "solib-svr4.h" void (*sh_show_regs) (void); @@ -1895,6 +1897,182 @@ } #endif /* SVR4_SHARED_LIBS */ + +/* This table matches the indices assigned to enum sh_abi. Keep + them in sync. */ +static const char * const sh_abi_names[] = +{ + "", + "GNU/Linux", + "NetBSD ELF", + NULL +}; + +static void +process_note_abi_tag_sections (bfd *abfd, asection *sect, void *obj) +{ + enum sh_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 = SH_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); + } + } + } + /* 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 = SH_ABI_NETBSD_ELF; + } +} + +static int +get_elfosabi (bfd *abfd) +{ + int elfosabi; + enum sh_abi sh_abi = SH_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. */ + if (elfosabi == 0) + { + bfd_map_over_sections (abfd, + process_note_abi_tag_sections, + &sh_abi); + } + + if (sh_abi != SH_ABI_UNKNOWN) + return sh_abi; + + switch (elfosabi) + { + case ELFOSABI_NONE: + /* Leave it as unknown. */ + break; + + case ELFOSABI_NETBSD: + return SH_ABI_NETBSD_ELF; + + case ELFOSABI_LINUX: + return SH_ABI_LINUX; + } + + return SH_ABI_UNKNOWN; +} + +struct sh_abi_handler +{ + struct sh_abi_handler *next; + enum sh_abi abi; + void (*init_abi)(struct gdbarch_info, struct gdbarch *); +}; + +struct sh_abi_handler *sh_abi_handler_list = NULL; + +void +sh_gdbarch_register_os_abi (enum sh_abi abi, + void (*init_abi)(struct gdbarch_info, + struct gdbarch *)) +{ + struct sh_abi_handler **handler_p; + + for (handler_p = &sh_abi_handler_list; *handler_p != NULL; + handler_p = &(*handler_p)->next) + { + if ((*handler_p)->abi == abi) + { + internal_error + (__FILE__, __LINE__, + "sh_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 sh_abi_handler *) xmalloc (sizeof (struct sh_abi_handler)); + (*handler_p)->next = NULL; + (*handler_p)->abi = abi; + (*handler_p)->init_abi = init_abi; +} + static gdbarch_init_ftype sh_gdbarch_init; static struct gdbarch * @@ -1906,17 +2084,51 @@ gdbarch_register_name_ftype *sh_register_name; gdbarch_store_return_value_ftype *sh_store_return_value; gdbarch_register_virtual_type_ftype *sh_register_virtual_type; + enum sh_abi sh_abi = SH_ABI_UNKNOWN; + struct sh_abi_handler *abi_handler; + + /* Try to determine the ABI of the object we are loading. */ + + if (info.abfd != NULL) + { + switch (bfd_get_flavour (info.abfd)) + { + case bfd_target_elf_flavour: + sh_abi = get_elfosabi (info.abfd); + break; + + default: + /* Just leave it as "unkown". */ + break; + } + } /* Find a candidate among the list of pre-declared architectures. */ - arches = gdbarch_list_lookup_by_info (arches, &info); - if (arches != NULL) - return arches->gdbarch; + 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->sh_abi == sh_abi) + return arches->gdbarch; + } /* None found, create a new architecture from the information provided. */ tdep = XMALLOC (struct gdbarch_tdep); gdbarch = gdbarch_alloc (&info, tdep); + tdep->sh_abi = sh_abi; + if (sh_abi < SH_ABI_INVALID) + tdep->abi_name = sh_abi_names[sh_abi]; + else + { + internal_error (__FILE__, __LINE__, "Invalid setting of sh_abi %d", + (int) sh_abi); + tdep->abi_name = ""; + } + /* Initialize the register numbers that are not common to all the variants to -1, if necessary thse will be overwritten in the case statement below. */ @@ -2167,15 +2379,58 @@ set_gdbarch_frame_num_args (gdbarch, frame_num_args_unknown); set_gdbarch_believe_pcc_promotion (gdbarch, 1); + /* Hook in ABI-specific overrides, if they have been registered. If + the ABI is unknown, this is probably an embedded target, so we + should not warn about this situation. */ + if (sh_abi != SH_ABI_UNKNOWN) + { + for (abi_handler = sh_abi_handler_list; abi_handler != NULL; + abi_handler = abi_handler->next) + if (abi_handler->abi == sh_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 SuperH settings", + sh_abi_names[sh_abi]); + } + } + return gdbarch; } +static void +sh_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, "sh_dump_tdep: ABI = %s\n", tdep->abi_name); + else + internal_error (__FILE__, __LINE__, + "sh_dump_tdep: illegal setting of tdep->sh_abi (%d)", + (int) tdep->sh_abi); +} + void _initialize_sh_tdep (void) { struct cmd_list_element *c; - register_gdbarch_init (bfd_arch_sh, sh_gdbarch_init); + gdbarch_register (bfd_arch_sh, sh_gdbarch_init, sh_dump_tdep); add_com ("regs", class_vars, sh_show_regs_command, "Print all registers"); } Index: config/sh/tm-sh.h =================================================================== RCS file: /cvs/src/src/gdb/config/sh/tm-sh.h,v retrieving revision 1.16 diff -u -r1.16 tm-sh.h --- config/sh/tm-sh.h 12 Jul 2001 02:29:33 -0000 1.16 +++ config/sh/tm-sh.h 7 May 2002 08:22:00 -0000 @@ -23,6 +23,17 @@ #define GDB_MULTI_ARCH 1 +/* ABI variants that we know about. If you add to this enum, please + update the table of names in sh-tdep.c. */ +enum sh_abi +{ + SH_ABI_UNKNOWN = 0, + SH_ABI_LINUX, + SH_ABI_NETBSD_ELF, + + SH_ABI_INVALID /* Keep this last. */ +}; + /* Information that is dependent on the processor variant. */ struct gdbarch_tdep @@ -56,6 +67,9 @@ int ARGLAST_REGNUM; int FLOAT_ARGLAST_REGNUM; int RETURN_REGNUM; + + enum sh_abi sh_abi; /* OS/ABI of the inferior */ + const char *abi_name; /* Name of the above */ }; /* Registers common to all the SH variants. */ @@ -81,4 +95,3 @@ #define LITTLE_REMOTE_BREAKPOINT { 0x20, 0xc3 } /* Used in remote.c */ /*#define NOP {0x20, 0x0b}*/ /* Who uses this???*/ -