| /* Generate ELF backend handle. |
| Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. |
| |
| This program is Open Source software; you can redistribute it and/or |
| modify it under the terms of the Open Software License version 1.0 as |
| published by the Open Source Initiative. |
| |
| You should have received a copy of the Open Software License along |
| with this program; if not, you may obtain a copy of the Open Software |
| License version 1.0 from http://www.opensource.org/licenses/osl.php or |
| by writing the Open Source Initiative c/o Lawrence Rosen, Esq., |
| 3001 King Ranch Road, Ukiah, CA 95482. */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <assert.h> |
| #include <dlfcn.h> |
| #include <error.h> |
| #include <gelf.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <libeblP.h> |
| |
| |
| /* This table should contain the complete list of architectures as far |
| as the ELF specification is concerned. */ |
| /* XXX When things are stable replace the string pointers with char |
| arrays to avoid relocations. */ |
| static const struct |
| { |
| const char *dsoname; |
| const char *emulation; |
| const char *prefix; |
| int prefix_len; |
| int em; |
| } machines[] = |
| { |
| { "i386", "elf_i386", "i386", 4, EM_386 }, |
| { "ia64", "elf_ia64", "ia64", 4, EM_IA_64 }, |
| { "alpha", "elf_alpha", "alpha", 5, EM_ALPHA }, |
| { "x86_64", "elf_x86_64", "x86_64", 6, EM_X86_64 }, |
| { "sh", "elf_sh", "sh", 2, EM_SH }, |
| { "arm", "ebl_arm", "arm", 3, EM_ARM }, |
| { "sparc", "elf_sparcv9", "sparc", 5, EM_SPARCV9 }, |
| { "sparc", "elf_sparc", "sparc", 5, EM_SPARC }, |
| { "sparc", "elf_sparcv8plus", "sparc", 5, EM_SPARC32PLUS }, |
| |
| { "m32", "elf_m32", "m32", 3, EM_M32 }, |
| { "m68k", "elf_m68k", "m68k", 4, EM_68K }, |
| { "m88k", "elf_m88k", "m88k", 4, EM_88K }, |
| { "i860", "elf_i860", "i860", 4, EM_860 }, |
| { "mips", "elf_mips", "mips", 4, EM_MIPS }, |
| { "s370", "ebl_s370", "s370", 4, EM_S370 }, |
| { "mips", "elf_mipsel", "mips", 4, EM_MIPS_RS3_LE }, |
| { "parisc", "elf_parisc", "parisc", 6, EM_PARISC }, |
| { "vpp500", "elf_vpp500", "vpp500", 5, EM_VPP500 }, |
| { "sparc", "elf_v8plus", "v8plus", 6, EM_SPARC32PLUS }, |
| { "i960", "elf_i960", "i960", 4, EM_960 }, |
| { "ppc", "elf_ppc", "ppc", 3, EM_PPC }, |
| { "ppc64", "elf_ppc64", "ppc64", 5, EM_PPC64 }, |
| { "s390", "ebl_s390", "s390", 4, EM_S390 }, |
| { "v800", "ebl_v800", "v800", 4, EM_V800 }, |
| { "fr20", "ebl_fr20", "fr20", 4, EM_FR20 }, |
| { "rh32", "ebl_rh32", "rh32", 4, EM_RH32 }, |
| { "rce", "ebl_rce", "rce", 3, EM_RCE }, |
| { "tricore", "elf_tricore", "tricore", 7, EM_TRICORE }, |
| { "arc", "elf_arc", "arc", 3, EM_ARC }, |
| { "h8", "elf_h8_300", "h8_300", 6, EM_H8_300 }, |
| { "h8", "elf_h8_300h", "h8_300h", 6, EM_H8_300H }, |
| { "h8", "elf_h8s", "h8s", 6, EM_H8S }, |
| { "h8", "elf_h8_500", "h8_500", 6, EM_H8_500 }, |
| { "mips_x", "elf_mips_x", "mips_x", 6, EM_MIPS_X }, |
| { "coldfire", "elf_coldfire", "coldfire", 8, EM_COLDFIRE }, |
| { "m68k", "elf_68hc12", "68hc12", 6, EM_68HC12 }, |
| { "mma", "elf_mma", "mma", 3, EM_MMA }, |
| { "pcp", "elf_pcp", "pcp", 3, EM_PCP }, |
| { "ncpu", "elf_ncpu", "ncpu", 4, EM_NCPU }, |
| { "ndr1", "elf_ndr1", "ndr1", 4, EM_NDR1 }, |
| { "starcore", "elf_starcore", "starcore", 8, EM_STARCORE }, |
| { "me16", "elf_me16", "em16", 4, EM_ME16 }, |
| { "st100", "elf_st100", "st100", 5, EM_ST100 }, |
| { "tinyj", "elf_tinyj", "tinyj", 5, EM_TINYJ }, |
| { "pdsp", "elf_pdsp", "pdsp", 4, EM_PDSP }, |
| { "fx66", "elf_fx66", "fx66", 4, EM_FX66 }, |
| { "st9plus", "elf_st9plus", "st9plus", 7, EM_ST9PLUS }, |
| { "st7", "elf_st7", "st7", 3, EM_ST7 }, |
| { "m68k", "elf_68hc16", "68hc16", 6, EM_68HC16 }, |
| { "m68k", "elf_68hc11", "68hc11", 6, EM_68HC11 }, |
| { "m68k", "elf_68hc08", "68hc08", 6, EM_68HC08 }, |
| { "m68k", "elf_68hc05", "68hc05", 6, EM_68HC05 }, |
| { "svx", "elf_svx", "svx", 3, EM_SVX }, |
| { "st19", "elf_st19", "st19", 4, EM_ST19 }, |
| { "vax", "elf_vax", "vax", 3, EM_VAX }, |
| { "cris", "elf_cris", "cris", 4, EM_CRIS }, |
| { "javelin", "elf_javelin", "javelin", 7, EM_JAVELIN }, |
| { "firepath", "elf_firepath", "firepath", 8, EM_FIREPATH }, |
| { "zsp", "elf_zsp", "zsp", 3, EM_ZSP}, |
| { "mmix", "elf_mmix", "mmix", 4, EM_MMIX }, |
| { "hunay", "elf_huany", "huany", 5, EM_HUANY }, |
| { "prism", "elf_prism", "prism", 5, EM_PRISM }, |
| { "avr", "elf_avr", "avr", 3, EM_AVR }, |
| { "fr30", "elf_fr30", "fr30", 4, EM_FR30 }, |
| { "dv10", "elf_dv10", "dv10", 4, EM_D10V }, |
| { "dv30", "elf_dv30", "dv30", 4, EM_D30V }, |
| { "v850", "elf_v850", "v850", 4, EM_V850 }, |
| { "m32r", "elf_m32r", "m32r", 4, EM_M32R }, |
| { "mn10300", "elf_mn10300", "mn10300", 7, EM_MN10300 }, |
| { "mn10200", "elf_mn10200", "mn10200", 7, EM_MN10200 }, |
| { "pj", "elf_pj", "pj", 2, EM_PJ }, |
| { "openrisc", "elf_openrisc", "openrisc", 8, EM_OPENRISC }, |
| { "arc", "elf_arc_a5", "arc_a5", 6, EM_ARC_A5 }, |
| { "xtensa", "elf_xtensa", "xtensa", 6, EM_XTENSA }, |
| }; |
| #define nmachines (sizeof (machines) / sizeof (machines[0])) |
| |
| |
| /* Default callbacks. Mostly they just return the error value. */ |
| static const char *default_object_type_name (int ignore, char *buf, |
| size_t len); |
| static const char *default_reloc_type_name (int ignore, char *buf, size_t len); |
| static bool default_reloc_type_check (int ignore); |
| static bool default_reloc_valid_use (Elf *elf, int ignore); |
| static bool default_gotpc_reloc_check (Elf *elf, int ignore); |
| static const char *default_segment_type_name (int ignore, char *buf, |
| size_t len); |
| static const char *default_section_type_name (int ignore, char *buf, |
| size_t len); |
| static const char *default_section_name (int ignore, int ignore2, char *buf, |
| size_t len); |
| static const char *default_machine_flag_name (Elf64_Word *ignore); |
| static bool default_machine_flag_check (Elf64_Word flags); |
| static const char *default_symbol_type_name (int ignore, char *buf, |
| size_t len); |
| static const char *default_symbol_binding_name (int ignore, char *buf, |
| size_t len); |
| static const char *default_dynamic_tag_name (int64_t ignore, char *buf, |
| size_t len); |
| static bool default_dynamic_tag_check (int64_t ignore); |
| static GElf_Word default_sh_flags_combine (GElf_Word flags1, GElf_Word flags2); |
| static const char *default_osabi_name (int ignore, char *buf, size_t len); |
| static void default_destr (struct ebl *ignore); |
| static const char *default_core_note_type_name (uint32_t, char *buf, |
| size_t len); |
| static const char *default_object_note_type_name (uint32_t, char *buf, |
| size_t len); |
| static bool default_core_note (const char *name, uint32_t type, |
| uint32_t descsz, const char *desc); |
| static bool default_object_note (const char *name, uint32_t type, |
| uint32_t descsz, const char *desc); |
| static bool default_debugscn_p (const char *name); |
| |
| |
| /* Find an appropriate backend for the file associated with ELF. */ |
| static Ebl * |
| openbackend (elf, emulation, machine) |
| Elf *elf; |
| const char *emulation; |
| GElf_Half machine; |
| { |
| Ebl *result; |
| size_t cnt; |
| |
| /* First allocate the data structure for the result. We do this |
| here since this assures that the structure is always large |
| enough. */ |
| result = (Ebl *) calloc (1, sizeof (Ebl)); |
| if (result == NULL) |
| { |
| // XXX uncomment |
| // __libebl_seterror (ELF_E_NOMEM); |
| return NULL; |
| } |
| |
| /* Fill in the default callbacks. The initializer for the machine |
| specific module can overwrite the values. */ |
| result->object_type_name = default_object_type_name; |
| result->reloc_type_name = default_reloc_type_name; |
| result->reloc_type_check = default_reloc_type_check; |
| result->reloc_valid_use = default_reloc_valid_use; |
| result->gotpc_reloc_check = default_gotpc_reloc_check; |
| result->segment_type_name = default_segment_type_name; |
| result->section_type_name = default_section_type_name; |
| result->section_name = default_section_name; |
| result->machine_flag_name = default_machine_flag_name; |
| result->machine_flag_check = default_machine_flag_check; |
| result->symbol_type_name = default_symbol_type_name; |
| result->symbol_binding_name = default_symbol_binding_name; |
| result->dynamic_tag_name = default_dynamic_tag_name; |
| result->dynamic_tag_check = default_dynamic_tag_check; |
| result->sh_flags_combine = default_sh_flags_combine; |
| result->osabi_name = default_osabi_name; |
| result->core_note_type_name = default_core_note_type_name; |
| result->object_note_type_name = default_object_note_type_name; |
| result->core_note = default_core_note; |
| result->object_note = default_object_note; |
| result->debugscn_p = default_debugscn_p; |
| result->destr = default_destr; |
| |
| /* XXX Currently all we do is to look at 'e_machine' value in the |
| ELF header. With an internal mapping table from EM_* value to |
| DSO name we try to load the appropriate module to handle this |
| binary type. |
| |
| Multiple modules for the same machine type are possible and they |
| will be tried in sequence. The lookup process will only stop |
| when a module which can handle the machine type is found or all |
| available matching modules are tried. */ |
| for (cnt = 0; cnt < nmachines; ++cnt) |
| if ((emulation != NULL && strcmp (emulation, machines[cnt].emulation) == 0) |
| || (emulation == NULL && machines[cnt].em == machine)) |
| { |
| /* Well, we know the emulation name now. */ |
| result->emulation = machines[cnt].emulation; |
| |
| /* Give it a try. At least the machine type matches. First |
| try to load the module. */ |
| char dsoname[100]; |
| strcpy (stpcpy (stpcpy (dsoname, |
| "$ORIGIN/../" LIBSTR "/elfutils/libebl_"), |
| machines[cnt].dsoname), |
| ".so"); |
| |
| void *h = dlopen (dsoname, RTLD_LAZY); |
| if (h == NULL) |
| { |
| strcpy (stpcpy (stpcpy (dsoname, "libebl_"), |
| machines[cnt].dsoname), |
| ".so"); |
| h = dlopen (dsoname, RTLD_LAZY); |
| } |
| |
| /* Try without an explicit path. */ |
| if (h != NULL) |
| { |
| /* We managed to load the object. Now see whether the |
| initialization function likes our file. */ |
| ebl_bhinit_t initp; |
| char symname[machines[cnt].prefix_len + sizeof "_init"]; |
| |
| strcpy (mempcpy (symname, machines[cnt].prefix, |
| machines[cnt].prefix_len), "_init"); |
| |
| initp = (ebl_bhinit_t) dlsym (h, symname); |
| if (initp != NULL |
| && initp (elf, machine, result, sizeof (Ebl)) == 0) |
| { |
| /* We found a module to handle our file. */ |
| result->dlhandle = h; |
| result->elf = elf; |
| |
| /* A few entries are mandatory. */ |
| assert (result->name != NULL); |
| assert (result->destr != NULL); |
| |
| return result; |
| } |
| |
| /* Not the module we need. */ |
| (void) dlclose (h); |
| } |
| |
| /* We cannot find a DSO but the emulation/machine ID matches. |
| Return that information. */ |
| result->dlhandle = NULL; |
| result->elf = elf; |
| result->name = machines[cnt].prefix; |
| |
| return result; |
| } |
| |
| /* Nothing matched. We use only the default callbacks. */ |
| result->dlhandle = NULL; |
| result->elf = elf; |
| result->emulation = "<unknown>"; |
| result->name = "<unknown>"; |
| |
| return result; |
| } |
| |
| |
| /* Find an appropriate backend for the file associated with ELF. */ |
| Ebl * |
| ebl_openbackend (elf) |
| Elf *elf; |
| { |
| GElf_Ehdr ehdr_mem; |
| GElf_Ehdr *ehdr; |
| |
| /* Get the ELF header of the object. */ |
| ehdr = gelf_getehdr (elf, &ehdr_mem); |
| if (ehdr == NULL) |
| { |
| // XXX uncomment |
| // __libebl_seterror (elf_errno ()); |
| return NULL; |
| } |
| |
| return openbackend (elf, NULL, ehdr->e_machine); |
| } |
| |
| |
| /* Find backend without underlying ELF file. */ |
| Ebl * |
| ebl_openbackend_machine (machine) |
| GElf_Half machine; |
| { |
| return openbackend (NULL, NULL, machine); |
| } |
| |
| |
| /* Find backend with given emulation name. */ |
| Ebl * |
| ebl_openbackend_emulation (const char *emulation) |
| { |
| return openbackend (NULL, emulation, EM_NONE); |
| } |
| |
| |
| /* Default callbacks. Mostly they just return the error value. */ |
| static const char * |
| default_object_type_name (int ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static const char * |
| default_reloc_type_name (int ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static bool |
| default_reloc_type_check (int ignore) |
| { |
| return false; |
| } |
| |
| static bool |
| default_reloc_valid_use (Elf *elf, int ignore) |
| { |
| return false; |
| } |
| |
| static bool |
| default_gotpc_reloc_check (Elf *elf, int ignore) |
| { |
| return false; |
| } |
| |
| static const char * |
| default_segment_type_name (int ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static const char * |
| default_section_type_name (int ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static const char * |
| default_section_name (int ignore, int ignore2, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static const char * |
| default_machine_flag_name (Elf64_Word *ignore) |
| { |
| return NULL; |
| } |
| |
| static bool |
| default_machine_flag_check (Elf64_Word flags) |
| { |
| return flags == 0; |
| } |
| |
| static const char * |
| default_symbol_type_name (int ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static const char * |
| default_symbol_binding_name (int ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static const char * |
| default_dynamic_tag_name (int64_t ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static bool |
| default_dynamic_tag_check (int64_t ignore) |
| { |
| return false; |
| } |
| |
| static GElf_Word |
| default_sh_flags_combine (GElf_Word flags1, GElf_Word flags2) |
| { |
| return SH_FLAGS_COMBINE (flags1, flags2); |
| } |
| |
| static void |
| default_destr (struct ebl *ignore) |
| { |
| } |
| |
| static const char * |
| default_osabi_name (int ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static const char * |
| default_core_note_type_name (uint32_t ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static const char * |
| default_object_note_type_name (uint32_t ignore, char *buf, size_t len) |
| { |
| return NULL; |
| } |
| |
| static bool |
| default_core_note (const char *name, uint32_t type, uint32_t descsz, |
| const char *desc) |
| { |
| return NULL; |
| } |
| |
| static bool |
| default_object_note (const char *name, uint32_t type, uint32_t descsz, |
| const char *desc) |
| { |
| return NULL; |
| } |
| |
| static bool |
| default_debugscn_p (const char *name) |
| { |
| /* We know by default only about the DWARF debug sections which have |
| fixed names. */ |
| static const char *dwarf_scn_names[] = |
| { |
| /* DWARF 1 */ |
| ".debug", |
| ".line", |
| /* GNU DWARF 1 extensions */ |
| ".debug_srcinfo", |
| ".debug_sfnames", |
| /* DWARF 1.1 and DWARF 2 */ |
| ".debug_aranges", |
| ".debug_pubnames", |
| /* DWARF 2 */ |
| ".debug_info", |
| ".debug_abbrev", |
| ".debug_line", |
| ".debug_frame", |
| ".debug_str", |
| ".debug_loc", |
| ".debug_macinfo", |
| /* DWARF 3 */ |
| ".debug_ranges", |
| /* SGI/MIPS DWARF 2 extensions */ |
| ".debug_weaknames", |
| ".debug_funcnames", |
| ".debug_typenames", |
| ".debug_varnames" |
| }; |
| const size_t ndwarf_scn_names = (sizeof (dwarf_scn_names) |
| / sizeof (dwarf_scn_names[0])); |
| size_t cnt; |
| |
| for (cnt = 0; cnt < ndwarf_scn_names; ++cnt) |
| if (strcmp (name, dwarf_scn_names[cnt]) == 0) |
| return true; |
| |
| return false; |
| } |