| /* readelf.c -- display contents of an ELF format file |
| Copyright (C) 1998-2024 Free Software Foundation, Inc. |
| |
| Originally developed by Eric Youngdale <eric@andante.jic.com> |
| Modifications by Nick Clifton <nickc@redhat.com> |
| |
| This file is part of GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| /* The difference between readelf and objdump: |
| |
| Both programs are capable of displaying the contents of ELF format files, |
| so why does the binutils project have two file dumpers ? |
| |
| The reason is that objdump sees an ELF file through a BFD filter of the |
| world; if BFD has a bug where, say, it disagrees about a machine constant |
| in e_flags, then the odds are good that it will remain internally |
| consistent. The linker sees it the BFD way, objdump sees it the BFD way, |
| GAS sees it the BFD way. There was need for a tool to go find out what |
| the file actually says. |
| |
| This is why the readelf program does not link against the BFD library - it |
| exists as an independent program to help verify the correct working of BFD. |
| |
| There is also the case that readelf can provide more information about an |
| ELF file than is provided by objdump. In particular it can display DWARF |
| debugging information which (at the moment) objdump cannot. */ |
| |
| #include "sysdep.h" |
| #include <assert.h> |
| #include <time.h> |
| #include <zlib.h> |
| #ifdef HAVE_ZSTD |
| #include <zstd.h> |
| #endif |
| #include <wchar.h> |
| |
| #if defined HAVE_MSGPACK |
| #include <msgpack.h> |
| #endif |
| |
| /* Define BFD64 here, even if our default architecture is 32 bit ELF |
| as this will allow us to read in and parse 64bit and 32bit ELF files. */ |
| #define BFD64 |
| |
| #include "bfd.h" |
| #include "bucomm.h" |
| #include "elfcomm.h" |
| #include "demanguse.h" |
| #include "dwarf.h" |
| #include "ctf-api.h" |
| #include "sframe-api.h" |
| #include "demangle.h" |
| |
| #include "elf/common.h" |
| #include "elf/external.h" |
| #include "elf/internal.h" |
| |
| |
| /* Included here, before RELOC_MACROS_GEN_FUNC is defined, so that |
| we can obtain the H8 reloc numbers. We need these for the |
| get_reloc_size() function. We include h8.h again after defining |
| RELOC_MACROS_GEN_FUNC so that we get the naming function as well. */ |
| |
| #include "elf/h8.h" |
| #undef _ELF_H8_H |
| |
| /* Undo the effects of #including reloc-macros.h. */ |
| |
| #undef START_RELOC_NUMBERS |
| #undef RELOC_NUMBER |
| #undef FAKE_RELOC |
| #undef EMPTY_RELOC |
| #undef END_RELOC_NUMBERS |
| #undef _RELOC_MACROS_H |
| |
| /* The following headers use the elf/reloc-macros.h file to |
| automatically generate relocation recognition functions |
| such as elf_mips_reloc_type() */ |
| |
| #define RELOC_MACROS_GEN_FUNC |
| |
| #include "elf/aarch64.h" |
| #include "elf/alpha.h" |
| #include "elf/amdgpu.h" |
| #include "elf/arc.h" |
| #include "elf/arm.h" |
| #include "elf/avr.h" |
| #include "elf/bfin.h" |
| #include "elf/cr16.h" |
| #include "elf/cris.h" |
| #include "elf/crx.h" |
| #include "elf/csky.h" |
| #include "elf/d10v.h" |
| #include "elf/d30v.h" |
| #include "elf/dlx.h" |
| #include "elf/bpf.h" |
| #include "elf/epiphany.h" |
| #include "elf/fr30.h" |
| #include "elf/frv.h" |
| #include "elf/ft32.h" |
| #include "elf/h8.h" |
| #include "elf/hppa.h" |
| #include "elf/i386.h" |
| #include "elf/i370.h" |
| #include "elf/i860.h" |
| #include "elf/i960.h" |
| #include "elf/ia64.h" |
| #include "elf/ip2k.h" |
| #include "elf/kvx.h" |
| #include "elf/lm32.h" |
| #include "elf/iq2000.h" |
| #include "elf/m32c.h" |
| #include "elf/m32r.h" |
| #include "elf/m68k.h" |
| #include "elf/m68hc11.h" |
| #include "elf/s12z.h" |
| #include "elf/mcore.h" |
| #include "elf/mep.h" |
| #include "elf/metag.h" |
| #include "elf/microblaze.h" |
| #include "elf/mips.h" |
| #include "elf/mmix.h" |
| #include "elf/mn10200.h" |
| #include "elf/mn10300.h" |
| #include "elf/moxie.h" |
| #include "elf/mt.h" |
| #include "elf/msp430.h" |
| #include "elf/nds32.h" |
| #include "elf/nfp.h" |
| #include "elf/nios2.h" |
| #include "elf/or1k.h" |
| #include "elf/pj.h" |
| #include "elf/ppc.h" |
| #include "elf/ppc64.h" |
| #include "elf/pru.h" |
| #include "elf/riscv.h" |
| #include "elf/rl78.h" |
| #include "elf/rx.h" |
| #include "elf/s390.h" |
| #include "elf/score.h" |
| #include "elf/sh.h" |
| #include "elf/sparc.h" |
| #include "elf/spu.h" |
| #include "elf/tic6x.h" |
| #include "elf/tilegx.h" |
| #include "elf/tilepro.h" |
| #include "elf/v850.h" |
| #include "elf/vax.h" |
| #include "elf/visium.h" |
| #include "elf/wasm32.h" |
| #include "elf/x86-64.h" |
| #include "elf/xgate.h" |
| #include "elf/xstormy16.h" |
| #include "elf/xtensa.h" |
| #include "elf/z80.h" |
| #include "elf/loongarch.h" |
| #include "elf/bpf.h" |
| |
| #include "getopt.h" |
| #include "libiberty.h" |
| #include "safe-ctype.h" |
| #include "filenames.h" |
| |
| #ifndef offsetof |
| #define offsetof(TYPE, MEMBER) ((size_t) &(((TYPE *) 0)->MEMBER)) |
| #endif |
| |
| typedef struct elf_section_list |
| { |
| Elf_Internal_Shdr * hdr; |
| struct elf_section_list * next; |
| } elf_section_list; |
| |
| /* Flag bits indicating particular types of dump. */ |
| #define HEX_DUMP (1 << 0) /* The -x command line switch. */ |
| #ifdef SUPPORT_DISASSEMBLY |
| #define DISASS_DUMP (1 << 1) /* The -i command line switch. */ |
| #endif |
| #define DEBUG_DUMP (1 << 2) /* The -w command line switch. */ |
| #define STRING_DUMP (1 << 3) /* The -p command line switch. */ |
| #define RELOC_DUMP (1 << 4) /* The -R command line switch. */ |
| #define CTF_DUMP (1 << 5) /* The --ctf command line switch. */ |
| #define SFRAME_DUMP (1 << 6) /* The --sframe command line switch. */ |
| #define AUTO_DUMP (1 << 7) /* The -j command line switch. */ |
| |
| typedef unsigned char dump_type; |
| |
| /* A linked list of the section names for which dumps were requested. */ |
| struct dump_list_entry |
| { |
| char * name; |
| dump_type type; |
| struct dump_list_entry * next; |
| }; |
| |
| /* A dynamic array of flags indicating for which sections a dump |
| has been requested via command line switches. */ |
| struct dump_data |
| { |
| dump_type * dump_sects; |
| unsigned int num_dump_sects; |
| }; |
| |
| static struct dump_data cmdline; |
| |
| static struct dump_list_entry * dump_sects_byname; |
| |
| char * program_name = "readelf"; |
| |
| static bool show_name = false; |
| static bool do_dynamic = false; |
| static bool do_syms = false; |
| static bool do_dyn_syms = false; |
| static bool do_lto_syms = false; |
| static bool do_reloc = false; |
| static bool do_sections = false; |
| static bool do_section_groups = false; |
| static bool do_section_details = false; |
| static bool do_segments = false; |
| static bool do_unwind = false; |
| static bool do_using_dynamic = false; |
| static bool do_header = false; |
| static bool do_dump = false; |
| static bool do_version = false; |
| static bool do_histogram = false; |
| static bool do_debugging = false; |
| static bool do_ctf = false; |
| static bool do_sframe = false; |
| static bool do_arch = false; |
| static bool do_notes = false; |
| static bool do_archive_index = false; |
| static bool check_all = false; |
| static bool is_32bit_elf = false; |
| static bool decompress_dumps = false; |
| static bool do_not_show_symbol_truncation = false; |
| static bool do_demangle = false; /* Pretty print C++ symbol names. */ |
| static bool process_links = false; |
| static bool dump_any_debugging = false; |
| static bool extra_sym_info = false; |
| static int demangle_flags = DMGL_ANSI | DMGL_PARAMS; |
| static int sym_base = 0; |
| |
| static char *dump_ctf_parent_name; |
| static char *dump_ctf_symtab_name; |
| static char *dump_ctf_strtab_name; |
| |
| struct group_list |
| { |
| struct group_list * next; |
| unsigned int section_index; |
| }; |
| |
| struct group |
| { |
| struct group_list * root; |
| unsigned int group_index; |
| }; |
| |
| typedef struct filedata |
| { |
| const char * file_name; |
| bool is_separate; |
| FILE * handle; |
| uint64_t file_size; |
| Elf_Internal_Ehdr file_header; |
| uint64_t archive_file_offset; |
| uint64_t archive_file_size; |
| /* Everything below this point is cleared out by free_filedata. */ |
| Elf_Internal_Shdr * section_headers; |
| Elf_Internal_Phdr * program_headers; |
| char * string_table; |
| uint64_t string_table_length; |
| uint64_t dynamic_addr; |
| uint64_t dynamic_size; |
| uint64_t dynamic_nent; |
| Elf_Internal_Dyn * dynamic_section; |
| Elf_Internal_Shdr * dynamic_strtab_section; |
| char * dynamic_strings; |
| uint64_t dynamic_strings_length; |
| Elf_Internal_Shdr * dynamic_symtab_section; |
| uint64_t num_dynamic_syms; |
| Elf_Internal_Sym * dynamic_symbols; |
| uint64_t version_info[16]; |
| unsigned int dynamic_syminfo_nent; |
| Elf_Internal_Syminfo * dynamic_syminfo; |
| uint64_t dynamic_syminfo_offset; |
| uint64_t nbuckets; |
| uint64_t nchains; |
| uint64_t * buckets; |
| uint64_t * chains; |
| uint64_t ngnubuckets; |
| uint64_t ngnuchains; |
| uint64_t * gnubuckets; |
| uint64_t * gnuchains; |
| uint64_t * mipsxlat; |
| uint64_t gnusymidx; |
| char * program_interpreter; |
| uint64_t dynamic_info[DT_RELRENT + 1]; |
| uint64_t dynamic_info_DT_GNU_HASH; |
| uint64_t dynamic_info_DT_MIPS_XHASH; |
| elf_section_list * symtab_shndx_list; |
| size_t group_count; |
| struct group * section_groups; |
| struct group ** section_headers_groups; |
| /* A dynamic array of flags indicating for which sections a dump of |
| some kind has been requested. It is reset on a per-object file |
| basis and then initialised from the cmdline_dump_sects array, |
| the results of interpreting the -w switch, and the |
| dump_sects_byname list. */ |
| struct dump_data dump; |
| } Filedata; |
| |
| /* How to print a vma value. */ |
| typedef enum print_mode |
| { |
| HEX, |
| HEX_5, |
| DEC, |
| DEC_5, |
| UNSIGNED, |
| UNSIGNED_5, |
| PREFIX_HEX, |
| PREFIX_HEX_5, |
| FULL_HEX, |
| LONG_HEX, |
| ZERO_HEX, |
| OCTAL, |
| OCTAL_5 |
| } |
| print_mode; |
| |
| typedef enum unicode_display_type |
| { |
| unicode_default = 0, |
| unicode_locale, |
| unicode_escape, |
| unicode_hex, |
| unicode_highlight, |
| unicode_invalid |
| } unicode_display_type; |
| |
| static unicode_display_type unicode_display = unicode_default; |
| |
| typedef enum |
| { |
| reltype_unknown, |
| reltype_rel, |
| reltype_rela, |
| reltype_relr |
| } relocation_type; |
| |
| /* Versioned symbol info. */ |
| enum versioned_symbol_info |
| { |
| symbol_undefined, |
| symbol_hidden, |
| symbol_public |
| }; |
| |
| static int |
| fseek64 (FILE *stream, int64_t offset, int whence) |
| { |
| #if defined (HAVE_FSEEKO64) |
| off64_t o = offset; |
| if (o != offset) |
| { |
| errno = EINVAL; |
| return -1; |
| } |
| return fseeko64 (stream, o, whence); |
| #elif defined (HAVE_FSEEKO) |
| off_t o = offset; |
| if (o != offset) |
| { |
| errno = EINVAL; |
| return -1; |
| } |
| return fseeko (stream, o, whence); |
| #else |
| long o = offset; |
| if (o != offset) |
| { |
| errno = EINVAL; |
| return -1; |
| } |
| return fseek (stream, o, whence); |
| #endif |
| } |
| |
| static const char * get_symbol_version_string |
| (Filedata *, bool, const char *, size_t, unsigned, |
| Elf_Internal_Sym *, enum versioned_symbol_info *, unsigned short *); |
| |
| static bool process_notes_at |
| (Filedata *, Elf_Internal_Shdr *, uint64_t, uint64_t, uint64_t); |
| |
| #define UNKNOWN -1 |
| |
| static inline const char * |
| section_name (const Filedata *filedata, const Elf_Internal_Shdr *hdr) |
| { |
| return filedata->string_table + hdr->sh_name; |
| } |
| |
| static inline bool |
| section_name_valid (const Filedata *filedata, const Elf_Internal_Shdr *hdr) |
| { |
| return (filedata != NULL |
| && hdr != NULL |
| && filedata->string_table != NULL |
| && hdr->sh_name < filedata->string_table_length); |
| } |
| |
| /* Returns true if the given index is real/valid. Note: "real" here |
| means "references a real section in the section header" and not |
| "is a valid section index as per the ELF standard". */ |
| |
| static inline bool |
| section_index_real (const Filedata *filedata, unsigned int ndx) |
| { |
| return (filedata != NULL |
| && filedata->section_headers != NULL |
| && ndx < filedata->file_header.e_shnum |
| && ndx > 0); |
| } |
| |
| #define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ |
| |
| static inline bool |
| valid_symbol_name (const char *strtab, size_t strtab_size, uint64_t offset) |
| { |
| return strtab != NULL && offset < strtab_size; |
| } |
| |
| static inline bool |
| valid_dynamic_name (const Filedata *filedata, uint64_t offset) |
| { |
| return valid_symbol_name (filedata->dynamic_strings, |
| filedata->dynamic_strings_length, offset); |
| } |
| |
| /* GET_DYNAMIC_NAME asssumes that VALID_DYNAMIC_NAME has |
| already been called and verified that the string exists. */ |
| static inline const char * |
| get_dynamic_name (const Filedata *filedata, size_t offset) |
| { |
| return filedata->dynamic_strings + offset; |
| } |
| |
| #define REMOVE_ARCH_BITS(ADDR) \ |
| do \ |
| { \ |
| if (filedata->file_header.e_machine == EM_ARM) \ |
| (ADDR) &= ~1; \ |
| } \ |
| while (0) |
| |
| /* Get the correct GNU hash section name. */ |
| #define GNU_HASH_SECTION_NAME(filedata) \ |
| filedata->dynamic_info_DT_MIPS_XHASH ? ".MIPS.xhash" : ".gnu.hash" |
| |
| /* Retrieve NMEMB structures, each SIZE bytes long from FILEDATA starting at |
| OFFSET + the offset of the current archive member, if we are examining an |
| archive. Put the retrieved data into VAR, if it is not NULL. Otherwise |
| allocate a buffer using malloc and fill that. In either case return the |
| pointer to the start of the retrieved data or NULL if something went wrong. |
| If something does go wrong and REASON is not NULL then emit an error |
| message using REASON as part of the context. */ |
| |
| static void * |
| get_data (void *var, |
| Filedata *filedata, |
| uint64_t offset, |
| uint64_t size, |
| uint64_t nmemb, |
| const char *reason) |
| { |
| void * mvar; |
| uint64_t amt = size * nmemb; |
| |
| if (size == 0 || nmemb == 0) |
| return NULL; |
| |
| /* If size_t is smaller than uint64_t, eg because you are building |
| on a 32-bit host, then make sure that when the sizes are cast to |
| size_t no information is lost. */ |
| if ((size_t) size != size |
| || (size_t) nmemb != nmemb |
| || (size_t) amt != amt |
| || amt / size != nmemb |
| || (size_t) amt + 1 == 0) |
| { |
| if (reason) |
| error (_("Size overflow prevents reading %" PRIu64 |
| " elements of size %" PRIu64 " for %s\n"), |
| nmemb, size, reason); |
| return NULL; |
| } |
| |
| /* Be kind to memory checkers (eg valgrind, address sanitizer) by not |
| attempting to allocate memory when the read is bound to fail. */ |
| if (filedata->archive_file_offset > filedata->file_size |
| || offset > filedata->file_size - filedata->archive_file_offset |
| || amt > filedata->file_size - filedata->archive_file_offset - offset) |
| { |
| if (reason) |
| error (_("Reading %" PRIu64 " bytes extends past end of file for %s\n"), |
| amt, reason); |
| return NULL; |
| } |
| |
| if (fseek64 (filedata->handle, filedata->archive_file_offset + offset, |
| SEEK_SET)) |
| { |
| if (reason) |
| error (_("Unable to seek to %#" PRIx64 " for %s\n"), |
| filedata->archive_file_offset + offset, reason); |
| return NULL; |
| } |
| |
| mvar = var; |
| if (mvar == NULL) |
| { |
| /* + 1 so that we can '\0' terminate invalid string table sections. */ |
| mvar = malloc ((size_t) amt + 1); |
| |
| if (mvar == NULL) |
| { |
| if (reason) |
| error (_("Out of memory allocating %" PRIu64 " bytes for %s\n"), |
| amt, reason); |
| return NULL; |
| } |
| |
| ((char *) mvar)[amt] = '\0'; |
| } |
| |
| if (fread (mvar, (size_t) size, (size_t) nmemb, filedata->handle) != nmemb) |
| { |
| if (reason) |
| error (_("Unable to read in %" PRIu64 " bytes of %s\n"), |
| amt, reason); |
| if (mvar != var) |
| free (mvar); |
| return NULL; |
| } |
| |
| return mvar; |
| } |
| |
| /* Print a VMA value in the MODE specified. |
| Returns the number of characters displayed. */ |
| |
| static unsigned int |
| print_vma (uint64_t vma, print_mode mode) |
| { |
| unsigned int nc = 0; |
| |
| switch (mode) |
| { |
| case FULL_HEX: |
| nc = printf ("0x"); |
| /* Fall through. */ |
| case LONG_HEX: |
| if (!is_32bit_elf) |
| return nc + printf ("%16.16" PRIx64, vma); |
| return nc + printf ("%8.8" PRIx64, vma); |
| |
| case ZERO_HEX: |
| if (is_32bit_elf) |
| return printf ("%08" PRIx64, vma); |
| return printf ("%016" PRIx64, vma); |
| |
| case DEC_5: |
| if (vma <= 99999) |
| return printf ("%5" PRId64, vma); |
| /* Fall through. */ |
| case PREFIX_HEX: |
| nc = printf ("0x"); |
| /* Fall through. */ |
| case HEX: |
| return nc + printf ("%" PRIx64, vma); |
| |
| case PREFIX_HEX_5: |
| nc = printf ("0x"); |
| /* Fall through. */ |
| case HEX_5: |
| return nc + printf ("%05" PRIx64, vma); |
| |
| case DEC: |
| return printf ("%" PRId64, vma); |
| |
| case UNSIGNED: |
| return printf ("%" PRIu64, vma); |
| |
| case UNSIGNED_5: |
| return printf ("%5" PRIu64, vma); |
| |
| case OCTAL: |
| return printf ("%" PRIo64, vma); |
| |
| case OCTAL_5: |
| return printf ("%5" PRIo64, vma); |
| |
| default: |
| /* FIXME: Report unrecognised mode ? */ |
| return 0; |
| } |
| } |
| |
| |
| /* Display a symbol on stdout. Handles the display of control characters and |
| multibye characters (assuming the host environment supports them). |
| |
| Display at most abs(WIDTH) characters, truncating as necessary, |
| unless do_wide or extra_sym_info is true. |
| |
| If truncation will happen and do_not_show_symbol_truncation is FALSE then display |
| abs(WIDTH) - 5 characters followed by "[...]". |
| |
| If WIDTH is negative then ensure that the output is at least (- WIDTH) characters, |
| padding as necessary. |
| |
| Returns the number of emitted characters. */ |
| |
| static unsigned int |
| print_symbol_name (signed int width, const char * symbol) |
| { |
| bool extra_padding = false; |
| bool do_dots = false; |
| signed int num_printed = 0; |
| #ifdef HAVE_MBSTATE_T |
| mbstate_t state; |
| #endif |
| unsigned int width_remaining; |
| const void * alloced_symbol = NULL; |
| |
| if (width < 0) |
| { |
| /* Keep the width positive. This helps the code below. */ |
| width = - width; |
| extra_padding = true; |
| } |
| else if (width == 0) |
| return 0; |
| |
| if (do_wide || extra_sym_info) |
| /* Set the remaining width to a very large value. |
| This simplifies the code below. */ |
| width_remaining = INT_MAX; |
| else |
| { |
| width_remaining = width; |
| |
| if (! do_not_show_symbol_truncation |
| && (int) strlen (symbol) > width) |
| { |
| width_remaining -= 5; |
| if ((int) width_remaining < 0) |
| width_remaining = 0; |
| do_dots = true; |
| } |
| } |
| |
| #ifdef HAVE_MBSTATE_T |
| /* Initialise the multibyte conversion state. */ |
| memset (& state, 0, sizeof (state)); |
| #endif |
| |
| if (do_demangle && *symbol) |
| { |
| const char * res = cplus_demangle (symbol, demangle_flags); |
| |
| if (res != NULL) |
| alloced_symbol = symbol = res; |
| } |
| |
| while (width_remaining) |
| { |
| size_t n; |
| const char c = *symbol++; |
| |
| if (c == 0) |
| break; |
| |
| if (ISPRINT (c)) |
| { |
| putchar (c); |
| width_remaining --; |
| num_printed ++; |
| } |
| else if (ISCNTRL (c)) |
| { |
| /* Do not print control characters directly as they can affect terminal |
| settings. Such characters usually appear in the names generated |
| by the assembler for local labels. */ |
| |
| if (width_remaining < 2) |
| break; |
| |
| printf ("^%c", c + 0x40); |
| width_remaining -= 2; |
| num_printed += 2; |
| } |
| else if (c == 0x7f) |
| { |
| if (width_remaining < 5) |
| break; |
| printf ("<DEL>"); |
| width_remaining -= 5; |
| num_printed += 5; |
| } |
| else if (unicode_display != unicode_locale |
| && unicode_display != unicode_default) |
| { |
| /* Display unicode characters as something else. */ |
| unsigned char bytes[4]; |
| bool is_utf8; |
| unsigned int nbytes; |
| |
| bytes[0] = c; |
| |
| if (bytes[0] < 0xc0) |
| { |
| nbytes = 1; |
| is_utf8 = false; |
| } |
| else |
| { |
| bytes[1] = *symbol++; |
| |
| if ((bytes[1] & 0xc0) != 0x80) |
| { |
| is_utf8 = false; |
| /* Do not consume this character. It may only |
| be the first byte in the sequence that was |
| corrupt. */ |
| --symbol; |
| nbytes = 1; |
| } |
| else if ((bytes[0] & 0x20) == 0) |
| { |
| is_utf8 = true; |
| nbytes = 2; |
| } |
| else |
| { |
| bytes[2] = *symbol++; |
| |
| if ((bytes[2] & 0xc0) != 0x80) |
| { |
| is_utf8 = false; |
| symbol -= 2; |
| nbytes = 1; |
| } |
| else if ((bytes[0] & 0x10) == 0) |
| { |
| is_utf8 = true; |
| nbytes = 3; |
| } |
| else |
| { |
| bytes[3] = *symbol++; |
| |
| nbytes = 4; |
| |
| if ((bytes[3] & 0xc0) != 0x80) |
| { |
| is_utf8 = false; |
| symbol -= 3; |
| nbytes = 1; |
| } |
| else |
| is_utf8 = true; |
| } |
| } |
| } |
| |
| if (unicode_display == unicode_invalid) |
| is_utf8 = false; |
| |
| if (unicode_display == unicode_hex || ! is_utf8) |
| { |
| unsigned int i; |
| |
| if (width_remaining < (nbytes * 2) + 2) |
| break; |
| |
| putchar (is_utf8 ? '<' : '{'); |
| printf ("0x"); |
| for (i = 0; i < nbytes; i++) |
| printf ("%02x", bytes[i]); |
| putchar (is_utf8 ? '>' : '}'); |
| } |
| else |
| { |
| if (unicode_display == unicode_highlight && isatty (1)) |
| printf ("\x1B[31;47m"); /* Red. */ |
| |
| switch (nbytes) |
| { |
| case 2: |
| if (width_remaining < 6) |
| break; |
| printf ("\\u%02x%02x", |
| (bytes[0] & 0x1c) >> 2, |
| ((bytes[0] & 0x03) << 6) | (bytes[1] & 0x3f)); |
| break; |
| case 3: |
| if (width_remaining < 6) |
| break; |
| printf ("\\u%02x%02x", |
| ((bytes[0] & 0x0f) << 4) | ((bytes[1] & 0x3c) >> 2), |
| ((bytes[1] & 0x03) << 6) | (bytes[2] & 0x3f)); |
| break; |
| case 4: |
| if (width_remaining < 8) |
| break; |
| printf ("\\u%02x%02x%02x", |
| ((bytes[0] & 0x07) << 6) | ((bytes[1] & 0x3c) >> 2), |
| ((bytes[1] & 0x03) << 6) | ((bytes[2] & 0x3c) >> 2), |
| ((bytes[2] & 0x03) << 6) | (bytes[3] & 0x3f)); |
| |
| break; |
| default: |
| /* URG. */ |
| break; |
| } |
| |
| if (unicode_display == unicode_highlight && isatty (1)) |
| printf ("\033[0m"); /* Default colour. */ |
| } |
| |
| if (bytes[nbytes - 1] == 0) |
| break; |
| } |
| else |
| { |
| #ifdef HAVE_MBSTATE_T |
| wchar_t w; |
| #endif |
| /* Let printf do the hard work of displaying multibyte characters. */ |
| printf ("%.1s", symbol - 1); |
| width_remaining --; |
| num_printed ++; |
| |
| #ifdef HAVE_MBSTATE_T |
| /* Try to find out how many bytes made up the character that was |
| just printed. Advance the symbol pointer past the bytes that |
| were displayed. */ |
| n = mbrtowc (& w, symbol - 1, MB_CUR_MAX, & state); |
| #else |
| n = 1; |
| #endif |
| if (n != (size_t) -1 && n != (size_t) -2 && n > 0) |
| symbol += (n - 1); |
| } |
| } |
| |
| if (do_dots) |
| num_printed += printf ("[...]"); |
| |
| if (extra_padding && num_printed < width) |
| { |
| /* Fill in the remaining spaces. */ |
| printf ("%-*s", width - num_printed, " "); |
| num_printed = width; |
| } |
| |
| free ((void *) alloced_symbol); |
| return num_printed; |
| } |
| |
| /* Returns a pointer to a static buffer containing a printable version of |
| the given section's name. Like print_symbol, except that it does not try |
| to print multibyte characters, it just interprets them as hex values. */ |
| |
| static const char * |
| printable_section_name (Filedata * filedata, const Elf_Internal_Shdr * sec) |
| { |
| #define NUM_SEC_NAME_BUFS 5 |
| #define MAX_PRINT_SEC_NAME_LEN 256 |
| |
| static int sec_name_buf_index = 0; |
| /* We use a rotating array of static buffers, so that multiple successive calls |
| to printable_section_name() will still work. eg when used in a printf. */ |
| static char sec_name_buf [NUM_SEC_NAME_BUFS][MAX_PRINT_SEC_NAME_LEN + 1]; |
| |
| const char * name; |
| char * buf; |
| char * buf_start; |
| char c; |
| unsigned int remaining = MAX_PRINT_SEC_NAME_LEN; |
| |
| /* Validate the input parameters. */ |
| if (filedata == NULL) |
| return _("<internal error>"); |
| if (sec == NULL) |
| return _("<none>"); |
| if (filedata->string_table == NULL) |
| return _("<no-strings>"); |
| if (sec->sh_name >= filedata->string_table_length) |
| return _("<corrupt>"); |
| |
| /* Select a buffer to use. */ |
| buf_start = buf = sec_name_buf[sec_name_buf_index]; |
| if (++sec_name_buf_index >= NUM_SEC_NAME_BUFS) |
| sec_name_buf_index = 0; |
| |
| name = section_name (filedata, sec); |
| |
| while ((c = * name ++) != 0) |
| { |
| if (ISCNTRL (c)) |
| { |
| if (remaining < 2) |
| break; |
| |
| * buf ++ = '^'; |
| * buf ++ = c + 0x40; |
| remaining -= 2; |
| } |
| else if (ISPRINT (c)) |
| { |
| * buf ++ = c; |
| remaining -= 1; |
| } |
| else |
| { |
| static char hex[17] = "0123456789ABCDEF"; |
| |
| if (remaining < 4) |
| break; |
| * buf ++ = '<'; |
| * buf ++ = hex[(c & 0xf0) >> 4]; |
| * buf ++ = hex[c & 0x0f]; |
| * buf ++ = '>'; |
| remaining -= 4; |
| } |
| |
| if (remaining == 0) |
| break; |
| } |
| |
| * buf = 0; |
| return buf_start; |
| } |
| |
| /* Return TRUE if the current file is for IA-64 machine and OpenVMS ABI. |
| This OS has so many departures from the ELF standard that we test it at |
| many places. */ |
| |
| static inline bool |
| is_ia64_vms (Filedata * filedata) |
| { |
| return filedata->file_header.e_machine == EM_IA_64 |
| && filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_OPENVMS; |
| } |
| |
| static const char * |
| printable_section_name_from_index (Filedata * filedata, |
| size_t ndx, |
| bool * is_special) |
| { |
| if (is_special != NULL) |
| * is_special = true; |
| |
| switch (ndx) |
| { |
| case SHN_UNDEF: return "UND"; |
| case SHN_ABS: return "ABS"; |
| case SHN_COMMON: return "COM"; |
| break; |
| } |
| |
| if (filedata != NULL) |
| { |
| switch (filedata->file_header.e_machine) |
| { |
| case EM_MIPS: |
| if (ndx == SHN_MIPS_SCOMMON) |
| return "SCOMMON"; |
| if (ndx == SHN_MIPS_SUNDEFINED) |
| return "SUNDEF"; |
| break; |
| |
| case EM_TI_C6000: |
| if (ndx == SHN_TIC6X_SCOMMON) |
| return "SCOM"; |
| break; |
| |
| case EM_X86_64: |
| case EM_L1OM: |
| case EM_K1OM: |
| if (ndx == SHN_X86_64_LCOMMON) |
| return "LARGE_COM"; |
| break; |
| |
| case EM_IA_64: |
| if (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_HPUX |
| && ndx == SHN_IA_64_ANSI_COMMON) |
| return "ANSI_COM"; |
| |
| if (is_ia64_vms (filedata) && ndx == SHN_IA_64_VMS_SYMVEC) |
| return "VMS_SYMVEC"; |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (filedata->section_headers != NULL |
| && ndx < filedata->file_header.e_shnum) |
| { |
| const char * res; |
| |
| res = printable_section_name (filedata, filedata->section_headers + ndx); |
| if (is_special != NULL) |
| * is_special = (res[0] == '<'); |
| |
| return res; |
| } |
| } |
| |
| static char name_buf[40]; |
| unsigned int short_ndx = (unsigned int) (ndx & 0xffff); |
| |
| if (ndx >= SHN_LOPROC && ndx <= SHN_HIPROC) |
| sprintf (name_buf, "PRC[0x%04x]", short_ndx); |
| else if (ndx >= SHN_LOOS && ndx <= SHN_HIOS) |
| sprintf (name_buf, "OS [0x%04x]", short_ndx); |
| else if (ndx >= SHN_LORESERVE) |
| sprintf (name_buf, "RSV[0x%04x]", short_ndx); |
| else if (filedata->file_header.e_shnum != 0 |
| && ndx >= filedata->file_header.e_shnum) |
| sprintf (name_buf, _("BAD[0x%lx]"), (long) ndx); |
| else |
| sprintf (name_buf, "<section 0x%lx>", (long) ndx); |
| |
| return name_buf; |
| } |
| |
| /* Return a pointer to section NAME, or NULL if no such section exists. */ |
| |
| static Elf_Internal_Shdr * |
| find_section (Filedata * filedata, const char * name) |
| { |
| unsigned int i; |
| |
| if (filedata->section_headers == NULL) |
| return NULL; |
| |
| for (i = 0; i < filedata->file_header.e_shnum; i++) |
| if (section_name_valid (filedata, filedata->section_headers + i) |
| && streq (section_name (filedata, filedata->section_headers + i), |
| name)) |
| return filedata->section_headers + i; |
| |
| return NULL; |
| } |
| |
| /* Return a pointer to a section containing ADDR, or NULL if no such |
| section exists. */ |
| |
| static Elf_Internal_Shdr * |
| find_section_by_address (Filedata * filedata, uint64_t addr) |
| { |
| unsigned int i; |
| |
| if (filedata->section_headers == NULL) |
| return NULL; |
| |
| for (i = 0; i < filedata->file_header.e_shnum; i++) |
| { |
| Elf_Internal_Shdr *sec = filedata->section_headers + i; |
| |
| if (addr >= sec->sh_addr && addr < sec->sh_addr + sec->sh_size) |
| return sec; |
| } |
| |
| return NULL; |
| } |
| |
| static Elf_Internal_Shdr * |
| find_section_by_type (Filedata * filedata, unsigned int type) |
| { |
| unsigned int i; |
| |
| if (filedata->section_headers == NULL) |
| return NULL; |
| |
| for (i = 0; i < filedata->file_header.e_shnum; i++) |
| { |
| Elf_Internal_Shdr *sec = filedata->section_headers + i; |
| |
| if (sec->sh_type == type) |
| return sec; |
| } |
| |
| return NULL; |
| } |
| |
| static Elf_Internal_Shdr * |
| find_section_by_name (Filedata * filedata, const char * name) |
| { |
| unsigned int i; |
| |
| if (filedata->section_headers == NULL || filedata->string_table_length == 0) |
| return NULL; |
| |
| for (i = 0; i < filedata->file_header.e_shnum; i++) |
| { |
| Elf_Internal_Shdr *sec = filedata->section_headers + i; |
| |
| if (sec->sh_name < filedata->string_table_length |
| && streq (name, filedata->string_table + sec->sh_name)) |
| return sec; |
| } |
| |
| return NULL; |
| } |
| |
| /* Return a pointer to section NAME, or NULL if no such section exists, |
| restricted to the list of sections given in SET. */ |
| |
| static Elf_Internal_Shdr * |
| find_section_in_set (Filedata * filedata, const char * name, unsigned int * set) |
| { |
| unsigned int i; |
| |
| if (filedata->section_headers == NULL) |
| return NULL; |
| |
| if (set != NULL) |
| { |
| while ((i = *set++) > 0) |
| { |
| /* See PR 21156 for a reproducer. */ |
| if (i >= filedata->file_header.e_shnum) |
| continue; /* FIXME: Should we issue an error message ? */ |
| |
| if (section_name_valid (filedata, filedata->section_headers + i) |
| && streq (section_name (filedata, filedata->section_headers + i), |
| name)) |
| return filedata->section_headers + i; |
| } |
| } |
| |
| return find_section (filedata, name); |
| } |
| |
| /* Guess the relocation size commonly used by the specific machines. */ |
| |
| static bool |
| guess_is_rela (unsigned int e_machine) |
| { |
| switch (e_machine) |
| { |
| /* Targets that use REL relocations. */ |
| case EM_386: |
| case EM_IAMCU: |
| case EM_960: |
| case EM_ARM: |
| case EM_D10V: |
| case EM_CYGNUS_D10V: |
| case EM_DLX: |
| case EM_MIPS: |
| case EM_MIPS_RS3_LE: |
| case EM_CYGNUS_M32R: |
| case EM_SCORE: |
| case EM_XGATE: |
| case EM_NFP: |
| case EM_BPF: |
| return false; |
| |
| /* Targets that use RELA relocations. */ |
| case EM_68K: |
| case EM_860: |
| case EM_AARCH64: |
| case EM_ADAPTEVA_EPIPHANY: |
| case EM_ALPHA: |
| case EM_ALTERA_NIOS2: |
| case EM_ARC: |
| case EM_ARC_COMPACT: |
| case EM_ARC_COMPACT2: |
| case EM_ARC_COMPACT3: |
| case EM_ARC_COMPACT3_64: |
| case EM_AVR: |
| case EM_AVR_OLD: |
| case EM_BLACKFIN: |
| case EM_CR16: |
| case EM_CRIS: |
| case EM_CRX: |
| case EM_CSKY: |
| case EM_D30V: |
| case EM_CYGNUS_D30V: |
| case EM_FR30: |
| case EM_FT32: |
| case EM_CYGNUS_FR30: |
| case EM_CYGNUS_FRV: |
| case EM_H8S: |
| case EM_H8_300: |
| case EM_H8_300H: |
| case EM_IA_64: |
| case EM_IP2K: |
| case EM_IP2K_OLD: |
| case EM_IQ2000: |
| case EM_KVX: |
| case EM_LATTICEMICO32: |
| case EM_M32C_OLD: |
| case EM_M32C: |
| case EM_M32R: |
| case EM_MCORE: |
| case EM_CYGNUS_MEP: |
| case EM_METAG: |
| case EM_MMIX: |
| case EM_MN10200: |
| case EM_CYGNUS_MN10200: |
| case EM_MN10300: |
| case EM_CYGNUS_MN10300: |
| case EM_MOXIE: |
| case EM_MSP430: |
| case EM_MSP430_OLD: |
| case EM_MT: |
| case EM_NDS32: |
| case EM_NIOS32: |
| case EM_OR1K: |
| case EM_PPC64: |
| case EM_PPC: |
| case EM_TI_PRU: |
| case EM_RISCV: |
| case EM_RL78: |
| case EM_RX: |
| case EM_S390: |
| case EM_S390_OLD: |
| case EM_SH: |
| case EM_SPARC: |
| case EM_SPARC32PLUS: |
| case EM_SPARCV9: |
| case EM_SPU: |
| case EM_TI_C6000: |
| case EM_TILEGX: |
| case EM_TILEPRO: |
| case EM_V800: |
| case EM_V850: |
| case EM_CYGNUS_V850: |
| case EM_VAX: |
| case EM_VISIUM: |
| case EM_X86_64: |
| case EM_L1OM: |
| case EM_K1OM: |
| case EM_XSTORMY16: |
| case EM_XTENSA: |
| case EM_XTENSA_OLD: |
| case EM_MICROBLAZE: |
| case EM_MICROBLAZE_OLD: |
| case EM_WEBASSEMBLY: |
| return true; |
| |
| case EM_68HC05: |
| case EM_68HC08: |
| case EM_68HC11: |
| case EM_68HC16: |
| case EM_FX66: |
| case EM_ME16: |
| case EM_MMA: |
| case EM_NCPU: |
| case EM_NDR1: |
| case EM_PCP: |
| case EM_ST100: |
| case EM_ST19: |
| case EM_ST7: |
| case EM_ST9PLUS: |
| case EM_STARCORE: |
| case EM_SVX: |
| case EM_TINYJ: |
| default: |
| warn (_("Don't know about relocations on this machine architecture\n")); |
| return false; |
| } |
| } |
| |
| /* Load RELA type relocations from FILEDATA at REL_OFFSET extending for REL_SIZE bytes. |
| Returns TRUE upon success, FALSE otherwise. If successful then a |
| pointer to a malloc'ed buffer containing the relocs is placed in *RELASP, |
| and the number of relocs loaded is placed in *NRELASP. It is the caller's |
| responsibility to free the allocated buffer. */ |
| |
| static bool |
| slurp_rela_relocs (Filedata *filedata, |
| uint64_t rel_offset, |
| uint64_t rel_size, |
| Elf_Internal_Rela **relasp, |
| uint64_t *nrelasp) |
| { |
| Elf_Internal_Rela * relas; |
| uint64_t nrelas; |
| unsigned int i; |
| |
| if (is_32bit_elf) |
| { |
| Elf32_External_Rela * erelas; |
| |
| erelas = (Elf32_External_Rela *) get_data (NULL, filedata, rel_offset, 1, |
| rel_size, _("32-bit relocation data")); |
| if (!erelas) |
| return false; |
| |
| nrelas = rel_size / sizeof (Elf32_External_Rela); |
| |
| relas = (Elf_Internal_Rela *) cmalloc (nrelas, |
| sizeof (Elf_Internal_Rela)); |
| |
| if (relas == NULL) |
| { |
| free (erelas); |
| error (_("out of memory parsing relocs\n")); |
| return false; |
| } |
| |
| for (i = 0; i < nrelas; i++) |
| { |
| relas[i].r_offset = BYTE_GET (erelas[i].r_offset); |
| relas[i].r_info = BYTE_GET (erelas[i].r_info); |
| relas[i].r_addend = BYTE_GET_SIGNED (erelas[i].r_addend); |
| } |
| |
| free (erelas); |
| } |
| else |
| { |
| Elf64_External_Rela * erelas; |
| |
| erelas = (Elf64_External_Rela *) get_data (NULL, filedata, rel_offset, 1, |
| rel_size, _("64-bit relocation data")); |
| if (!erelas) |
| return false; |
| |
| nrelas = rel_size / sizeof (Elf64_External_Rela); |
| |
| relas = (Elf_Internal_Rela *) cmalloc (nrelas, |
| sizeof (Elf_Internal_Rela)); |
| |
| if (relas == NULL) |
| { |
| free (erelas); |
| error (_("out of memory parsing relocs\n")); |
| return false; |
| } |
| |
| for (i = 0; i < nrelas; i++) |
| { |
| relas[i].r_offset = BYTE_GET (erelas[i].r_offset); |
| relas[i].r_info = BYTE_GET (erelas[i].r_info); |
| relas[i].r_addend = BYTE_GET_SIGNED (erelas[i].r_addend); |
| |
| if (filedata->file_header.e_machine == EM_MIPS |
| && filedata->file_header.e_ident[EI_DATA] != ELFDATA2MSB) |
| { |
| /* In little-endian objects, r_info isn't really a |
| 64-bit little-endian value: it has a 32-bit |
| little-endian symbol index followed by four |
| individual byte fields. Reorder INFO |
| accordingly. */ |
| uint64_t inf = relas[i].r_info; |
| inf = (((inf & 0xffffffff) << 32) |
| | ((inf >> 56) & 0xff) |
| | ((inf >> 40) & 0xff00) |
| | ((inf >> 24) & 0xff0000) |
| | ((inf >> 8) & 0xff000000)); |
| relas[i].r_info = inf; |
| } |
| } |
| |
| free (erelas); |
| } |
| |
| *relasp = relas; |
| *nrelasp = nrelas; |
| return true; |
| } |
| |
| /* Load REL type relocations from FILEDATA at REL_OFFSET extending for REL_SIZE bytes. |
| Returns TRUE upon success, FALSE otherwise. If successful then a |
| pointer to a malloc'ed buffer containing the relocs is placed in *RELSP, |
| and the number of relocs loaded is placed in *NRELSP. It is the caller's |
| responsibility to free the allocated buffer. */ |
| |
| static bool |
| slurp_rel_relocs (Filedata *filedata, |
| uint64_t rel_offset, |
| uint64_t rel_size, |
| Elf_Internal_Rela **relsp, |
| uint64_t *nrelsp) |
| { |
| Elf_Internal_Rela * rels; |
| uint64_t nrels; |
| unsigned int i; |
| |
| if (is_32bit_elf) |
| { |
| Elf32_External_Rel * erels; |
| |
| erels = (Elf32_External_Rel *) get_data (NULL, filedata, rel_offset, 1, |
| rel_size, _("32-bit relocation data")); |
| if (!erels) |
| return false; |
| |
| nrels = rel_size / sizeof (Elf32_External_Rel); |
| |
| rels = (Elf_Internal_Rela *) cmalloc (nrels, sizeof (Elf_Internal_Rela)); |
| |
| if (rels == NULL) |
| { |
| free (erels); |
| error (_("out of memory parsing relocs\n")); |
| return false; |
| } |
| |
| for (i = 0; i < nrels; i++) |
| { |
| rels[i].r_offset = BYTE_GET (erels[i].r_offset); |
| rels[i].r_info = BYTE_GET (erels[i].r_info); |
| rels[i].r_addend = 0; |
| } |
| |
| free (erels); |
| } |
| else |
| { |
| Elf64_External_Rel * erels; |
| |
| erels = (Elf64_External_Rel *) get_data (NULL, filedata, rel_offset, 1, |
| rel_size, _("64-bit relocation data")); |
| if (!erels) |
| return false; |
| |
| nrels = rel_size / sizeof (Elf64_External_Rel); |
| |
| rels = (Elf_Internal_Rela *) cmalloc (nrels, sizeof (Elf_Internal_Rela)); |
| |
| if (rels == NULL) |
| { |
| free (erels); |
| error (_("out of memory parsing relocs\n")); |
| return false; |
| } |
| |
| for (i = 0; i < nrels; i++) |
| { |
| rels[i].r_offset = BYTE_GET (erels[i].r_offset); |
| rels[i].r_info = BYTE_GET (erels[i].r_info); |
| rels[i].r_addend = 0; |
| |
| if (filedata->file_header.e_machine == EM_MIPS |
| && filedata->file_header.e_ident[EI_DATA] != ELFDATA2MSB) |
| { |
| /* In little-endian objects, r_info isn't really a |
| 64-bit little-endian value: it has a 32-bit |
| little-endian symbol index followed by four |
| individual byte fields. Reorder INFO |
| accordingly. */ |
| uint64_t inf = rels[i].r_info; |
| inf = (((inf & 0xffffffff) << 32) |
| | ((inf >> 56) & 0xff) |
| | ((inf >> 40) & 0xff00) |
| | ((inf >> 24) & 0xff0000) |
| | ((inf >> 8) & 0xff000000)); |
| rels[i].r_info = inf; |
| } |
| } |
| |
| free (erels); |
| } |
| |
| *relsp = rels; |
| *nrelsp = nrels; |
| return true; |
| } |
| |
| /* Returns the reloc type extracted from the reloc info field. */ |
| |
| static unsigned int |
| get_reloc_type (Filedata * filedata, uint64_t reloc_info) |
| { |
| if (is_32bit_elf) |
| return ELF32_R_TYPE (reloc_info); |
| |
| switch (filedata->file_header.e_machine) |
| { |
| case EM_MIPS: |
| /* Note: We assume that reloc_info has already been adjusted for us. */ |
| return ELF64_MIPS_R_TYPE (reloc_info); |
| |
| case EM_SPARCV9: |
| return ELF64_R_TYPE_ID (reloc_info); |
| |
| default: |
| return ELF64_R_TYPE (reloc_info); |
| } |
| } |
| |
| /* Return the symbol index extracted from the reloc info field. */ |
| |
| static uint64_t |
| get_reloc_symindex (uint64_t reloc_info) |
| { |
| return is_32bit_elf ? ELF32_R_SYM (reloc_info) : ELF64_R_SYM (reloc_info); |
| } |
| |
| static inline bool |
| uses_msp430x_relocs (Filedata * filedata) |
| { |
| return |
| filedata->file_header.e_machine == EM_MSP430 /* Paranoia. */ |
| /* GCC uses osabi == ELFOSBI_STANDALONE. */ |
| && (((filedata->file_header.e_flags & EF_MSP430_MACH) == E_MSP430_MACH_MSP430X) |
| /* TI compiler uses ELFOSABI_NONE. */ |
| || (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_NONE)); |
| } |
| |
| |
| static const char * |
| get_symbol_at (Filedata * filedata, |
| Elf_Internal_Sym * symtab, |
| uint64_t nsyms, |
| char * strtab, |
| uint64_t where, |
| uint64_t * offset_return) |
| { |
| Elf_Internal_Sym * beg = symtab; |
| Elf_Internal_Sym * end = symtab + nsyms; |
| Elf_Internal_Sym * best = NULL; |
| uint64_t dist = 0x100000; |
| |
| /* FIXME: Since this function is likely to be called repeatedly with |
| slightly increasing addresses each time, we could speed things up by |
| caching the last returned value and starting our search from there. */ |
| while (beg < end) |
| { |
| Elf_Internal_Sym * sym; |
| uint64_t value; |
| |
| sym = beg + (end - beg) / 2; |
| |
| value = sym->st_value; |
| |
| if (where >= value |
| && where - value < dist) |
| { |
| best = sym; |
| dist = where - value; |
| if (dist == 0) |
| break; |
| } |
| |
| if (where < value) |
| end = sym; |
| else |
| beg = sym + 1; |
| } |
| |
| const char *name; |
| |
| /* If there is a section start closer than the found symbol then |
| use that for symbolizing the address. */ |
| Elf_Internal_Shdr *sec = find_section_by_address (filedata, where); |
| if (sec != NULL |
| && where - sec->sh_addr < dist |
| && section_name_valid (filedata, sec)) |
| { |
| name = section_name (filedata, sec); |
| dist = where - sec->sh_addr; |
| } |
| else if (best != NULL) |
| name = strtab + best->st_name; |
| else |
| return NULL; |
| |
| if (offset_return != NULL) |
| * offset_return = dist; |
| |
| return name; |
| } |
| |
| static void |
| print_relr_addr_and_sym (Filedata * filedata, |
| Elf_Internal_Sym * symtab, |
| uint64_t nsyms, |
| char * strtab, |
| uint64_t where) |
| { |
| const char * symname = NULL; |
| uint64_t offset = 0; |
| |
| print_vma (where, ZERO_HEX); |
| printf (" "); |
| |
| symname = get_symbol_at (filedata, symtab, nsyms, strtab, where, & offset); |
| |
| if (symname == NULL) |
| printf ("<no sym>"); |
| else if (offset == 0) |
| print_symbol_name (38, symname); |
| else |
| { |
| print_symbol_name (28, symname); |
| printf (" + "); |
| print_vma (offset, PREFIX_HEX); |
| } |
| } |
| |
| /* See bfd_is_aarch64_special_symbol_name. */ |
| |
| static bool |
| is_aarch64_special_symbol_name (const char *name) |
| { |
| if (!name || name[0] != '$') |
| return false; |
| if (name[1] == 'x' || name[1] == 'd') |
| /* Map. */; |
| else if (name[1] == 'm' || name[1] == 'f' || name[1] == 'p') |
| /* Tag. */; |
| else |
| return false; |
| return name[2] == 0 || name[2] == '.'; |
| } |
| |
| static bool |
| is_special_symbol_name (Filedata * filedata, const char * s) |
| { |
| switch (filedata->file_header.e_machine) |
| { |
| case EM_AARCH64: |
| return is_aarch64_special_symbol_name (s); |
| |
| default: |
| return false; |
| } |
| } |
| |
| /* Allows selecting the best symbol from a set for displaying addresses. |
| BEST is the current best or NULL if there are no good symbols yet. |
| SYM is the next symbol to consider, if it is better than BEST then |
| return SYM else return BEST. */ |
| |
| static Elf_Internal_Sym * |
| select_display_sym (Filedata * filedata, |
| char * strtab, |
| uint64_t strtablen, |
| Elf_Internal_Sym * best, |
| Elf_Internal_Sym * sym) |
| { |
| /* Ignore empty or invalid syms. */ |
| if (sym->st_name == 0) |
| return best; |
| if (sym->st_name >= strtablen) |
| return best; |
| /* Ignore undefined or TLS syms. */ |
| if (sym->st_shndx == SHN_UNDEF) |
| return best; |
| if (ELF_ST_TYPE (sym->st_info) == STT_TLS) |
| return best; |
| |
| char *s = strtab + sym->st_name; |
| |
| /* Don't display special symbols. */ |
| if (is_special_symbol_name (filedata, s)) |
| return best; |
| |
| /* Here SYM is good for display. */ |
| |
| if (best == NULL) |
| return sym; |
| |
| char *sbest = strtab + best->st_name; |
| |
| /* Prefer non-local symbols. */ |
| if (ELF_ST_BIND (sym->st_info) == STB_LOCAL |
| && ELF_ST_BIND (best->st_info) != STB_LOCAL) |
| return best; |
| if (ELF_ST_BIND (sym->st_info) != STB_LOCAL |
| && ELF_ST_BIND (best->st_info) == STB_LOCAL) |
| return sym; |
| |
| /* Select based on lexicographic order. */ |
| return strcmp (s, sbest) < 0 ? sym : best; |
| } |
| |
| /* Filter the sorted SYMTAB symbol array in-place to select at most one |
| symbol for an address and drop symbols that are not good to display. |
| Returns the new array length. */ |
| |
| static uint64_t |
| filter_display_syms (Filedata * filedata, |
| Elf_Internal_Sym * symtab, |
| uint64_t nsyms, |
| char * strtab, |
| uint64_t strtablen) |
| { |
| Elf_Internal_Sym *r = symtab; |
| Elf_Internal_Sym *w = symtab; |
| Elf_Internal_Sym *best = NULL; |
| Elf_Internal_Sym *end = symtab + nsyms; |
| while (r < end) |
| { |
| /* Select the best symbol for an address. */ |
| while (r < end |
| && (best == NULL || best->st_value == r->st_value)) |
| { |
| best = select_display_sym (filedata, strtab, strtablen, best, r); |
| r++; |
| } |
| if (best != NULL) |
| { |
| *w = *best; |
| w++; |
| best = NULL; |
| } |
| } |
| return w - symtab; |
| } |
| |
| static /* signed */ int |
| symcmp (const void *p, const void *q) |
| { |
| Elf_Internal_Sym *sp = (Elf_Internal_Sym *) p; |
| Elf_Internal_Sym *sq = (Elf_Internal_Sym *) q; |
| |
| return sp->st_value > sq->st_value ? 1 : (sp->st_value < sq->st_value ? -1 : 0); |
| } |
| |
| static uint64_t |
| count_relr_relocations (Filedata * filedata, |
| Elf_Internal_Shdr * section) |
| { |
| uint64_t * relrs; |
| uint64_t nentries; |
| uint64_t i; |
| uint64_t count; |
| int entsize; |
| |
| if (section == NULL |
| || section->sh_type != SHT_RELR |
| || section->sh_size == 0) |
| return 0; |
| |
| entsize = section->sh_entsize; |
| if (entsize == 0) |
| entsize = is_32bit_elf |
| ? sizeof (Elf32_External_Relr) : sizeof (Elf64_External_Relr); |
| else if (entsize != sizeof (Elf32_External_Relr) |
| && entsize != sizeof (Elf64_External_Relr)) |
| return 0; |
| |
| nentries = section->sh_size / entsize; |
| if (nentries == 0) |
| return 0; |
| |
| /* FIXME: This call to get_data duplicates one that follows in |
| dump_relr_relocations(). They could be combined into just |
| one call. */ |
| relrs = get_data (NULL, filedata, section->sh_offset, 1, |
| section->sh_size, _("RELR relocation data")); |
| if (relrs == NULL) |
| return 0; |
| |
| for (count = i = 0; i < nentries; i++) |
| { |
| uint64_t entry; |
| |
| if (entsize == sizeof (Elf32_External_Relr)) |
| entry = BYTE_GET (((Elf32_External_Relr *)relrs)[i].r_data); |
| else |
| entry = BYTE_GET (((Elf64_External_Relr *)relrs)[i].r_data); |
| |
| if ((entry & 1) == 0) |
| { |
| ++ count; |
| } |
| else |
| { |
| if (entry == 1) |
| continue; |
| |
| for (; entry >>= 1;) |
| if ((entry & 1) == 1) |
| ++ count; |
| } |
| } |
| |
| free (relrs); |
| return count; |
| } |
| |
| static bool |
| dump_relr_relocations (Filedata * filedata, |
| Elf_Internal_Shdr * section, |
| Elf_Internal_Sym * symtab, |
| uint64_t nsyms, |
| char * strtab, |
| uint64_t strtablen) |
| { |
| uint64_t * relrs; |
| uint64_t nentries, i; |
| uint64_t relr_size = section->sh_size; |
| int relr_entsize = section->sh_entsize; |
| uint64_t relr_offset = section->sh_offset; |
| uint64_t where = 0; |
| int num_bits_in_entry; |
| |
| if (relr_entsize == 0) |
| relr_entsize = is_32bit_elf |
| ? sizeof (Elf32_External_Relr) : sizeof (Elf64_External_Relr); |
| |
| nentries = relr_size / relr_entsize; |
| |
| if (nentries == 0) |
| return true; |
| |
| if (relr_entsize == sizeof (Elf32_External_Relr)) |
| num_bits_in_entry = 31; |
| else if (relr_entsize == sizeof (Elf64_External_Relr)) |
| num_bits_in_entry = 63; |
| else |
| { |
| warn (_("Unexpected entsize for RELR section\n")); |
| return false; |
| } |
| |
| relrs = get_data (NULL, filedata, relr_offset, 1, relr_size, _("RELR relocation data")); |
| if (relrs == NULL) |
| return false; |
| |
| /* Paranoia. */ |
| if (strtab == NULL) |
| strtablen = 0; |
| if (symtab == NULL) |
| nsyms = 0; |
| |
| if (symtab != NULL) |
| { |
| /* Symbol tables are not sorted on address, but we want a quick lookup |
| for the symbol associated with each address computed below, so sort |
| the table then filter out unwanted entries. FIXME: This assumes that |
| the symbol table will not be used later on for some other purpose. */ |
| qsort (symtab, nsyms, sizeof (Elf_Internal_Sym), symcmp); |
| nsyms = filter_display_syms (filedata, symtab, nsyms, strtab, strtablen); |
| } |
| |
| if (relr_entsize == sizeof (Elf32_External_Relr)) |
| printf (_ ("Index: Entry Address Symbolic Address\n")); |
| else |
| printf (_ ("Index: Entry Address Symbolic Address\n")); |
| |
| for (i = 0; i < nentries; i++) |
| { |
| uint64_t entry; |
| |
| if (relr_entsize == sizeof (Elf32_External_Relr)) |
| entry = BYTE_GET (((Elf32_External_Relr *)relrs)[i].r_data); |
| else |
| entry = BYTE_GET (((Elf64_External_Relr *)relrs)[i].r_data); |
| |
| /* We assume that there will never be more than 9999 entries. */ |
| printf (_("%04u: "), (unsigned int) i); |
| print_vma (entry, ZERO_HEX); |
| printf (" "); |
| |
| if ((entry & 1) == 0) |
| { |
| where = entry; |
| print_relr_addr_and_sym (filedata, symtab, nsyms, strtab, where); |
| printf ("\n"); |
| where += relr_entsize; |
| } |
| else |
| { |
| bool first = true; |
| int j; |
| |
| /* The least significant bit is ignored. */ |
| if (entry == 1) |
| /* This can actually happen when the linker is allowed to shrink |
| RELR sections. For more details see: https://reviews.llvm.org/D67164. */ |
| continue; |
| else if (i == 0) |
| warn (_("Unusual RELR bitmap - no previous entry to set the base address\n")); |
| |
| for (j = 0; entry >>= 1; j++) |
| if ((entry & 1) == 1) |
| { |
| uint64_t addr = where + (j * relr_entsize); |
| |
| if (first) |
| { |
| print_relr_addr_and_sym (filedata, symtab, nsyms, strtab, addr); |
| first = false; |
| } |
| else |
| { |
| printf (_("\n%*s "), relr_entsize == 4 ? 15 : 23, " "); |
| print_relr_addr_and_sym (filedata, symtab, nsyms, strtab, addr); |
| } |
| } |
| |
| printf ("\n"); |
| where += num_bits_in_entry * relr_entsize; |
| } |
| } |
| |
| free (relrs); |
| return true; |
| } |
| |
| /* Display the contents of the relocation data found at the specified |
| offset. */ |
| |
| static bool |
| dump_relocations (Filedata * filedata, |
| uint64_t rel_offset, |
| uint64_t rel_size, |
| Elf_Internal_Sym * symtab, |
| uint64_t nsyms, |
| char * strtab, |
| uint64_t strtablen, |
| relocation_type rel_type, |
| bool is_dynsym) |
| { |
| size_t i; |
| Elf_Internal_Rela * rels; |
| bool res = true; |
| |
| if (rel_type == reltype_unknown) |
| rel_type = guess_is_rela (filedata->file_header.e_machine) ? reltype_rela : reltype_rel; |
| |
| if (rel_type == reltype_rela) |
| { |
| if (!slurp_rela_relocs (filedata, rel_offset, rel_size, &rels, &rel_size)) |
| return false; |
| } |
| else if (rel_type == reltype_rel) |
| { |
| if (!slurp_rel_relocs (filedata, rel_offset, rel_size, &rels, &rel_size)) |
| return false; |
| } |
| else if (rel_type == reltype_relr) |
| { |
| /* This should have been handled by display_relocations(). */ |
| return false; |
| } |
| |
| if (is_32bit_elf) |
| { |
| if (rel_type == reltype_rela) |
| { |
| if (do_wide) |
| printf (_(" Offset Info Type Sym. Value Symbol's Name + Addend\n")); |
| else |
| printf (_(" Offset Info Type Sym.Value Sym. Name + Addend\n")); |
| } |
| else |
| { |
| if (do_wide) |
| printf (_(" Offset Info Type Sym. Value Symbol's Name\n")); |
| else |
| printf (_(" Offset Info Type Sym.Value Sym. Name\n")); |
| } |
| } |
| else |
| { |
| if (rel_type == reltype_rela) |
| { |
| if (do_wide) |
| printf (_(" Offset Info Type Symbol's Value Symbol's Name + Addend\n")); |
| else |
| printf (_(" Offset Info Type Sym. Value Sym. Name + Addend\n")); |
| } |
| else |
| { |
| if (do_wide) |
| printf (_(" Offset Info Type Symbol's Value Symbol's Name\n")); |
| else |
| printf (_(" Offset Info Type Sym. Value Sym. Name\n")); |
| } |
| } |
| |
| for (i = 0; i < rel_size; i++) |
| { |
| const char * rtype; |
| uint64_t offset; |
| uint64_t inf; |
| uint64_t symtab_index; |
| uint64_t type; |
| |
| offset = rels[i].r_offset; |
| inf = rels[i].r_info; |
| |
| type = get_reloc_type (filedata, inf); |
| symtab_index = get_reloc_symindex (inf); |
| |
| if (is_32bit_elf) |
| { |
| printf ("%8.8lx %8.8lx ", |
| (unsigned long) offset & 0xffffffff, |
| (unsigned long) inf & 0xffffffff); |
| } |
| else |
| { |
| printf (do_wide |
| ? "%16.16" PRIx64 " %16.16" PRIx64 " " |
| : "%12.12" PRIx64 " %12.12" PRIx64 " ", |
| offset, inf); |
| } |
| |
| switch (filedata->file_header.e_machine) |
| { |
| default: |
| rtype = NULL; |
| break; |
| |
| case EM_AARCH64: |
| rtype = elf_aarch64_reloc_type (type); |
| break; |
| |
| case EM_M32R: |
| case EM_CYGNUS_M32R: |
| rtype = elf_m32r_reloc_type (type); |
| break; |
| |
| case EM_386: |
| case EM_IAMCU: |
| rtype = elf_i386_reloc_type (type); |
| break; |
| |
| case EM_68HC11: |
| case EM_68HC12: |
| rtype = elf_m68hc11_reloc_type (type); |
| break; |
| |
| case EM_S12Z: |
| rtype = elf_s12z_reloc_type (type); |
| break; |
| |
| case EM_68K: |
| rtype = elf_m68k_reloc_type (type); |
| break; |
| |
| case EM_960: |
| rtype = elf_i960_reloc_type (type); |
| break; |
| |
| case EM_AVR: |
| case EM_AVR_OLD: |
| rtype = elf_avr_reloc_type (type); |
| break; |
| |
| case EM_OLD_SPARCV9: |
| case EM_SPARC32PLUS: |
| case EM_SPARCV9: |
| case EM_SPARC: |
| rtype = elf_sparc_reloc_type (type); |
| break; |
| |
| case EM_SPU: |
| rtype = elf_spu_reloc_type (type); |
| break; |
| |
| case EM_V800: |
| rtype = v800_reloc_type (type); |
| break; |
| case EM_V850: |
| case EM_CYGNUS_V850: |
| rtype = v850_reloc_type (type); |
| break; |
| |
| case EM_D10V: |
| case EM_CYGNUS_D10V: |
| rtype = elf_d10v_reloc_type (type); |
| break; |
| |
| case EM_D30V: |
| case EM_CYGNUS_D30V: |
| rtype = elf_d30v_reloc_type (type); |
| break; |
| |
| case EM_DLX: |
| rtype = elf_dlx_reloc_type (type); |
| break; |
| |
| case EM_SH: |
| rtype = elf_sh_reloc_type (type); |
| break; |
| |
| case EM_MN10300: |
| case EM_CYGNUS_MN10300: |
| rtype = elf_mn10300_reloc_type (type); |
| break; |
| |
| case EM_MN10200: |
| case EM_CYGNUS_MN10200: |
| rtype = elf_mn10200_reloc_type (type); |
| break; |
| |
| case EM_FR30: |
| case EM_CYGNUS_FR30: |
| rtype = elf_fr30_reloc_type (type); |
| break; |
| |
| case EM_CYGNUS_FRV: |
| rtype = elf_frv_reloc_type (type); |
| break; |
| |
| case EM_CSKY: |
| rtype = elf_csky_reloc_type (type); |
| break; |
| |
| case EM_FT32: |
| rtype = elf_ft32_reloc_type (type); |
| break; |
| |
| case EM_MCORE: |
| rtype = elf_mcore_reloc_type (type); |
| break; |
| |
| case EM_MMIX: |
| rtype = elf_mmix_reloc_type (type); |
| break; |
| |
| case EM_MOXIE: |
| rtype = elf_moxie_reloc_type (type); |
| break; |
| |
| case EM_MSP430: |
| if (uses_msp430x_relocs (filedata)) |
| { |
| rtype = elf_msp430x_reloc_type (type); |
| break; |
| } |
| /* Fall through. */ |
| case EM_MSP430_OLD: |
| rtype = elf_msp430_reloc_type (type); |
| break; |
| |
| case EM_NDS32: |
| rtype = elf_nds32_reloc_type (type); |
| break; |
| |
| case EM_PPC: |
| rtype = elf_ppc_reloc_type (type); |
| break; |
| |
| case EM_PPC64: |
| rtype = elf_ppc64_reloc_type (type); |
| break; |
| |
| case EM_MIPS: |
| case EM_MIPS_RS3_LE: |
| rtype = elf_mips_reloc_type (type); |
| break; |
| |
| case EM_RISCV: |
| rtype = elf_riscv_reloc_type (type); |
| break; |
| |
| case EM_ALPHA: |
| rtype = elf_alpha_reloc_type (type); |
| break; |
| |
| case EM_ARM: |
| rtype = elf_arm_reloc_type (type); |
| break; |
| |
| case EM_ARC: |
| case EM_ARC_COMPACT: |
| case EM_ARC_COMPACT2: |
| case EM_ARC_COMPACT3: |
| case EM_ARC_COMPACT3_64: |
| rtype = elf_arc_reloc_type (type); |
| break; |
| |
| case EM_PARISC: |
| rtype = elf_hppa_reloc_type (type); |
| break; |
| |
| case EM_H8_300: |
| case EM_H8_300H: |
| case EM_H8S: |
| rtype = elf_h8_reloc_type (type); |
| break; |
| |
| case EM_OR1K: |
| rtype = elf_or1k_reloc_type (type); |
| break; |
| |
| case EM_PJ: |
| case EM_PJ_OLD: |
| rtype = elf_pj_reloc_type (type); |
| break; |
| case EM_IA_64: |
| rtype = elf_ia64_reloc_type (type); |
| break; |
| |
| case EM_KVX: |
| rtype = elf_kvx_reloc_type (type); |
| break; |
| |
| case EM_CRIS: |
| rtype = elf_cris_reloc_type (type); |
| break; |
| |
| case EM_860: |
| rtype = elf_i860_reloc_type (type); |
| break; |
| |
| case EM_X86_64: |
| case EM_L1OM: |
| case EM_K1OM: |
| rtype = elf_x86_64_reloc_type (type); |
| break; |
| |
| case EM_S370: |
| rtype = i370_reloc_type (type); |
| break; |
| |
| case EM_S390_OLD: |
| case EM_S390: |
| rtype = elf_s390_reloc_type (type); |
| break; |
| |
| case EM_SCORE: |
| rtype = elf_score_reloc_type (type); |
| break; |
| |
| case EM_XSTORMY16: |
| rtype = elf_xstormy16_reloc_type (type); |
| break; |
| |
| case EM_CRX: |
| rtype = elf_crx_reloc_type (type); |
| break; |
| |
| case EM_VAX: |
| rtype = elf_vax_reloc_type (type); |
| break; |
| |
| case EM_VISIUM: |
| rtype = elf_visium_reloc_type (type); |
| break; |
| |
| case EM_BPF: |
| rtype = elf_bpf_reloc_type (type); |
| break; |
| |
| case EM_ADAPTEVA_EPIPHANY: |
| rtype = elf_epiphany_reloc_type (type); |
| break; |
| |
| case EM_IP2K: |
| case EM_IP2K_OLD: |
| rtype = elf_ip2k_reloc_type (type); |
| break; |
| |
| case EM_IQ2000: |
| rtype = elf_iq2000_reloc_type (type); |
| break; |
| |
| case EM_XTENSA_OLD: |
| case EM_XTENSA: |
| rtype = elf_xtensa_reloc_type (type); |
| break; |
| |
| case EM_LATTICEMICO32: |
| rtype = elf_lm32_reloc_type (type); |
| break; |
| |
| case EM_M32C_OLD: |
| case EM_M32C: |
| rtype = elf_m32c_reloc_type (type); |
| break; |
| |
| case EM_MT: |
| rtype = elf_mt_reloc_type (type); |
| break; |
| |
| case EM_BLACKFIN: |
| rtype = elf_bfin_reloc_type (type); |
| break; |
| |
| case EM_CYGNUS_MEP: |
| rtype = elf_mep_reloc_type (type); |
| break; |
| |
| case EM_CR16: |
| rtype = elf_cr16_reloc_type (type); |
| break; |
| |
| case EM_MICROBLAZE: |
| case EM_MICROBLAZE_OLD: |
| rtype = elf_microblaze_reloc_type (type); |
| break; |
| |
| case EM_RL78: |
| rtype = elf_rl78_reloc_type (type); |
| break; |
| |
| case EM_RX: |
| rtype = elf_rx_reloc_type (type); |
| break; |
| |
| case EM_METAG: |
| rtype = elf_metag_reloc_type (type); |
| break; |
| |
| case EM_TI_C6000: |
| rtype = elf_tic6x_reloc_type (type); |
| break; |
| |
| case EM_TILEGX: |
| rtype = elf_tilegx_reloc_type (type); |
| break; |
| |
| case EM_TILEPRO: |
| rtype = elf_tilepro_reloc_type (type); |
| break; |
| |
| case EM_WEBASSEMBLY: |
| rtype = elf_wasm32_reloc_type (type); |
| break; |
| |
| case EM_XGATE: |
| rtype = elf_xgate_reloc_type (type); |
| break; |
| |
| case EM_ALTERA_NIOS2: |
| rtype = elf_nios2_reloc_type (type); |
| break; |
| |
| case EM_TI_PRU: |
| rtype = elf_pru_reloc_type (type); |
| break; |
| |
| case EM_NFP: |
| if (EF_NFP_MACH (filedata->file_header.e_flags) == E_NFP_MACH_3200) |
| rtype = elf_nfp3200_reloc_type (type); |
| else |
| rtype = elf_nfp_reloc_type (type); |
| break; |
| |
| case EM_Z80: |
| rtype = elf_z80_reloc_type (type); |
| break; |
| |
| case EM_LOONGARCH: |
| rtype = elf_loongarch_reloc_type (type); |
| break; |
| |
| case EM_AMDGPU: |
| rtype = elf_amdgpu_reloc_type (type); |
| break; |
| } |
| |
| if (rtype == NULL) |
| printf (_("unrecognized: %-7lx"), (unsigned long) type & 0xffffffff); |
| else |
| printf (do_wide ? "%-22s" : "%-17.17s", rtype); |
| |
| if (filedata->file_header.e_machine == EM_ALPHA |
| && rtype != NULL |
| && streq (rtype, "R_ALPHA_LITUSE") |
| && rel_type == reltype_rela) |
| { |
| switch (rels[i].r_addend) |
| { |
| case LITUSE_ALPHA_ADDR: rtype = "ADDR"; break; |
| case LITUSE_ALPHA_BASE: rtype = "BASE"; break; |
| case LITUSE_ALPHA_BYTOFF: rtype = "BYTOFF"; break; |
| case LITUSE_ALPHA_JSR: rtype = "JSR"; break; |
| case LITUSE_ALPHA_TLSGD: rtype = "TLSGD"; break; |
| case LITUSE_ALPHA_TLSLDM: rtype = "TLSLDM"; break; |
| case LITUSE_ALPHA_JSRDIRECT: rtype = "JSRDIRECT"; break; |
| default: rtype = NULL; |
| } |
| |
| if (rtype) |
| printf (" (%s)", rtype); |
| else |
| { |
| putchar (' '); |
| printf (_("<unknown addend: %" PRIx64 ">"), |
| rels[i].r_addend); |
| res = false; |
| } |
| } |
| else if (symtab_index) |
| { |
| if (symtab == NULL || symtab_index >= nsyms) |
| { |
| error (_(" bad symbol index: %08lx in reloc\n"), |
| (unsigned long) symtab_index); |
| res = false; |
| } |
| else |
| { |
| Elf_Internal_Sym * psym; |
| const char * version_string; |
| enum versioned_symbol_info sym_info; |
| unsigned short vna_other; |
| |
| psym = symtab + symtab_index; |
| |
| version_string |
| = get_symbol_version_string (filedata, is_dynsym, |
| strtab, strtablen, |
| symtab_index, |
| psym, |
| &sym_info, |
| &vna_other); |
| |
| printf (" "); |
| |
| if (ELF_ST_TYPE (psym->st_info) == STT_GNU_IFUNC) |
| { |
| const char * name; |
| unsigned int len; |
| unsigned int width = is_32bit_elf ? 8 : 14; |
| |
| /* Relocations against GNU_IFUNC symbols do not use the value |
| of the symbol as the address to relocate against. Instead |
| they invoke the function named by the symbol and use its |
| result as the address for relocation. |
| |
| To indicate this to the user, do not display the value of |
| the symbol in the "Symbols's Value" field. Instead show |
| its name followed by () as a hint that the symbol is |
| invoked. */ |
| |
| if (strtab == NULL |
| || psym->st_name == 0 |
| || psym->st_name >= strtablen) |
| name = "??"; |
| else |
| name = strtab + psym->st_name; |
| |
| len = print_symbol_name (width, name); |
| if (version_string) |
| printf (sym_info == symbol_public ? "@@%s" : "@%s", |
| version_string); |
| printf ("()%-*s", len <= width ? (width + 1) - len : 1, " "); |
| } |
| else |
| { |
| print_vma (psym->st_value, LONG_HEX); |
| |
| printf (is_32bit_elf ? " " : " "); |
| } |
| |
| if (psym->st_name == 0) |
| { |
| const char * sec_name = "<null>"; |
| |
| if (ELF_ST_TYPE (psym->st_info) == STT_SECTION) |
| sec_name = printable_section_name_from_index |
| (filedata, psym->st_shndx, NULL); |
| |
| print_symbol_name (22, sec_name); |
| } |
| else if (strtab == NULL) |
| printf (_("<string table index: %3ld>"), psym->st_name); |
| else if (psym->st_name >= strtablen) |
| { |
| error (_("<corrupt string table index: %3ld>\n"), |
| psym->st_name); |
| res = false; |
| } |
| else |
| { |
| print_symbol_name (22, strtab + psym->st_name); |
| if (version_string) |
| printf (sym_info == symbol_public ? "@@%s" : "@%s", |
| version_string); |
| } |
| |
| if (rel_type == reltype_rela) |
| { |
| uint64_t off = rels[i].r_addend; |
| |
| if ((int64_t) off < 0) |
| printf (" - %" PRIx64, -off); |
| else |
| printf (" + %" PRIx64, off); |
| } |
| } |
| } |
| else if (rel_type == reltype_rela) |
| { |
| uint64_t off = rels[i].r_addend; |
| |
| printf ("%*c", is_32bit_elf ? 12 : 20, ' '); |
| if ((int64_t) off < 0) |
| printf ("-%" PRIx64, -off); |
| else |
| printf ("%" PRIx64, off); |
| } |
| |
| if (filedata->file_header.e_machine == EM_SPARCV9 |
| && rtype != NULL |
| && streq (rtype, "R_SPARC_OLO10")) |
| printf (" + %" PRIx64, ELF64_R_TYPE_DATA (inf)); |
| |
| putchar ('\n'); |
| |
| if (! is_32bit_elf && filedata->file_header.e_machine == EM_MIPS) |
| { |
| uint64_t type2 = ELF64_MIPS_R_TYPE2 (inf); |
| uint64_t type3 = ELF64_MIPS_R_TYPE3 (inf); |
| const char * rtype2 = elf_mips_reloc_type (type2); |
| const char * rtype3 = elf_mips_reloc_type (type3); |
| |
| printf (" Type2: "); |
| |
| if (rtype2 == NULL) |
| printf (_("unrecognized: %-7lx"), |
| (unsigned long) type2 & 0xffffffff); |
| else |
| printf ("%-17.17s", rtype2); |
| |
| printf ("\n Type3: "); |
| |
| if (rtype3 == NULL) |
| printf (_("unrecognized: %-7lx"), |
| (unsigned long) type3 & 0xffffffff); |
| else |
| printf ("%-17.17s", rtype3); |
| |
| putchar ('\n'); |
| } |
| } |
| |
| free (rels); |
| |
| return res; |
| } |
| |
| static const char * |
| get_aarch64_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_AARCH64_BTI_PLT: return "AARCH64_BTI_PLT"; |
| case DT_AARCH64_PAC_PLT: return "AARCH64_PAC_PLT"; |
| case DT_AARCH64_VARIANT_PCS: return "AARCH64_VARIANT_PCS"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_mips_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_MIPS_RLD_VERSION: return "MIPS_RLD_VERSION"; |
| case DT_MIPS_TIME_STAMP: return "MIPS_TIME_STAMP"; |
| case DT_MIPS_ICHECKSUM: return "MIPS_ICHECKSUM"; |
| case DT_MIPS_IVERSION: return "MIPS_IVERSION"; |
| case DT_MIPS_FLAGS: return "MIPS_FLAGS"; |
| case DT_MIPS_BASE_ADDRESS: return "MIPS_BASE_ADDRESS"; |
| case DT_MIPS_MSYM: return "MIPS_MSYM"; |
| case DT_MIPS_CONFLICT: return "MIPS_CONFLICT"; |
| case DT_MIPS_LIBLIST: return "MIPS_LIBLIST"; |
| case DT_MIPS_LOCAL_GOTNO: return "MIPS_LOCAL_GOTNO"; |
| case DT_MIPS_CONFLICTNO: return "MIPS_CONFLICTNO"; |
| case DT_MIPS_LIBLISTNO: return "MIPS_LIBLISTNO"; |
| case DT_MIPS_SYMTABNO: return "MIPS_SYMTABNO"; |
| case DT_MIPS_UNREFEXTNO: return "MIPS_UNREFEXTNO"; |
| case DT_MIPS_GOTSYM: return "MIPS_GOTSYM"; |
| case DT_MIPS_HIPAGENO: return "MIPS_HIPAGENO"; |
| case DT_MIPS_RLD_MAP: return "MIPS_RLD_MAP"; |
| case DT_MIPS_RLD_MAP_REL: return "MIPS_RLD_MAP_REL"; |
| case DT_MIPS_DELTA_CLASS: return "MIPS_DELTA_CLASS"; |
| case DT_MIPS_DELTA_CLASS_NO: return "MIPS_DELTA_CLASS_NO"; |
| case DT_MIPS_DELTA_INSTANCE: return "MIPS_DELTA_INSTANCE"; |
| case DT_MIPS_DELTA_INSTANCE_NO: return "MIPS_DELTA_INSTANCE_NO"; |
| case DT_MIPS_DELTA_RELOC: return "MIPS_DELTA_RELOC"; |
| case DT_MIPS_DELTA_RELOC_NO: return "MIPS_DELTA_RELOC_NO"; |
| case DT_MIPS_DELTA_SYM: return "MIPS_DELTA_SYM"; |
| case DT_MIPS_DELTA_SYM_NO: return "MIPS_DELTA_SYM_NO"; |
| case DT_MIPS_DELTA_CLASSSYM: return "MIPS_DELTA_CLASSSYM"; |
| case DT_MIPS_DELTA_CLASSSYM_NO: return "MIPS_DELTA_CLASSSYM_NO"; |
| case DT_MIPS_CXX_FLAGS: return "MIPS_CXX_FLAGS"; |
| case DT_MIPS_PIXIE_INIT: return "MIPS_PIXIE_INIT"; |
| case DT_MIPS_SYMBOL_LIB: return "MIPS_SYMBOL_LIB"; |
| case DT_MIPS_LOCALPAGE_GOTIDX: return "MIPS_LOCALPAGE_GOTIDX"; |
| case DT_MIPS_LOCAL_GOTIDX: return "MIPS_LOCAL_GOTIDX"; |
| case DT_MIPS_HIDDEN_GOTIDX: return "MIPS_HIDDEN_GOTIDX"; |
| case DT_MIPS_PROTECTED_GOTIDX: return "MIPS_PROTECTED_GOTIDX"; |
| case DT_MIPS_OPTIONS: return "MIPS_OPTIONS"; |
| case DT_MIPS_INTERFACE: return "MIPS_INTERFACE"; |
| case DT_MIPS_DYNSTR_ALIGN: return "MIPS_DYNSTR_ALIGN"; |
| case DT_MIPS_INTERFACE_SIZE: return "MIPS_INTERFACE_SIZE"; |
| case DT_MIPS_RLD_TEXT_RESOLVE_ADDR: return "MIPS_RLD_TEXT_RESOLVE_ADDR"; |
| case DT_MIPS_PERF_SUFFIX: return "MIPS_PERF_SUFFIX"; |
| case DT_MIPS_COMPACT_SIZE: return "MIPS_COMPACT_SIZE"; |
| case DT_MIPS_GP_VALUE: return "MIPS_GP_VALUE"; |
| case DT_MIPS_AUX_DYNAMIC: return "MIPS_AUX_DYNAMIC"; |
| case DT_MIPS_PLTGOT: return "MIPS_PLTGOT"; |
| case DT_MIPS_RWPLT: return "MIPS_RWPLT"; |
| case DT_MIPS_XHASH: return "MIPS_XHASH"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_sparc64_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_SPARC_REGISTER: return "SPARC_REGISTER"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_ppc_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_PPC_GOT: return "PPC_GOT"; |
| case DT_PPC_OPT: return "PPC_OPT"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_ppc64_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_PPC64_GLINK: return "PPC64_GLINK"; |
| case DT_PPC64_OPD: return "PPC64_OPD"; |
| case DT_PPC64_OPDSZ: return "PPC64_OPDSZ"; |
| case DT_PPC64_OPT: return "PPC64_OPT"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_parisc_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_HP_LOAD_MAP: return "HP_LOAD_MAP"; |
| case DT_HP_DLD_FLAGS: return "HP_DLD_FLAGS"; |
| case DT_HP_DLD_HOOK: return "HP_DLD_HOOK"; |
| case DT_HP_UX10_INIT: return "HP_UX10_INIT"; |
| case DT_HP_UX10_INITSZ: return "HP_UX10_INITSZ"; |
| case DT_HP_PREINIT: return "HP_PREINIT"; |
| case DT_HP_PREINITSZ: return "HP_PREINITSZ"; |
| case DT_HP_NEEDED: return "HP_NEEDED"; |
| case DT_HP_TIME_STAMP: return "HP_TIME_STAMP"; |
| case DT_HP_CHECKSUM: return "HP_CHECKSUM"; |
| case DT_HP_GST_SIZE: return "HP_GST_SIZE"; |
| case DT_HP_GST_VERSION: return "HP_GST_VERSION"; |
| case DT_HP_GST_HASHVAL: return "HP_GST_HASHVAL"; |
| case DT_HP_EPLTREL: return "HP_GST_EPLTREL"; |
| case DT_HP_EPLTRELSZ: return "HP_GST_EPLTRELSZ"; |
| case DT_HP_FILTERED: return "HP_FILTERED"; |
| case DT_HP_FILTER_TLS: return "HP_FILTER_TLS"; |
| case DT_HP_COMPAT_FILTERED: return "HP_COMPAT_FILTERED"; |
| case DT_HP_LAZYLOAD: return "HP_LAZYLOAD"; |
| case DT_HP_BIND_NOW_COUNT: return "HP_BIND_NOW_COUNT"; |
| case DT_PLT: return "PLT"; |
| case DT_PLT_SIZE: return "PLT_SIZE"; |
| case DT_DLT: return "DLT"; |
| case DT_DLT_SIZE: return "DLT_SIZE"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_ia64_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_IA_64_PLT_RESERVE: return "IA_64_PLT_RESERVE"; |
| case DT_IA_64_VMS_SUBTYPE: return "VMS_SUBTYPE"; |
| case DT_IA_64_VMS_IMGIOCNT: return "VMS_IMGIOCNT"; |
| case DT_IA_64_VMS_LNKFLAGS: return "VMS_LNKFLAGS"; |
| case DT_IA_64_VMS_VIR_MEM_BLK_SIZ: return "VMS_VIR_MEM_BLK_SIZ"; |
| case DT_IA_64_VMS_IDENT: return "VMS_IDENT"; |
| case DT_IA_64_VMS_NEEDED_IDENT: return "VMS_NEEDED_IDENT"; |
| case DT_IA_64_VMS_IMG_RELA_CNT: return "VMS_IMG_RELA_CNT"; |
| case DT_IA_64_VMS_SEG_RELA_CNT: return "VMS_SEG_RELA_CNT"; |
| case DT_IA_64_VMS_FIXUP_RELA_CNT: return "VMS_FIXUP_RELA_CNT"; |
| case DT_IA_64_VMS_FIXUP_NEEDED: return "VMS_FIXUP_NEEDED"; |
| case DT_IA_64_VMS_SYMVEC_CNT: return "VMS_SYMVEC_CNT"; |
| case DT_IA_64_VMS_XLATED: return "VMS_XLATED"; |
| case DT_IA_64_VMS_STACKSIZE: return "VMS_STACKSIZE"; |
| case DT_IA_64_VMS_UNWINDSZ: return "VMS_UNWINDSZ"; |
| case DT_IA_64_VMS_UNWIND_CODSEG: return "VMS_UNWIND_CODSEG"; |
| case DT_IA_64_VMS_UNWIND_INFOSEG: return "VMS_UNWIND_INFOSEG"; |
| case DT_IA_64_VMS_LINKTIME: return "VMS_LINKTIME"; |
| case DT_IA_64_VMS_SEG_NO: return "VMS_SEG_NO"; |
| case DT_IA_64_VMS_SYMVEC_OFFSET: return "VMS_SYMVEC_OFFSET"; |
| case DT_IA_64_VMS_SYMVEC_SEG: return "VMS_SYMVEC_SEG"; |
| case DT_IA_64_VMS_UNWIND_OFFSET: return "VMS_UNWIND_OFFSET"; |
| case DT_IA_64_VMS_UNWIND_SEG: return "VMS_UNWIND_SEG"; |
| case DT_IA_64_VMS_STRTAB_OFFSET: return "VMS_STRTAB_OFFSET"; |
| case DT_IA_64_VMS_SYSVER_OFFSET: return "VMS_SYSVER_OFFSET"; |
| case DT_IA_64_VMS_IMG_RELA_OFF: return "VMS_IMG_RELA_OFF"; |
| case DT_IA_64_VMS_SEG_RELA_OFF: return "VMS_SEG_RELA_OFF"; |
| case DT_IA_64_VMS_FIXUP_RELA_OFF: return "VMS_FIXUP_RELA_OFF"; |
| case DT_IA_64_VMS_PLTGOT_OFFSET: return "VMS_PLTGOT_OFFSET"; |
| case DT_IA_64_VMS_PLTGOT_SEG: return "VMS_PLTGOT_SEG"; |
| case DT_IA_64_VMS_FPMODE: return "VMS_FPMODE"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_solaris_section_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case 0x6fffffee: return "SUNW_ancillary"; |
| case 0x6fffffef: return "SUNW_capchain"; |
| case 0x6ffffff0: return "SUNW_capinfo"; |
| case 0x6ffffff1: return "SUNW_symsort"; |
| case 0x6ffffff2: return "SUNW_tlssort"; |
| case 0x6ffffff3: return "SUNW_LDYNSYM"; |
| case 0x6ffffff4: return "SUNW_dof"; |
| case 0x6ffffff5: return "SUNW_cap"; |
| case 0x6ffffff6: return "SUNW_SIGNATURE"; |
| case 0x6ffffff7: return "SUNW_ANNOTATE"; |
| case 0x6ffffff8: return "SUNW_DEBUGSTR"; |
| case 0x6ffffff9: return "SUNW_DEBUG"; |
| case 0x6ffffffa: return "SUNW_move"; |
| case 0x6ffffffb: return "SUNW_COMDAT"; |
| case 0x6ffffffc: return "SUNW_syminfo"; |
| case 0x6ffffffd: return "SUNW_verdef"; |
| case 0x6ffffffe: return "SUNW_verneed"; |
| case 0x6fffffff: return "SUNW_versym"; |
| case 0x70000000: return "SPARC_GOTDATA"; |
| default: return NULL; |
| } |
| } |
| |
| static const char * |
| get_alpha_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_ALPHA_PLTRO: return "ALPHA_PLTRO"; |
| default: return NULL; |
| } |
| } |
| |
| static const char * |
| get_score_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_SCORE_BASE_ADDRESS: return "SCORE_BASE_ADDRESS"; |
| case DT_SCORE_LOCAL_GOTNO: return "SCORE_LOCAL_GOTNO"; |
| case DT_SCORE_SYMTABNO: return "SCORE_SYMTABNO"; |
| case DT_SCORE_GOTSYM: return "SCORE_GOTSYM"; |
| case DT_SCORE_UNREFEXTNO: return "SCORE_UNREFEXTNO"; |
| case DT_SCORE_HIPAGENO: return "SCORE_HIPAGENO"; |
| default: return NULL; |
| } |
| } |
| |
| static const char * |
| get_tic6x_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_C6000_GSYM_OFFSET: return "C6000_GSYM_OFFSET"; |
| case DT_C6000_GSTR_OFFSET: return "C6000_GSTR_OFFSET"; |
| case DT_C6000_DSBT_BASE: return "C6000_DSBT_BASE"; |
| case DT_C6000_DSBT_SIZE: return "C6000_DSBT_SIZE"; |
| case DT_C6000_PREEMPTMAP: return "C6000_PREEMPTMAP"; |
| case DT_C6000_DSBT_INDEX: return "C6000_DSBT_INDEX"; |
| default: return NULL; |
| } |
| } |
| |
| static const char * |
| get_nios2_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_NIOS2_GP: return "NIOS2_GP"; |
| default: return NULL; |
| } |
| } |
| |
| static const char * |
| get_solaris_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case 0x6000000d: return "SUNW_AUXILIARY"; |
| case 0x6000000e: return "SUNW_RTLDINF"; |
| case 0x6000000f: return "SUNW_FILTER"; |
| case 0x60000010: return "SUNW_CAP"; |
| case 0x60000011: return "SUNW_SYMTAB"; |
| case 0x60000012: return "SUNW_SYMSZ"; |
| case 0x60000013: return "SUNW_SORTENT"; |
| case 0x60000014: return "SUNW_SYMSORT"; |
| case 0x60000015: return "SUNW_SYMSORTSZ"; |
| case 0x60000016: return "SUNW_TLSSORT"; |
| case 0x60000017: return "SUNW_TLSSORTSZ"; |
| case 0x60000018: return "SUNW_CAPINFO"; |
| case 0x60000019: return "SUNW_STRPAD"; |
| case 0x6000001a: return "SUNW_CAPCHAIN"; |
| case 0x6000001b: return "SUNW_LDMACH"; |
| case 0x6000001d: return "SUNW_CAPCHAINENT"; |
| case 0x6000001f: return "SUNW_CAPCHAINSZ"; |
| case 0x60000021: return "SUNW_PARENT"; |
| case 0x60000023: return "SUNW_ASLR"; |
| case 0x60000025: return "SUNW_RELAX"; |
| case 0x60000029: return "SUNW_NXHEAP"; |
| case 0x6000002b: return "SUNW_NXSTACK"; |
| |
| case 0x70000001: return "SPARC_REGISTER"; |
| case 0x7ffffffd: return "AUXILIARY"; |
| case 0x7ffffffe: return "USED"; |
| case 0x7fffffff: return "FILTER"; |
| |
| default: return NULL; |
| } |
| } |
| |
| static const char * |
| get_riscv_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_RISCV_VARIANT_CC: return "RISCV_VARIANT_CC"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_x86_64_dynamic_type (unsigned long type) |
| { |
| switch (type) |
| { |
| case DT_X86_64_PLT: |
| return "DT_X86_64_PLT"; |
| case DT_X86_64_PLTSZ: |
| return "DT_X86_64_PLTSZ"; |
| case DT_X86_64_PLTENT: |
| return "DT_X86_64_PLTENT"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static const char * |
| get_dynamic_type (Filedata * filedata, unsigned long type) |
| { |
| static char buff[64]; |
| |
| switch (type) |
| { |
| case DT_NULL: return "NULL"; |
| case DT_NEEDED: return "NEEDED"; |
| case DT_PLTRELSZ: return "PLTRELSZ"; |
| case DT_PLTGOT: return "PLTGOT"; |
| case DT_HASH: return "HASH"; |
| case DT_STRTAB: return "STRTAB"; |
| case DT_SYMTAB: return "SYMTAB"; |
| case DT_RELA: return "RELA"; |
| case DT_RELASZ: return "RELASZ"; |
| case DT_RELAENT: return "RELAENT"; |
| case DT_STRSZ: return "STRSZ"; |
| case DT_SYMENT: return "SYMENT"; |
| case DT_INIT: return "INIT"; |
| case DT_FINI: return "FINI"; |
| case DT_SONAME: return "SONAME"; |
| case DT_RPATH: return "RPATH"; |
| case DT_SYMBOLIC: return "SYMBOLIC"; |
| case DT_REL: return "REL"; |
| case DT_RELSZ: return "RELSZ"; |
| case DT_RELENT: return "RELENT"; |
| case DT_RELR: return "RELR"; |
| case DT_RELRSZ: return "RELRSZ"; |
| case DT_RELRENT: return "RELRENT"; |
| case DT_PLTREL: return "PLTREL"; |
| case DT_DEBUG: return "DEBUG"; |
| case DT_TEXTREL: return "TEXTREL"; |
| case DT_JMPREL: return "JMPREL"; |
| case DT_BIND_NOW: return "BIND_NOW"; |
| case DT_INIT_ARRAY: return "INIT_ARRAY"; |
| case DT_FINI_ARRAY: return "FINI_ARRAY"; |
| case DT_INIT_ARRAYSZ: return "INIT_ARRAYSZ"; |
| case DT_FINI_ARRAYSZ: return "FINI_ARRAYSZ"; |
| case DT_RUNPATH: return "RUNPATH"; |
| case DT_FLAGS: return "FLAGS"; |
| |
| case DT_PREINIT_ARRAY: return "PREINIT_ARRAY"; |
| case DT_PREINIT_ARRAYSZ: return "PREINIT_ARRAYSZ"; |
| case DT_SYMTAB_SHNDX: return "SYMTAB_SHNDX"; |
| |
| case DT_CHECKSUM: return "CHECKSUM"; |
| case DT_PLTPADSZ: return "PLTPADSZ"; |
| case DT_MOVEENT: return "MOVEENT"; |
| case DT_MOVESZ: return "MOVESZ"; |
| case DT_FEATURE: return "FEATURE"; |
| case DT_POSFLAG_1: return "POSFLAG_1"; |
| case DT_SYMINSZ: return "SYMINSZ"; |
| case DT_SYMINENT: return "SYMINENT"; /* aka VALRNGHI */ |
| |
| case DT_ADDRRNGLO: return "ADDRRNGLO"; |
| case DT_CONFIG: return "CONFIG"; |
| case DT_DEPAUDIT: return "DEPAUDIT"; |
| case DT_AUDIT: return "AUDIT"; |
| case DT_PLTPAD: return "PLTPAD"; |
| case DT_MOVETAB: return "MOVETAB"; |
| case DT_SYMINFO: return "SYMINFO"; /* aka ADDRRNGHI */ |
| |
| case DT_VERSYM: return "VERSYM"; |
| |
| case DT_TLSDESC_GOT: return "TLSDESC_GOT"; |
| case DT_TLSDESC_PLT: return "TLSDESC_PLT"; |
| case DT_RELACOUNT: return "RELACOUNT"; |
| case DT_RELCOUNT: return "RELCOUNT"; |
| case DT_FLAGS_1: return "FLAGS_1"; |
| case DT_VERDEF: return "VERDEF"; |
| case DT_VERDEFNUM: return "VERDEFNUM"; |
| case DT_VERNEED: return "VERNEED"; |
| case DT_VERNEEDNUM: return "VERNEEDNUM"; |
| |
| case DT_AUXILIARY: return "AUXILIARY"; |
| case DT_USED: return "USED"; |
| case DT_FILTER: return "FILTER"; |
| |
| case DT_GNU_PRELINKED: return "GNU_PRELINKED"; |
| case DT_GNU_CONFLICT: return "GNU_CONFLICT"; |
| case DT_GNU_CONFLICTSZ: return "GNU_CONFLICTSZ"; |
| case DT_GNU_LIBLIST: return "GNU_LIBLIST"; |
| case DT_GNU_LIBLISTSZ: return "GNU_LIBLISTSZ"; |
| case DT_GNU_HASH: return "GNU_HASH"; |
| case DT_GNU_FLAGS_1: return "GNU_FLAGS_1"; |
| |
| default: |
| if ((type >= DT_LOPROC) && (type <= DT_HIPROC)) |
| { |
| const char * result; |
| |
| switch (filedata->file_header.e_machine) |
| { |
| case EM_AARCH64: |
| result = get_aarch64_dynamic_type (type); |
| break; |
| case EM_MIPS: |
| case EM_MIPS_RS3_LE: |
| result = get_mips_dynamic_type (type); |
| break; |
| case EM_SPARCV9: |
| result = get_sparc64_dynamic_type (type); |
| break; |
| case EM_PPC: |
| result = get_ppc_dynamic_type (type); |
| break; |
| case EM_PPC64: |
| result = get_ppc64_dynamic_type (type); |
| break; |
| case EM_IA_64: |
| result = get_ia64_dynamic_type (type); |
| break; |
| case EM_ALPHA: |
| result = get_alpha_dynamic_type (type); |
| break; |
| case EM_SCORE: |
| result = get_score_dynamic_type (type); |
| break; |
| case EM_TI_C6000: |
| result = get_tic6x_dynamic_type (type); |
| break; |
| case EM_ALTERA_NIOS2: |
| result = get_nios2_dynamic_type (type); |
| break; |
| case EM_RISCV: |
| result = get_riscv_dynamic_type (type); |
| break; |
| case EM_X86_64: |
| result = get_x86_64_dynamic_type (type); |
| break; |
| default: |
| if (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS) |
| result = get_solaris_dynamic_type (type); |
| else |
| result = NULL; |
| break; |
| } |
| |
| if (result != NULL) |
| return result; |
| |
| snprintf (buff, sizeof (buff), _("Processor Specific: %lx"), type); |
| } |
| else if (((type >= DT_LOOS) && (type <= DT_HIOS)) |
| || (filedata->file_header.e_machine == EM_PARISC |
| && (type >= OLD_DT_LOOS) && (type <= OLD_DT_HIOS))) |
| { |
| const char * result; |
| |
| switch (filedata->file_header.e_machine) |
| { |
| case EM_PARISC: |
| result = get_parisc_dynamic_type (type); |
| break; |
| case EM_IA_64: |
| result = get_ia64_dynamic_type (type); |
| break; |
| default: |
| if (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS) |
| result = get_solaris_dynamic_type (type); |
| else |
| result = NULL; |
| break; |
| } |
| |
| if (result != NULL) |
| return result; |
| |
| snprintf (buff, sizeof (buff), _("Operating System specific: %lx"), |
| type); |
| } |
| else |
| snprintf (buff, sizeof (buff), _("<unknown>: %lx"), type); |
| |
| return buff; |
| } |
| } |
| |
| static bool get_program_headers (Filedata *); |
| static bool get_dynamic_section (Filedata *); |
| |
| static void |
| locate_dynamic_section (Filedata *filedata) |
| { |
| uint64_t dynamic_addr = 0; |
| uint64_t dynamic_size = 0; |
| |
| if (filedata->file_header.e_phnum != 0 |
| && get_program_headers (filedata)) |
| { |
| Elf_Internal_Phdr *segment; |
| unsigned int i; |
| |
| for (i = 0, segment = filedata->program_headers; |
| i < filedata->file_header.e_phnum; |
| i++, segment++) |
| { |
| if (segment->p_type == PT_DYNAMIC) |
| { |
| dynamic_addr = segment->p_offset; |
| dynamic_size = segment->p_filesz; |
| |
| if (filedata->section_headers != NULL) |
| { |
| Elf_Internal_Shdr *sec; |
| |
| sec = find_section (filedata, ".dynamic"); |
| if (sec != NULL) |
| { |
| if (sec->sh_size == 0 |
| || sec->sh_type == SHT_NOBITS) |
| { |
| dynamic_addr = 0; |
| dynamic_size = 0; |
| } |
| else |
| { |
| dynamic_addr = sec->sh_offset; |
| dynamic_size = sec->sh_size; |
| } |
| } |
| } |
| |
| if (dynamic_addr > filedata->file_size |
| || (dynamic_size > filedata->file_size - dynamic_addr |