Index: stable/4/contrib/binutils/bfd/ppcboot.c =================================================================== --- stable/4/contrib/binutils/bfd/ppcboot.c (nonexistent) +++ stable/4/contrib/binutils/bfd/ppcboot.c (revision 62108) @@ -0,0 +1,541 @@ +/* BFD back-end for PPCbug boot records. + Copyright 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Written by Michael Meissner, Cygnus Support, + +This file is part of BFD, the Binary File Descriptor library. + +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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This is a BFD backend which may be used to write PowerPCBug boot objects. + It may only be used for output, not input. The intention is that this may + be used as an output format for objcopy in order to generate raw binary + data. + + This is very simple. The only complication is that the real data + will start at some address X, and in some cases we will not want to + include X zeroes just to get to that point. Since the start + address is not meaningful for this object file format, we use it + instead to indicate the number of zeroes to skip at the start of + the file. objcopy cooperates by specially setting the start + address to zero by default. */ + +#include + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" + +/* PPCbug location structure */ +typedef struct ppcboot_location { + bfd_byte ind; + bfd_byte head; + bfd_byte sector; + bfd_byte cylinder; +} ppcboot_location_t; + +/* PPCbug partition table layout */ +typedef struct ppcboot_partition { + ppcboot_location_t partition_begin; /* partition begin */ + ppcboot_location_t partition_end; /* partition end */ + bfd_byte sector_begin[4]; /* 32-bit start RBA (zero-based), little endian */ + bfd_byte sector_length[4]; /* 32-bit RBA count (one-based), little endian */ +} ppcboot_partition_t; + +/* PPCbug boot layout. */ +typedef struct ppcboot_hdr { + bfd_byte pc_compatibility[446]; /* x86 instruction field */ + ppcboot_partition_t partition[4]; /* partition information */ + bfd_byte signature[2]; /* 0x55 and 0xaa */ + bfd_byte entry_offset[4]; /* entry point offset, little endian */ + bfd_byte length[4]; /* load image length, little endian */ + bfd_byte flags; /* flag field */ + bfd_byte os_id; /* OS_ID */ + char partition_name[32]; /* partition name */ + bfd_byte reserved1[470]; /* reserved */ +} +#ifdef __GNUC__ + __attribute__ ((packed)) +#endif +ppcboot_hdr_t; + +/* Signature bytes for last 2 bytes of the 512 byte record */ +#define SIGNATURE0 0x55 +#define SIGNATURE1 0xaa + +/* PowerPC boot type */ +#define PPC_IND 0x41 + +/* Information needed for ppcboot header */ +typedef struct ppcboot_data { + ppcboot_hdr_t header; /* raw header */ + asection *sec; /* single section */ +} ppcboot_data_t; + +/* Any bfd we create by reading a ppcboot file has three symbols: + a start symbol, an end symbol, and an absolute length symbol. */ +#define PPCBOOT_SYMS 3 + +static boolean ppcboot_mkobject PARAMS ((bfd *)); +static const bfd_target *ppcboot_object_p PARAMS ((bfd *)); +static boolean ppcboot_set_arch_mach + PARAMS ((bfd *, enum bfd_architecture, unsigned long)); +static boolean ppcboot_get_section_contents + PARAMS ((bfd *, asection *, PTR, file_ptr, bfd_size_type)); +static long ppcboot_get_symtab_upper_bound PARAMS ((bfd *)); +static char *mangle_name PARAMS ((bfd *, char *)); +static long ppcboot_get_symtab PARAMS ((bfd *, asymbol **)); +static asymbol *ppcboot_make_empty_symbol PARAMS ((bfd *)); +static void ppcboot_get_symbol_info PARAMS ((bfd *, asymbol *, symbol_info *)); +static boolean ppcboot_set_section_contents + PARAMS ((bfd *, asection *, PTR, file_ptr, bfd_size_type)); +static int ppcboot_sizeof_headers PARAMS ((bfd *, boolean)); +static boolean ppcboot_bfd_print_private_bfd_data PARAMS ((bfd *, PTR)); + +#define ppcboot_set_tdata(abfd, ptr) ((abfd)->tdata.any = (PTR) (ptr)) +#define ppcboot_get_tdata(abfd) ((ppcboot_data_t *) ((abfd)->tdata.any)) + +/* Create a ppcboot object. Invoked via bfd_set_format. */ + +static boolean +ppcboot_mkobject (abfd) + bfd *abfd; +{ + if (!ppcboot_get_tdata (abfd)) + ppcboot_set_tdata (abfd, bfd_zalloc (abfd, sizeof (ppcboot_data_t))); + + return true; +} + + +/* Set the architecture to PowerPC */ +static boolean +ppcboot_set_arch_mach (abfd, arch, machine) + bfd *abfd; + enum bfd_architecture arch; + unsigned long machine; +{ + if (arch == bfd_arch_unknown) + arch = bfd_arch_powerpc; + + else if (arch != bfd_arch_powerpc) + return false; + + return bfd_default_set_arch_mach (abfd, arch, machine); +} + + +/* Any file may be considered to be a ppcboot file, provided the target + was not defaulted. That is, it must be explicitly specified as + being ppcboot. */ + +static const bfd_target * +ppcboot_object_p (abfd) + bfd *abfd; +{ + struct stat statbuf; + asection *sec; + ppcboot_hdr_t hdr; + size_t i; + ppcboot_data_t *tdata; + + BFD_ASSERT (sizeof (ppcboot_hdr_t) == 1024); + + if (abfd->target_defaulted) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + /* Find the file size. */ + if (bfd_stat (abfd, &statbuf) < 0) + { + bfd_set_error (bfd_error_system_call); + return NULL; + } + + if ((size_t) statbuf.st_size < sizeof (ppcboot_hdr_t)) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + if (bfd_read ((PTR) &hdr, sizeof (hdr), 1, abfd) != sizeof (hdr)) + { + if (bfd_get_error () != bfd_error_system_call) + bfd_set_error (bfd_error_wrong_format); + + return NULL; + } + + /* Now do some basic checks. */ + for (i = 0; i < sizeof (hdr.pc_compatibility); i++) + if (hdr.pc_compatibility[i]) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + if (hdr.signature[0] != SIGNATURE0 || hdr.signature[1] != SIGNATURE1) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + if (hdr.partition[0].partition_end.ind != PPC_IND) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + abfd->symcount = PPCBOOT_SYMS; + + /* One data section. */ + sec = bfd_make_section (abfd, ".data"); + if (sec == NULL) + return NULL; + sec->flags = SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_CODE | SEC_HAS_CONTENTS; + sec->vma = 0; + sec->_raw_size = statbuf.st_size - sizeof (ppcboot_hdr_t); + sec->filepos = sizeof (ppcboot_hdr_t); + + ppcboot_mkobject (abfd); + tdata = ppcboot_get_tdata (abfd); + tdata->sec = sec; + memcpy ((PTR) &tdata->header, (PTR) &hdr, sizeof (ppcboot_hdr_t)); + + ppcboot_set_arch_mach (abfd, bfd_arch_powerpc, 0); + return abfd->xvec; +} + +#define ppcboot_close_and_cleanup _bfd_generic_close_and_cleanup +#define ppcboot_bfd_free_cached_info _bfd_generic_bfd_free_cached_info +#define ppcboot_new_section_hook _bfd_generic_new_section_hook + + +/* Get contents of the only section. */ + +static boolean +ppcboot_get_section_contents (abfd, section, location, offset, count) + bfd *abfd; + asection *section ATTRIBUTE_UNUSED; + PTR location; + file_ptr offset; + bfd_size_type count; +{ + if (bfd_seek (abfd, offset + sizeof(ppcboot_hdr_t), SEEK_SET) != 0 + || bfd_read (location, 1, count, abfd) != count) + return false; + return true; +} + + +/* Return the amount of memory needed to read the symbol table. */ + +static long +ppcboot_get_symtab_upper_bound (abfd) + bfd *abfd ATTRIBUTE_UNUSED; +{ + return (PPCBOOT_SYMS + 1) * sizeof (asymbol *); +} + + +/* Create a symbol name based on the bfd's filename. */ + +static char * +mangle_name (abfd, suffix) + bfd *abfd; + char *suffix; +{ + int size; + char *buf; + char *p; + + size = (strlen (bfd_get_filename (abfd)) + + strlen (suffix) + + sizeof "_ppcboot__"); + + buf = (char *) bfd_alloc (abfd, size); + if (buf == NULL) + return ""; + + sprintf (buf, "_ppcboot_%s_%s", bfd_get_filename (abfd), suffix); + + /* Change any non-alphanumeric characters to underscores. */ + for (p = buf; *p; p++) + if (! isalnum ((unsigned char) *p)) + *p = '_'; + + return buf; +} + + +/* Return the symbol table. */ + +static long +ppcboot_get_symtab (abfd, alocation) + bfd *abfd; + asymbol **alocation; +{ + asection *sec = ppcboot_get_tdata (abfd)->sec; + asymbol *syms; + unsigned int i; + + syms = (asymbol *) bfd_alloc (abfd, PPCBOOT_SYMS * sizeof (asymbol)); + if (syms == NULL) + return false; + + /* Start symbol. */ + syms[0].the_bfd = abfd; + syms[0].name = mangle_name (abfd, "start"); + syms[0].value = 0; + syms[0].flags = BSF_GLOBAL; + syms[0].section = sec; + syms[0].udata.p = NULL; + + /* End symbol. */ + syms[1].the_bfd = abfd; + syms[1].name = mangle_name (abfd, "end"); + syms[1].value = sec->_raw_size; + syms[1].flags = BSF_GLOBAL; + syms[1].section = sec; + syms[1].udata.p = NULL; + + /* Size symbol. */ + syms[2].the_bfd = abfd; + syms[2].name = mangle_name (abfd, "size"); + syms[2].value = sec->_raw_size; + syms[2].flags = BSF_GLOBAL; + syms[2].section = bfd_abs_section_ptr; + syms[2].udata.p = NULL; + + for (i = 0; i < PPCBOOT_SYMS; i++) + *alocation++ = syms++; + *alocation = NULL; + + return PPCBOOT_SYMS; +} + + +/* Make an empty symbol. */ + +static asymbol * +ppcboot_make_empty_symbol (abfd) + bfd *abfd; +{ + return (asymbol *) bfd_alloc (abfd, sizeof (asymbol)); +} + + +#define ppcboot_print_symbol _bfd_nosymbols_print_symbol + +/* Get information about a symbol. */ + +static void +ppcboot_get_symbol_info (ignore_abfd, symbol, ret) + bfd *ignore_abfd ATTRIBUTE_UNUSED; + asymbol *symbol; + symbol_info *ret; +{ + bfd_symbol_info (symbol, ret); +} + +#define ppcboot_bfd_is_local_label_name bfd_generic_is_local_label_name +#define ppcboot_get_lineno _bfd_nosymbols_get_lineno +#define ppcboot_find_nearest_line _bfd_nosymbols_find_nearest_line +#define ppcboot_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol +#define ppcboot_read_minisymbols _bfd_generic_read_minisymbols +#define ppcboot_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol + +#define ppcboot_get_reloc_upper_bound \ + ((long (*) PARAMS ((bfd *, asection *))) bfd_0l) +#define ppcboot_canonicalize_reloc \ + ((long (*) PARAMS ((bfd *, asection *, arelent **, asymbol **))) bfd_0l) +#define ppcboot_bfd_reloc_type_lookup _bfd_norelocs_bfd_reloc_type_lookup + +/* Write section contents of a ppcboot file. */ + +static boolean +ppcboot_set_section_contents (abfd, sec, data, offset, size) + bfd *abfd; + asection *sec; + PTR data; + file_ptr offset; + bfd_size_type size; +{ + if (! abfd->output_has_begun) + { + bfd_vma low; + asection *s; + + /* The lowest section VMA sets the virtual address of the start + of the file. We use the set the file position of all the + sections. */ + low = abfd->sections->vma; + for (s = abfd->sections->next; s != NULL; s = s->next) + if (s->vma < low) + low = s->vma; + + for (s = abfd->sections; s != NULL; s = s->next) + s->filepos = s->vma - low; + + abfd->output_has_begun = true; + } + + return _bfd_generic_set_section_contents (abfd, sec, data, offset, size); +} + + +static int +ppcboot_sizeof_headers (abfd, exec) + bfd *abfd ATTRIBUTE_UNUSED; + boolean exec ATTRIBUTE_UNUSED; +{ + return sizeof (ppcboot_hdr_t); +} + + +/* Print out the program headers. */ + +static boolean +ppcboot_bfd_print_private_bfd_data (abfd, farg) + bfd *abfd; + PTR farg; +{ + FILE *f = (FILE *)farg; + ppcboot_data_t *tdata = ppcboot_get_tdata (abfd); + long entry_offset = bfd_getl_signed_32 ((PTR) tdata->header.entry_offset); + long length = bfd_getl_signed_32 ((PTR) tdata->header.length); + int i; + + fprintf (f, _("\nppcboot header:\n")); + fprintf (f, _("Entry offset = 0x%.8lx (%ld)\n"), entry_offset, entry_offset); + fprintf (f, _("Length = 0x%.8lx (%ld)\n"), length, length); + + if (tdata->header.flags) + fprintf (f, _("Flag field = 0x%.2x\n"), tdata->header.flags); + + if (tdata->header.os_id) + fprintf (f, "OS_ID = 0x%.2x\n", tdata->header.os_id); + + if (tdata->header.partition_name) + fprintf (f, _("Partition name = \"%s\"\n"), tdata->header.partition_name); + + for (i = 0; i < 4; i++) + { + long sector_begin = bfd_getl_signed_32 ((PTR) tdata->header.partition[i].sector_begin); + long sector_length = bfd_getl_signed_32 ((PTR) tdata->header.partition[i].sector_length); + + /* Skip all 0 entries */ + if (!tdata->header.partition[i].partition_begin.ind + && !tdata->header.partition[i].partition_begin.head + && !tdata->header.partition[i].partition_begin.sector + && !tdata->header.partition[i].partition_begin.cylinder + && !tdata->header.partition[i].partition_end.ind + && !tdata->header.partition[i].partition_end.head + && !tdata->header.partition[i].partition_end.sector + && !tdata->header.partition[i].partition_end.cylinder + && !sector_begin && !sector_length) + continue; + + fprintf (f, _("\nPartition[%d] start = { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }\n"), i, + tdata->header.partition[i].partition_begin.ind, + tdata->header.partition[i].partition_begin.head, + tdata->header.partition[i].partition_begin.sector, + tdata->header.partition[i].partition_begin.cylinder); + + fprintf (f, _("Partition[%d] end = { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }\n"), i, + tdata->header.partition[i].partition_end.ind, + tdata->header.partition[i].partition_end.head, + tdata->header.partition[i].partition_end.sector, + tdata->header.partition[i].partition_end.cylinder); + + fprintf (f, _("Partition[%d] sector = 0x%.8lx (%ld)\n"), i, sector_begin, sector_begin); + fprintf (f, _("Partition[%d] length = 0x%.8lx (%ld)\n"), i, sector_length, sector_length); + } + + fprintf (f, "\n"); + return true; +} + + +#define ppcboot_bfd_get_relocated_section_contents \ + bfd_generic_get_relocated_section_contents +#define ppcboot_bfd_relax_section bfd_generic_relax_section +#define ppcboot_bfd_gc_sections bfd_generic_gc_sections +#define ppcboot_bfd_link_hash_table_create _bfd_generic_link_hash_table_create +#define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols +#define ppcboot_bfd_final_link _bfd_generic_final_link +#define ppcboot_bfd_link_split_section _bfd_generic_link_split_section +#define ppcboot_get_section_contents_in_window \ + _bfd_generic_get_section_contents_in_window + +#define ppcboot_bfd_copy_private_bfd_data _bfd_generic_bfd_copy_private_bfd_data +#define ppcboot_bfd_merge_private_bfd_data _bfd_generic_bfd_merge_private_bfd_data +#define ppcboot_bfd_copy_private_section_data _bfd_generic_bfd_copy_private_section_data +#define ppcboot_bfd_copy_private_symbol_data _bfd_generic_bfd_copy_private_symbol_data +#define ppcboot_bfd_set_private_flags _bfd_generic_bfd_set_private_flags +#define ppcboot_bfd_print_private_bfd_dat ppcboot_bfd_print_private_bfd_data + +const bfd_target ppcboot_vec = +{ + "ppcboot", /* name */ + bfd_target_unknown_flavour, /* flavour */ + BFD_ENDIAN_BIG, /* byteorder is big endian for code */ + BFD_ENDIAN_LITTLE, /* header_byteorder */ + EXEC_P, /* object_flags */ + (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA + | SEC_ROM | SEC_HAS_CONTENTS), /* section_flags */ + 0, /* symbol_leading_char */ + ' ', /* ar_pad_char */ + 16, /* ar_max_namelen */ + bfd_getb64, bfd_getb_signed_64, bfd_putb64, + bfd_getb32, bfd_getb_signed_32, bfd_putb32, + bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */ + bfd_getl64, bfd_getl_signed_64, bfd_putl64, + bfd_getl32, bfd_getl_signed_32, bfd_putl32, + bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */ + { /* bfd_check_format */ + _bfd_dummy_target, + ppcboot_object_p, /* bfd_check_format */ + _bfd_dummy_target, + _bfd_dummy_target, + }, + { /* bfd_set_format */ + bfd_false, + ppcboot_mkobject, + bfd_false, + bfd_false, + }, + { /* bfd_write_contents */ + bfd_false, + bfd_true, + bfd_false, + bfd_false, + }, + + BFD_JUMP_TABLE_GENERIC (ppcboot), + BFD_JUMP_TABLE_COPY (ppcboot), + BFD_JUMP_TABLE_CORE (_bfd_nocore), + BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), + BFD_JUMP_TABLE_SYMBOLS (ppcboot), + BFD_JUMP_TABLE_RELOCS (ppcboot), + BFD_JUMP_TABLE_WRITE (ppcboot), + BFD_JUMP_TABLE_LINK (ppcboot), + BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), + + NULL, + + NULL +}; Property changes on: stable/4/contrib/binutils/bfd/ppcboot.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/contrib/binutils/binutils/readelf.c =================================================================== --- stable/4/contrib/binutils/binutils/readelf.c (nonexistent) +++ stable/4/contrib/binutils/binutils/readelf.c (revision 62108) @@ -0,0 +1,7365 @@ +/* readelf.c -- display contents of an ELF format file + Copyright (C) 1998, 99, 2000 Free Software Foundation, Inc. + + Originally developed by Eric Youngdale + Modifications by Nick Clifton + + 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 2 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., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + + +#include +#include +#include +#include +#include + +#if __GNUC__ >= 2 +/* 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. + Only do this if we belive that the compiler can support a 64 bit + data type. For now we only rely on GCC being able to do this. */ +#define BFD64 +#endif + +#include "bfd.h" + +#include "elf/common.h" +#include "elf/external.h" +#include "elf/internal.h" +#include "elf/dwarf2.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/i386.h" +#include "elf/v850.h" +#include "elf/ppc.h" +#include "elf/mips.h" +#include "elf/alpha.h" +#include "elf/arm.h" +#include "elf/m68k.h" +#include "elf/sparc.h" +#include "elf/m32r.h" +#include "elf/d10v.h" +#include "elf/d30v.h" +#include "elf/sh.h" +#include "elf/mn10200.h" +#include "elf/mn10300.h" +#include "elf/hppa.h" +#include "elf/arc.h" +#include "elf/fr30.h" +#include "elf/mcore.h" +#include "elf/i960.h" +#include "elf/pj.h" +#include "elf/avr.h" + +#include "bucomm.h" +#include "getopt.h" + +char * program_name = "readelf"; +unsigned int dynamic_addr; +bfd_size_type dynamic_size; +unsigned int rela_addr; +unsigned int rela_size; +char * dynamic_strings; +char * string_table; +unsigned long num_dynamic_syms; +Elf_Internal_Sym * dynamic_symbols; +Elf_Internal_Syminfo * dynamic_syminfo; +unsigned long dynamic_syminfo_offset; +unsigned int dynamic_syminfo_nent; +char program_interpreter [64]; +int dynamic_info[DT_JMPREL + 1]; +int version_info[16]; +int loadaddr = 0; +Elf_Internal_Ehdr elf_header; +Elf_Internal_Shdr * section_headers; +Elf_Internal_Dyn * dynamic_segment; +int show_name; +int do_dynamic; +int do_syms; +int do_reloc; +int do_sections; +int do_segments; +int do_using_dynamic; +int do_header; +int do_dump; +int do_version; +int do_histogram; +int do_debugging; +int do_debug_info; +int do_debug_abbrevs; +int do_debug_lines; +int do_debug_pubnames; +int do_debug_aranges; +int do_arch; +int do_notes; +int is_32bit_elf; + +/* A dynamic array of flags indicating which sections require dumping. */ +char * dump_sects = NULL; +unsigned int num_dump_sects = 0; + +#define HEX_DUMP (1 << 0) +#define DISASS_DUMP (1 << 1) +#define DEBUG_DUMP (1 << 2) + +/* How to rpint a vma value. */ +typedef enum print_mode +{ + HEX, + DEC, + DEC_5, + UNSIGNED, + PREFIX_HEX, + FULL_HEX, + LONG_HEX +} +print_mode; + +/* Forward declarations for dumb compilers. */ +static void print_vma PARAMS ((bfd_vma, print_mode)); +static bfd_vma (* byte_get) PARAMS ((unsigned char *, int)); +static bfd_vma byte_get_little_endian PARAMS ((unsigned char *, int)); +static bfd_vma byte_get_big_endian PARAMS ((unsigned char *, int)); +static const char * get_mips_dynamic_type PARAMS ((unsigned long)); +static const char * get_sparc64_dynamic_type PARAMS ((unsigned long)); +static const char * get_parisc_dynamic_type PARAMS ((unsigned long)); +static const char * get_dynamic_type PARAMS ((unsigned long)); +static int dump_relocations PARAMS ((FILE *, unsigned long, unsigned long, Elf_Internal_Sym *, unsigned long, char *, int)); +static char * get_file_type PARAMS ((unsigned)); +static char * get_machine_name PARAMS ((unsigned)); +static char * get_machine_flags PARAMS ((unsigned, unsigned)); +static const char * get_mips_segment_type PARAMS ((unsigned long)); +static const char * get_parisc_segment_type PARAMS ((unsigned long)); +static const char * get_segment_type PARAMS ((unsigned long)); +static const char * get_mips_section_type_name PARAMS ((unsigned int)); +static const char * get_parisc_section_type_name PARAMS ((unsigned int)); +static const char * get_section_type_name PARAMS ((unsigned int)); +static const char * get_symbol_binding PARAMS ((unsigned int)); +static const char * get_symbol_type PARAMS ((unsigned int)); +static const char * get_symbol_visibility PARAMS ((unsigned int)); +static const char * get_symbol_index_type PARAMS ((unsigned int)); +static const char * get_dynamic_flags PARAMS ((bfd_vma)); +static void usage PARAMS ((void)); +static void parse_args PARAMS ((int, char **)); +static int process_file_header PARAMS ((void)); +static int process_program_headers PARAMS ((FILE *)); +static int process_section_headers PARAMS ((FILE *)); +static void dynamic_segment_mips_val PARAMS ((Elf_Internal_Dyn *)); +static void dynamic_segment_parisc_val PARAMS ((Elf_Internal_Dyn *)); +static int process_dynamic_segment PARAMS ((FILE *)); +static int process_symbol_table PARAMS ((FILE *)); +static int process_section_contents PARAMS ((FILE *)); +static void process_file PARAMS ((char *)); +static int process_relocs PARAMS ((FILE *)); +static int process_version_sections PARAMS ((FILE *)); +static char * get_ver_flags PARAMS ((unsigned int)); +static int get_32bit_section_headers PARAMS ((FILE *)); +static int get_64bit_section_headers PARAMS ((FILE *)); +static int get_32bit_program_headers PARAMS ((FILE *, Elf_Internal_Phdr *)); +static int get_64bit_program_headers PARAMS ((FILE *, Elf_Internal_Phdr *)); +static int get_file_header PARAMS ((FILE *)); +static Elf_Internal_Sym * get_32bit_elf_symbols PARAMS ((FILE *, unsigned long, unsigned long)); +static Elf_Internal_Sym * get_64bit_elf_symbols PARAMS ((FILE *, unsigned long, unsigned long)); +static int * get_dynamic_data PARAMS ((FILE *, unsigned int)); +static int get_32bit_dynamic_segment PARAMS ((FILE *)); +static int get_64bit_dynamic_segment PARAMS ((FILE *)); +#ifdef SUPPORT_DISASSEMBLY +static int disassemble_section PARAMS ((Elf32_Internal_Shdr *, FILE *)); +#endif +static int dump_section PARAMS ((Elf32_Internal_Shdr *, FILE *)); +static int display_debug_section PARAMS ((Elf32_Internal_Shdr *, FILE *)); +static int display_debug_info PARAMS ((Elf32_Internal_Shdr *, unsigned char *, FILE *)); +static int display_debug_not_supported PARAMS ((Elf32_Internal_Shdr *, unsigned char *, FILE *)); +static int display_debug_lines PARAMS ((Elf32_Internal_Shdr *, unsigned char *, FILE *)); +static int display_debug_abbrev PARAMS ((Elf32_Internal_Shdr *, unsigned char *, FILE *)); +static int display_debug_aranges PARAMS ((Elf32_Internal_Shdr *, unsigned char *, FILE *)); +static unsigned char * process_abbrev_section PARAMS ((unsigned char *, unsigned char *)); +static unsigned long read_leb128 PARAMS ((unsigned char *, int *, int)); +static int process_extended_line_op PARAMS ((unsigned char *, int, int)); +static void reset_state_machine PARAMS ((int)); +static char * get_TAG_name PARAMS ((unsigned long)); +static char * get_AT_name PARAMS ((unsigned long)); +static char * get_FORM_name PARAMS ((unsigned long)); +static void free_abbrevs PARAMS ((void)); +static void add_abbrev PARAMS ((unsigned long, unsigned long, int)); +static void add_abbrev_attr PARAMS ((unsigned long, unsigned long)); +static unsigned char * read_and_display_attr PARAMS ((unsigned long, unsigned long, unsigned char *, unsigned long)); +static unsigned char * display_block PARAMS ((unsigned char *, unsigned long)); +static void decode_location_expression PARAMS ((unsigned char *, unsigned int)); +static void request_dump PARAMS ((unsigned int, char)); +static const char * get_elf_class PARAMS ((unsigned char)); +static const char * get_data_encoding PARAMS ((unsigned char)); +static const char * get_osabi_name PARAMS ((unsigned char)); +static int guess_is_rela PARAMS ((unsigned long)); +static char * get_note_type PARAMS ((unsigned int)); +static int process_note PARAMS ((Elf32_Internal_Note *)); +static int process_corefile_note_segment PARAMS ((FILE *, bfd_vma, bfd_vma)); +static int process_corefile_note_segments PARAMS ((FILE *)); +static int process_corefile_contents PARAMS ((FILE *)); + +typedef int Elf32_Word; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +#define UNKNOWN -1 + +#define SECTION_NAME(X) (string_table + (X)->sh_name) + +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ + +#define BYTE_GET(field) byte_get (field, sizeof (field)) + +/* If we can support a 64 bit data type then BFD64 should be defined + and sizeof (bfd_vma) == 8. In this case when translating from an + external 8 byte field to an internal field, we can assume that the + internal field is also 8 bytes wide and so we can extact all the data. + If, however, BFD64 is not defined, then we must assume that the + internal data structure only has 4 byte wide fields that are the + equivalent of the 8 byte wide external counterparts, and so we must + truncate the data. */ +#ifdef BFD64 +#define BYTE_GET8(field) byte_get (field, -8) +#else +#define BYTE_GET8(field) byte_get (field, 8) +#endif + +#define NUM_ELEM(array) (sizeof (array) / sizeof ((array)[0])) + +#define GET_DATA_ALLOC(offset, size, var, type, reason) \ + if (fseek (file, offset, SEEK_SET)) \ + { \ + error (_("Unable to seek to start of %s at %x\n"), reason, offset); \ + return 0; \ + } \ + \ + var = (type) malloc (size); \ + \ + if (var == NULL) \ + { \ + error (_("Out of memory allocating %d bytes for %s\n"), size, reason); \ + return 0; \ + } \ + \ + if (fread (var, size, 1, file) != 1) \ + { \ + error (_("Unable to read in %d bytes of %s\n"), size, reason); \ + free (var); \ + var = NULL; \ + return 0; \ + } + + +#define GET_DATA(offset, var, reason) \ + if (fseek (file, offset, SEEK_SET)) \ + { \ + error (_("Unable to seek to %x for %s\n"), offset, reason); \ + return 0; \ + } \ + else if (fread (& var, sizeof (var), 1, file) != 1) \ + { \ + error (_("Unable to read data at %x for %s\n"), offset, reason); \ + return 0; \ + } + +#define GET_ELF_SYMBOLS(file, offset, size) \ + (is_32bit_elf ? get_32bit_elf_symbols (file, offset, size) \ + : get_64bit_elf_symbols (file, offset, size)) + + +#ifdef ANSI_PROTOTYPES +static void +error (const char * message, ...) +{ + va_list args; + + fprintf (stderr, _("%s: Error: "), program_name); + va_start (args, message); + vfprintf (stderr, message, args); + va_end (args); + return; +} + +static void +warn (const char * message, ...) +{ + va_list args; + + fprintf (stderr, _("%s: Warning: "), program_name); + va_start (args, message); + vfprintf (stderr, message, args); + va_end (args); + return; +} +#else +static void +error (va_alist) + va_dcl +{ + char * message; + va_list args; + + fprintf (stderr, _("%s: Error: "), program_name); + va_start (args); + message = va_arg (args, char *); + vfprintf (stderr, message, args); + va_end (args); + return; +} + +static void +warn (va_alist) + va_dcl +{ + char * message; + va_list args; + + fprintf (stderr, _("%s: Warning: "), program_name); + va_start (args); + message = va_arg (args, char *); + vfprintf (stderr, message, args); + va_end (args); + return; +} +#endif + +static bfd_vma +byte_get_little_endian (field, size) + unsigned char * field; + int size; +{ + switch (size) + { + case 1: + return * field; + + case 2: + return ((unsigned int) (field [0])) + | (((unsigned int) (field [1])) << 8); + + case 8: + /* We want to extract data from an 8 byte wide field and + place it into a 4 byte wide field. Since this is a little + endian source we can juts use the 4 byte extraction code. */ + /* Fall through. */ + case 4: + return ((unsigned long) (field [0])) + | (((unsigned long) (field [1])) << 8) + | (((unsigned long) (field [2])) << 16) + | (((unsigned long) (field [3])) << 24); + +#ifdef BFD64 + case -8: + /* This is a special case, generated by the BYTE_GET8 macro. + It means that we are loading an 8 byte value from a field + in an external structure into an 8 byte value in a field + in an internal strcuture. */ + return ((bfd_vma) (field [0])) + | (((bfd_vma) (field [1])) << 8) + | (((bfd_vma) (field [2])) << 16) + | (((bfd_vma) (field [3])) << 24) + | (((bfd_vma) (field [4])) << 32) + | (((bfd_vma) (field [5])) << 40) + | (((bfd_vma) (field [6])) << 48) + | (((bfd_vma) (field [7])) << 56); +#endif + default: + error (_("Unhandled data length: %d\n"), size); + abort (); + } +} + +/* Print a VMA value. */ +static void +print_vma (vma, mode) + bfd_vma vma; + print_mode mode; +{ +#ifdef BFD64 + if (is_32bit_elf) +#endif + { + switch (mode) + { + case FULL_HEX: printf ("0x"); /* drop through */ + case LONG_HEX: printf ("%8.8lx", (unsigned long) vma); break; + case PREFIX_HEX: printf ("0x"); /* drop through */ + case HEX: printf ("%lx", (unsigned long) vma); break; + case DEC: printf ("%ld", (unsigned long) vma); break; + case DEC_5: printf ("%5ld", (long) vma); break; + case UNSIGNED: printf ("%lu", (unsigned long) vma); break; + } + } +#ifdef BFD64 + else + { + switch (mode) + { + case FULL_HEX: + printf ("0x"); + /* drop through */ + + case LONG_HEX: + printf_vma (vma); + break; + + case PREFIX_HEX: + printf ("0x"); + /* drop through */ + + case HEX: +#if BFD_HOST_64BIT_LONG + printf ("%lx", vma); +#else + if (_bfd_int64_high (vma)) + printf ("%lx%lx", _bfd_int64_high (vma), _bfd_int64_low (vma)); + else + printf ("%lx", _bfd_int64_low (vma)); +#endif + break; + + case DEC: +#if BFD_HOST_64BIT_LONG + printf ("%ld", vma); +#else + if (_bfd_int64_high (vma)) + /* ugg */ + printf ("++%ld", _bfd_int64_low (vma)); + else + printf ("%ld", _bfd_int64_low (vma)); +#endif + break; + + case DEC_5: +#if BFD_HOST_64BIT_LONG + printf ("%5ld", vma); +#else + if (_bfd_int64_high (vma)) + /* ugg */ + printf ("++%ld", _bfd_int64_low (vma)); + else + printf ("%5ld", _bfd_int64_low (vma)); +#endif + break; + + case UNSIGNED: +#if BFD_HOST_64BIT_LONG + printf ("%lu", vma); +#else + if (_bfd_int64_high (vma)) + /* ugg */ + printf ("++%lu", _bfd_int64_low (vma)); + else + printf ("%lu", _bfd_int64_low (vma)); +#endif + break; + } + } +#endif +} + +static bfd_vma +byte_get_big_endian (field, size) + unsigned char * field; + int size; +{ + switch (size) + { + case 1: + return * field; + + case 2: + return ((unsigned int) (field [1])) | (((int) (field [0])) << 8); + + case 4: + return ((unsigned long) (field [3])) + | (((unsigned long) (field [2])) << 8) + | (((unsigned long) (field [1])) << 16) + | (((unsigned long) (field [0])) << 24); + + case 8: + /* Although we are extracing data from an 8 byte wide field, we + are returning only 4 bytes of data. */ + return ((unsigned long) (field [7])) + | (((unsigned long) (field [6])) << 8) + | (((unsigned long) (field [5])) << 16) + | (((unsigned long) (field [4])) << 24); + +#ifdef BFD64 + case -8: + /* This is a special case, generated by the BYTE_GET8 macro. + It means that we are loading an 8 byte value from a field + in an external structure into an 8 byte value in a field + in an internal strcuture. */ + return ((bfd_vma) (field [7])) + | (((bfd_vma) (field [6])) << 8) + | (((bfd_vma) (field [5])) << 16) + | (((bfd_vma) (field [4])) << 24) + | (((bfd_vma) (field [3])) << 32) + | (((bfd_vma) (field [2])) << 40) + | (((bfd_vma) (field [1])) << 48) + | (((bfd_vma) (field [0])) << 56); +#endif + + default: + error (_("Unhandled data length: %d\n"), size); + abort (); + } +} + + +/* Guess the relocation sized based on the sized commonly used by the specific machine. */ +static int +guess_is_rela (e_machine) + unsigned long e_machine; +{ + switch (e_machine) + { + /* Targets that use REL relocations. */ + case EM_ARM: + case EM_386: + case EM_486: + case EM_960: + case EM_CYGNUS_M32R: + case EM_CYGNUS_D10V: + case EM_MIPS: + case EM_MIPS_RS4_BE: + return FALSE; + + /* Targets that use RELA relocations. */ + case EM_68K: + case EM_SPARC32PLUS: + case EM_SPARCV9: + case EM_SPARC: + case EM_PPC: + case EM_CYGNUS_V850: + case EM_CYGNUS_D30V: + case EM_CYGNUS_MN10200: + case EM_CYGNUS_MN10300: + case EM_CYGNUS_FR30: + case EM_SH: + case EM_ALPHA: + case EM_MCORE: + return TRUE; + + case EM_MMA: + case EM_PCP: + case EM_NCPU: + case EM_NDR1: + case EM_STARCORE: + case EM_ME16: + case EM_ST100: + case EM_TINYJ: + case EM_FX66: + case EM_ST9PLUS: + case EM_ST7: + case EM_68HC16: + case EM_68HC11: + case EM_68HC08: + case EM_68HC05: + case EM_SVX: + case EM_ST19: + case EM_VAX: + default: + warn (_("Don't know about relocations on this machine architecture\n")); + return FALSE; + } +} + +/* Display the contents of the relocation data found at the specified offset. */ +static int +dump_relocations (file, rel_offset, rel_size, symtab, nsyms, strtab, is_rela) + FILE * file; + unsigned long rel_offset; + unsigned long rel_size; + Elf_Internal_Sym * symtab; + unsigned long nsyms; + char * strtab; + int is_rela; +{ + unsigned int i; + Elf_Internal_Rel * rels; + Elf_Internal_Rela * relas; + + + if (is_rela == UNKNOWN) + is_rela = guess_is_rela (elf_header.e_machine); + + if (is_rela) + { + if (is_32bit_elf) + { + Elf32_External_Rela * erelas; + + GET_DATA_ALLOC (rel_offset, rel_size, erelas, + Elf32_External_Rela *, "relocs"); + + rel_size = rel_size / sizeof (Elf32_External_Rela); + + relas = (Elf_Internal_Rela *) + malloc (rel_size * sizeof (Elf_Internal_Rela)); + + if (relas == NULL) + { + error(_("out of memory parsing relocs")); + return 0; + } + + for (i = 0; i < rel_size; 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 (erelas[i].r_addend); + } + + free (erelas); + + rels = (Elf_Internal_Rel *) relas; + } + else + { + Elf64_External_Rela * erelas; + + GET_DATA_ALLOC (rel_offset, rel_size, erelas, + Elf64_External_Rela *, "relocs"); + + rel_size = rel_size / sizeof (Elf64_External_Rela); + + relas = (Elf_Internal_Rela *) + malloc (rel_size * sizeof (Elf_Internal_Rela)); + + if (relas == NULL) + { + error(_("out of memory parsing relocs")); + return 0; + } + + for (i = 0; i < rel_size; i++) + { + relas[i].r_offset = BYTE_GET8 (erelas[i].r_offset); + relas[i].r_info = BYTE_GET8 (erelas[i].r_info); + relas[i].r_addend = BYTE_GET8 (erelas[i].r_addend); + } + + free (erelas); + + rels = (Elf_Internal_Rel *) relas; + } + } + else + { + if (is_32bit_elf) + { + Elf32_External_Rel * erels; + + GET_DATA_ALLOC (rel_offset, rel_size, erels, + Elf32_External_Rel *, "relocs"); + + rel_size = rel_size / sizeof (Elf32_External_Rel); + + rels = (Elf_Internal_Rel *) + malloc (rel_size * sizeof (Elf_Internal_Rel)); + + if (rels == NULL) + { + error(_("out of memory parsing relocs")); + return 0; + } + + for (i = 0; i < rel_size; i++) + { + rels[i].r_offset = BYTE_GET (erels[i].r_offset); + rels[i].r_info = BYTE_GET (erels[i].r_info); + } + + free (erels); + + relas = (Elf_Internal_Rela *) rels; + } + else + { + Elf64_External_Rel * erels; + + GET_DATA_ALLOC (rel_offset, rel_size, erels, + Elf64_External_Rel *, "relocs"); + + rel_size = rel_size / sizeof (Elf64_External_Rel); + + rels = (Elf_Internal_Rel *) + malloc (rel_size * sizeof (Elf_Internal_Rel)); + + if (rels == NULL) + { + error(_("out of memory parsing relocs")); + return 0; + } + + for (i = 0; i < rel_size; i++) + { + rels[i].r_offset = BYTE_GET8 (erels[i].r_offset); + rels[i].r_info = BYTE_GET8 (erels[i].r_info); + } + + free (erels); + + relas = (Elf_Internal_Rela *) rels; + } + } + + if (is_rela) + printf + (_(" Offset Info Type Symbol's Value Symbol's Name Addend\n")); + else + printf + (_(" Offset Info Type Symbol's Value Symbol's Name\n")); + + for (i = 0; i < rel_size; i++) + { + const char * rtype; + bfd_vma offset; + bfd_vma info; + bfd_vma symtab_index; + bfd_vma type; + + if (is_rela) + { + offset = relas [i].r_offset; + info = relas [i].r_info; + } + else + { + offset = rels [i].r_offset; + info = rels [i].r_info; + } + + if (is_32bit_elf) + { + type = ELF32_R_TYPE (info); + symtab_index = ELF32_R_SYM (info); + } + else + { + if (elf_header.e_machine == EM_SPARCV9) + type = ELF64_R_TYPE_ID (info); + else + type = ELF64_R_TYPE (info); + /* The #ifdef BFD64 below is to prevent a compile time warning. + We know that if we do not have a 64 bit data type that we + will never execute this code anyway. */ +#ifdef BFD64 + symtab_index = ELF64_R_SYM (info); +#endif + } + +#ifdef _bfd_int64_low + printf (" %8.8lx %5.5lx ", _bfd_int64_low (offset), _bfd_int64_low (info)); +#else + printf (" %8.8lx %5.5lx ", offset, info); +#endif + + switch (elf_header.e_machine) + { + default: + rtype = NULL; + break; + + case EM_CYGNUS_M32R: + rtype = elf_m32r_reloc_type (type); + break; + + case EM_386: + case EM_486: + rtype = elf_i386_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: + 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_CYGNUS_V850: + rtype = v850_reloc_type (type); + break; + + case EM_CYGNUS_D10V: + rtype = elf_d10v_reloc_type (type); + break; + + case EM_CYGNUS_D30V: + rtype = elf_d30v_reloc_type (type); + break; + + case EM_SH: + rtype = elf_sh_reloc_type (type); + break; + + case EM_CYGNUS_MN10300: + rtype = elf_mn10300_reloc_type (type); + break; + + case EM_CYGNUS_MN10200: + rtype = elf_mn10200_reloc_type (type); + break; + + case EM_CYGNUS_FR30: + rtype = elf_fr30_reloc_type (type); + break; + + case EM_MCORE: + rtype = elf_mcore_reloc_type (type); + break; + + case EM_PPC: + rtype = elf_ppc_reloc_type (type); + break; + + case EM_MIPS: + case EM_MIPS_RS4_BE: + rtype = elf_mips_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_CYGNUS_ARC: + rtype = elf_arc_reloc_type (type); + break; + + case EM_PARISC: + rtype = elf_hppa_reloc_type (type); + break; + + case EM_PJ: + rtype = elf_pj_reloc_type (type); + break; + } + + if (rtype == NULL) +#ifdef _bfd_int64_low + printf (_("unrecognised: %-7lx"), _bfd_int64_low (type)); +#else + printf (_("unrecognised: %-7lx"), type); +#endif + else + printf ("%-21.21s", rtype); + + if (symtab_index) + { + if (symtab != NULL) + { + if (symtab_index >= nsyms) + printf (" bad symbol index: %08lx", (unsigned long) symtab_index); + else + { + Elf_Internal_Sym * psym; + + psym = symtab + symtab_index; + + printf (" "); + print_vma (psym->st_value, LONG_HEX); + printf (" "); + + if (psym->st_name == 0) + printf ("%-25.25s", + SECTION_NAME (section_headers + psym->st_shndx)); + else if (strtab == NULL) + printf (_(""), psym->st_name); + else + printf ("%-25.25s", strtab + psym->st_name); + + if (is_rela) + printf (" + %lx", (unsigned long) relas [i].r_addend); + } + } + } + else if (is_rela) + { + printf ("%*c", is_32bit_elf ? 34 : 26, ' '); + print_vma (relas[i].r_addend, LONG_HEX); + } + + if (elf_header.e_machine == EM_SPARCV9 + && !strcmp (rtype, "R_SPARC_OLO10")) + printf (" + %lx", (unsigned long) ELF64_R_TYPE_DATA (info)); + + putchar ('\n'); + } + + free (relas); + + return 1; +} + +static const char * +get_mips_dynamic_type (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_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"; + default: + return NULL; + } +} + +static const char * +get_sparc64_dynamic_type (type) + unsigned long type; +{ + switch (type) + { + case DT_SPARC_REGISTER: return "SPARC_REGISTER"; + default: + return NULL; + } +} + +static const char * +get_parisc_dynamic_type (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"; + default: + return NULL; + } +} + +static const char * +get_dynamic_type (type) + unsigned long type; +{ + static char buff [32]; + + 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_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_PLTPADSZ: return "PLTPADSZ"; + case DT_MOVEENT: return "MOVEENT"; + case DT_MOVESZ: return "MOVESZ"; + case DT_FEATURE_1: return "FEATURE_1"; + 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_SYMINFO: return "SYMINFO"; /* aka ADDRRNGHI */ + + case DT_VERSYM: return "VERSYM"; + + 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 "AUXILARY"; + case DT_USED: return "USED"; + case DT_FILTER: return "FILTER"; + + default: + if ((type >= DT_LOPROC) && (type <= DT_HIPROC)) + { + const char * result; + + switch (elf_header.e_machine) + { + case EM_MIPS: + case EM_MIPS_RS4_BE: + result = get_mips_dynamic_type (type); + break; + case EM_SPARCV9: + result = get_sparc64_dynamic_type (type); + break; + default: + result = NULL; + break; + } + + if (result != NULL) + return result; + + sprintf (buff, _("Processor Specific: %lx"), type); + } + else if ((type >= DT_LOOS) && (type <= DT_HIOS)) + { + const char * result; + + switch (elf_header.e_machine) + { + case EM_PARISC: + result = get_parisc_dynamic_type (type); + break; + default: + result = NULL; + break; + } + + if (result != NULL) + return result; + + sprintf (buff, _("Operating System specific: %lx"), type); + } + else + sprintf (buff, _(": %lx"), type); + + return buff; + } +} + +static char * +get_file_type (e_type) + unsigned e_type; +{ + static char buff [32]; + + switch (e_type) + { + case ET_NONE: return _("NONE (None)"); + case ET_REL: return _("REL (Relocatable file)"); + case ET_EXEC: return _("EXEC (Executable file)"); + case ET_DYN: return _("DYN (Shared object file)"); + case ET_CORE: return _("CORE (Core file)"); + + default: + if ((e_type >= ET_LOPROC) && (e_type <= ET_HIPROC)) + sprintf (buff, _("Processor Specific: (%x)"), e_type); + else if ((e_type >= ET_LOOS) && (e_type <= ET_HIOS)) + sprintf (buff, _("OS Specific: (%x)"), e_type); + else + sprintf (buff, _(": %x"), e_type); + return buff; + } +} + +static char * +get_machine_name (e_machine) + unsigned e_machine; +{ + static char buff [64]; /* XXX */ + + switch (e_machine) + { + case EM_NONE: return _("None"); + case EM_M32: return "WE32100"; + case EM_SPARC: return "Sparc"; + case EM_386: return "Intel 80386"; + case EM_68K: return "MC68000"; + case EM_88K: return "MC88000"; + case EM_486: return "Intel 80486"; + case EM_860: return "Intel 80860"; + case EM_MIPS: return "MIPS R3000"; + case EM_S370: return "IBM System/370"; + case EM_MIPS_RS4_BE: return "MIPS R4000 big-endian"; + case EM_OLD_SPARCV9: return "Sparc v9 (old)"; + case EM_PARISC: return "HPPA"; + case EM_PPC_OLD: return "Power PC (old)"; + case EM_SPARC32PLUS: return "Sparc v8+" ; + case EM_960: return "Intel 90860"; + case EM_PPC: return "PowerPC"; + case EM_V800: return "NEC V800"; + case EM_FR20: return "Fujitsu FR20"; + case EM_RH32: return "TRW RH32"; + case EM_MCORE: return "MCORE"; + case EM_ARM: return "ARM"; + case EM_OLD_ALPHA: return "Digital Alpha (old)"; + case EM_SH: return "Hitachi SH"; + case EM_SPARCV9: return "Sparc v9"; + case EM_TRICORE: return "Siemens Tricore"; + case EM_ARC: return "Argonaut RISC Core"; + case EM_H8_300: return "Hitachi H8/300"; + case EM_H8_300H: return "Hitachi H8/300H"; + case EM_H8S: return "Hitachi H8S"; + case EM_H8_500: return "Hitachi H8/500"; + case EM_IA_64: return "Intel IA-64"; + case EM_MIPS_X: return "Stanford MIPS-X"; + case EM_COLDFIRE: return "Motorola Coldfire"; + case EM_68HC12: return "Motorola M68HC12"; + case EM_ALPHA: return "Alpha"; + case EM_CYGNUS_D10V: return "d10v"; + case EM_CYGNUS_D30V: return "d30v"; + case EM_CYGNUS_ARC: return "Arc"; + case EM_CYGNUS_M32R: return "Mitsubishi M32r"; + case EM_CYGNUS_V850: return "NEC v850"; + case EM_CYGNUS_MN10300: return "mn10300"; + case EM_CYGNUS_MN10200: return "mn10200"; + case EM_CYGNUS_FR30: return "Fujitsu FR30"; + case EM_PJ: return "picoJava"; + case EM_MMA: return "Fujitsu Multimedia Accelerator"; + case EM_PCP: return "Siemens PCP"; + case EM_NCPU: return "Sony nCPU embedded RISC processor"; + case EM_NDR1: return "Denso NDR1 microprocesspr"; + case EM_STARCORE: return "Motorola Star*Core processor"; + case EM_ME16: return "Toyota ME16 processor"; + case EM_ST100: return "STMicroelectronics ST100 processor"; + case EM_TINYJ: return "Advanced Logic Corp. TinyJ embedded processor"; + case EM_FX66: return "Siemens FX66 microcontroller"; + case EM_ST9PLUS: return "STMicroelectronics ST9+ 8/16 bit microcontroller"; + case EM_ST7: return "STMicroelectronics ST7 8-bit microcontroller"; + case EM_68HC16: return "Motorola MC68HC16 Microcontroller"; + case EM_68HC11: return "Motorola MC68HC11 Microcontroller"; + case EM_68HC08: return "Motorola MC68HC08 Microcontroller"; + case EM_68HC05: return "Motorola MC68HC05 Microcontroller"; + case EM_SVX: return "Silicon Graphics SVx"; + case EM_ST19: return "STMicroelectronics ST19 8-bit microcontroller"; + case EM_VAX: return "Digital VAX"; + default: + sprintf (buff, _(": %x"), e_machine); + return buff; + } +} + +static char * +get_machine_flags (e_flags, e_machine) + unsigned e_flags; + unsigned e_machine; +{ + static char buf [1024]; + + buf[0] = '\0'; + + if (e_flags) + { + switch (e_machine) + { + default: + break; + + case EM_68K: + if (e_flags & EF_CPU32) + strcat (buf, ", cpu32"); + break; + + case EM_PPC: + if (e_flags & EF_PPC_EMB) + strcat (buf, ", emb"); + + if (e_flags & EF_PPC_RELOCATABLE) + strcat (buf, ", relocatable"); + + if (e_flags & EF_PPC_RELOCATABLE_LIB) + strcat (buf, ", relocatable-lib"); + break; + + case EM_CYGNUS_V850: + switch (e_flags & EF_V850_ARCH) + { + case E_V850E_ARCH: + strcat (buf, ", v850e"); + break; + case E_V850EA_ARCH: + strcat (buf, ", v850ea"); + break; + case E_V850_ARCH: + strcat (buf, ", v850"); + break; + default: + strcat (buf, ", unknown v850 architecture variant"); + break; + } + break; + + case EM_CYGNUS_M32R: + if ((e_flags & EF_M32R_ARCH) == E_M32R_ARCH) + strcat (buf, ", m32r"); + + break; + + case EM_MIPS: + case EM_MIPS_RS4_BE: + if (e_flags & EF_MIPS_NOREORDER) + strcat (buf, ", noreorder"); + + if (e_flags & EF_MIPS_PIC) + strcat (buf, ", pic"); + + if (e_flags & EF_MIPS_CPIC) + strcat (buf, ", cpic"); + + if (e_flags & EF_MIPS_ABI2) + strcat (buf, ", abi2"); + + if ((e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1) + strcat (buf, ", mips1"); + + if ((e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2) + strcat (buf, ", mips2"); + + if ((e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_3) + strcat (buf, ", mips3"); + + if ((e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_4) + strcat (buf, ", mips4"); + break; + + case EM_SPARCV9: + if (e_flags & EF_SPARC_32PLUS) + strcat (buf, ", v8+"); + + if (e_flags & EF_SPARC_SUN_US1) + strcat (buf, ", ultrasparcI"); + + if (e_flags & EF_SPARC_SUN_US3) + strcat (buf, ", ultrasparcIII"); + + if (e_flags & EF_SPARC_HAL_R1) + strcat (buf, ", halr1"); + + if (e_flags & EF_SPARC_LEDATA) + strcat (buf, ", ledata"); + + if ((e_flags & EF_SPARCV9_MM) == EF_SPARCV9_TSO) + strcat (buf, ", tso"); + + if ((e_flags & EF_SPARCV9_MM) == EF_SPARCV9_PSO) + strcat (buf, ", pso"); + + if ((e_flags & EF_SPARCV9_MM) == EF_SPARCV9_RMO) + strcat (buf, ", rmo"); + break; + + case EM_PARISC: + switch (e_flags & EF_PARISC_ARCH) + { + case EFA_PARISC_1_0: + strcpy (buf, ", PA-RISC 1.0"); + break; + case EFA_PARISC_1_1: + strcpy (buf, ", PA-RISC 1.1"); + break; + case EFA_PARISC_2_0: + strcpy (buf, ", PA-RISC 2.0"); + break; + default: + break; + } + if (e_flags & EF_PARISC_TRAPNIL) + strcat (buf, ", trapnil"); + if (e_flags & EF_PARISC_EXT) + strcat (buf, ", ext"); + if (e_flags & EF_PARISC_LSB) + strcat (buf, ", lsb"); + if (e_flags & EF_PARISC_WIDE) + strcat (buf, ", wide"); + if (e_flags & EF_PARISC_NO_KABP) + strcat (buf, ", no kabp"); + if (e_flags & EF_PARISC_LAZYSWAP) + strcat (buf, ", lazyswap"); + break; + + case EM_PJ: + if ((e_flags & EF_PICOJAVA_NEWCALLS) == EF_PICOJAVA_NEWCALLS) + strcat (buf, ", new calling convention"); + + if ((e_flags & EF_PICOJAVA_GNUCALLS) == EF_PICOJAVA_GNUCALLS) + strcat (buf, ", gnu calling convention"); + break; + } + } + + return buf; +} + +static const char * +get_mips_segment_type (type) + unsigned long type; +{ + switch (type) + { + case PT_MIPS_REGINFO: + return "REGINFO"; + case PT_MIPS_RTPROC: + return "RTPROC"; + case PT_MIPS_OPTIONS: + return "OPTIONS"; + default: + break; + } + + return NULL; +} + +static const char * +get_parisc_segment_type (type) + unsigned long type; +{ + switch (type) + { + case PT_HP_TLS: return "HP_TLS"; + case PT_HP_CORE_NONE: return "HP_CORE_NONE"; + case PT_HP_CORE_VERSION: return "HP_CORE_VERSION"; + case PT_HP_CORE_KERNEL: return "HP_CORE_KERNEL"; + case PT_HP_CORE_COMM: return "HP_CORE_COMM"; + case PT_HP_CORE_PROC: return "HP_CORE_PROC"; + case PT_HP_CORE_LOADABLE: return "HP_CORE_LOADABLE"; + case PT_HP_CORE_STACK: return "HP_CORE_STACK"; + case PT_HP_CORE_SHM: return "HP_CORE_SHM"; + case PT_HP_CORE_MMF: return "HP_CORE_MMF"; + case PT_HP_PARALLEL: return "HP_PARALLEL"; + case PT_HP_FASTBIND: return "HP_FASTBIND"; + case PT_PARISC_ARCHEXT: return "PARISC_ARCHEXT"; + case PT_PARISC_UNWIND: return "PARISC_UNWIND"; + default: + break; + } + + return NULL; +} + +static const char * +get_segment_type (p_type) + unsigned long p_type; +{ + static char buff [32]; + + switch (p_type) + { + case PT_NULL: return "NULL"; + case PT_LOAD: return "LOAD"; + case PT_DYNAMIC: return "DYNAMIC"; + case PT_INTERP: return "INTERP"; + case PT_NOTE: return "NOTE"; + case PT_SHLIB: return "SHLIB"; + case PT_PHDR: return "PHDR"; + + default: + if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC)) + { + const char * result; + + switch (elf_header.e_machine) + { + case EM_MIPS: + case EM_MIPS_RS4_BE: + result = get_mips_segment_type (p_type); + break; + case EM_PARISC: + result = get_parisc_segment_type (p_type); + break; + default: + result = NULL; + break; + } + + if (result != NULL) + return result; + + sprintf (buff, "LOPROC+%lx", p_type - PT_LOPROC); + } + else if ((p_type >= PT_LOOS) && (p_type <= PT_HIOS)) + { + const char * result; + + switch (elf_header.e_machine) + { + case EM_PARISC: + result = get_parisc_segment_type (p_type); + break; + default: + result = NULL; + break; + } + + if (result != NULL) + return result; + + sprintf (buff, "LOOS+%lx", p_type - PT_LOOS); + } + else + sprintf (buff, _(": %lx"), p_type); + + return buff; + } +} + +static const char * +get_mips_section_type_name (sh_type) + unsigned int sh_type; +{ + switch (sh_type) + { + case SHT_MIPS_LIBLIST: return "MIPS_LIBLIST"; + case SHT_MIPS_MSYM: return "MIPS_MSYM"; + case SHT_MIPS_CONFLICT: return "MIPS_CONFLICT"; + case SHT_MIPS_GPTAB: return "MIPS_GPTAB"; + case SHT_MIPS_UCODE: return "MIPS_UCODE"; + case SHT_MIPS_DEBUG: return "MIPS_DEBUG"; + case SHT_MIPS_REGINFO: return "MIPS_REGINFO"; + case SHT_MIPS_PACKAGE: return "MIPS_PACKAGE"; + case SHT_MIPS_PACKSYM: return "MIPS_PACKSYM"; + case SHT_MIPS_RELD: return "MIPS_RELD"; + case SHT_MIPS_IFACE: return "MIPS_IFACE"; + case SHT_MIPS_CONTENT: return "MIPS_CONTENT"; + case SHT_MIPS_OPTIONS: return "MIPS_OPTIONS"; + case SHT_MIPS_SHDR: return "MIPS_SHDR"; + case SHT_MIPS_FDESC: return "MIPS_FDESC"; + case SHT_MIPS_EXTSYM: return "MIPS_EXTSYM"; + case SHT_MIPS_DENSE: return "MIPS_DENSE"; + case SHT_MIPS_PDESC: return "MIPS_PDESC"; + case SHT_MIPS_LOCSYM: return "MIPS_LOCSYM"; + case SHT_MIPS_AUXSYM: return "MIPS_AUXSYM"; + case SHT_MIPS_OPTSYM: return "MIPS_OPTSYM"; + case SHT_MIPS_LOCSTR: return "MIPS_LOCSTR"; + case SHT_MIPS_LINE: return "MIPS_LINE"; + case SHT_MIPS_RFDESC: return "MIPS_RFDESC"; + case SHT_MIPS_DELTASYM: return "MIPS_DELTASYM"; + case SHT_MIPS_DELTAINST: return "MIPS_DELTAINST"; + case SHT_MIPS_DELTACLASS: return "MIPS_DELTACLASS"; + case SHT_MIPS_DWARF: return "MIPS_DWARF"; + case SHT_MIPS_DELTADECL: return "MIPS_DELTADECL"; + case SHT_MIPS_SYMBOL_LIB: return "MIPS_SYMBOL_LIB"; + case SHT_MIPS_EVENTS: return "MIPS_EVENTS"; + case SHT_MIPS_TRANSLATE: return "MIPS_TRANSLATE"; + case SHT_MIPS_PIXIE: return "MIPS_PIXIE"; + case SHT_MIPS_XLATE: return "MIPS_XLATE"; + case SHT_MIPS_XLATE_DEBUG: return "MIPS_XLATE_DEBUG"; + case SHT_MIPS_WHIRL: return "MIPS_WHIRL"; + case SHT_MIPS_EH_REGION: return "MIPS_EH_REGION"; + case SHT_MIPS_XLATE_OLD: return "MIPS_XLATE_OLD"; + case SHT_MIPS_PDR_EXCEPTION: return "MIPS_PDR_EXCEPTION"; + default: + break; + } + return NULL; +} + +static const char * +get_parisc_section_type_name (sh_type) + unsigned int sh_type; +{ + switch (sh_type) + { + case SHT_PARISC_EXT: return "PARISC_EXT"; + case SHT_PARISC_UNWIND: return "PARISC_UNWIND"; + case SHT_PARISC_DOC: return "PARISC_DOC"; + default: + break; + } + return NULL; +} + +static const char * +get_section_type_name (sh_type) + unsigned int sh_type; +{ + static char buff [32]; + + switch (sh_type) + { + case SHT_NULL: return "NULL"; + case SHT_PROGBITS: return "PROGBITS"; + case SHT_SYMTAB: return "SYMTAB"; + case SHT_STRTAB: return "STRTAB"; + case SHT_RELA: return "RELA"; + case SHT_HASH: return "HASH"; + case SHT_DYNAMIC: return "DYNAMIC"; + case SHT_NOTE: return "NOTE"; + case SHT_NOBITS: return "NOBITS"; + case SHT_REL: return "REL"; + case SHT_SHLIB: return "SHLIB"; + case SHT_DYNSYM: return "DYNSYM"; + case SHT_INIT_ARRAY: return "INIT_ARRAY"; + case SHT_FINI_ARRAY: return "FINI_ARRAY"; + case SHT_PREINIT_ARRAY: return "PREINIT_ARRAY"; + case SHT_GNU_verdef: return "VERDEF"; + case SHT_GNU_verneed: return "VERNEED"; + case SHT_GNU_versym: return "VERSYM"; + case 0x6ffffff0: return "VERSYM"; + case 0x6ffffffc: return "VERDEF"; + case 0x7ffffffd: return "AUXILIARY"; + case 0x7fffffff: return "FILTER"; + + default: + if ((sh_type >= SHT_LOPROC) && (sh_type <= SHT_HIPROC)) + { + const char * result; + + switch (elf_header.e_machine) + { + case EM_MIPS: + case EM_MIPS_RS4_BE: + result = get_mips_section_type_name (sh_type); + break; + case EM_PARISC: + result = get_parisc_section_type_name (sh_type); + break; + default: + result = NULL; + break; + } + + if (result != NULL) + return result; + + sprintf (buff, "SHT_LOPROC+%x", sh_type - SHT_LOPROC); + } + else if ((sh_type >= SHT_LOOS) && (sh_type <= SHT_HIOS)) + sprintf (buff, "SHT_LOOS+%x", sh_type - SHT_LOOS); + else if ((sh_type >= SHT_LOUSER) && (sh_type <= SHT_HIUSER)) + sprintf (buff, "SHT_LOUSER+%x", sh_type - SHT_LOUSER); + else + sprintf (buff, _(": %x"), sh_type); + + return buff; + } +} + +struct option options [] = +{ + {"all", no_argument, 0, 'a'}, + {"file-header", no_argument, 0, 'h'}, + {"program-headers", no_argument, 0, 'l'}, + {"headers", no_argument, 0, 'e'}, + {"histogram", no_argument, 0, 'I'}, + {"segments", no_argument, 0, 'l'}, + {"sections", no_argument, 0, 'S'}, + {"section-headers", no_argument, 0, 'S'}, + {"symbols", no_argument, 0, 's'}, + {"syms", no_argument, 0, 's'}, + {"relocs", no_argument, 0, 'r'}, + {"notes", no_argument, 0, 'n'}, + {"dynamic", no_argument, 0, 'd'}, + {"arch-specific", no_argument, 0, 'A'}, + {"version-info", no_argument, 0, 'V'}, + {"use-dynamic", no_argument, 0, 'D'}, + {"hex-dump", required_argument, 0, 'x'}, + {"debug-dump", optional_argument, 0, 'w'}, +#ifdef SUPPORT_DISASSEMBLY + {"instruction-dump", required_argument, 0, 'i'}, +#endif + + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'H'}, + {0, no_argument, 0, 0} +}; + +static void +usage () +{ + fprintf (stdout, _("Usage: readelf {options} elf-file(s)\n")); + fprintf (stdout, _(" Options are:\n")); + fprintf (stdout, _(" -a or --all Equivalent to: -h -l -S -s -r -d -V -A -I\n")); + fprintf (stdout, _(" -h or --file-header Display the ELF file header\n")); + fprintf (stdout, _(" -l or --program-headers or --segments\n")); + fprintf (stdout, _(" Display the program headers\n")); + fprintf (stdout, _(" -S or --section-headers or --sections\n")); + fprintf (stdout, _(" Display the sections' header\n")); + fprintf (stdout, _(" -e or --headers Equivalent to: -h -l -S\n")); + fprintf (stdout, _(" -s or --syms or --symbols Display the symbol table\n")); + fprintf (stdout, _(" -n or --notes Display the core notes (if present)\n")); + fprintf (stdout, _(" -r or --relocs Display the relocations (if present)\n")); + fprintf (stdout, _(" -d or --dynamic Display the dynamic segment (if present)\n")); + fprintf (stdout, _(" -V or --version-info Display the version sections (if present)\n")); + fprintf (stdout, _(" -A or --arch-specific Display architecture specific information (if any).\n")); + fprintf (stdout, _(" -D or --use-dynamic Use the dynamic section info when displaying symbols\n")); + fprintf (stdout, _(" -x or --hex-dump=\n")); + fprintf (stdout, _(" Dump the contents of section \n")); + fprintf (stdout, _(" -w[liapr] or --debug-dump[=line,=info,=abbrev,=pubnames,=ranges]\n")); + fprintf (stdout, _(" Display the contents of DWARF2 debug sections\n")); +#ifdef SUPPORT_DISASSEMBLY + fprintf (stdout, _(" -i or --instruction-dump=\n")); + fprintf (stdout, _(" Disassemble the contents of section \n")); +#endif + fprintf (stdout, _(" -I or --histogram Display histogram of bucket list lengths\n")); + fprintf (stdout, _(" -v or --version Display the version number of readelf\n")); + fprintf (stdout, _(" -H or --help Display this information\n")); + fprintf (stdout, _("Report bugs to %s\n"), REPORT_BUGS_TO); + + exit (0); +} + +static void +request_dump (section, type) + unsigned int section; + char type; +{ + if (section >= num_dump_sects) + { + char * new_dump_sects; + + new_dump_sects = (char *) calloc (section + 1, 1); + + if (new_dump_sects == NULL) + error (_("Out of memory allocating dump request table.")); + else + { + /* Copy current flag settings. */ + memcpy (new_dump_sects, dump_sects, num_dump_sects); + + free (dump_sects); + + dump_sects = new_dump_sects; + num_dump_sects = section + 1; + } + } + + if (dump_sects) + dump_sects [section] |= type; + + return; +} + +static void +parse_args (argc, argv) + int argc; + char ** argv; +{ + int c; + + if (argc < 2) + usage (); + + while ((c = getopt_long + (argc, argv, "ersahnldSDAIw::x:i:vV", options, NULL)) != EOF) + { + char * cp; + int section; + + switch (c) + { + case 0: + /* Long options. */ + break; + case 'H': + usage (); + break; + + case 'a': + do_syms ++; + do_reloc ++; + do_dynamic ++; + do_header ++; + do_sections ++; + do_segments ++; + do_version ++; + do_histogram ++; + do_arch ++; + do_notes ++; + break; + case 'e': + do_header ++; + do_sections ++; + do_segments ++; + break; + case 'A': + do_arch ++; + break; + case 'D': + do_using_dynamic ++; + break; + case 'r': + do_reloc ++; + break; + case 'h': + do_header ++; + break; + case 'l': + do_segments ++; + break; + case 's': + do_syms ++; + break; + case 'S': + do_sections ++; + break; + case 'd': + do_dynamic ++; + break; + case 'I': + do_histogram ++; + break; + case 'n': + do_notes ++; + break; + case 'x': + do_dump ++; + section = strtoul (optarg, & cp, 0); + if (! * cp && section >= 0) + { + request_dump (section, HEX_DUMP); + break; + } + goto oops; + case 'w': + do_dump ++; + if (optarg == 0) + do_debugging = 1; + else + { + do_debugging = 0; + switch (optarg[0]) + { + case 'i': + case 'I': + do_debug_info = 1; + break; + + case 'a': + case 'A': + do_debug_abbrevs = 1; + break; + + case 'l': + case 'L': + do_debug_lines = 1; + break; + + case 'p': + case 'P': + do_debug_pubnames = 1; + break; + + case 'r': + case 'R': + do_debug_aranges = 1; + break; + + default: + warn (_("Unrecognised debug option '%s'\n"), optarg); + break; + } + } + break; +#ifdef SUPPORT_DISASSEMBLY + case 'i': + do_dump ++; + section = strtoul (optarg, & cp, 0); + if (! * cp && section >= 0) + { + request_dump (section, DISASS_DUMP); + break; + } + goto oops; +#endif + case 'v': + print_version (program_name); + break; + case 'V': + do_version ++; + break; + default: + oops: + /* xgettext:c-format */ + error (_("Invalid option '-%c'\n"), c); + /* Drop through. */ + case '?': + usage (); + } + } + + if (!do_dynamic && !do_syms && !do_reloc && !do_sections + && !do_segments && !do_header && !do_dump && !do_version + && !do_histogram && !do_debugging && !do_arch && !do_notes) + usage (); + else if (argc < 3) + { + warn (_("Nothing to do.\n")); + usage(); + } +} + +static const char * +get_elf_class (elf_class) + unsigned char elf_class; +{ + static char buff [32]; + + switch (elf_class) + { + case ELFCLASSNONE: return _("none"); + case ELFCLASS32: return _("ELF32"); + case ELFCLASS64: return _("ELF64"); + default: + sprintf (buff, _(""), elf_class); + return buff; + } +} + +static const char * +get_data_encoding (encoding) + unsigned char encoding; +{ + static char buff [32]; + + switch (encoding) + { + case ELFDATANONE: return _("none"); + case ELFDATA2LSB: return _("2's complement, little endian"); + case ELFDATA2MSB: return _("2's complement, big endian"); + default: + sprintf (buff, _(""), encoding); + return buff; + } +} + +static const char * +get_osabi_name (osabi) + unsigned char osabi; +{ + static char buff [32]; + + switch (osabi) + { + case ELFOSABI_NONE: return _("UNIX - System V"); + case ELFOSABI_HPUX: return _("UNIX - HP-UX"); + case ELFOSABI_NETBSD: return _("UNIX - NetBSD"); + case ELFOSABI_LINUX: return _("UNIX - Linux"); + case ELFOSABI_HURD: return _("GNU/Hurd"); + case ELFOSABI_SOLARIS: return _("UNIX - Solaris"); + case ELFOSABI_MONTEREY: return _("UNIX - Monterey"); + case ELFOSABI_IRIX: return _("UNIX - IRIX"); + case ELFOSABI_FREEBSD: return _("UNIX - FreeBSD"); + case ELFOSABI_TRU64: return _("UNIX - TRU64"); + case ELFOSABI_MODESTO: return _("Novell - Modesto"); + case ELFOSABI_OPENBSD: return _("UNIX - OpenBSD"); + case ELFOSABI_STANDALONE: return _("Standalone App"); + case ELFOSABI_ARM: return _("ARM"); + default: + sprintf (buff, _(""), osabi); + return buff; + } +} + +/* Decode the data held in 'elf_header'. */ +static int +process_file_header () +{ + if ( elf_header.e_ident [EI_MAG0] != ELFMAG0 + || elf_header.e_ident [EI_MAG1] != ELFMAG1 + || elf_header.e_ident [EI_MAG2] != ELFMAG2 + || elf_header.e_ident [EI_MAG3] != ELFMAG3) + { + error + (_("Not an ELF file - it has the wrong magic bytes at the start\n")); + return 0; + } + + if (do_header) + { + int i; + + printf (_("ELF Header:\n")); + printf (_(" Magic: ")); + for (i = 0; i < EI_NIDENT; i ++) + printf ("%2.2x ", elf_header.e_ident [i]); + printf ("\n"); + printf (_(" Class: %s\n"), + get_elf_class (elf_header.e_ident [EI_CLASS])); + printf (_(" Data: %s\n"), + get_data_encoding (elf_header.e_ident [EI_DATA])); + printf (_(" Version: %d %s\n"), + elf_header.e_ident [EI_VERSION], + (elf_header.e_ident [EI_VERSION] == EV_CURRENT + ? "(current)" + : (elf_header.e_ident [EI_VERSION] != EV_NONE + ? "" + : ""))); + printf (_(" OS/ABI: %s\n"), + get_osabi_name (elf_header.e_ident [EI_OSABI])); + printf (_(" ABI Version: %d\n"), + elf_header.e_ident [EI_ABIVERSION]); + printf (_(" Type: %s\n"), + get_file_type (elf_header.e_type)); + printf (_(" Machine: %s\n"), + get_machine_name (elf_header.e_machine)); + printf (_(" Version: 0x%lx\n"), + (unsigned long) elf_header.e_version); + + printf (_(" Entry point address: ")); + print_vma ((bfd_vma) elf_header.e_entry, PREFIX_HEX); + printf (_("\n Start of program headers: ")); + print_vma ((bfd_vma) elf_header.e_phoff, DEC); + printf (_(" (bytes into file)\n Start of section headers: ")); + print_vma ((bfd_vma) elf_header.e_shoff, DEC); + printf (_(" (bytes into file)\n")); + + printf (_(" Flags: 0x%lx%s\n"), + (unsigned long) elf_header.e_flags, + get_machine_flags (elf_header.e_flags, elf_header.e_machine)); + printf (_(" Size of this header: %ld (bytes)\n"), + (long) elf_header.e_ehsize); + printf (_(" Size of program headers: %ld (bytes)\n"), + (long) elf_header.e_phentsize); + printf (_(" Number of program headers: %ld\n"), + (long) elf_header.e_phnum); + printf (_(" Size of section headers: %ld (bytes)\n"), + (long) elf_header.e_shentsize); + printf (_(" Number of section headers: %ld\n"), + (long) elf_header.e_shnum); + printf (_(" Section header string table index: %ld\n"), + (long) elf_header.e_shstrndx); + } + + return 1; +} + + +static int +get_32bit_program_headers (file, program_headers) + FILE * file; + Elf_Internal_Phdr * program_headers; +{ + Elf32_External_Phdr * phdrs; + Elf32_External_Phdr * external; + Elf32_Internal_Phdr * internal; + unsigned int i; + + GET_DATA_ALLOC (elf_header.e_phoff, + elf_header.e_phentsize * elf_header.e_phnum, + phdrs, Elf32_External_Phdr *, "program headers"); + + for (i = 0, internal = program_headers, external = phdrs; + i < elf_header.e_phnum; + i ++, internal ++, external ++) + { + internal->p_type = BYTE_GET (external->p_type); + internal->p_offset = BYTE_GET (external->p_offset); + internal->p_vaddr = BYTE_GET (external->p_vaddr); + internal->p_paddr = BYTE_GET (external->p_paddr); + internal->p_filesz = BYTE_GET (external->p_filesz); + internal->p_memsz = BYTE_GET (external->p_memsz); + internal->p_flags = BYTE_GET (external->p_flags); + internal->p_align = BYTE_GET (external->p_align); + } + + free (phdrs); + + return 1; +} + +static int +get_64bit_program_headers (file, program_headers) + FILE * file; + Elf_Internal_Phdr * program_headers; +{ + Elf64_External_Phdr * phdrs; + Elf64_External_Phdr * external; + Elf64_Internal_Phdr * internal; + unsigned int i; + + GET_DATA_ALLOC (elf_header.e_phoff, + elf_header.e_phentsize * elf_header.e_phnum, + phdrs, Elf64_External_Phdr *, "program headers"); + + for (i = 0, internal = program_headers, external = phdrs; + i < elf_header.e_phnum; + i ++, internal ++, external ++) + { + internal->p_type = BYTE_GET (external->p_type); + internal->p_flags = BYTE_GET (external->p_flags); + internal->p_offset = BYTE_GET8 (external->p_offset); + internal->p_vaddr = BYTE_GET8 (external->p_vaddr); + internal->p_paddr = BYTE_GET8 (external->p_paddr); + internal->p_filesz = BYTE_GET8 (external->p_filesz); + internal->p_memsz = BYTE_GET8 (external->p_memsz); + internal->p_align = BYTE_GET8 (external->p_align); + } + + free (phdrs); + + return 1; +} + +static int +process_program_headers (file) + FILE * file; +{ + Elf_Internal_Phdr * program_headers; + Elf_Internal_Phdr * segment; + unsigned int i; + + if (elf_header.e_phnum == 0) + { + if (do_segments) + printf (_("\nThere are no program headers in this file.\n")); + return 1; + } + + if (do_segments && !do_header) + { + printf (_("\nElf file type is %s\n"), get_file_type (elf_header.e_type)); + printf (_("Entry point ")); + print_vma ((bfd_vma) elf_header.e_entry, PREFIX_HEX); + printf (_("\nThere are %d program headers, starting at offset "), + elf_header.e_phnum); + print_vma ((bfd_vma) elf_header.e_phoff, DEC); + printf ("\n"); + } + + program_headers = (Elf_Internal_Phdr *) malloc + (elf_header.e_phnum * sizeof (Elf_Internal_Phdr)); + + if (program_headers == NULL) + { + error (_("Out of memory\n")); + return 0; + } + + if (is_32bit_elf) + i = get_32bit_program_headers (file, program_headers); + else + i = get_64bit_program_headers (file, program_headers); + + if (i == 0) + { + free (program_headers); + return 0; + } + + if (do_segments) + { + printf + (_("\nProgram Header%s:\n"), elf_header.e_phnum > 1 ? "s" : ""); + + if (is_32bit_elf) + printf + (_(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n")); + else + { + printf + (_(" Type Offset VirtAddr PhysAddr\n")); + printf + (_(" FileSiz MemSiz Flags Align\n")); + } + } + + loadaddr = -1; + dynamic_addr = 0; + dynamic_size = 0; + + for (i = 0, segment = program_headers; + i < elf_header.e_phnum; + i ++, segment ++) + { + if (do_segments) + { + printf (" %-14.14s ", get_segment_type (segment->p_type)); + + if (is_32bit_elf) + { + printf ("0x%6.6lx ", (unsigned long) segment->p_offset); + printf ("0x%8.8lx ", (unsigned long) segment->p_vaddr); + printf ("0x%8.8lx ", (unsigned long) segment->p_paddr); + printf ("0x%5.5lx ", (unsigned long) segment->p_filesz); + printf ("0x%5.5lx ", (unsigned long) segment->p_memsz); + printf ("%c%c%c ", + (segment->p_flags & PF_R ? 'R' : ' '), + (segment->p_flags & PF_W ? 'W' : ' '), + (segment->p_flags & PF_X ? 'E' : ' ')); + printf ("%#lx", (unsigned long) segment->p_align); + } + else + { + print_vma (segment->p_offset, FULL_HEX); + putchar (' '); + print_vma (segment->p_vaddr, FULL_HEX); + putchar (' '); + print_vma (segment->p_paddr, FULL_HEX); + printf ("\n "); + print_vma (segment->p_filesz, FULL_HEX); + putchar (' '); + print_vma (segment->p_memsz, FULL_HEX); + printf (" %c%c%c ", + (segment->p_flags & PF_R ? 'R' : ' '), + (segment->p_flags & PF_W ? 'W' : ' '), + (segment->p_flags & PF_X ? 'E' : ' ')); + print_vma (segment->p_align, HEX); + } + } + + switch (segment->p_type) + { + case PT_LOAD: + if (loadaddr == -1) + loadaddr = (segment->p_vaddr & 0xfffff000) + - (segment->p_offset & 0xfffff000); + break; + + case PT_DYNAMIC: + if (dynamic_addr) + error (_("more than one dynamic segment\n")); + + dynamic_addr = segment->p_offset; + dynamic_size = segment->p_filesz; + break; + + case PT_INTERP: + if (fseek (file, (long) segment->p_offset, SEEK_SET)) + error (_("Unable to find program interpreter name\n")); + else + { + program_interpreter[0] = 0; + fscanf (file, "%63s", program_interpreter); + + if (do_segments) + printf (_("\n [Requesting program interpreter: %s]"), + program_interpreter); + } + break; + } + + if (do_segments) + putc ('\n', stdout); + } + + if (loadaddr == -1) + { + /* Very strange. */ + loadaddr = 0; + } + + if (do_segments && section_headers != NULL) + { + printf (_("\n Section to Segment mapping:\n")); + printf (_(" Segment Sections...\n")); + + assert (string_table != NULL); + + for (i = 0; i < elf_header.e_phnum; i++) + { + int j; + Elf_Internal_Shdr * section; + + segment = program_headers + i; + section = section_headers; + + printf (" %2.2d ", i); + + for (j = 0; j < elf_header.e_shnum; j++, section ++) + { + if (section->sh_size > 0 + /* Compare allocated sections by VMA, unallocated + sections by file offset. */ + && (section->sh_flags & SHF_ALLOC + ? (section->sh_addr >= segment->p_vaddr + && section->sh_addr + section->sh_size + <= segment->p_vaddr + segment->p_memsz) + : ((bfd_vma) section->sh_offset >= segment->p_offset + && (section->sh_offset + section->sh_size + <= segment->p_offset + segment->p_filesz)))) + printf ("%s ", SECTION_NAME (section)); + } + + putc ('\n',stdout); + } + } + + free (program_headers); + + return 1; +} + + +static int +get_32bit_section_headers (file) + FILE * file; +{ + Elf32_External_Shdr * shdrs; + Elf32_Internal_Shdr * internal; + unsigned int i; + + GET_DATA_ALLOC (elf_header.e_shoff, + elf_header.e_shentsize * elf_header.e_shnum, + shdrs, Elf32_External_Shdr *, "section headers"); + + section_headers = (Elf_Internal_Shdr *) malloc + (elf_header.e_shnum * sizeof (Elf_Internal_Shdr)); + + if (section_headers == NULL) + { + error (_("Out of memory\n")); + return 0; + } + + for (i = 0, internal = section_headers; + i < elf_header.e_shnum; + i ++, internal ++) + { + internal->sh_name = BYTE_GET (shdrs[i].sh_name); + internal->sh_type = BYTE_GET (shdrs[i].sh_type); + internal->sh_flags = BYTE_GET (shdrs[i].sh_flags); + internal->sh_addr = BYTE_GET (shdrs[i].sh_addr); + internal->sh_offset = BYTE_GET (shdrs[i].sh_offset); + internal->sh_size = BYTE_GET (shdrs[i].sh_size); + internal->sh_link = BYTE_GET (shdrs[i].sh_link); + internal->sh_info = BYTE_GET (shdrs[i].sh_info); + internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign); + internal->sh_entsize = BYTE_GET (shdrs[i].sh_entsize); + } + + free (shdrs); + + return 1; +} + +static int +get_64bit_section_headers (file) + FILE * file; +{ + Elf64_External_Shdr * shdrs; + Elf64_Internal_Shdr * internal; + unsigned int i; + + GET_DATA_ALLOC (elf_header.e_shoff, + elf_header.e_shentsize * elf_header.e_shnum, + shdrs, Elf64_External_Shdr *, "section headers"); + + section_headers = (Elf_Internal_Shdr *) malloc + (elf_header.e_shnum * sizeof (Elf_Internal_Shdr)); + + if (section_headers == NULL) + { + error (_("Out of memory\n")); + return 0; + } + + for (i = 0, internal = section_headers; + i < elf_header.e_shnum; + i ++, internal ++) + { + internal->sh_name = BYTE_GET (shdrs[i].sh_name); + internal->sh_type = BYTE_GET (shdrs[i].sh_type); + internal->sh_flags = BYTE_GET8 (shdrs[i].sh_flags); + internal->sh_addr = BYTE_GET8 (shdrs[i].sh_addr); + internal->sh_size = BYTE_GET8 (shdrs[i].sh_size); + internal->sh_entsize = BYTE_GET8 (shdrs[i].sh_entsize); + internal->sh_link = BYTE_GET (shdrs[i].sh_link); + internal->sh_info = BYTE_GET (shdrs[i].sh_info); + internal->sh_offset = BYTE_GET (shdrs[i].sh_offset); + internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign); + } + + free (shdrs); + + return 1; +} + +static Elf_Internal_Sym * +get_32bit_elf_symbols (file, offset, number) + FILE * file; + unsigned long offset; + unsigned long number; +{ + Elf32_External_Sym * esyms; + Elf_Internal_Sym * isyms; + Elf_Internal_Sym * psym; + unsigned int j; + + GET_DATA_ALLOC (offset, number * sizeof (Elf32_External_Sym), + esyms, Elf32_External_Sym *, "symbols"); + + isyms = (Elf_Internal_Sym *) malloc (number * sizeof (Elf_Internal_Sym)); + + if (isyms == NULL) + { + error (_("Out of memory\n")); + free (esyms); + + return NULL; + } + + for (j = 0, psym = isyms; + j < number; + j ++, psym ++) + { + psym->st_name = BYTE_GET (esyms[j].st_name); + psym->st_value = BYTE_GET (esyms[j].st_value); + psym->st_size = BYTE_GET (esyms[j].st_size); + psym->st_shndx = BYTE_GET (esyms[j].st_shndx); + psym->st_info = BYTE_GET (esyms[j].st_info); + psym->st_other = BYTE_GET (esyms[j].st_other); + } + + free (esyms); + + return isyms; +} + +static Elf_Internal_Sym * +get_64bit_elf_symbols (file, offset, number) + FILE * file; + unsigned long offset; + unsigned long number; +{ + Elf64_External_Sym * esyms; + Elf_Internal_Sym * isyms; + Elf_Internal_Sym * psym; + unsigned int j; + + GET_DATA_ALLOC (offset, number * sizeof (Elf64_External_Sym), + esyms, Elf64_External_Sym *, "symbols"); + + isyms = (Elf_Internal_Sym *) malloc (number * sizeof (Elf_Internal_Sym)); + + if (isyms == NULL) + { + error (_("Out of memory\n")); + free (esyms); + + return NULL; + } + + for (j = 0, psym = isyms; + j < number; + j ++, psym ++) + { + psym->st_name = BYTE_GET (esyms[j].st_name); + psym->st_info = BYTE_GET (esyms[j].st_info); + psym->st_other = BYTE_GET (esyms[j].st_other); + psym->st_shndx = BYTE_GET (esyms[j].st_shndx); + psym->st_value = BYTE_GET8 (esyms[j].st_value); + psym->st_size = BYTE_GET8 (esyms[j].st_size); + } + + free (esyms); + + return isyms; +} + +static const char * +get_elf_section_flags (sh_flags) + bfd_vma sh_flags; +{ + static char buff [32]; + + * buff = 0; + + while (sh_flags) + { + bfd_vma flag; + + flag = sh_flags & - sh_flags; + sh_flags &= ~ flag; + + switch (flag) + { + case SHF_WRITE: strcat (buff, "W"); break; + case SHF_ALLOC: strcat (buff, "A"); break; + case SHF_EXECINSTR: strcat (buff, "X"); break; + case SHF_MERGE: strcat (buff, "M"); break; + case SHF_STRINGS: strcat (buff, "S"); break; + case SHF_INFO_LINK: strcat (buff, "I"); break; + case SHF_LINK_ORDER: strcat (buff, "L"); break; + case SHF_OS_NONCONFORMING: strcat (buff, "O"); break; + + default: + if (flag & SHF_MASKOS) + { + strcat (buff, "o"); + sh_flags &= ~ SHF_MASKOS; + } + else if (flag & SHF_MASKPROC) + { + strcat (buff, "p"); + sh_flags &= ~ SHF_MASKPROC; + } + else + strcat (buff, "x"); + break; + } + } + + return buff; +} + +static int +process_section_headers (file) + FILE * file; +{ + Elf_Internal_Shdr * section; + int i; + + section_headers = NULL; + + if (elf_header.e_shnum == 0) + { + if (do_sections) + printf (_("\nThere are no sections in this file.\n")); + + return 1; + } + + if (do_sections && !do_header) + printf (_("There are %d section headers, starting at offset 0x%lx:\n"), + elf_header.e_shnum, (unsigned long) elf_header.e_shoff); + + if (is_32bit_elf) + { + if (! get_32bit_section_headers (file)) + return 0; + } + else if (! get_64bit_section_headers (file)) + return 0; + + /* Read in the string table, so that we have names to display. */ + section = section_headers + elf_header.e_shstrndx; + + if (section->sh_size != 0) + { + unsigned long string_table_offset; + + string_table_offset = section->sh_offset; + + GET_DATA_ALLOC (section->sh_offset, section->sh_size, + string_table, char *, "string table"); + } + + /* Scan the sections for the dynamic symbol table + and dynamic string table and debug sections. */ + dynamic_symbols = NULL; + dynamic_strings = NULL; + dynamic_syminfo = NULL; + + for (i = 0, section = section_headers; + i < elf_header.e_shnum; + i ++, section ++) + { + char * name = SECTION_NAME (section); + + if (section->sh_type == SHT_DYNSYM) + { + if (dynamic_symbols != NULL) + { + error (_("File contains multiple dynamic symbol tables\n")); + continue; + } + + num_dynamic_syms = section->sh_size / section->sh_entsize; + dynamic_symbols = + GET_ELF_SYMBOLS (file, section->sh_offset, num_dynamic_syms); + } + else if (section->sh_type == SHT_STRTAB + && strcmp (name, ".dynstr") == 0) + { + if (dynamic_strings != NULL) + { + error (_("File contains multiple dynamic string tables\n")); + continue; + } + + GET_DATA_ALLOC (section->sh_offset, section->sh_size, + dynamic_strings, char *, "dynamic strings"); + } + else if ((do_debugging || do_debug_info || do_debug_abbrevs + || do_debug_lines || do_debug_pubnames || do_debug_aranges) + && strncmp (name, ".debug_", 7) == 0) + { + name += 7; + + if (do_debugging + || (do_debug_info && (strcmp (name, "info") == 0)) + || (do_debug_abbrevs && (strcmp (name, "abbrev") == 0)) + || (do_debug_lines && (strcmp (name, "line") == 0)) + || (do_debug_pubnames && (strcmp (name, "pubnames") == 0)) + || (do_debug_aranges && (strcmp (name, "aranges") == 0)) + ) + request_dump (i, DEBUG_DUMP); + } + } + + if (! do_sections) + return 1; + + printf (_("\nSection Header%s:\n"), elf_header.e_shnum > 1 ? "s" : ""); + + if (is_32bit_elf) + printf + (_(" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n")); + else + { + printf (_(" [Nr] Name Type Address Offset\n")); + printf (_(" Size EntSize Flags Link Info Align\n")); + } + + for (i = 0, section = section_headers; + i < elf_header.e_shnum; + i ++, section ++) + { + printf (" [%2d] %-17.17s %-15.15s ", + i, + SECTION_NAME (section), + get_section_type_name (section->sh_type)); + + if (is_32bit_elf) + { + print_vma (section->sh_addr, LONG_HEX); + + printf ( " %6.6lx %6.6lx %2.2lx", + (unsigned long) section->sh_offset, + (unsigned long) section->sh_size, + (unsigned long) section->sh_entsize); + + printf (" %3s ", get_elf_section_flags (section->sh_flags)); + + printf (" %2ld %3lx %ld\n", + (unsigned long) section->sh_link, + (unsigned long) section->sh_info, + (unsigned long) section->sh_addralign); + } + else + { + putchar (' '); + print_vma (section->sh_addr, LONG_HEX); + printf (" %8.8lx", section->sh_offset); + printf ("\n "); + print_vma (section->sh_size, LONG_HEX); + printf (" "); + print_vma (section->sh_entsize, LONG_HEX); + + printf (" %3s ", get_elf_section_flags (section->sh_flags)); + + printf (" %2ld %3lx %ld\n", + (unsigned long) section->sh_link, + (unsigned long) section->sh_info, + (unsigned long) section->sh_addralign); + } + } + + printf (_("Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings)\n")); + printf (_(" I (info), L (link order), O (extra OS processing required)\n")); + printf (_(" o (os specific), p (processor specific) x (unknown)\n")); + + return 1; +} + +/* Process the reloc section. */ +static int +process_relocs (file) + FILE * file; +{ + unsigned long rel_size; + unsigned long rel_offset; + + + if (!do_reloc) + return 1; + + if (do_using_dynamic) + { + int is_rela = FALSE; + + rel_size = 0; + rel_offset = 0; + + if (dynamic_info[DT_REL]) + { + rel_offset = dynamic_info[DT_REL]; + rel_size = dynamic_info[DT_RELSZ]; + is_rela = FALSE; + } + else if (dynamic_info [DT_RELA]) + { + rel_offset = dynamic_info[DT_RELA]; + rel_size = dynamic_info[DT_RELASZ]; + is_rela = TRUE; + } + else if (dynamic_info[DT_JMPREL]) + { + rel_offset = dynamic_info[DT_JMPREL]; + rel_size = dynamic_info[DT_PLTRELSZ]; + + switch (dynamic_info[DT_PLTREL]) + { + case DT_REL: + is_rela = FALSE; + break; + case DT_RELA: + is_rela = TRUE; + break; + default: + is_rela = UNKNOWN; + break; + } + } + + if (rel_size) + { + printf + (_("\nRelocation section at offset 0x%lx contains %ld bytes:\n"), + rel_offset, rel_size); + + dump_relocations (file, rel_offset - loadaddr, rel_size, + dynamic_symbols, num_dynamic_syms, dynamic_strings, is_rela); + } + else + printf (_("\nThere are no dynamic relocations in this file.\n")); + } + else + { + Elf32_Internal_Shdr * section; + unsigned long i; + int found = 0; + + for (i = 0, section = section_headers; + i < elf_header.e_shnum; + i++, section ++) + { + if ( section->sh_type != SHT_RELA + && section->sh_type != SHT_REL) + continue; + + rel_offset = section->sh_offset; + rel_size = section->sh_size; + + if (rel_size) + { + Elf32_Internal_Shdr * strsec; + Elf32_Internal_Shdr * symsec; + Elf_Internal_Sym * symtab; + char * strtab; + int is_rela; + unsigned long nsyms; + + printf (_("\nRelocation section ")); + + if (string_table == NULL) + printf ("%d", section->sh_name); + else + printf ("'%s'", SECTION_NAME (section)); + + printf (_(" at offset 0x%lx contains %lu entries:\n"), + rel_offset, (unsigned long) (rel_size / section->sh_entsize)); + + symsec = section_headers + section->sh_link; + + nsyms = symsec->sh_size / symsec->sh_entsize; + symtab = GET_ELF_SYMBOLS (file, symsec->sh_offset, nsyms); + + if (symtab == NULL) + continue; + + strsec = section_headers + symsec->sh_link; + + GET_DATA_ALLOC (strsec->sh_offset, strsec->sh_size, strtab, + char *, "string table"); + + is_rela = section->sh_type == SHT_RELA; + + dump_relocations (file, rel_offset, rel_size, symtab, nsyms, strtab, is_rela); + + free (strtab); + free (symtab); + + found = 1; + } + } + + if (! found) + printf (_("\nThere are no relocations in this file.\n")); + } + + return 1; +} + + +static void +dynamic_segment_mips_val (entry) + Elf_Internal_Dyn * entry; +{ + switch (entry->d_tag) + { + case DT_MIPS_FLAGS: + if (entry->d_un.d_val == 0) + printf ("NONE\n"); + else + { + static const char * opts[] = + { + "QUICKSTART", "NOTPOT", "NO_LIBRARY_REPLACEMENT", + "NO_MOVE", "SGI_ONLY", "GUARANTEE_INIT", "DELTA_C_PLUS_PLUS", + "GUARANTEE_START_INIT", "PIXIE", "DEFAULT_DELAY_LOAD", + "REQUICKSTART", "REQUICKSTARTED", "CORD", "NO_UNRES_UNDEF", + "RLD_ORDER_SAFE" + }; + unsigned int cnt; + int first = 1; + for (cnt = 0; cnt < NUM_ELEM (opts); ++ cnt) + if (entry->d_un.d_val & (1 << cnt)) + { + printf ("%s%s", first ? "" : " ", opts[cnt]); + first = 0; + } + puts (""); + } + break; + + case DT_MIPS_IVERSION: + if (dynamic_strings != NULL) + printf ("Interface Version: %s\n", + dynamic_strings + entry->d_un.d_val); + else + printf ("%ld\n", (long) entry->d_un.d_ptr); + break; + + case DT_MIPS_TIME_STAMP: + { + char timebuf[20]; + struct tm * tmp; + + time_t time = entry->d_un.d_val; + tmp = gmtime (&time); + sprintf (timebuf, "%04u-%02u-%02uT%02u:%02u:%02u", + tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + printf ("Time Stamp: %s\n", timebuf); + } + break; + + case DT_MIPS_RLD_VERSION: + case DT_MIPS_LOCAL_GOTNO: + case DT_MIPS_CONFLICTNO: + case DT_MIPS_LIBLISTNO: + case DT_MIPS_SYMTABNO: + case DT_MIPS_UNREFEXTNO: + case DT_MIPS_HIPAGENO: + case DT_MIPS_DELTA_CLASS_NO: + case DT_MIPS_DELTA_INSTANCE_NO: + case DT_MIPS_DELTA_RELOC_NO: + case DT_MIPS_DELTA_SYM_NO: + case DT_MIPS_DELTA_CLASSSYM_NO: + case DT_MIPS_COMPACT_SIZE: + printf ("%ld\n", (long) entry->d_un.d_ptr); + break; + + default: + printf ("%#lx\n", (long) entry->d_un.d_ptr); + } +} + + +static void +dynamic_segment_parisc_val (entry) + Elf_Internal_Dyn * entry; +{ + switch (entry->d_tag) + { + case DT_HP_DLD_FLAGS: + { + static struct + { + long int bit; + const char * str; + } + flags[] = + { + { DT_HP_DEBUG_PRIVATE, "HP_DEBUG_PRIVATE" }, + { DT_HP_DEBUG_CALLBACK, "HP_DEBUG_CALLBACK" }, + { DT_HP_DEBUG_CALLBACK_BOR, "HP_DEBUG_CALLBACK_BOR" }, + { DT_HP_NO_ENVVAR, "HP_NO_ENVVAR" }, + { DT_HP_BIND_NOW, "HP_BIND_NOW" }, + { DT_HP_BIND_NONFATAL, "HP_BIND_NONFATAL" }, + { DT_HP_BIND_VERBOSE, "HP_BIND_VERBOSE" }, + { DT_HP_BIND_RESTRICTED, "HP_BIND_RESTRICTED" }, + { DT_HP_BIND_SYMBOLIC, "HP_BIND_SYMBOLIC" }, + { DT_HP_RPATH_FIRST, "HP_RPATH_FIRST" }, + { DT_HP_BIND_DEPTH_FIRST, "HP_BIND_DEPTH_FIRST" } + }; + int first = 1; + size_t cnt; + bfd_vma val = entry->d_un.d_val; + + for (cnt = 0; cnt < sizeof (flags) / sizeof (flags[0]); ++cnt) + if (val & flags[cnt].bit) + { + if (! first) + putchar (' '); + fputs (flags[cnt].str, stdout); + first = 0; + val ^= flags[cnt].bit; + } + + if (val != 0 || first) + { + if (! first) + putchar (' '); + print_vma (val, HEX); + } + } + break; + + default: + print_vma (entry->d_un.d_ptr, PREFIX_HEX); + break; + } +} + +static int +get_32bit_dynamic_segment (file) + FILE * file; +{ + Elf32_External_Dyn * edyn; + Elf_Internal_Dyn * entry; + bfd_size_type i; + + GET_DATA_ALLOC (dynamic_addr, dynamic_size, + edyn, Elf32_External_Dyn *, "dynamic segment"); + + /* SGI's ELF has more than one section in the DYNAMIC segment. Determine + how large this .dynamic is now. We can do this even before the byte + swapping since the DT_NULL tag is recognizable. */ + dynamic_size = 0; + while (*(Elf32_Word *) edyn [dynamic_size++].d_tag != DT_NULL) + ; + + dynamic_segment = (Elf_Internal_Dyn *) + malloc (dynamic_size * sizeof (Elf_Internal_Dyn)); + + if (dynamic_segment == NULL) + { + error (_("Out of memory\n")); + free (edyn); + return 0; + } + + for (i = 0, entry = dynamic_segment; + i < dynamic_size; + i ++, entry ++) + { + entry->d_tag = BYTE_GET (edyn [i].d_tag); + entry->d_un.d_val = BYTE_GET (edyn [i].d_un.d_val); + } + + free (edyn); + + return 1; +} + +static int +get_64bit_dynamic_segment (file) + FILE * file; +{ + Elf64_External_Dyn * edyn; + Elf_Internal_Dyn * entry; + bfd_size_type i; + + GET_DATA_ALLOC (dynamic_addr, dynamic_size, + edyn, Elf64_External_Dyn *, "dynamic segment"); + + /* SGI's ELF has more than one section in the DYNAMIC segment. Determine + how large this .dynamic is now. We can do this even before the byte + swapping since the DT_NULL tag is recognizable. */ + dynamic_size = 0; + while (*(bfd_vma *) edyn [dynamic_size ++].d_tag != DT_NULL) + ; + + dynamic_segment = (Elf_Internal_Dyn *) + malloc (dynamic_size * sizeof (Elf_Internal_Dyn)); + + if (dynamic_segment == NULL) + { + error (_("Out of memory\n")); + free (edyn); + return 0; + } + + for (i = 0, entry = dynamic_segment; + i < dynamic_size; + i ++, entry ++) + { + entry->d_tag = BYTE_GET8 (edyn [i].d_tag); + entry->d_un.d_val = BYTE_GET8 (edyn [i].d_un.d_val); + } + + free (edyn); + + return 1; +} + +static const char * +get_dynamic_flags (flags) + bfd_vma flags; +{ + static char buff [64]; + while (flags) + { + bfd_vma flag; + + flag = flags & - flags; + flags &= ~ flag; + + switch (flag) + { + case DF_ORIGIN: strcat (buff, "ORIGIN "); break; + case DF_SYMBOLIC: strcat (buff, "SYMBOLIC "); break; + case DF_TEXTREL: strcat (buff, "TEXTREL "); break; + case DF_BIND_NOW: strcat (buff, "BIND_NOW "); break; + default: strcat (buff, "unknown "); break; + } + } + return buff; +} + +/* Parse and display the contents of the dynamic segment. */ +static int +process_dynamic_segment (file) + FILE * file; +{ + Elf_Internal_Dyn * entry; + bfd_size_type i; + + if (dynamic_size == 0) + { + if (do_dynamic) + printf (_("\nThere is no dynamic segment in this file.\n")); + + return 1; + } + + if (is_32bit_elf) + { + if (! get_32bit_dynamic_segment (file)) + return 0; + } + else if (! get_64bit_dynamic_segment (file)) + return 0; + + /* Find the appropriate symbol table. */ + if (dynamic_symbols == NULL) + { + for (i = 0, entry = dynamic_segment; + i < dynamic_size; + ++i, ++ entry) + { + unsigned long offset; + + if (entry->d_tag != DT_SYMTAB) + continue; + + dynamic_info[DT_SYMTAB] = entry->d_un.d_val; + + /* Since we do not know how big the symbol table is, + we default to reading in the entire file (!) and + processing that. This is overkill, I know, but it + should work. */ + offset = entry->d_un.d_val - loadaddr; + + if (fseek (file, 0, SEEK_END)) + error (_("Unable to seek to end of file!")); + + if (is_32bit_elf) + num_dynamic_syms = (ftell (file) - offset) / sizeof (Elf32_External_Sym); + else + num_dynamic_syms = (ftell (file) - offset) / sizeof (Elf64_External_Sym); + + if (num_dynamic_syms < 1) + { + error (_("Unable to determine the number of symbols to load\n")); + continue; + } + + dynamic_symbols = GET_ELF_SYMBOLS (file, offset, num_dynamic_syms); + } + } + + /* Similarly find a string table. */ + if (dynamic_strings == NULL) + { + for (i = 0, entry = dynamic_segment; + i < dynamic_size; + ++i, ++ entry) + { + unsigned long offset; + long str_tab_len; + + if (entry->d_tag != DT_STRTAB) + continue; + + dynamic_info[DT_STRTAB] = entry->d_un.d_val; + + /* Since we do not know how big the string table is, + we default to reading in the entire file (!) and + processing that. This is overkill, I know, but it + should work. */ + + offset = entry->d_un.d_val - loadaddr; + if (fseek (file, 0, SEEK_END)) + error (_("Unable to seek to end of file\n")); + str_tab_len = ftell (file) - offset; + + if (str_tab_len < 1) + { + error + (_("Unable to determine the length of the dynamic string table\n")); + continue; + } + + GET_DATA_ALLOC (offset, str_tab_len, dynamic_strings, char *, + "dynamic string table"); + + break; + } + } + + /* And find the syminfo section if available. */ + if (dynamic_syminfo == NULL) + { + unsigned int syminsz = 0; + + for (i = 0, entry = dynamic_segment; + i < dynamic_size; + ++i, ++ entry) + { + if (entry->d_tag == DT_SYMINENT) + { + /* Note: these braces are necessary to avoid a syntax + error from the SunOS4 C compiler. */ + assert (sizeof (Elf_External_Syminfo) == entry->d_un.d_val); + } + else if (entry->d_tag == DT_SYMINSZ) + syminsz = entry->d_un.d_val; + else if (entry->d_tag == DT_SYMINFO) + dynamic_syminfo_offset = entry->d_un.d_val - loadaddr; + } + + if (dynamic_syminfo_offset != 0 && syminsz != 0) + { + Elf_External_Syminfo * extsyminfo; + Elf_Internal_Syminfo * syminfo; + + /* There is a syminfo section. Read the data. */ + GET_DATA_ALLOC (dynamic_syminfo_offset, syminsz, extsyminfo, + Elf_External_Syminfo *, "symbol information"); + + dynamic_syminfo = (Elf_Internal_Syminfo *) malloc (syminsz); + if (dynamic_syminfo == NULL) + { + error (_("Out of memory\n")); + return 0; + } + + dynamic_syminfo_nent = syminsz / sizeof (Elf_External_Syminfo); + for (i = 0, syminfo = dynamic_syminfo; i < dynamic_syminfo_nent; + ++i, ++syminfo) + { + syminfo->si_boundto = BYTE_GET (extsyminfo[i].si_boundto); + syminfo->si_flags = BYTE_GET (extsyminfo[i].si_flags); + } + + free (extsyminfo); + } + } + + if (do_dynamic && dynamic_addr) + printf (_("\nDynamic segment at offset 0x%x contains %ld entries:\n"), + dynamic_addr, (long) dynamic_size); + if (do_dynamic) + printf (_(" Tag Type Name/Value\n")); + + for (i = 0, entry = dynamic_segment; + i < dynamic_size; + i++, entry ++) + { + if (do_dynamic) + { + const char *dtype; + + putchar (' '); + print_vma (entry->d_tag, FULL_HEX); + dtype = get_dynamic_type (entry->d_tag); + printf (" (%s)%*s", dtype, + ((is_32bit_elf ? 27 : 19) + - (int) strlen (dtype)), + " "); + } + + switch (entry->d_tag) + { + case DT_FLAGS: + if (do_dynamic) + printf ("%s", get_dynamic_flags (entry->d_un.d_val)); + break; + + case DT_AUXILIARY: + case DT_FILTER: + if (do_dynamic) + { + if (entry->d_tag == DT_AUXILIARY) + printf (_("Auxiliary library")); + else + printf (_("Filter library")); + + if (dynamic_strings) + printf (": [%s]\n", dynamic_strings + entry->d_un.d_val); + else + { + printf (": "); + print_vma (entry->d_un.d_val, PREFIX_HEX); + putchar ('\n'); + } + } + break; + + case DT_FEATURE_1: + if (do_dynamic) + { + printf (_("Flags:")); + if (entry->d_un.d_val == 0) + printf (_(" None\n")); + else + { + unsigned long int val = entry->d_un.d_val; + if (val & DTF_1_PARINIT) + { + printf (" PARINIT"); + val ^= DTF_1_PARINIT; + } + if (val != 0) + printf (" %lx", val); + puts (""); + } + } + break; + + case DT_POSFLAG_1: + if (do_dynamic) + { + printf (_("Flags:")); + if (entry->d_un.d_val == 0) + printf (_(" None\n")); + else + { + unsigned long int val = entry->d_un.d_val; + if (val & DF_P1_LAZYLOAD) + { + printf (" LAZYLOAD"); + val ^= DF_P1_LAZYLOAD; + } + if (val & DF_P1_GROUPPERM) + { + printf (" GROUPPERM"); + val ^= DF_P1_GROUPPERM; + } + if (val != 0) + printf (" %lx", val); + puts (""); + } + } + break; + + case DT_FLAGS_1: + if (do_dynamic) + { + printf (_("Flags:")); + if (entry->d_un.d_val == 0) + printf (_(" None\n")); + else + { + unsigned long int val = entry->d_un.d_val; + if (val & DF_1_NOW) + { + printf (" NOW"); + val ^= DF_1_NOW; + } + if (val & DF_1_GLOBAL) + { + printf (" GLOBAL"); + val ^= DF_1_GLOBAL; + } + if (val & DF_1_GROUP) + { + printf (" GROUP"); + val ^= DF_1_GROUP; + } + if (val & DF_1_NODELETE) + { + printf (" NODELETE"); + val ^= DF_1_NODELETE; + } + if (val & DF_1_LOADFLTR) + { + printf (" LOADFLTR"); + val ^= DF_1_LOADFLTR; + } + if (val & DF_1_INITFIRST) + { + printf (" INITFIRST"); + val ^= DF_1_INITFIRST; + } + if (val & DF_1_NOOPEN) + { + printf (" NOOPEN"); + val ^= DF_1_NOOPEN; + } + if (val & DF_1_ORIGIN) + { + printf (" ORIGIN"); + val ^= DF_1_ORIGIN; + } + if (val & DF_1_DIRECT) + { + printf (" DIRECT"); + val ^= DF_1_DIRECT; + } + if (val & DF_1_TRANS) + { + printf (" TRANS"); + val ^= DF_1_TRANS; + } + if (val & DF_1_INTERPOSE) + { + printf (" INTERPOSE"); + val ^= DF_1_INTERPOSE; + } + if (val != 0) + printf (" %lx", val); + puts (""); + } + } + break; + + case DT_PLTREL: + if (do_dynamic) + puts (get_dynamic_type (entry->d_un.d_val)); + break; + + case DT_NULL : + case DT_NEEDED : + case DT_PLTGOT : + case DT_HASH : + case DT_STRTAB : + case DT_SYMTAB : + case DT_RELA : + case DT_INIT : + case DT_FINI : + case DT_SONAME : + case DT_RPATH : + case DT_SYMBOLIC: + case DT_REL : + case DT_DEBUG : + case DT_TEXTREL : + case DT_JMPREL : + dynamic_info[entry->d_tag] = entry->d_un.d_val; + + if (do_dynamic) + { + char * name; + + if (dynamic_strings == NULL) + name = NULL; + else + name = dynamic_strings + entry->d_un.d_val; + + if (name) + { + switch (entry->d_tag) + { + case DT_NEEDED: + printf (_("Shared library: [%s]"), name); + + if (strcmp (name, program_interpreter) == 0) + printf (_(" program interpreter")); + break; + + case DT_SONAME: + printf (_("Library soname: [%s]"), name); + break; + + case DT_RPATH: + printf (_("Library rpath: [%s]"), name); + break; + + default: + print_vma (entry->d_un.d_val, PREFIX_HEX); + break; + } + } + else + print_vma (entry->d_un.d_val, PREFIX_HEX); + + putchar ('\n'); + } + break; + + case DT_PLTRELSZ: + case DT_RELASZ : + case DT_STRSZ : + case DT_RELSZ : + case DT_RELAENT : + case DT_SYMENT : + case DT_RELENT : + case DT_PLTPADSZ: + case DT_MOVEENT : + case DT_MOVESZ : + case DT_INIT_ARRAYSZ: + case DT_FINI_ARRAYSZ: + if (do_dynamic) + { + print_vma (entry->d_un.d_val, UNSIGNED); + printf (" (bytes)\n"); + } + break; + + case DT_VERDEFNUM: + case DT_VERNEEDNUM: + case DT_RELACOUNT: + case DT_RELCOUNT: + if (do_dynamic) + { + print_vma (entry->d_un.d_val, UNSIGNED); + putchar ('\n'); + } + break; + + case DT_SYMINSZ: + case DT_SYMINENT: + case DT_SYMINFO: + case DT_USED: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + if (do_dynamic) + { + if (dynamic_strings != NULL && entry->d_tag == DT_USED) + { + char * name; + + name = dynamic_strings + entry->d_un.d_val; + + if (* name) + { + printf (_("Not needed object: [%s]\n"), name); + break; + } + } + + print_vma (entry->d_un.d_val, PREFIX_HEX); + putchar ('\n'); + } + break; + + case DT_BIND_NOW: + /* The value of this entry is ignored. */ + break; + + default: + if ((entry->d_tag >= DT_VERSYM) && (entry->d_tag <= DT_VERNEEDNUM)) + version_info [DT_VERSIONTAGIDX (entry->d_tag)] = + entry->d_un.d_val; + + if (do_dynamic) + { + switch (elf_header.e_machine) + { + case EM_MIPS: + case EM_MIPS_RS4_BE: + dynamic_segment_mips_val (entry); + break; + case EM_PARISC: + dynamic_segment_parisc_val (entry); + break; + default: + print_vma (entry->d_un.d_val, PREFIX_HEX); + putchar ('\n'); + } + } + break; + } + } + + return 1; +} + +static char * +get_ver_flags (flags) + unsigned int flags; +{ + static char buff [32]; + + buff[0] = 0; + + if (flags == 0) + return _("none"); + + if (flags & VER_FLG_BASE) + strcat (buff, "BASE "); + + if (flags & VER_FLG_WEAK) + { + if (flags & VER_FLG_BASE) + strcat (buff, "| "); + + strcat (buff, "WEAK "); + } + + if (flags & ~(VER_FLG_BASE | VER_FLG_WEAK)) + strcat (buff, "| "); + + return buff; +} + +/* Display the contents of the version sections. */ +static int +process_version_sections (file) + FILE * file; +{ + Elf32_Internal_Shdr * section; + unsigned i; + int found = 0; + + if (! do_version) + return 1; + + for (i = 0, section = section_headers; + i < elf_header.e_shnum; + i++, section ++) + { + switch (section->sh_type) + { + case SHT_GNU_verdef: + { + Elf_External_Verdef * edefs; + unsigned int idx; + unsigned int cnt; + + found = 1; + + printf + (_("\nVersion definition section '%s' contains %ld entries:\n"), + SECTION_NAME (section), section->sh_info); + + printf (_(" Addr: 0x")); + printf_vma (section->sh_addr); + printf (_(" Offset: %#08lx Link: %lx (%s)\n"), + (unsigned long) section->sh_offset, section->sh_link, + SECTION_NAME (section_headers + section->sh_link)); + + GET_DATA_ALLOC (section->sh_offset, section->sh_size, + edefs, Elf_External_Verdef *, + "version definition section"); + + for (idx = cnt = 0; cnt < section->sh_info; ++ cnt) + { + char * vstart; + Elf_External_Verdef * edef; + Elf_Internal_Verdef ent; + Elf_External_Verdaux * eaux; + Elf_Internal_Verdaux aux; + int j; + int isum; + + vstart = ((char *) edefs) + idx; + + edef = (Elf_External_Verdef *) vstart; + + ent.vd_version = BYTE_GET (edef->vd_version); + ent.vd_flags = BYTE_GET (edef->vd_flags); + ent.vd_ndx = BYTE_GET (edef->vd_ndx); + ent.vd_cnt = BYTE_GET (edef->vd_cnt); + ent.vd_hash = BYTE_GET (edef->vd_hash); + ent.vd_aux = BYTE_GET (edef->vd_aux); + ent.vd_next = BYTE_GET (edef->vd_next); + + printf (_(" %#06x: Rev: %d Flags: %s"), + idx, ent.vd_version, get_ver_flags (ent.vd_flags)); + + printf (_(" Index: %d Cnt: %d "), + ent.vd_ndx, ent.vd_cnt); + + vstart += ent.vd_aux; + + eaux = (Elf_External_Verdaux *) vstart; + + aux.vda_name = BYTE_GET (eaux->vda_name); + aux.vda_next = BYTE_GET (eaux->vda_next); + + if (dynamic_strings) + printf (_("Name: %s\n"), dynamic_strings + aux.vda_name); + else + printf (_("Name index: %ld\n"), aux.vda_name); + + isum = idx + ent.vd_aux; + + for (j = 1; j < ent.vd_cnt; j ++) + { + isum += aux.vda_next; + vstart += aux.vda_next; + + eaux = (Elf_External_Verdaux *) vstart; + + aux.vda_name = BYTE_GET (eaux->vda_name); + aux.vda_next = BYTE_GET (eaux->vda_next); + + if (dynamic_strings) + printf (_(" %#06x: Parent %d: %s\n"), + isum, j, dynamic_strings + aux.vda_name); + else + printf (_(" %#06x: Parent %d, name index: %ld\n"), + isum, j, aux.vda_name); + } + + idx += ent.vd_next; + } + + free (edefs); + } + break; + + case SHT_GNU_verneed: + { + Elf_External_Verneed * eneed; + unsigned int idx; + unsigned int cnt; + + found = 1; + + printf (_("\nVersion needs section '%s' contains %ld entries:\n"), + SECTION_NAME (section), section->sh_info); + + printf (_(" Addr: 0x")); + printf_vma (section->sh_addr); + printf (_(" Offset: %#08lx Link to section: %ld (%s)\n"), + (unsigned long) section->sh_offset, section->sh_link, + SECTION_NAME (section_headers + section->sh_link)); + + GET_DATA_ALLOC (section->sh_offset, section->sh_size, + eneed, Elf_External_Verneed *, + "version need section"); + + for (idx = cnt = 0; cnt < section->sh_info; ++cnt) + { + Elf_External_Verneed * entry; + Elf_Internal_Verneed ent; + int j; + int isum; + char * vstart; + + vstart = ((char *) eneed) + idx; + + entry = (Elf_External_Verneed *) vstart; + + ent.vn_version = BYTE_GET (entry->vn_version); + ent.vn_cnt = BYTE_GET (entry->vn_cnt); + ent.vn_file = BYTE_GET (entry->vn_file); + ent.vn_aux = BYTE_GET (entry->vn_aux); + ent.vn_next = BYTE_GET (entry->vn_next); + + printf (_(" %#06x: Version: %d"), idx, ent.vn_version); + + if (dynamic_strings) + printf (_(" File: %s"), dynamic_strings + ent.vn_file); + else + printf (_(" File: %lx"), ent.vn_file); + + printf (_(" Cnt: %d\n"), ent.vn_cnt); + + vstart += ent.vn_aux; + + for (j = 0, isum = idx + ent.vn_aux; j < ent.vn_cnt; ++j) + { + Elf_External_Vernaux * eaux; + Elf_Internal_Vernaux aux; + + eaux = (Elf_External_Vernaux *) vstart; + + aux.vna_hash = BYTE_GET (eaux->vna_hash); + aux.vna_flags = BYTE_GET (eaux->vna_flags); + aux.vna_other = BYTE_GET (eaux->vna_other); + aux.vna_name = BYTE_GET (eaux->vna_name); + aux.vna_next = BYTE_GET (eaux->vna_next); + + if (dynamic_strings) + printf (_(" %#06x: Name: %s"), + isum, dynamic_strings + aux.vna_name); + else + printf (_(" %#06x: Name index: %lx"), + isum, aux.vna_name); + + printf (_(" Flags: %s Version: %d\n"), + get_ver_flags (aux.vna_flags), aux.vna_other); + + isum += aux.vna_next; + vstart += aux.vna_next; + } + + idx += ent.vn_next; + } + + free (eneed); + } + break; + + case SHT_GNU_versym: + { + Elf32_Internal_Shdr * link_section; + int total; + int cnt; + unsigned char * edata; + unsigned short * data; + char * strtab; + Elf_Internal_Sym * symbols; + Elf32_Internal_Shdr * string_sec; + + link_section = section_headers + section->sh_link; + total = section->sh_size / section->sh_entsize; + + found = 1; + + symbols = GET_ELF_SYMBOLS (file, link_section->sh_offset, + link_section->sh_size / link_section->sh_entsize); + + string_sec = section_headers + link_section->sh_link; + + GET_DATA_ALLOC (string_sec->sh_offset, string_sec->sh_size, + strtab, char *, "version string table"); + + printf (_("\nVersion symbols section '%s' contains %d entries:\n"), + SECTION_NAME (section), total); + + printf (_(" Addr: ")); + printf_vma (section->sh_addr); + printf (_(" Offset: %#08lx Link: %lx (%s)\n"), + (unsigned long) section->sh_offset, section->sh_link, + SECTION_NAME (link_section)); + + GET_DATA_ALLOC (version_info [DT_VERSIONTAGIDX (DT_VERSYM)] + - loadaddr, + total * sizeof (short), edata, + unsigned char *, "version symbol data"); + + data = (unsigned short *) malloc (total * sizeof (short)); + + for (cnt = total; cnt --;) + data [cnt] = byte_get (edata + cnt * sizeof (short), + sizeof (short)); + + free (edata); + + for (cnt = 0; cnt < total; cnt += 4) + { + int j, nn; + char *name; + + printf (" %03x:", cnt); + + for (j = 0; (j < 4) && (cnt + j) < total; ++j) + switch (data [cnt + j]) + { + case 0: + fputs (_(" 0 (*local*) "), stdout); + break; + + case 1: + fputs (_(" 1 (*global*) "), stdout); + break; + + default: + nn = printf ("%4x%c", data [cnt + j] & 0x7fff, + data [cnt + j] & 0x8000 ? 'h' : ' '); + + if (symbols [cnt + j].st_shndx < SHN_LORESERVE + && section_headers[symbols [cnt + j].st_shndx].sh_type + == SHT_NOBITS) + { + /* We must test both. */ + Elf_Internal_Verneed ivn; + unsigned long offset; + + offset = version_info [DT_VERSIONTAGIDX (DT_VERNEED)] + - loadaddr; + + do + { + Elf_External_Verneed evn; + Elf_External_Vernaux evna; + Elf_Internal_Vernaux ivna; + unsigned long vna_off; + + GET_DATA (offset, evn, "version need"); + + ivn.vn_aux = BYTE_GET (evn.vn_aux); + ivn.vn_next = BYTE_GET (evn.vn_next); + + vna_off = offset + ivn.vn_aux; + + do + { + GET_DATA (vna_off, evna, + "version need aux (1)"); + + ivna.vna_next = BYTE_GET (evna.vna_next); + ivna.vna_other = BYTE_GET (evna.vna_other); + + vna_off += ivna.vna_next; + } + while (ivna.vna_other != data [cnt + j] + && ivna.vna_next != 0); + + if (ivna.vna_other == data [cnt + j]) + { + ivna.vna_name = BYTE_GET (evna.vna_name); + + name = strtab + ivna.vna_name; + nn += printf ("(%s%-*s", + name, + 12 - (int) strlen (name), + ")"); + break; + } + else if (ivn.vn_next == 0) + { + if (data [cnt + j] != 0x8001) + { + Elf_Internal_Verdef ivd; + Elf_External_Verdef evd; + + offset = version_info + [DT_VERSIONTAGIDX (DT_VERDEF)] + - loadaddr; + + do + { + GET_DATA (offset, evd, + "version definition"); + + ivd.vd_next = BYTE_GET (evd.vd_next); + ivd.vd_ndx = BYTE_GET (evd.vd_ndx); + + offset += ivd.vd_next; + } + while (ivd.vd_ndx + != (data [cnt + j] & 0x7fff) + && ivd.vd_next != 0); + + if (ivd.vd_ndx + == (data [cnt + j] & 0x7fff)) + { + Elf_External_Verdaux evda; + Elf_Internal_Verdaux ivda; + + ivd.vd_aux = BYTE_GET (evd.vd_aux); + + GET_DATA (offset + ivd.vd_aux, evda, + "version definition aux"); + + ivda.vda_name = + BYTE_GET (evda.vda_name); + + name = strtab + ivda.vda_name; + nn += + printf ("(%s%-*s", + name, + 12 - (int) strlen (name), + ")"); + } + } + + break; + } + else + offset += ivn.vn_next; + } + while (ivn.vn_next); + } + else if (symbols [cnt + j].st_shndx == SHN_UNDEF) + { + Elf_Internal_Verneed ivn; + unsigned long offset; + + offset = version_info [DT_VERSIONTAGIDX (DT_VERNEED)] + - loadaddr; + + do + { + Elf_Internal_Vernaux ivna; + Elf_External_Verneed evn; + Elf_External_Vernaux evna; + unsigned long a_off; + + GET_DATA (offset, evn, "version need"); + + ivn.vn_aux = BYTE_GET (evn.vn_aux); + ivn.vn_next = BYTE_GET (evn.vn_next); + + a_off = offset + ivn.vn_aux; + + do + { + GET_DATA (a_off, evna, + "version need aux (2)"); + + ivna.vna_next = BYTE_GET (evna.vna_next); + ivna.vna_other = BYTE_GET (evna.vna_other); + + a_off += ivna.vna_next; + } + while (ivna.vna_other != data [cnt + j] + && ivna.vna_next != 0); + + if (ivna.vna_other == data [cnt + j]) + { + ivna.vna_name = BYTE_GET (evna.vna_name); + + name = strtab + ivna.vna_name; + nn += printf ("(%s%-*s", + name, + 12 - (int) strlen (name), + ")"); + break; + } + + offset += ivn.vn_next; + } + while (ivn.vn_next); + } + else if (data [cnt + j] != 0x8001) + { + Elf_Internal_Verdef ivd; + Elf_External_Verdef evd; + unsigned long offset; + + offset = version_info + [DT_VERSIONTAGIDX (DT_VERDEF)] - loadaddr; + + do + { + GET_DATA (offset, evd, "version def"); + + ivd.vd_next = BYTE_GET (evd.vd_next); + ivd.vd_ndx = BYTE_GET (evd.vd_ndx); + + offset += ivd.vd_next; + } + while (ivd.vd_ndx != (data [cnt + j] & 0x7fff) + && ivd.vd_next != 0); + + if (ivd.vd_ndx == (data [cnt + j] & 0x7fff)) + { + Elf_External_Verdaux evda; + Elf_Internal_Verdaux ivda; + + ivd.vd_aux = BYTE_GET (evd.vd_aux); + + GET_DATA (offset - ivd.vd_next + ivd.vd_aux, + evda, "version def aux"); + + ivda.vda_name = BYTE_GET (evda.vda_name); + + name = strtab + ivda.vda_name; + nn += printf ("(%s%-*s", + name, + 12 - (int) strlen (name), + ")"); + } + } + + if (nn < 18) + printf ("%*c", 18 - nn, ' '); + } + + putchar ('\n'); + } + + free (data); + free (strtab); + free (symbols); + } + break; + + default: + break; + } + } + + if (! found) + printf (_("\nNo version information found in this file.\n")); + + return 1; +} + +static const char * +get_symbol_binding (binding) + unsigned int binding; +{ + static char buff [32]; + + switch (binding) + { + case STB_LOCAL: return "LOCAL"; + case STB_GLOBAL: return "GLOBAL"; + case STB_WEAK: return "WEAK"; + default: + if (binding >= STB_LOPROC && binding <= STB_HIPROC) + sprintf (buff, _(": %d"), binding); + else if (binding >= STB_LOOS && binding <= STB_HIOS) + sprintf (buff, _(": %d"), binding); + else + sprintf (buff, _(": %d"), binding); + return buff; + } +} + +static const char * +get_symbol_type (type) + unsigned int type; +{ + static char buff [32]; + + switch (type) + { + case STT_NOTYPE: return "NOTYPE"; + case STT_OBJECT: return "OBJECT"; + case STT_FUNC: return "FUNC"; + case STT_SECTION: return "SECTION"; + case STT_FILE: return "FILE"; + case STT_COMMON: return "COMMON"; + default: + if (type >= STT_LOPROC && type <= STT_HIPROC) + { + if (elf_header.e_machine == EM_ARM && type == STT_ARM_TFUNC) + return "THUMB_FUNC"; + + if (elf_header.e_machine == EM_SPARCV9 && type == STT_REGISTER) + return "REGISTER"; + + if (elf_header.e_machine == EM_PARISC && type == STT_PARISC_MILLI) + return "PARISC_MILLI"; + + sprintf (buff, _(": %d"), type); + } + else if (type >= STT_LOOS && type <= STT_HIOS) + { + if (elf_header.e_machine == EM_PARISC) + { + if (type == STT_HP_OPAQUE) + return "HP_OPAQUE"; + if (type == STT_HP_STUB) + return "HP_STUB"; + } + + sprintf (buff, _(": %d"), type); + } + else + sprintf (buff, _(": %d"), type); + return buff; + } +} + +static const char * +get_symbol_visibility (visibility) + unsigned int visibility; +{ + switch (visibility) + { + case STV_DEFAULT: return "DEFAULT"; + case STV_INTERNAL: return "INTERNAL"; + case STV_HIDDEN: return "HIDDEN"; + case STV_PROTECTED: return "PROTECTED"; + default: abort (); + } +} + +static const char * +get_symbol_index_type (type) + unsigned int type; +{ + switch (type) + { + case SHN_UNDEF: return "UND"; + case SHN_ABS: return "ABS"; + case SHN_COMMON: return "COM"; + default: + if (type >= SHN_LOPROC && type <= SHN_HIPROC) + return "PRC"; + else if (type >= SHN_LORESERVE && type <= SHN_HIRESERVE) + return "RSV"; + else if (type >= SHN_LOOS && type <= SHN_HIOS) + return "OS "; + else + { + static char buff [32]; + + sprintf (buff, "%3d", type); + return buff; + } + } +} + +static int * +get_dynamic_data (file, number) + FILE * file; + unsigned int number; +{ + char * e_data; + int * i_data; + + e_data = (char *) malloc (number * 4); + + if (e_data == NULL) + { + error (_("Out of memory\n")); + return NULL; + } + + if (fread (e_data, 4, number, file) != number) + { + error (_("Unable to read in dynamic data\n")); + return NULL; + } + + i_data = (int *) malloc (number * sizeof (* i_data)); + + if (i_data == NULL) + { + error (_("Out of memory\n")); + free (e_data); + return NULL; + } + + while (number--) + i_data [number] = byte_get (e_data + number * 4, 4); + + free (e_data); + + return i_data; +} + +/* Dump the symbol table */ +static int +process_symbol_table (file) + FILE * file; +{ + Elf32_Internal_Shdr * section; + char nb [4]; + char nc [4]; + int nbuckets = 0; + int nchains = 0; + int * buckets = NULL; + int * chains = NULL; + + if (! do_syms && !do_histogram) + return 1; + + if (dynamic_info[DT_HASH] && ((do_using_dynamic && dynamic_strings != NULL) + || do_histogram)) + { + if (fseek (file, dynamic_info[DT_HASH] - loadaddr, SEEK_SET)) + { + error (_("Unable to seek to start of dynamic information")); + return 0; + } + + if (fread (nb, sizeof (nb), 1, file) != 1) + { + error (_("Failed to read in number of buckets\n")); + return 0; + } + + if (fread (nc, sizeof (nc), 1, file) != 1) + { + error (_("Failed to read in number of chains\n")); + return 0; + } + + nbuckets = byte_get (nb, 4); + nchains = byte_get (nc, 4); + + buckets = get_dynamic_data (file, nbuckets); + chains = get_dynamic_data (file, nchains); + + if (buckets == NULL || chains == NULL) + return 0; + } + + if (do_syms + && dynamic_info[DT_HASH] && do_using_dynamic && dynamic_strings != NULL) + { + int hn; + int si; + + printf (_("\nSymbol table for image:\n")); + if (is_32bit_elf) + printf (_(" Num Buc: Value Size Type Bind Vis Ndx Name\n")); + else + printf (_(" Num Buc: Value Size Type Bind Vis Ndx Name\n")); + + for (hn = 0; hn < nbuckets; hn++) + { + if (! buckets [hn]) + continue; + + for (si = buckets [hn]; si < nchains && si > 0; si = chains [si]) + { + Elf_Internal_Sym * psym; + + psym = dynamic_symbols + si; + + printf (" %3d %3d: ", si, hn); + print_vma (psym->st_value, LONG_HEX); + putchar (' ' ); + print_vma (psym->st_size, DEC_5); + + printf (" %6s", get_symbol_type (ELF_ST_TYPE (psym->st_info))); + printf (" %6s", get_symbol_binding (ELF_ST_BIND (psym->st_info))); + printf (" %3s", get_symbol_visibility (ELF_ST_VISIBILITY (psym->st_other))); + printf (" %3.3s", get_symbol_index_type (psym->st_shndx)); + printf (" %s\n", dynamic_strings + psym->st_name); + } + } + } + else if (do_syms && !do_using_dynamic) + { + unsigned int i; + + for (i = 0, section = section_headers; + i < elf_header.e_shnum; + i++, section++) + { + unsigned int si; + char * strtab; + Elf_Internal_Sym * symtab; + Elf_Internal_Sym * psym; + + + if ( section->sh_type != SHT_SYMTAB + && section->sh_type != SHT_DYNSYM) + continue; + + printf (_("\nSymbol table '%s' contains %lu entries:\n"), + SECTION_NAME (section), + (unsigned long) (section->sh_size / section->sh_entsize)); + if (is_32bit_elf) + printf (_(" Num: Value Size Type Bind Vis Ndx Name\n")); + else + printf (_(" Num: Value Size Type Bind Vis Ndx Name\n")); + + symtab = GET_ELF_SYMBOLS (file, section->sh_offset, + section->sh_size / section->sh_entsize); + if (symtab == NULL) + continue; + + if (section->sh_link == elf_header.e_shstrndx) + strtab = string_table; + else + { + Elf32_Internal_Shdr * string_sec; + + string_sec = section_headers + section->sh_link; + + GET_DATA_ALLOC (string_sec->sh_offset, string_sec->sh_size, + strtab, char *, "string table"); + } + + for (si = 0, psym = symtab; + si < section->sh_size / section->sh_entsize; + si ++, psym ++) + { + printf ("%6d: ", si); + print_vma (psym->st_value, LONG_HEX); + putchar (' '); + print_vma (psym->st_size, DEC_5); + printf (" %-7s", get_symbol_type (ELF_ST_TYPE (psym->st_info))); + printf (" %-6s", get_symbol_binding (ELF_ST_BIND (psym->st_info))); + printf (" %-3s", get_symbol_visibility (ELF_ST_VISIBILITY (psym->st_other))); + printf (" %4s", get_symbol_index_type (psym->st_shndx)); + printf (" %s", strtab + psym->st_name); + + if (section->sh_type == SHT_DYNSYM && + version_info [DT_VERSIONTAGIDX (DT_VERSYM)] != 0) + { + unsigned char data[2]; + unsigned short vers_data; + unsigned long offset; + int is_nobits; + int check_def; + + offset = version_info [DT_VERSIONTAGIDX (DT_VERSYM)] + - loadaddr; + + GET_DATA (offset + si * sizeof (vers_data), data, + "version data"); + + vers_data = byte_get (data, 2); + + is_nobits = psym->st_shndx < SHN_LORESERVE ? + (section_headers [psym->st_shndx].sh_type == SHT_NOBITS) + : 0; + + check_def = (psym->st_shndx != SHN_UNDEF); + + if ((vers_data & 0x8000) || vers_data > 1) + { + if (is_nobits || ! check_def) + { + Elf_External_Verneed evn; + Elf_Internal_Verneed ivn; + Elf_Internal_Vernaux ivna; + + /* We must test both. */ + offset = version_info + [DT_VERSIONTAGIDX (DT_VERNEED)] - loadaddr; + + do + { + unsigned long vna_off; + + GET_DATA (offset, evn, "version need"); + + ivn.vn_aux = BYTE_GET (evn.vn_aux); + ivn.vn_next = BYTE_GET (evn.vn_next); + + vna_off = offset + ivn.vn_aux; + + do + { + Elf_External_Vernaux evna; + + GET_DATA (vna_off, evna, + "version need aux (3)"); + + ivna.vna_other = BYTE_GET (evna.vna_other); + ivna.vna_next = BYTE_GET (evna.vna_next); + ivna.vna_name = BYTE_GET (evna.vna_name); + + vna_off += ivna.vna_next; + } + while (ivna.vna_other != vers_data + && ivna.vna_next != 0); + + if (ivna.vna_other == vers_data) + break; + + offset += ivn.vn_next; + } + while (ivn.vn_next != 0); + + if (ivna.vna_other == vers_data) + { + printf ("@%s (%d)", + strtab + ivna.vna_name, ivna.vna_other); + check_def = 0; + } + else if (! is_nobits) + error (_("bad dynamic symbol")); + else + check_def = 1; + } + + if (check_def) + { + if (vers_data != 0x8001) + { + Elf_Internal_Verdef ivd; + Elf_Internal_Verdaux ivda; + Elf_External_Verdaux evda; + unsigned long offset; + + offset = + version_info [DT_VERSIONTAGIDX (DT_VERDEF)] + - loadaddr; + + do + { + Elf_External_Verdef evd; + + GET_DATA (offset, evd, "version def"); + + ivd.vd_ndx = BYTE_GET (evd.vd_ndx); + ivd.vd_aux = BYTE_GET (evd.vd_aux); + ivd.vd_next = BYTE_GET (evd.vd_next); + + offset += ivd.vd_next; + } + while (ivd.vd_ndx != (vers_data & 0x7fff) + && ivd.vd_next != 0); + + offset -= ivd.vd_next; + offset += ivd.vd_aux; + + GET_DATA (offset, evda, "version def aux"); + + ivda.vda_name = BYTE_GET (evda.vda_name); + + if (psym->st_name != ivda.vda_name) + printf ((vers_data & 0x8000) + ? "@%s" : "@@%s", + strtab + ivda.vda_name); + } + } + } + } + + putchar ('\n'); + } + + free (symtab); + if (strtab != string_table) + free (strtab); + } + } + else if (do_syms) + printf + (_("\nDynamic symbol information is not available for displaying symbols.\n")); + + if (do_histogram && buckets != NULL) + { + int *lengths; + int *counts; + int hn; + int si; + int maxlength = 0; + int nzero_counts = 0; + int nsyms = 0; + + printf (_("\nHistogram for bucket list length (total of %d buckets):\n"), + nbuckets); + printf (_(" Length Number %% of total Coverage\n")); + + lengths = (int *) calloc (nbuckets, sizeof (int)); + if (lengths == NULL) + { + error (_("Out of memory")); + return 0; + } + for (hn = 0; hn < nbuckets; ++hn) + { + if (! buckets [hn]) + continue; + + for (si = buckets[hn]; si > 0 && si < nchains; si = chains[si]) + { + ++ nsyms; + if (maxlength < ++lengths[hn]) + ++ maxlength; + } + } + + counts = (int *) calloc (maxlength + 1, sizeof (int)); + if (counts == NULL) + { + error (_("Out of memory")); + return 0; + } + + for (hn = 0; hn < nbuckets; ++hn) + ++ counts [lengths [hn]]; + + if (nbuckets > 0) + { + printf (" 0 %-10d (%5.1f%%)\n", + counts[0], (counts[0] * 100.0) / nbuckets); + for (si = 1; si <= maxlength; ++si) + { + nzero_counts += counts[si] * si; + printf ("%7d %-10d (%5.1f%%) %5.1f%%\n", + si, counts[si], (counts[si] * 100.0) / nbuckets, + (nzero_counts * 100.0) / nsyms); + } + } + + free (counts); + free (lengths); + } + + if (buckets != NULL) + { + free (buckets); + free (chains); + } + + return 1; +} + +static int +process_syminfo (file) + FILE * file ATTRIBUTE_UNUSED; +{ + unsigned int i; + + if (dynamic_syminfo == NULL + || !do_dynamic) + /* No syminfo, this is ok. */ + return 1; + + /* There better should be a dynamic symbol section. */ + if (dynamic_symbols == NULL || dynamic_strings == NULL) + return 0; + + if (dynamic_addr) + printf (_("\nDynamic info segment at offset 0x%lx contains %d entries:\n"), + dynamic_syminfo_offset, dynamic_syminfo_nent); + + printf (_(" Num: Name BoundTo Flags\n")); + for (i = 0; i < dynamic_syminfo_nent; ++i) + { + unsigned short int flags = dynamic_syminfo[i].si_flags; + + printf ("%4d: %-30s ", i, + dynamic_strings + dynamic_symbols[i].st_name); + + switch (dynamic_syminfo[i].si_boundto) + { + case SYMINFO_BT_SELF: + fputs ("SELF ", stdout); + break; + case SYMINFO_BT_PARENT: + fputs ("PARENT ", stdout); + break; + default: + if (dynamic_syminfo[i].si_boundto > 0 + && dynamic_syminfo[i].si_boundto < dynamic_size) + printf ("%-10s ", + dynamic_strings + + dynamic_segment[dynamic_syminfo[i].si_boundto].d_un.d_val); + else + printf ("%-10d ", dynamic_syminfo[i].si_boundto); + break; + } + + if (flags & SYMINFO_FLG_DIRECT) + printf (" DIRECT"); + if (flags & SYMINFO_FLG_PASSTHRU) + printf (" PASSTHRU"); + if (flags & SYMINFO_FLG_COPY) + printf (" COPY"); + if (flags & SYMINFO_FLG_LAZYLOAD) + printf (" LAZYLOAD"); + + puts (""); + } + + return 1; +} + +#ifdef SUPPORT_DISASSEMBLY +static void +disassemble_section (section, file) + Elf32_Internal_Shdr * section; + FILE * file; +{ + printf (_("\nAssembly dump of section %s\n"), + SECTION_NAME (section)); + + /* XXX -- to be done --- XXX */ + + return 1; +} +#endif + +static int +dump_section (section, file) + Elf32_Internal_Shdr * section; + FILE * file; +{ + bfd_size_type bytes; + bfd_vma addr; + unsigned char * data; + unsigned char * start; + + bytes = section->sh_size; + + if (bytes == 0) + { + printf (_("\nSection '%s' has no data to dump.\n"), + SECTION_NAME (section)); + return 0; + } + else + printf (_("\nHex dump of section '%s':\n"), SECTION_NAME (section)); + + addr = section->sh_addr; + + GET_DATA_ALLOC (section->sh_offset, bytes, start, unsigned char *, + "section data"); + + data = start; + + while (bytes) + { + int j; + int k; + int lbytes; + + lbytes = (bytes > 16 ? 16 : bytes); + + printf (" 0x%8.8lx ", (unsigned long) addr); + + switch (elf_header.e_ident [EI_DATA]) + { + default: + case ELFDATA2LSB: + for (j = 15; j >= 0; j --) + { + if (j < lbytes) + printf ("%2.2x", data [j]); + else + printf (" "); + + if (!(j & 0x3)) + printf (" "); + } + break; + + case ELFDATA2MSB: + for (j = 0; j < 16; j++) + { + if (j < lbytes) + printf ("%2.2x", data [j]); + else + printf (" "); + + if ((j & 3) == 3) + printf (" "); + } + break; + } + + for (j = 0; j < lbytes; j++) + { + k = data [j]; + if (k >= ' ' && k < 0x80) + printf ("%c", k); + else + printf ("."); + } + + putchar ('\n'); + + data += lbytes; + addr += lbytes; + bytes -= lbytes; + } + + free (start); + + return 1; +} + + +static unsigned long int +read_leb128 (data, length_return, sign) + unsigned char * data; + int * length_return; + int sign; +{ + unsigned long int result = 0; + unsigned int num_read = 0; + int shift = 0; + unsigned char byte; + + do + { + byte = * data ++; + num_read ++; + + result |= (byte & 0x7f) << shift; + + shift += 7; + + } + while (byte & 0x80); + + if (length_return != NULL) + * length_return = num_read; + + if (sign && (shift < 32) && (byte & 0x40)) + result |= -1 << shift; + + return result; +} + +typedef struct State_Machine_Registers +{ + unsigned long address; + unsigned int file; + unsigned int line; + unsigned int column; + int is_stmt; + int basic_block; + int end_sequence; +/* This variable hold the number of the last entry seen + in the File Table. */ + unsigned int last_file_entry; +} SMR; + +static SMR state_machine_regs; + +static void +reset_state_machine (is_stmt) + int is_stmt; +{ + state_machine_regs.address = 0; + state_machine_regs.file = 1; + state_machine_regs.line = 1; + state_machine_regs.column = 0; + state_machine_regs.is_stmt = is_stmt; + state_machine_regs.basic_block = 0; + state_machine_regs.end_sequence = 0; + state_machine_regs.last_file_entry = 0; +} + +/* Handled an extend line op. Returns true if this is the end + of sequence. */ +static int +process_extended_line_op (data, is_stmt, pointer_size) + unsigned char * data; + int is_stmt; + int pointer_size; +{ + unsigned char op_code; + int bytes_read; + unsigned int len; + unsigned char * name; + unsigned long adr; + + len = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + + if (len == 0) + { + warn (_("badly formed extended line op encountered!")); + return bytes_read; + } + + len += bytes_read; + op_code = * data ++; + + printf (_(" Extended opcode %d: "), op_code); + + switch (op_code) + { + case DW_LNE_end_sequence: + printf (_("End of Sequence\n\n")); + reset_state_machine (is_stmt); + break; + + case DW_LNE_set_address: + adr = byte_get (data, pointer_size); + printf (_("set Address to 0x%lx\n"), adr); + state_machine_regs.address = adr; + break; + + case DW_LNE_define_file: + printf (_(" define new File Table entry\n")); + printf (_(" Entry\tDir\tTime\tSize\tName\n")); + + printf (_(" %d\t"), ++ state_machine_regs.last_file_entry); + name = data; + data += strlen (data) + 1; + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + data += bytes_read; + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + data += bytes_read; + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + printf (_("%s\n\n"), name); + break; + + default: + printf (_("UNKNOWN: length %d\n"), len - bytes_read); + break; + } + + return len; +} + +/* Size of pointers in the .debug_line section. This information is not + really present in that section. It's obtained before dumping the debug + sections by doing some pre-scan of the .debug_info section. */ +static int debug_line_pointer_size = 4; + +static int +display_debug_lines (section, start, file) + Elf32_Internal_Shdr * section; + unsigned char * start; + FILE * file ATTRIBUTE_UNUSED; +{ + DWARF2_External_LineInfo * external; + DWARF2_Internal_LineInfo info; + unsigned char * standard_opcodes; + unsigned char * data = start; + unsigned char * end = start + section->sh_size; + unsigned char * end_of_sequence; + int i; + + printf (_("\nDump of debug contents of section %s:\n\n"), + SECTION_NAME (section)); + + while (data < end) + { + external = (DWARF2_External_LineInfo *) data; + + /* Check the length of the block. */ + info.li_length = BYTE_GET (external->li_length); + if (info.li_length > section->sh_size) + { + warn + (_("The line info appears to be corrupt - the section is too small\n")); + return 0; + } + + /* Check its version number. */ + info.li_version = BYTE_GET (external->li_version); + if (info.li_version != 2) + { + warn (_("Only DWARF version 2 line info is currently supported.\n")); + return 0; + } + + info.li_prologue_length = BYTE_GET (external->li_prologue_length); + info.li_min_insn_length = BYTE_GET (external->li_min_insn_length); + info.li_default_is_stmt = BYTE_GET (external->li_default_is_stmt); + info.li_line_base = BYTE_GET (external->li_line_base); + info.li_line_range = BYTE_GET (external->li_line_range); + info.li_opcode_base = BYTE_GET (external->li_opcode_base); + + /* Sign extend the line base field. */ + info.li_line_base <<= 24; + info.li_line_base >>= 24; + + printf (_(" Length: %ld\n"), info.li_length); + printf (_(" DWARF Version: %d\n"), info.li_version); + printf (_(" Prolgue Length: %d\n"), info.li_prologue_length); + printf (_(" Minimum Instruction Length: %d\n"), info.li_min_insn_length); + printf (_(" Initial value of 'is_stmt': %d\n"), info.li_default_is_stmt); + printf (_(" Line Base: %d\n"), info.li_line_base); + printf (_(" Line Range: %d\n"), info.li_line_range); + printf (_(" Opcode Base: %d\n"), info.li_opcode_base); + + end_of_sequence = data + info.li_length + sizeof (info.li_length); + + reset_state_machine (info.li_default_is_stmt); + + /* Display the contents of the Opcodes table. */ + standard_opcodes = data + sizeof (* external); + + printf (_("\n Opcodes:\n")); + + for (i = 1; i < info.li_opcode_base; i++) + printf (_(" Opcode %d has %d args\n"), i, standard_opcodes[i - 1]); + + /* Display the contents of the Directory table. */ + data = standard_opcodes + info.li_opcode_base - 1; + + if (* data == 0) + printf (_("\n The Directory Table is empty.\n")); + else + { + printf (_("\n The Directory Table:\n")); + + while (* data != 0) + { + printf (_(" %s\n"), data); + + data += strlen (data) + 1; + } + } + + /* Skip the NUL at the end of the table. */ + data ++; + + /* Display the contents of the File Name table. */ + if (* data == 0) + printf (_("\n The File Name Table is empty.\n")); + else + { + printf (_("\n The File Name Table:\n")); + printf (_(" Entry\tDir\tTime\tSize\tName\n")); + + while (* data != 0) + { + char * name; + int bytes_read; + + printf (_(" %d\t"), ++ state_machine_regs.last_file_entry); + name = data; + + data += strlen (data) + 1; + + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + data += bytes_read; + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + data += bytes_read; + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + data += bytes_read; + printf (_("%s\n"), name); + } + } + + /* Skip the NUL at the end of the table. */ + data ++; + + /* Now display the statements. */ + printf (_("\n Line Number Statements:\n")); + + + while (data < end_of_sequence) + { + unsigned char op_code; + int adv; + int bytes_read; + + op_code = * data ++; + + switch (op_code) + { + case DW_LNS_extended_op: + data += process_extended_line_op (data, info.li_default_is_stmt, + debug_line_pointer_size); + break; + + case DW_LNS_copy: + printf (_(" Copy\n")); + break; + + case DW_LNS_advance_pc: + adv = info.li_min_insn_length * read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.address += adv; + printf (_(" Advance PC by %d to %lx\n"), adv, + state_machine_regs.address); + break; + + case DW_LNS_advance_line: + adv = read_leb128 (data, & bytes_read, 1); + data += bytes_read; + state_machine_regs.line += adv; + printf (_(" Advance Line by %d to %d\n"), adv, + state_machine_regs.line); + break; + + case DW_LNS_set_file: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + printf (_(" Set File Name to entry %d in the File Name Table\n"), + adv); + state_machine_regs.file = adv; + break; + + case DW_LNS_set_column: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + printf (_(" Set column to %d\n"), adv); + state_machine_regs.column = adv; + break; + + case DW_LNS_negate_stmt: + adv = state_machine_regs.is_stmt; + adv = ! adv; + printf (_(" Set is_stmt to %d\n"), adv); + state_machine_regs.is_stmt = adv; + break; + + case DW_LNS_set_basic_block: + printf (_(" Set basic block\n")); + state_machine_regs.basic_block = 1; + break; + + case DW_LNS_const_add_pc: + adv = (((255 - info.li_opcode_base) / info.li_line_range) + * info.li_min_insn_length); + state_machine_regs.address += adv; + printf (_(" Advance PC by constant %d to 0x%lx\n"), adv, + state_machine_regs.address); + break; + + case DW_LNS_fixed_advance_pc: + adv = byte_get (data, 2); + data += 2; + state_machine_regs.address += adv; + printf (_(" Advance PC by fixed size amount %d to 0x%lx\n"), + adv, state_machine_regs.address); + break; + + default: + op_code -= info.li_opcode_base; + adv = (op_code / info.li_line_range) * info.li_min_insn_length; + state_machine_regs.address += adv; + printf (_(" Special opcode %d: advance Address by %d to 0x%lx"), + op_code, adv, state_machine_regs.address); + adv = (op_code % info.li_line_range) + info.li_line_base; + state_machine_regs.line += adv; + printf (_(" and Line by %d to %d\n"), + adv, state_machine_regs.line); + break; + } + } + printf ("\n"); + } + + return 1; +} + +static int +display_debug_pubnames (section, start, file) + Elf32_Internal_Shdr * section; + unsigned char * start; + FILE * file ATTRIBUTE_UNUSED; +{ + DWARF2_External_PubNames * external; + DWARF2_Internal_PubNames pubnames; + unsigned char * end; + + end = start + section->sh_size; + + printf (_("Contents of the %s section:\n\n"), SECTION_NAME (section)); + + while (start < end) + { + unsigned char * data; + unsigned long offset; + + external = (DWARF2_External_PubNames *) start; + + pubnames.pn_length = BYTE_GET (external->pn_length); + pubnames.pn_version = BYTE_GET (external->pn_version); + pubnames.pn_offset = BYTE_GET (external->pn_offset); + pubnames.pn_size = BYTE_GET (external->pn_size); + + data = start + sizeof (* external); + start += pubnames.pn_length + sizeof (external->pn_length); + + if (pubnames.pn_version != 2) + { + warn (_("Only DWARF 2 pubnames are currently supported")); + continue; + } + + printf (_(" Length: %ld\n"), + pubnames.pn_length); + printf (_(" Version: %d\n"), + pubnames.pn_version); + printf (_(" Offset into .debug_info section: %ld\n"), + pubnames.pn_offset); + printf (_(" Size of area in .debug_info section: %ld\n"), + pubnames.pn_size); + + printf (_("\n Offset\tName\n")); + + do + { + offset = byte_get (data, 4); + + if (offset != 0) + { + data += 4; + printf (" %ld\t\t%s\n", offset, data); + data += strlen (data) + 1; + } + } + while (offset != 0); + } + + printf ("\n"); + return 1; +} + +static char * +get_TAG_name (tag) + unsigned long tag; +{ + switch (tag) + { + case DW_TAG_padding: return "DW_TAG_padding"; + case DW_TAG_array_type: return "DW_TAG_array_type"; + case DW_TAG_class_type: return "DW_TAG_class_type"; + case DW_TAG_entry_point: return "DW_TAG_entry_point"; + case DW_TAG_enumeration_type: return "DW_TAG_enumeration_type"; + case DW_TAG_formal_parameter: return "DW_TAG_formal_parameter"; + case DW_TAG_imported_declaration: return "DW_TAG_imported_declaration"; + case DW_TAG_label: return "DW_TAG_label"; + case DW_TAG_lexical_block: return "DW_TAG_lexical_block"; + case DW_TAG_member: return "DW_TAG_member"; + case DW_TAG_pointer_type: return "DW_TAG_pointer_type"; + case DW_TAG_reference_type: return "DW_TAG_reference_type"; + case DW_TAG_compile_unit: return "DW_TAG_compile_unit"; + case DW_TAG_string_type: return "DW_TAG_string_type"; + case DW_TAG_structure_type: return "DW_TAG_structure_type"; + case DW_TAG_subroutine_type: return "DW_TAG_subroutine_type"; + case DW_TAG_typedef: return "DW_TAG_typedef"; + case DW_TAG_union_type: return "DW_TAG_union_type"; + case DW_TAG_unspecified_parameters: return "DW_TAG_unspecified_parameters"; + case DW_TAG_variant: return "DW_TAG_variant"; + case DW_TAG_common_block: return "DW_TAG_common_block"; + case DW_TAG_common_inclusion: return "DW_TAG_common_inclusion"; + case DW_TAG_inheritance: return "DW_TAG_inheritance"; + case DW_TAG_inlined_subroutine: return "DW_TAG_inlined_subroutine"; + case DW_TAG_module: return "DW_TAG_module"; + case DW_TAG_ptr_to_member_type: return "DW_TAG_ptr_to_member_type"; + case DW_TAG_set_type: return "DW_TAG_set_type"; + case DW_TAG_subrange_type: return "DW_TAG_subrange_type"; + case DW_TAG_with_stmt: return "DW_TAG_with_stmt"; + case DW_TAG_access_declaration: return "DW_TAG_access_declaration"; + case DW_TAG_base_type: return "DW_TAG_base_type"; + case DW_TAG_catch_block: return "DW_TAG_catch_block"; + case DW_TAG_const_type: return "DW_TAG_const_type"; + case DW_TAG_constant: return "DW_TAG_constant"; + case DW_TAG_enumerator: return "DW_TAG_enumerator"; + case DW_TAG_file_type: return "DW_TAG_file_type"; + case DW_TAG_friend: return "DW_TAG_friend"; + case DW_TAG_namelist: return "DW_TAG_namelist"; + case DW_TAG_namelist_item: return "DW_TAG_namelist_item"; + case DW_TAG_packed_type: return "DW_TAG_packed_type"; + case DW_TAG_subprogram: return "DW_TAG_subprogram"; + case DW_TAG_template_type_param: return "DW_TAG_template_type_param"; + case DW_TAG_template_value_param: return "DW_TAG_template_value_param"; + case DW_TAG_thrown_type: return "DW_TAG_thrown_type"; + case DW_TAG_try_block: return "DW_TAG_try_block"; + case DW_TAG_variant_part: return "DW_TAG_variant_part"; + case DW_TAG_variable: return "DW_TAG_variable"; + case DW_TAG_volatile_type: return "DW_TAG_volatile_type"; + case DW_TAG_MIPS_loop: return "DW_TAG_MIPS_loop"; + case DW_TAG_format_label: return "DW_TAG_format_label"; + case DW_TAG_function_template: return "DW_TAG_function_template"; + case DW_TAG_class_template: return "DW_TAG_class_template"; + default: + { + static char buffer [100]; + + sprintf (buffer, _("Unknown TAG value: %lx"), tag); + return buffer; + } + } +} + +static char * +get_AT_name (attribute) + unsigned long attribute; +{ + switch (attribute) + { + case DW_AT_sibling: return "DW_AT_sibling"; + case DW_AT_location: return "DW_AT_location"; + case DW_AT_name: return "DW_AT_name"; + case DW_AT_ordering: return "DW_AT_ordering"; + case DW_AT_subscr_data: return "DW_AT_subscr_data"; + case DW_AT_byte_size: return "DW_AT_byte_size"; + case DW_AT_bit_offset: return "DW_AT_bit_offset"; + case DW_AT_bit_size: return "DW_AT_bit_size"; + case DW_AT_element_list: return "DW_AT_element_list"; + case DW_AT_stmt_list: return "DW_AT_stmt_list"; + case DW_AT_low_pc: return "DW_AT_low_pc"; + case DW_AT_high_pc: return "DW_AT_high_pc"; + case DW_AT_language: return "DW_AT_language"; + case DW_AT_member: return "DW_AT_member"; + case DW_AT_discr: return "DW_AT_discr"; + case DW_AT_discr_value: return "DW_AT_discr_value"; + case DW_AT_visibility: return "DW_AT_visibility"; + case DW_AT_import: return "DW_AT_import"; + case DW_AT_string_length: return "DW_AT_string_length"; + case DW_AT_common_reference: return "DW_AT_common_reference"; + case DW_AT_comp_dir: return "DW_AT_comp_dir"; + case DW_AT_const_value: return "DW_AT_const_value"; + case DW_AT_containing_type: return "DW_AT_containing_type"; + case DW_AT_default_value: return "DW_AT_default_value"; + case DW_AT_inline: return "DW_AT_inline"; + case DW_AT_is_optional: return "DW_AT_is_optional"; + case DW_AT_lower_bound: return "DW_AT_lower_bound"; + case DW_AT_producer: return "DW_AT_producer"; + case DW_AT_prototyped: return "DW_AT_prototyped"; + case DW_AT_return_addr: return "DW_AT_return_addr"; + case DW_AT_start_scope: return "DW_AT_start_scope"; + case DW_AT_stride_size: return "DW_AT_stride_size"; + case DW_AT_upper_bound: return "DW_AT_upper_bound"; + case DW_AT_abstract_origin: return "DW_AT_abstract_origin"; + case DW_AT_accessibility: return "DW_AT_accessibility"; + case DW_AT_address_class: return "DW_AT_address_class"; + case DW_AT_artificial: return "DW_AT_artificial"; + case DW_AT_base_types: return "DW_AT_base_types"; + case DW_AT_calling_convention: return "DW_AT_calling_convention"; + case DW_AT_count: return "DW_AT_count"; + case DW_AT_data_member_location: return "DW_AT_data_member_location"; + case DW_AT_decl_column: return "DW_AT_decl_column"; + case DW_AT_decl_file: return "DW_AT_decl_file"; + case DW_AT_decl_line: return "DW_AT_decl_line"; + case DW_AT_declaration: return "DW_AT_declaration"; + case DW_AT_discr_list: return "DW_AT_discr_list"; + case DW_AT_encoding: return "DW_AT_encoding"; + case DW_AT_external: return "DW_AT_external"; + case DW_AT_frame_base: return "DW_AT_frame_base"; + case DW_AT_friend: return "DW_AT_friend"; + case DW_AT_identifier_case: return "DW_AT_identifier_case"; + case DW_AT_macro_info: return "DW_AT_macro_info"; + case DW_AT_namelist_items: return "DW_AT_namelist_items"; + case DW_AT_priority: return "DW_AT_priority"; + case DW_AT_segment: return "DW_AT_segment"; + case DW_AT_specification: return "DW_AT_specification"; + case DW_AT_static_link: return "DW_AT_static_link"; + case DW_AT_type: return "DW_AT_type"; + case DW_AT_use_location: return "DW_AT_use_location"; + case DW_AT_variable_parameter: return "DW_AT_variable_parameter"; + case DW_AT_virtuality: return "DW_AT_virtuality"; + case DW_AT_vtable_elem_location: return "DW_AT_vtable_elem_location"; + case DW_AT_MIPS_fde: return "DW_AT_MIPS_fde"; + case DW_AT_MIPS_loop_begin: return "DW_AT_MIPS_loop_begin"; + case DW_AT_MIPS_tail_loop_begin: return "DW_AT_MIPS_tail_loop_begin"; + case DW_AT_MIPS_epilog_begin: return "DW_AT_MIPS_epilog_begin"; + case DW_AT_MIPS_loop_unroll_factor: return "DW_AT_MIPS_loop_unroll_factor"; + case DW_AT_MIPS_software_pipeline_depth: return "DW_AT_MIPS_software_pipeline_depth"; + case DW_AT_MIPS_linkage_name: return "DW_AT_MIPS_linkage_name"; + case DW_AT_MIPS_stride: return "DW_AT_MIPS_stride"; + case DW_AT_MIPS_abstract_name: return "DW_AT_MIPS_abstract_name"; + case DW_AT_MIPS_clone_origin: return "DW_AT_MIPS_clone_origin"; + case DW_AT_MIPS_has_inlines: return "DW_AT_MIPS_has_inlines"; + case DW_AT_sf_names: return "DW_AT_sf_names"; + case DW_AT_src_info: return "DW_AT_src_info"; + case DW_AT_mac_info: return "DW_AT_mac_info"; + case DW_AT_src_coords: return "DW_AT_src_coords"; + case DW_AT_body_begin: return "DW_AT_body_begin"; + case DW_AT_body_end: return "DW_AT_body_end"; + default: + { + static char buffer [100]; + + sprintf (buffer, _("Unknown AT value: %lx"), attribute); + return buffer; + } + } +} + +static char * +get_FORM_name (form) + unsigned long form; +{ + switch (form) + { + case DW_FORM_addr: return "DW_FORM_addr"; + case DW_FORM_block2: return "DW_FORM_block2"; + case DW_FORM_block4: return "DW_FORM_block4"; + case DW_FORM_data2: return "DW_FORM_data2"; + case DW_FORM_data4: return "DW_FORM_data4"; + case DW_FORM_data8: return "DW_FORM_data8"; + case DW_FORM_string: return "DW_FORM_string"; + case DW_FORM_block: return "DW_FORM_block"; + case DW_FORM_block1: return "DW_FORM_block1"; + case DW_FORM_data1: return "DW_FORM_data1"; + case DW_FORM_flag: return "DW_FORM_flag"; + case DW_FORM_sdata: return "DW_FORM_sdata"; + case DW_FORM_strp: return "DW_FORM_strp"; + case DW_FORM_udata: return "DW_FORM_udata"; + case DW_FORM_ref_addr: return "DW_FORM_ref_addr"; + case DW_FORM_ref1: return "DW_FORM_ref1"; + case DW_FORM_ref2: return "DW_FORM_ref2"; + case DW_FORM_ref4: return "DW_FORM_ref4"; + case DW_FORM_ref8: return "DW_FORM_ref8"; + case DW_FORM_ref_udata: return "DW_FORM_ref_udata"; + case DW_FORM_indirect: return "DW_FORM_indirect"; + default: + { + static char buffer [100]; + + sprintf (buffer, _("Unknown FORM value: %lx"), form); + return buffer; + } + } +} + +/* FIXME: There are better and more effiecint ways to handle + these structures. For now though, I just want something that + is simple to implement. */ +typedef struct abbrev_attr +{ + unsigned long attribute; + unsigned long form; + struct abbrev_attr * next; +} +abbrev_attr; + +typedef struct abbrev_entry +{ + unsigned long entry; + unsigned long tag; + int children; + struct abbrev_attr * first_attr; + struct abbrev_attr * last_attr; + struct abbrev_entry * next; +} +abbrev_entry; + +static abbrev_entry * first_abbrev = NULL; +static abbrev_entry * last_abbrev = NULL; + +static void +free_abbrevs PARAMS ((void)) +{ + abbrev_entry * abbrev; + + for (abbrev = first_abbrev; abbrev;) + { + abbrev_entry * next = abbrev->next; + abbrev_attr * attr; + + for (attr = abbrev->first_attr; attr;) + { + abbrev_attr * next = attr->next; + + free (attr); + attr = next; + } + + free (abbrev); + abbrev = next; + } + + last_abbrev = first_abbrev = NULL; +} + +static void +add_abbrev (number, tag, children) + unsigned long number; + unsigned long tag; + int children; +{ + abbrev_entry * entry; + + entry = (abbrev_entry *) malloc (sizeof (* entry)); + + if (entry == NULL) + /* ugg */ + return; + + entry->entry = number; + entry->tag = tag; + entry->children = children; + entry->first_attr = NULL; + entry->last_attr = NULL; + entry->next = NULL; + + if (first_abbrev == NULL) + first_abbrev = entry; + else + last_abbrev->next = entry; + + last_abbrev = entry; +} + +static void +add_abbrev_attr (attribute, form) + unsigned long attribute; + unsigned long form; +{ + abbrev_attr * attr; + + attr = (abbrev_attr *) malloc (sizeof (* attr)); + + if (attr == NULL) + /* ugg */ + return; + + attr->attribute = attribute; + attr->form = form; + attr->next = NULL; + + if (last_abbrev->first_attr == NULL) + last_abbrev->first_attr = attr; + else + last_abbrev->last_attr->next = attr; + + last_abbrev->last_attr = attr; +} + +/* Processes the (partial) contents of a .debug_abbrev section. + Returns NULL if the end of the section was encountered. + Returns the address after the last byte read if the end of + an abbreviation set was found. */ + +static unsigned char * +process_abbrev_section (start, end) + unsigned char * start; + unsigned char * end; +{ + if (first_abbrev != NULL) + return NULL; + + while (start < end) + { + int bytes_read; + unsigned long entry; + unsigned long tag; + unsigned long attribute; + int children; + + entry = read_leb128 (start, & bytes_read, 0); + start += bytes_read; + + /* A single zero is supposed to end the section according + to the standard. If there's more, then signal that to + the caller. */ + if (entry == 0) + return start == end ? NULL : start; + + tag = read_leb128 (start, & bytes_read, 0); + start += bytes_read; + + children = * start ++; + + add_abbrev (entry, tag, children); + + do + { + unsigned long form; + + attribute = read_leb128 (start, & bytes_read, 0); + start += bytes_read; + + form = read_leb128 (start, & bytes_read, 0); + start += bytes_read; + + if (attribute != 0) + add_abbrev_attr (attribute, form); + } + while (attribute != 0); + } + + return NULL; +} + + +static int +display_debug_abbrev (section, start, file) + Elf32_Internal_Shdr * section; + unsigned char * start; + FILE * file ATTRIBUTE_UNUSED; +{ + abbrev_entry * entry; + unsigned char * end = start + section->sh_size; + + printf (_("Contents of the %s section:\n\n"), SECTION_NAME (section)); + + do + { + start = process_abbrev_section (start, end); + + printf (_(" Number TAG\n")); + + for (entry = first_abbrev; entry; entry = entry->next) + { + abbrev_attr * attr; + + printf (_(" %ld %s [%s]\n"), + entry->entry, + get_TAG_name (entry->tag), + entry->children ? _("has children") : _("no children")); + + for (attr = entry->first_attr; attr; attr = attr->next) + { + printf (_(" %-18s %s\n"), + get_AT_name (attr->attribute), + get_FORM_name (attr->form)); + } + } + } + while (start); + + printf ("\n"); + + return 1; +} + + +static unsigned char * +display_block (data, length) + unsigned char * data; + unsigned long length; +{ + printf (_(" %lu byte block: "), length); + + while (length --) + printf ("%lx ", (unsigned long) byte_get (data ++, 1)); + + return data; +} + +static void +decode_location_expression (data, pointer_size) + unsigned char * data; + unsigned int pointer_size; +{ + unsigned char op; + int bytes_read; + unsigned long uvalue; + + op = * data ++; + + switch (op) + { + case DW_OP_addr: + printf ("DW_OP_addr: %lx", (unsigned long) byte_get (data, pointer_size)); + break; + case DW_OP_deref: + printf ("DW_OP_deref"); + break; + case DW_OP_const1u: + printf ("DW_OP_const1u: %lu", (unsigned long) byte_get (data, 1)); + break; + case DW_OP_const1s: + printf ("DW_OP_const1s: %ld", (long) byte_get (data, 1)); + break; + case DW_OP_const2u: + printf ("DW_OP_const2u: %lu", (unsigned long) byte_get (data, 2)); + break; + case DW_OP_const2s: + printf ("DW_OP_const2s: %ld", (long) byte_get (data, 2)); + break; + case DW_OP_const4u: + printf ("DW_OP_const4u: %lu", (unsigned long) byte_get (data, 4)); + break; + case DW_OP_const4s: + printf ("DW_OP_const4s: %ld", (long) byte_get (data, 4)); + break; + case DW_OP_const8u: + printf ("DW_OP_const8u: %lu %lu", (unsigned long) byte_get (data, 4), + (unsigned long) byte_get (data + 4, 4)); + break; + case DW_OP_const8s: + printf ("DW_OP_const8s: %ld %ld", (long) byte_get (data, 4), + (long) byte_get (data + 4, 4)); + break; + case DW_OP_constu: + printf ("DW_OP_constu: %lu", read_leb128 (data, NULL, 0)); + break; + case DW_OP_consts: + printf ("DW_OP_consts: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_dup: + printf ("DW_OP_dup"); + break; + case DW_OP_drop: + printf ("DW_OP_drop"); + break; + case DW_OP_over: + printf ("DW_OP_over"); + break; + case DW_OP_pick: + printf ("DW_OP_pick: %ld", (unsigned long) byte_get (data, 1)); + break; + case DW_OP_swap: + printf ("DW_OP_swap"); + break; + case DW_OP_rot: + printf ("DW_OP_rot"); + break; + case DW_OP_xderef: + printf ("DW_OP_xderef"); + break; + case DW_OP_abs: + printf ("DW_OP_abs"); + break; + case DW_OP_and: + printf ("DW_OP_and"); + break; + case DW_OP_div: + printf ("DW_OP_div"); + break; + case DW_OP_minus: + printf ("DW_OP_minus"); + break; + case DW_OP_mod: + printf ("DW_OP_mod"); + break; + case DW_OP_mul: + printf ("DW_OP_mul"); + break; + case DW_OP_neg: + printf ("DW_OP_neg"); + break; + case DW_OP_not: + printf ("DW_OP_not"); + break; + case DW_OP_or: + printf ("DW_OP_or"); + break; + case DW_OP_plus: + printf ("DW_OP_plus"); + break; + case DW_OP_plus_uconst: + printf ("DW_OP_plus_uconst: %lu", read_leb128 (data, NULL, 0)); + break; + case DW_OP_shl: + printf ("DW_OP_shl"); + break; + case DW_OP_shr: + printf ("DW_OP_shr"); + break; + case DW_OP_shra: + printf ("DW_OP_shra"); + break; + case DW_OP_xor: + printf ("DW_OP_xor"); + break; + case DW_OP_bra: + printf ("DW_OP_bra: %ld", (long) byte_get (data, 2)); + break; + case DW_OP_eq: + printf ("DW_OP_eq"); + break; + case DW_OP_ge: + printf ("DW_OP_ge"); + break; + case DW_OP_gt: + printf ("DW_OP_gt"); + break; + case DW_OP_le: + printf ("DW_OP_le"); + break; + case DW_OP_lt: + printf ("DW_OP_lt"); + break; + case DW_OP_ne: + printf ("DW_OP_ne"); + break; + case DW_OP_skip: + printf ("DW_OP_skip: %ld", (long) byte_get (data, 2)); + break; + case DW_OP_lit0: + printf ("DW_OP_lit0"); + break; + case DW_OP_lit1: + printf ("DW_OP_lit1"); + break; + case DW_OP_lit2: + printf ("DW_OP_lit2"); + break; + case DW_OP_lit3: + printf ("DW_OP_lit3"); + break; + case DW_OP_lit4: + printf ("DW_OP_lit4"); + break; + case DW_OP_lit5: + printf ("DW_OP_lit5"); + break; + case DW_OP_lit6: + printf ("DW_OP_lit6"); + break; + case DW_OP_lit7: + printf ("DW_OP_lit7"); + break; + case DW_OP_lit8: + printf ("DW_OP_lit8"); + break; + case DW_OP_lit9: + printf ("DW_OP_lit9"); + break; + case DW_OP_lit10: + printf ("DW_OP_lit10"); + break; + case DW_OP_lit11: + printf ("DW_OP_lit11"); + break; + case DW_OP_lit12: + printf ("DW_OP_lit12"); + break; + case DW_OP_lit13: + printf ("DW_OP_lit13"); + break; + case DW_OP_lit14: + printf ("DW_OP_lit14"); + break; + case DW_OP_lit15: + printf ("DW_OP_lit15"); + break; + case DW_OP_lit16: + printf ("DW_OP_lit16"); + break; + case DW_OP_lit17: + printf ("DW_OP_lit17"); + break; + case DW_OP_lit18: + printf ("DW_OP_lit18"); + break; + case DW_OP_lit19: + printf ("DW_OP_lit19"); + break; + case DW_OP_lit20: + printf ("DW_OP_lit20"); + break; + case DW_OP_lit21: + printf ("DW_OP_lit21"); + break; + case DW_OP_lit22: + printf ("DW_OP_lit22"); + break; + case DW_OP_lit23: + printf ("DW_OP_lit23"); + break; + case DW_OP_lit24: + printf ("DW_OP_lit24"); + break; + case DW_OP_lit25: + printf ("DW_OP_lit25"); + break; + case DW_OP_lit26: + printf ("DW_OP_lit26"); + break; + case DW_OP_lit27: + printf ("DW_OP_lit27"); + break; + case DW_OP_lit28: + printf ("DW_OP_lit28"); + break; + case DW_OP_lit29: + printf ("DW_OP_lit29"); + break; + case DW_OP_lit30: + printf ("DW_OP_lit30"); + break; + case DW_OP_lit31: + printf ("DW_OP_lit31"); + break; + case DW_OP_reg0: + printf ("DW_OP_reg0"); + break; + case DW_OP_reg1: + printf ("DW_OP_reg1"); + break; + case DW_OP_reg2: + printf ("DW_OP_reg2"); + break; + case DW_OP_reg3: + printf ("DW_OP_reg3"); + break; + case DW_OP_reg4: + printf ("DW_OP_reg4"); + break; + case DW_OP_reg5: + printf ("DW_OP_reg5"); + break; + case DW_OP_reg6: + printf ("DW_OP_reg6"); + break; + case DW_OP_reg7: + printf ("DW_OP_reg7"); + break; + case DW_OP_reg8: + printf ("DW_OP_reg8"); + break; + case DW_OP_reg9: + printf ("DW_OP_reg9"); + break; + case DW_OP_reg10: + printf ("DW_OP_reg10"); + break; + case DW_OP_reg11: + printf ("DW_OP_reg11"); + break; + case DW_OP_reg12: + printf ("DW_OP_reg12"); + break; + case DW_OP_reg13: + printf ("DW_OP_reg13"); + break; + case DW_OP_reg14: + printf ("DW_OP_reg14"); + break; + case DW_OP_reg15: + printf ("DW_OP_reg15"); + break; + case DW_OP_reg16: + printf ("DW_OP_reg16"); + break; + case DW_OP_reg17: + printf ("DW_OP_reg17"); + break; + case DW_OP_reg18: + printf ("DW_OP_reg18"); + break; + case DW_OP_reg19: + printf ("DW_OP_reg19"); + break; + case DW_OP_reg20: + printf ("DW_OP_reg20"); + break; + case DW_OP_reg21: + printf ("DW_OP_reg21"); + break; + case DW_OP_reg22: + printf ("DW_OP_reg22"); + break; + case DW_OP_reg23: + printf ("DW_OP_reg23"); + break; + case DW_OP_reg24: + printf ("DW_OP_reg24"); + break; + case DW_OP_reg25: + printf ("DW_OP_reg25"); + break; + case DW_OP_reg26: + printf ("DW_OP_reg26"); + break; + case DW_OP_reg27: + printf ("DW_OP_reg27"); + break; + case DW_OP_reg28: + printf ("DW_OP_reg28"); + break; + case DW_OP_reg29: + printf ("DW_OP_reg29"); + break; + case DW_OP_reg30: + printf ("DW_OP_reg30"); + break; + case DW_OP_reg31: + printf ("DW_OP_reg31"); + break; + case DW_OP_breg0: + printf ("DW_OP_breg0: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg1: + printf ("DW_OP_breg1: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg2: + printf ("DW_OP_breg2: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg3: + printf ("DW_OP_breg3: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg4: + printf ("DW_OP_breg4: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg5: + printf ("DW_OP_breg5: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg6: + printf ("DW_OP_breg6: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg7: + printf ("DW_OP_breg7: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg8: + printf ("DW_OP_breg8: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg9: + printf ("DW_OP_breg9: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg10: + printf ("DW_OP_breg10: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg11: + printf ("DW_OP_breg11: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg12: + printf ("DW_OP_breg12: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg13: + printf ("DW_OP_breg13: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg14: + printf ("DW_OP_breg14: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg15: + printf ("DW_OP_breg15: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg16: + printf ("DW_OP_breg16: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg17: + printf ("DW_OP_breg17: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg18: + printf ("DW_OP_breg18: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg19: + printf ("DW_OP_breg19: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg20: + printf ("DW_OP_breg20: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg21: + printf ("DW_OP_breg21: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg22: + printf ("DW_OP_breg22: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg23: + printf ("DW_OP_breg23: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg24: + printf ("DW_OP_breg24: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg25: + printf ("DW_OP_breg25: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg26: + printf ("DW_OP_breg26: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg27: + printf ("DW_OP_breg27: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg28: + printf ("DW_OP_breg28: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg29: + printf ("DW_OP_breg29: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg30: + printf ("DW_OP_breg30: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_breg31: + printf ("DW_OP_breg31: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_regx: + printf ("DW_OP_regx: %lu", read_leb128 (data, NULL, 0)); + break; + case DW_OP_fbreg: + printf ("DW_OP_fbreg: %ld", read_leb128 (data, NULL, 1)); + break; + case DW_OP_bregx: + uvalue = read_leb128 (data, &bytes_read, 0); + printf ("DW_OP_bregx: %lu %ld", uvalue, + read_leb128 (data + bytes_read, NULL, 1)); + break; + case DW_OP_piece: + printf ("DW_OP_piece: %lu", read_leb128 (data, NULL, 0)); + break; + case DW_OP_deref_size: + printf ("DW_OP_deref_size: %ld", (long) byte_get (data, 1)); + break; + case DW_OP_xderef_size: + printf ("DW_OP_xderef_size: %ld", (long) byte_get (data, 1)); + break; + case DW_OP_nop: + printf ("DW_OP_nop"); + break; + + default: + if (op >= DW_OP_lo_user + && op <= DW_OP_hi_user) + printf (_("(User defined location op)")); + else + printf (_("(Unknown location op)")); + break; + } +} + + +static unsigned char * +read_and_display_attr (attribute, form, data, pointer_size) + unsigned long attribute; + unsigned long form; + unsigned char * data; + unsigned long pointer_size; +{ + unsigned long uvalue = 0; + unsigned char * block_start = NULL; + int bytes_read; + int is_ref = 0; + + printf (" %-18s:", get_AT_name (attribute)); + + switch (form) + { + case DW_FORM_ref_addr: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + is_ref = 1; + } + + switch (form) + { + case DW_FORM_ref_addr: + case DW_FORM_addr: + uvalue = byte_get (data, pointer_size); + printf (is_ref ? " <%lx>" : " %#lx", uvalue); + data += pointer_size; + break; + + case DW_FORM_ref1: + case DW_FORM_flag: + case DW_FORM_data1: + uvalue = byte_get (data ++, 1); + printf (is_ref ? " <%lx>" : " %ld", uvalue); + break; + + case DW_FORM_ref2: + case DW_FORM_data2: + uvalue = byte_get (data, 2); + data += 2; + printf (is_ref ? " <%lx>" : " %ld", uvalue); + break; + + case DW_FORM_ref4: + case DW_FORM_data4: + uvalue = byte_get (data, 4); + data += 4; + printf (is_ref ? " <%lx>" : " %ld", uvalue); + break; + + case DW_FORM_ref8: + case DW_FORM_data8: + uvalue = byte_get (data, 4); + printf (" %lx", uvalue); + printf (" %lx", (unsigned long) byte_get (data + 4, 4)); + data += 8; + break; + + case DW_FORM_string: + printf (" %s", data); + data += strlen (data) + 1; + break; + + case DW_FORM_sdata: + uvalue = read_leb128 (data, & bytes_read, 1); + data += bytes_read; + printf (" %ld", (long) uvalue); + break; + + case DW_FORM_ref_udata: + case DW_FORM_udata: + uvalue = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + printf (is_ref ? " <%lx>" : " %ld", uvalue); + break; + + case DW_FORM_block: + uvalue = read_leb128 (data, & bytes_read, 0); + block_start = data + bytes_read; + data = display_block (block_start, uvalue); + uvalue = * block_start; + break; + + case DW_FORM_block1: + uvalue = byte_get (data, 1); + block_start = data + 1; + data = display_block (block_start, uvalue); + uvalue = * block_start; + break; + + case DW_FORM_block2: + uvalue = byte_get (data, 2); + block_start = data + 2; + data = display_block (block_start, uvalue); + uvalue = * block_start; + break; + + case DW_FORM_block4: + uvalue = byte_get (data, 4); + block_start = data + 4; + data = display_block (block_start, uvalue); + uvalue = * block_start; + break; + + case DW_FORM_strp: + case DW_FORM_indirect: + warn (_("Unable to handle FORM: %d"), form); + break; + + default: + warn (_("Unrecognised form: %d"), form); + break; + } + + /* For some attributes we can display futher information. */ + + printf ("\t"); + + switch (attribute) + { + case DW_AT_inline: + switch (uvalue) + { + case DW_INL_not_inlined: printf (_("(not inlined)")); break; + case DW_INL_inlined: printf (_("(inlined)")); break; + case DW_INL_declared_not_inlined: printf (_("(declared as inline but ignored)")); break; + case DW_INL_declared_inlined: printf (_("(declared as inline and inlined)")); break; + default: printf (_(" (Unknown inline attribute value: %lx)"), uvalue); break; + } + break; + + case DW_AT_frame_base: + if (uvalue >= DW_OP_reg0 && uvalue <= DW_OP_reg31) + printf ("(reg %ld)", uvalue - DW_OP_reg0); + break; + + case DW_AT_language: + switch (uvalue) + { + case DW_LANG_C: printf ("(non-ANSI C)"); break; + case DW_LANG_C89: printf ("(ANSI C)"); break; + case DW_LANG_C_plus_plus: printf ("(C++)"); break; + case DW_LANG_Fortran77: printf ("(FORTRAN 77)"); break; + case DW_LANG_Fortran90: printf ("(Fortran 90)"); break; + case DW_LANG_Modula2: printf ("(Modula 2)"); break; + case DW_LANG_Pascal83: printf ("(ANSI Pascal)"); break; + case DW_LANG_Ada83: printf ("(Ada)"); break; + case DW_LANG_Cobol74: printf ("(Cobol 74)"); break; + case DW_LANG_Cobol85: printf ("(Cobol 85)"); break; + case DW_LANG_Mips_Assembler: printf ("(MIPS assembler)"); break; + default: printf ("(Unknown: %lx)", uvalue); break; + } + break; + + case DW_AT_encoding: + switch (uvalue) + { + case DW_ATE_void: printf ("(void)"); break; + case DW_ATE_address: printf ("(machine address)"); break; + case DW_ATE_boolean: printf ("(boolean)"); break; + case DW_ATE_complex_float: printf ("(complex float)"); break; + case DW_ATE_float: printf ("(float)"); break; + case DW_ATE_signed: printf ("(signed)"); break; + case DW_ATE_signed_char: printf ("(signed char)"); break; + case DW_ATE_unsigned: printf ("(unsigned)"); break; + case DW_ATE_unsigned_char: printf ("(unsigned char)"); break; + default: + if (uvalue >= DW_ATE_lo_user + && uvalue <= DW_ATE_hi_user) + printf ("(user defined type)"); + else + printf ("(unknown type)"); + break; + } + break; + + case DW_AT_accessibility: + switch (uvalue) + { + case DW_ACCESS_public: printf ("(public)"); break; + case DW_ACCESS_protected: printf ("(protected)"); break; + case DW_ACCESS_private: printf ("(private)"); break; + default: printf ("(unknown accessibility)"); break; + } + break; + + case DW_AT_visibility: + switch (uvalue) + { + case DW_VIS_local: printf ("(local)"); break; + case DW_VIS_exported: printf ("(exported)"); break; + case DW_VIS_qualified: printf ("(qualified)"); break; + default: printf ("(unknown visibility)"); break; + } + break; + + case DW_AT_virtuality: + switch (uvalue) + { + case DW_VIRTUALITY_none: printf ("(none)"); break; + case DW_VIRTUALITY_virtual: printf ("(virtual)"); break; + case DW_VIRTUALITY_pure_virtual:printf ("(pure_virtual)"); break; + default: printf ("(unknown virtuality)"); break; + } + break; + + case DW_AT_identifier_case: + switch (uvalue) + { + case DW_ID_case_sensitive: printf ("(case_sensitive)"); break; + case DW_ID_up_case: printf ("(up_case)"); break; + case DW_ID_down_case: printf ("(down_case)"); break; + case DW_ID_case_insensitive: printf ("(case_insensitive)"); break; + default: printf ("(unknown case)"); break; + } + break; + + case DW_AT_calling_convention: + switch (uvalue) + { + case DW_CC_normal: printf ("(normal)"); break; + case DW_CC_program: printf ("(program)"); break; + case DW_CC_nocall: printf ("(nocall)"); break; + default: + if (uvalue >= DW_CC_lo_user + && uvalue <= DW_CC_hi_user) + printf ("(user defined)"); + else + printf ("(unknown convention)"); + } + break; + + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + printf ("("); + decode_location_expression (block_start, pointer_size); + printf (")"); + break; + + default: + break; + } + + printf ("\n"); + return data; +} + +static int +display_debug_info (section, start, file) + Elf32_Internal_Shdr * section; + unsigned char * start; + FILE * file; +{ + unsigned char * end = start + section->sh_size; + unsigned char * section_begin = start; + + printf (_("The section %s contains:\n\n"), SECTION_NAME (section)); + + while (start < end) + { + DWARF2_External_CompUnit * external; + DWARF2_Internal_CompUnit compunit; + unsigned char * tags; + int i; + int level; + + external = (DWARF2_External_CompUnit *) start; + + compunit.cu_length = BYTE_GET (external->cu_length); + compunit.cu_version = BYTE_GET (external->cu_version); + compunit.cu_abbrev_offset = BYTE_GET (external->cu_abbrev_offset); + compunit.cu_pointer_size = BYTE_GET (external->cu_pointer_size); + + tags = start + sizeof (* external); + start += compunit.cu_length + sizeof (external->cu_length); + + if (compunit.cu_version != 2) + { + warn (_("Only version 2 DWARF debug information is currently supported.\n")); + continue; + } + + printf (_(" Compilation Unit:\n")); + printf (_(" Length: %ld\n"), compunit.cu_length); + printf (_(" Version: %d\n"), compunit.cu_version); + printf (_(" Abbrev Offset: %ld\n"), compunit.cu_abbrev_offset); + printf (_(" Pointer Size: %d\n"), compunit.cu_pointer_size); + + if (first_abbrev != NULL) + free_abbrevs (); + + /* Read in the abbrevs used by this compilation unit. */ + + { + Elf32_Internal_Shdr * sec; + unsigned char * begin; + + /* Locate the .debug_abbrev section and process it. */ + for (i = 0, sec = section_headers; + i < elf_header.e_shnum; + i ++, sec ++) + if (strcmp (SECTION_NAME (sec), ".debug_abbrev") == 0) + break; + + if (i == -1 || sec->sh_size == 0) + { + warn (_("Unable to locate .debug_abbrev section!\n")); + return 0; + } + + GET_DATA_ALLOC (sec->sh_offset, sec->sh_size, begin, unsigned char *, + "debug_abbrev section data"); + + process_abbrev_section (begin + compunit.cu_abbrev_offset, + begin + sec->sh_size); + + free (begin); + } + + level = 0; + while (tags < start) + { + int bytes_read; + unsigned long abbrev_number; + abbrev_entry * entry; + abbrev_attr * attr; + + abbrev_number = read_leb128 (tags, & bytes_read, 0); + tags += bytes_read; + + /* A null DIE marks the end of a list of children. */ + if (abbrev_number == 0) + { + --level; + continue; + } + + /* Scan through the abbreviation list until we reach the + correct entry. */ + for (entry = first_abbrev; + entry && entry->entry != abbrev_number; + entry = entry->next) + continue; + + if (entry == NULL) + { + warn (_("Unable to locate entry %lu in the abbreviation table\n"), + abbrev_number); + return 0; + } + + printf (_(" <%d><%x>: Abbrev Number: %lu (%s)\n"), + level, tags - section_begin - bytes_read, + abbrev_number, + get_TAG_name (entry->tag)); + + for (attr = entry->first_attr; attr; attr = attr->next) + tags = read_and_display_attr (attr->attribute, + attr->form, + tags, + compunit.cu_pointer_size); + + if (entry->children) + ++level; + } + } + + printf ("\n"); + + return 1; +} + +static int +display_debug_aranges (section, start, file) + Elf32_Internal_Shdr * section; + unsigned char * start; + FILE * file ATTRIBUTE_UNUSED; +{ + unsigned char * end = start + section->sh_size; + + printf (_("The section %s contains:\n\n"), SECTION_NAME (section)); + + while (start < end) + { + DWARF2_External_ARange * external; + DWARF2_Internal_ARange arange; + unsigned char * ranges; + unsigned long length; + unsigned long address; + int excess; + + external = (DWARF2_External_ARange *) start; + + arange.ar_length = BYTE_GET (external->ar_length); + arange.ar_version = BYTE_GET (external->ar_version); + arange.ar_info_offset = BYTE_GET (external->ar_info_offset); + arange.ar_pointer_size = BYTE_GET (external->ar_pointer_size); + arange.ar_segment_size = BYTE_GET (external->ar_segment_size); + + printf (_(" Length: %ld\n"), arange.ar_length); + printf (_(" Version: %d\n"), arange.ar_version); + printf (_(" Offset into .debug_info: %lx\n"), arange.ar_info_offset); + printf (_(" Pointer Size: %d\n"), arange.ar_pointer_size); + printf (_(" Segment Size: %d\n"), arange.ar_segment_size); + + printf (_("\n Address Length\n")); + + ranges = start + sizeof (* external); + + /* Must pad to an alignment boundary that is twice the pointer size. */ + excess = sizeof (*external) % (2 * arange.ar_pointer_size); + if (excess) + ranges += (2 * arange.ar_pointer_size) - excess; + + for (;;) + { + address = byte_get (ranges, arange.ar_pointer_size); + + ranges += arange.ar_pointer_size; + + length = byte_get (ranges, arange.ar_pointer_size); + + ranges += arange.ar_pointer_size; + + /* A pair of zeros marks the end of the list. */ + if (address == 0 && length == 0) + break; + + printf (" %8.8lx %lu\n", address, length); + } + + start += arange.ar_length + sizeof (external->ar_length); + } + + printf ("\n"); + + return 1; +} + + +static int +display_debug_not_supported (section, start, file) + Elf32_Internal_Shdr * section; + unsigned char * start ATTRIBUTE_UNUSED; + FILE * file ATTRIBUTE_UNUSED; +{ + printf (_("Displaying the debug contents of section %s is not yet supported.\n"), + SECTION_NAME (section)); + + return 1; +} + +/* Pre-scan the .debug_info section to record the size of address. + When dumping the .debug_line, we use that size information, assuming + that all compilation units have the same address size. */ +static int +prescan_debug_info (section, start, file) + Elf32_Internal_Shdr * section ATTRIBUTE_UNUSED; + unsigned char * start; + FILE * file ATTRIBUTE_UNUSED; +{ + DWARF2_External_CompUnit * external; + + external = (DWARF2_External_CompUnit *) start; + + debug_line_pointer_size = BYTE_GET (external->cu_pointer_size); + return 0; +} + + /* A structure containing the name of a debug section and a pointer + to a function that can decode it. The third field is a prescan + function to be run over the section before displaying any of the + sections. */ +struct +{ + char * name; + int (* display) PARAMS ((Elf32_Internal_Shdr *, unsigned char *, FILE *)); + int (* prescan) PARAMS ((Elf32_Internal_Shdr *, unsigned char *, FILE *)); +} +debug_displays[] = +{ + { ".debug_info", display_debug_info, prescan_debug_info }, + { ".debug_abbrev", display_debug_abbrev, NULL }, + { ".debug_line", display_debug_lines, NULL }, + { ".debug_aranges", display_debug_aranges, NULL }, + { ".debug_pubnames", display_debug_pubnames, NULL }, + { ".debug_macinfo", display_debug_not_supported, NULL }, + { ".debug_frame", display_debug_not_supported, NULL }, + { ".debug_str", display_debug_not_supported, NULL }, + { ".debug_static_func", display_debug_not_supported, NULL }, + { ".debug_static_vars", display_debug_not_supported, NULL }, + { ".debug_types", display_debug_not_supported, NULL }, + { ".debug_weaknames", display_debug_not_supported, NULL } +}; + +static int +display_debug_section (section, file) + Elf32_Internal_Shdr * section; + FILE * file; +{ + char * name = SECTION_NAME (section); + bfd_size_type length; + unsigned char * start; + int i; + + length = section->sh_size; + if (length == 0) + { + printf (_("\nSection '%s' has no debugging data.\n"), name); + return 0; + } + + GET_DATA_ALLOC (section->sh_offset, length, start, unsigned char *, + "debug section data"); + + /* See if we know how to display the contents of this section. */ + for (i = NUM_ELEM (debug_displays); i--;) + if (strcmp (debug_displays[i].name, name) == 0) + { + debug_displays[i].display (section, start, file); + break; + } + + if (i == -1) + printf (_("Unrecognised debug section: %s\n"), name); + + free (start); + + /* If we loaded in the abbrev section at some point, + we must release it here. */ + if (first_abbrev != NULL) + free_abbrevs (); + + return 1; +} + +static int +process_section_contents (file) + FILE * file; +{ + Elf32_Internal_Shdr * section; + unsigned int i; + + if (! do_dump) + return 1; + + /* Pre-scan the debug sections to find some debug information not + present in some of them. For the .debug_line, we must find out the + size of address (specified in .debug_info and .debug_aranges). */ + for (i = 0, section = section_headers; + i < elf_header.e_shnum && i < num_dump_sects; + i ++, section ++) + { + char * name = SECTION_NAME (section); + int j; + + if (section->sh_size == 0) + continue; + + /* See if there is some pre-scan operation for this section. */ + for (j = NUM_ELEM (debug_displays); j--;) + if (strcmp (debug_displays[j].name, name) == 0) + { + if (debug_displays[j].prescan != NULL) + { + bfd_size_type length; + unsigned char * start; + + length = section->sh_size; + GET_DATA_ALLOC (section->sh_offset, length, start, unsigned char *, + "debug section data"); + + debug_displays[j].prescan (section, start, file); + free (start); + } + + break; + } + } + + for (i = 0, section = section_headers; + i < elf_header.e_shnum && i < num_dump_sects; + i ++, section ++) + { +#ifdef SUPPORT_DISASSEMBLY + if (dump_sects[i] & DISASS_DUMP) + disassemble_section (section, file); +#endif + if (dump_sects[i] & HEX_DUMP) + dump_section (section, file); + + if (dump_sects[i] & DEBUG_DUMP) + display_debug_section (section, file); + } + + if (i < num_dump_sects) + warn (_("Some sections were not dumped because they do not exist!\n")); + + return 1; +} + +static void +process_mips_fpe_exception (mask) + int mask; +{ + if (mask) + { + int first = 1; + if (mask & OEX_FPU_INEX) + fputs ("INEX", stdout), first = 0; + if (mask & OEX_FPU_UFLO) + printf ("%sUFLO", first ? "" : "|"), first = 0; + if (mask & OEX_FPU_OFLO) + printf ("%sOFLO", first ? "" : "|"), first = 0; + if (mask & OEX_FPU_DIV0) + printf ("%sDIV0", first ? "" : "|"), first = 0; + if (mask & OEX_FPU_INVAL) + printf ("%sINVAL", first ? "" : "|"); + } + else + fputs ("0", stdout); +} + +static int +process_mips_specific (file) + FILE * file; +{ + Elf_Internal_Dyn * entry; + size_t liblist_offset = 0; + size_t liblistno = 0; + size_t conflictsno = 0; + size_t options_offset = 0; + size_t conflicts_offset = 0; + + /* We have a lot of special sections. Thanks SGI! */ + if (dynamic_segment == NULL) + /* No information available. */ + return 0; + + for (entry = dynamic_segment; entry->d_tag != DT_NULL; ++entry) + switch (entry->d_tag) + { + case DT_MIPS_LIBLIST: + liblist_offset = entry->d_un.d_val - loadaddr; + break; + case DT_MIPS_LIBLISTNO: + liblistno = entry->d_un.d_val; + break; + case DT_MIPS_OPTIONS: + options_offset = entry->d_un.d_val - loadaddr; + break; + case DT_MIPS_CONFLICT: + conflicts_offset = entry->d_un.d_val - loadaddr; + break; + case DT_MIPS_CONFLICTNO: + conflictsno = entry->d_un.d_val; + break; + default: + break; + } + + if (liblist_offset != 0 && liblistno != 0 && do_dynamic) + { + Elf32_External_Lib * elib; + size_t cnt; + + GET_DATA_ALLOC (liblist_offset, liblistno * sizeof (Elf32_External_Lib), + elib, Elf32_External_Lib *, "liblist"); + + printf ("\nSection '.liblist' contains %lu entries:\n", + (unsigned long) liblistno); + fputs (" Library Time Stamp Checksum Version Flags\n", + stdout); + + for (cnt = 0; cnt < liblistno; ++cnt) + { + Elf32_Lib liblist; + time_t time; + char timebuf[20]; + struct tm * tmp; + + liblist.l_name = BYTE_GET (elib[cnt].l_name); + time = BYTE_GET (elib[cnt].l_time_stamp); + liblist.l_checksum = BYTE_GET (elib[cnt].l_checksum); + liblist.l_version = BYTE_GET (elib[cnt].l_version); + liblist.l_flags = BYTE_GET (elib[cnt].l_flags); + + tmp = gmtime (&time); + sprintf (timebuf, "%04u-%02u-%02uT%02u:%02u:%02u", + tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + printf ("%3lu: %-20s %s %#10lx %-7ld", (unsigned long) cnt, + dynamic_strings + liblist.l_name, timebuf, + liblist.l_checksum, liblist.l_version); + + if (liblist.l_flags == 0) + puts (" NONE"); + else + { + static const struct + { + const char * name; + int bit; + } + l_flags_vals[] = + { + { " EXACT_MATCH", LL_EXACT_MATCH }, + { " IGNORE_INT_VER", LL_IGNORE_INT_VER }, + { " REQUIRE_MINOR", LL_REQUIRE_MINOR }, + { " EXPORTS", LL_EXPORTS }, + { " DELAY_LOAD", LL_DELAY_LOAD }, + { " DELTA", LL_DELTA } + }; + int flags = liblist.l_flags; + size_t fcnt; + + for (fcnt = 0; + fcnt < sizeof (l_flags_vals) / sizeof (l_flags_vals[0]); + ++fcnt) + if ((flags & l_flags_vals[fcnt].bit) != 0) + { + fputs (l_flags_vals[fcnt].name, stdout); + flags ^= l_flags_vals[fcnt].bit; + } + if (flags != 0) + printf (" %#x", (unsigned int) flags); + + puts (""); + } + } + + free (elib); + } + + if (options_offset != 0) + { + Elf_External_Options * eopt; + Elf_Internal_Shdr * sect = section_headers; + Elf_Internal_Options * iopt; + Elf_Internal_Options * option; + size_t offset; + int cnt; + + /* Find the section header so that we get the size. */ + while (sect->sh_type != SHT_MIPS_OPTIONS) + ++ sect; + + GET_DATA_ALLOC (options_offset, sect->sh_size, eopt, + Elf_External_Options *, "options"); + + iopt = (Elf_Internal_Options *) malloc ((sect->sh_size / sizeof (eopt)) + * sizeof (*iopt)); + if (iopt == NULL) + { + error (_("Out of memory")); + return 0; + } + + offset = cnt = 0; + option = iopt; + + while (offset < sect->sh_size) + { + Elf_External_Options * eoption; + + eoption = (Elf_External_Options *) ((char *) eopt + offset); + + option->kind = BYTE_GET (eoption->kind); + option->size = BYTE_GET (eoption->size); + option->section = BYTE_GET (eoption->section); + option->info = BYTE_GET (eoption->info); + + offset += option->size; + + ++option; + ++cnt; + } + + printf (_("\nSection '%s' contains %d entries:\n"), + string_table + sect->sh_name, cnt); + + option = iopt; + + while (cnt-- > 0) + { + size_t len; + + switch (option->kind) + { + case ODK_NULL: + /* This shouldn't happen. */ + printf (" NULL %d %lx", option->section, option->info); + break; + case ODK_REGINFO: + printf (" REGINFO "); + if (elf_header.e_machine == EM_MIPS) + { + /* 32bit form. */ + Elf32_External_RegInfo *ereg; + Elf32_RegInfo reginfo; + + ereg = (Elf32_External_RegInfo *) (option + 1); + reginfo.ri_gprmask = BYTE_GET (ereg->ri_gprmask); + reginfo.ri_cprmask[0] = BYTE_GET (ereg->ri_cprmask[0]); + reginfo.ri_cprmask[1] = BYTE_GET (ereg->ri_cprmask[1]); + reginfo.ri_cprmask[2] = BYTE_GET (ereg->ri_cprmask[2]); + reginfo.ri_cprmask[3] = BYTE_GET (ereg->ri_cprmask[3]); + reginfo.ri_gp_value = BYTE_GET (ereg->ri_gp_value); + + printf ("GPR %08lx GP 0x%lx\n", + reginfo.ri_gprmask, + (unsigned long) reginfo.ri_gp_value); + printf (" CPR0 %08lx CPR1 %08lx CPR2 %08lx CPR3 %08lx\n", + reginfo.ri_cprmask[0], reginfo.ri_cprmask[1], + reginfo.ri_cprmask[2], reginfo.ri_cprmask[3]); + } + else + { + /* 64 bit form. */ + Elf64_External_RegInfo * ereg; + Elf64_Internal_RegInfo reginfo; + + ereg = (Elf64_External_RegInfo *) (option + 1); + reginfo.ri_gprmask = BYTE_GET (ereg->ri_gprmask); + reginfo.ri_cprmask[0] = BYTE_GET (ereg->ri_cprmask[0]); + reginfo.ri_cprmask[1] = BYTE_GET (ereg->ri_cprmask[1]); + reginfo.ri_cprmask[2] = BYTE_GET (ereg->ri_cprmask[2]); + reginfo.ri_cprmask[3] = BYTE_GET (ereg->ri_cprmask[3]); + reginfo.ri_gp_value = BYTE_GET8 (ereg->ri_gp_value); + + printf ("GPR %08lx GP 0x", + reginfo.ri_gprmask); + printf_vma (reginfo.ri_gp_value); + printf ("\n"); + + printf (" CPR0 %08lx CPR1 %08lx CPR2 %08lx CPR3 %08lx\n", + reginfo.ri_cprmask[0], reginfo.ri_cprmask[1], + reginfo.ri_cprmask[2], reginfo.ri_cprmask[3]); + } + ++option; + continue; + case ODK_EXCEPTIONS: + fputs (" EXCEPTIONS fpe_min(", stdout); + process_mips_fpe_exception (option->info & OEX_FPU_MIN); + fputs (") fpe_max(", stdout); + process_mips_fpe_exception ((option->info & OEX_FPU_MAX) >> 8); + fputs (")", stdout); + + if (option->info & OEX_PAGE0) + fputs (" PAGE0", stdout); + if (option->info & OEX_SMM) + fputs (" SMM", stdout); + if (option->info & OEX_FPDBUG) + fputs (" FPDBUG", stdout); + if (option->info & OEX_DISMISS) + fputs (" DISMISS", stdout); + break; + case ODK_PAD: + fputs (" PAD ", stdout); + if (option->info & OPAD_PREFIX) + fputs (" PREFIX", stdout); + if (option->info & OPAD_POSTFIX) + fputs (" POSTFIX", stdout); + if (option->info & OPAD_SYMBOL) + fputs (" SYMBOL", stdout); + break; + case ODK_HWPATCH: + fputs (" HWPATCH ", stdout); + if (option->info & OHW_R4KEOP) + fputs (" R4KEOP", stdout); + if (option->info & OHW_R8KPFETCH) + fputs (" R8KPFETCH", stdout); + if (option->info & OHW_R5KEOP) + fputs (" R5KEOP", stdout); + if (option->info & OHW_R5KCVTL) + fputs (" R5KCVTL", stdout); + break; + case ODK_FILL: + fputs (" FILL ", stdout); + /* XXX Print content of info word? */ + break; + case ODK_TAGS: + fputs (" TAGS ", stdout); + /* XXX Print content of info word? */ + break; + case ODK_HWAND: + fputs (" HWAND ", stdout); + if (option->info & OHWA0_R4KEOP_CHECKED) + fputs (" R4KEOP_CHECKED", stdout); + if (option->info & OHWA0_R4KEOP_CLEAN) + fputs (" R4KEOP_CLEAN", stdout); + break; + case ODK_HWOR: + fputs (" HWOR ", stdout); + if (option->info & OHWA0_R4KEOP_CHECKED) + fputs (" R4KEOP_CHECKED", stdout); + if (option->info & OHWA0_R4KEOP_CLEAN) + fputs (" R4KEOP_CLEAN", stdout); + break; + case ODK_GP_GROUP: + printf (" GP_GROUP %#06lx self-contained %#06lx", + option->info & OGP_GROUP, + (option->info & OGP_SELF) >> 16); + break; + case ODK_IDENT: + printf (" IDENT %#06lx self-contained %#06lx", + option->info & OGP_GROUP, + (option->info & OGP_SELF) >> 16); + break; + default: + /* This shouldn't happen. */ + printf (" %3d ??? %d %lx", + option->kind, option->section, option->info); + break; + } + + len = sizeof (*eopt); + while (len < option->size) + if (((char *) option)[len] >= ' ' + && ((char *) option)[len] < 0x7f) + printf ("%c", ((char *) option)[len++]); + else + printf ("\\%03o", ((char *) option)[len++]); + + fputs ("\n", stdout); + ++option; + } + + free (eopt); + } + + if (conflicts_offset != 0 && conflictsno != 0) + { + Elf32_External_Conflict * econf32; + Elf64_External_Conflict * econf64; + Elf32_Conflict * iconf; + size_t cnt; + + if (dynamic_symbols == NULL) + { + error (_("conflict list with without table")); + return 0; + } + + iconf = (Elf32_Conflict *) malloc (conflictsno * sizeof (*iconf)); + if (iconf == NULL) + { + error (_("Out of memory")); + return 0; + } + + if (is_32bit_elf) + { + GET_DATA_ALLOC (conflicts_offset, conflictsno * sizeof (*econf32), + econf32, Elf32_External_Conflict *, "conflict"); + + for (cnt = 0; cnt < conflictsno; ++cnt) + iconf[cnt] = BYTE_GET (econf32[cnt]); + } + else + { + GET_DATA_ALLOC (conflicts_offset, conflictsno * sizeof (*econf64), + econf64, Elf64_External_Conflict *, "conflict"); + + for (cnt = 0; cnt < conflictsno; ++cnt) + iconf[cnt] = BYTE_GET (econf64[cnt]); + } + + printf (_("\nSection '.conflict' contains %d entries:\n"), conflictsno); + puts (_(" Num: Index Value Name")); + + for (cnt = 0; cnt < conflictsno; ++cnt) + { + Elf_Internal_Sym * psym = &dynamic_symbols[iconf[cnt]]; + + printf ("%5lu: %8lu ", (unsigned long) cnt, iconf[cnt]); + print_vma (psym->st_value, FULL_HEX); + printf (" %s\n", dynamic_strings + psym->st_name); + } + + free (iconf); + } + + return 1; +} + +static char * +get_note_type (e_type) + unsigned e_type; +{ + static char buff[64]; + + switch (e_type) + { + case NT_PRSTATUS: return _("NT_PRSTATUS (prstatus structure)"); + case NT_FPREGSET: return _("NT_FPREGSET (floating point registers)"); + case NT_PRPSINFO: return _("NT_PRPSINFO (prpsinfo structure)"); + case NT_TASKSTRUCT: return _("NT_TASKSTRUCT (task structure)"); + case NT_PRXFPREG: return _("NT_PRXFPREG (user_xfpregs structure)"); + case NT_PSTATUS: return _("NT_PSTATUS (pstatus structure)"); + case NT_FPREGS: return _("NT_FPREGS (floating point registers)"); + case NT_PSINFO: return _("NT_PSINFO (psinfo structure)"); + case NT_LWPSTATUS: return _("NT_LWPSTATUS (lwpstatus_t structure)"); + case NT_LWPSINFO: return _("NT_LWPSINFO (lwpsinfo_t structure)"); + case NT_WIN32PSTATUS: return _("NT_WIN32PSTATUS (win32_pstatus strcuture)"); + default: + sprintf (buff, _("Unknown note type: (0x%08x)"), e_type); + return buff; + } +} + +/* Note that by the ELF standard, the name field is already null byte + terminated, and namesz includes the terminating null byte. + I.E. the value of namesz for the name "FSF" is 4. + + If the value of namesz is zero, there is no name present. */ +static int +process_note (pnote) + Elf32_Internal_Note * pnote; +{ + printf (" %s\t\t0x%08lx\t%s\n", + pnote->namesz ? pnote->namedata : "(NONE)", + pnote->descsz, get_note_type (pnote->type)); + return 1; +} + + +static int +process_corefile_note_segment (file, offset, length) + FILE * file; + bfd_vma offset; + bfd_vma length; +{ + Elf_External_Note * pnotes; + Elf_External_Note * external; + int res = 1; + + if (length <= 0) + return 0; + + GET_DATA_ALLOC (offset, length, pnotes, Elf_External_Note *, "notes"); + + external = pnotes; + + printf (_("\nNotes at offset 0x%08lx with length 0x%08lx:\n"), + (unsigned long) offset, + (unsigned long) length); + printf (_(" Owner\t\tData size\tDescription\n")); + + while (external < (Elf_External_Note *)((char *) pnotes + length)) + { + Elf32_Internal_Note inote; + char * temp = NULL; + + inote.type = BYTE_GET (external->type); + inote.namesz = BYTE_GET (external->namesz); + inote.namedata = external->name; + inote.descsz = BYTE_GET (external->descsz); + inote.descdata = inote.namedata + align_power (inote.namesz, 2); + inote.descpos = offset + (inote.descdata - (char *) pnotes); + + external = (Elf_External_Note *)(inote.descdata + align_power (inote.descsz, 2)); + + /* Verify that name is null terminated. It appears that at least + one version of Linux (RedHat 6.0) generates corefiles that don't + comply with the ELF spec by failing to include the null byte in + namesz. */ + if (inote.namedata[inote.namesz] != '\0') + { + temp = malloc (inote.namesz + 1); + + if (temp == NULL) + { + error (_("Out of memory\n")); + res = 0; + break; + } + + strncpy (temp, inote.namedata, inote.namesz); + temp[inote.namesz] = 0; + + /* warn (_("'%s' NOTE name not properly null terminated\n"), temp); */ + inote.namedata = temp; + } + + res &= process_note (& inote); + + if (temp != NULL) + { + free (temp); + temp = NULL; + } + } + + free (pnotes); + + return res; +} + +static int +process_corefile_note_segments (file) + FILE * file; +{ + Elf_Internal_Phdr * program_headers; + Elf_Internal_Phdr * segment; + unsigned int i; + int res = 1; + + program_headers = (Elf_Internal_Phdr *) malloc + (elf_header.e_phnum * sizeof (Elf_Internal_Phdr)); + + if (program_headers == NULL) + { + error (_("Out of memory\n")); + return 0; + } + + if (is_32bit_elf) + i = get_32bit_program_headers (file, program_headers); + else + i = get_64bit_program_headers (file, program_headers); + + if (i == 0) + { + free (program_headers); + return 0; + } + + for (i = 0, segment = program_headers; + i < elf_header.e_phnum; + i ++, segment ++) + { + if (segment->p_type == PT_NOTE) + res &= process_corefile_note_segment (file, + (bfd_vma) segment->p_offset, + (bfd_vma) segment->p_filesz); + } + + free (program_headers); + + return res; +} + +static int +process_corefile_contents (file) + FILE * file; +{ + /* If we have not been asked to display the notes then do nothing. */ + if (! do_notes) + return 1; + + /* If file is not a core file then exit. */ + if (elf_header.e_type != ET_CORE) + return 1; + + /* No program headers means no NOTE segment. */ + if (elf_header.e_phnum == 0) + { + printf (_("No note segments present in the core file.\n")); + return 1; + } + + return process_corefile_note_segments (file); +} + +static int +process_arch_specific (file) + FILE * file; +{ + if (! do_arch) + return 1; + + switch (elf_header.e_machine) + { + case EM_MIPS: + case EM_MIPS_RS4_BE: + return process_mips_specific (file); + break; + default: + break; + } + return 1; +} + +static int +get_file_header (file) + FILE * file; +{ + /* Read in the identity array. */ + if (fread (elf_header.e_ident, EI_NIDENT, 1, file) != 1) + return 0; + + /* Determine how to read the rest of the header. */ + switch (elf_header.e_ident [EI_DATA]) + { + default: /* fall through */ + case ELFDATANONE: /* fall through */ + case ELFDATA2LSB: byte_get = byte_get_little_endian; break; + case ELFDATA2MSB: byte_get = byte_get_big_endian; break; + } + + /* For now we only support 32 bit and 64 bit ELF files. */ + is_32bit_elf = (elf_header.e_ident [EI_CLASS] != ELFCLASS64); + + /* Read in the rest of the header. */ + if (is_32bit_elf) + { + Elf32_External_Ehdr ehdr32; + + if (fread (ehdr32.e_type, sizeof (ehdr32) - EI_NIDENT, 1, file) != 1) + return 0; + + elf_header.e_type = BYTE_GET (ehdr32.e_type); + elf_header.e_machine = BYTE_GET (ehdr32.e_machine); + elf_header.e_version = BYTE_GET (ehdr32.e_version); + elf_header.e_entry = BYTE_GET (ehdr32.e_entry); + elf_header.e_phoff = BYTE_GET (ehdr32.e_phoff); + elf_header.e_shoff = BYTE_GET (ehdr32.e_shoff); + elf_header.e_flags = BYTE_GET (ehdr32.e_flags); + elf_header.e_ehsize = BYTE_GET (ehdr32.e_ehsize); + elf_header.e_phentsize = BYTE_GET (ehdr32.e_phentsize); + elf_header.e_phnum = BYTE_GET (ehdr32.e_phnum); + elf_header.e_shentsize = BYTE_GET (ehdr32.e_shentsize); + elf_header.e_shnum = BYTE_GET (ehdr32.e_shnum); + elf_header.e_shstrndx = BYTE_GET (ehdr32.e_shstrndx); + } + else + { + Elf64_External_Ehdr ehdr64; + + /* If we have been compiled with sizeof (bfd_vma) == 4, then + we will not be able to cope with the 64bit data found in + 64 ELF files. Detect this now and abort before we start + overwritting things. */ + if (sizeof (bfd_vma) < 8) + { + error (_("This instance of readelf has been built without support for a\n")); + error (_("64 bit data type and so it cannot read 64 bit ELF files.\n")); + return 0; + } + + if (fread (ehdr64.e_type, sizeof (ehdr64) - EI_NIDENT, 1, file) != 1) + return 0; + + elf_header.e_type = BYTE_GET (ehdr64.e_type); + elf_header.e_machine = BYTE_GET (ehdr64.e_machine); + elf_header.e_version = BYTE_GET (ehdr64.e_version); + elf_header.e_entry = BYTE_GET8 (ehdr64.e_entry); + elf_header.e_phoff = BYTE_GET8 (ehdr64.e_phoff); + elf_header.e_shoff = BYTE_GET8 (ehdr64.e_shoff); + elf_header.e_flags = BYTE_GET (ehdr64.e_flags); + elf_header.e_ehsize = BYTE_GET (ehdr64.e_ehsize); + elf_header.e_phentsize = BYTE_GET (ehdr64.e_phentsize); + elf_header.e_phnum = BYTE_GET (ehdr64.e_phnum); + elf_header.e_shentsize = BYTE_GET (ehdr64.e_shentsize); + elf_header.e_shnum = BYTE_GET (ehdr64.e_shnum); + elf_header.e_shstrndx = BYTE_GET (ehdr64.e_shstrndx); + } + + return 1; +} + +static void +process_file (file_name) + char * file_name; +{ + FILE * file; + struct stat statbuf; + unsigned int i; + + if (stat (file_name, & statbuf) < 0) + { + error (_("Cannot stat input file %s.\n"), file_name); + return; + } + + file = fopen (file_name, "rb"); + if (file == NULL) + { + error (_("Input file %s not found.\n"), file_name); + return; + } + + if (! get_file_header (file)) + { + error (_("%s: Failed to read file header\n"), file_name); + fclose (file); + return; + } + + /* Initialise per file variables. */ + for (i = NUM_ELEM (version_info); i--;) + version_info[i] = 0; + + for (i = NUM_ELEM (dynamic_info); i--;) + dynamic_info[i] = 0; + + /* Process the file. */ + if (show_name) + printf (_("\nFile: %s\n"), file_name); + + if (! process_file_header ()) + { + fclose (file); + return; + } + + process_section_headers (file); + + process_program_headers (file); + + process_dynamic_segment (file); + + process_relocs (file); + + process_symbol_table (file); + + process_syminfo (file); + + process_version_sections (file); + + process_section_contents (file); + + process_corefile_contents (file); + + process_arch_specific (file); + + fclose (file); + + if (section_headers) + { + free (section_headers); + section_headers = NULL; + } + + if (string_table) + { + free (string_table); + string_table = NULL; + } + + if (dynamic_strings) + { + free (dynamic_strings); + dynamic_strings = NULL; + } + + if (dynamic_symbols) + { + free (dynamic_symbols); + dynamic_symbols = NULL; + num_dynamic_syms = 0; + } + + if (dynamic_syminfo) + { + free (dynamic_syminfo); + dynamic_syminfo = NULL; + } +} + +#ifdef SUPPORT_DISASSEMBLY +/* Needed by the i386 disassembler. For extra credit, someone could + fix this so that we insert symbolic addresses here, esp for GOT/PLT + symbols */ + +void +print_address (unsigned int addr, FILE * outfile) +{ + fprintf (outfile,"0x%8.8x", addr); +} + +/* Needed by the i386 disassembler. */ +void +db_task_printsym (unsigned int addr) +{ + print_address (addr, stderr); +} +#endif + +int +main (argc, argv) + int argc; + char ** argv; +{ +#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) + setlocale (LC_MESSAGES, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + parse_args (argc, argv); + + if (optind < (argc - 1)) + show_name = 1; + + while (optind < argc) + process_file (argv [optind ++]); + + if (dump_sects != NULL) + free (dump_sects); + + return 0; +} Property changes on: stable/4/contrib/binutils/binutils/readelf.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/contrib/binutils/gas/config/tc-arm.c =================================================================== --- stable/4/contrib/binutils/gas/config/tc-arm.c (nonexistent) +++ stable/4/contrib/binutils/gas/config/tc-arm.c (revision 62108) @@ -0,0 +1,7236 @@ +/* tc-arm.c -- Assemble for the ARM + Copyright (C) 1994, 95, 96, 97, 98, 1999, 2000 Free Software Foundation, Inc. + Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) + Modified by David Taylor (dtaylor@armltd.co.uk) + + This file is part of GAS, the GNU Assembler. + + GAS 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 2, or (at your option) + any later version. + + GAS 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 GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include +#include +#define NO_RELOC 0 +#include "as.h" + +/* need TARGET_CPU */ +#include "config.h" +#include "subsegs.h" +#include "obstack.h" +#include "symbols.h" +#include "listing.h" + +#ifdef OBJ_ELF +#include "elf/arm.h" +#endif + +/* Types of processor to assemble for. */ +#define ARM_1 0x00000001 +#define ARM_2 0x00000002 +#define ARM_3 0x00000004 +#define ARM_250 ARM_3 +#define ARM_6 0x00000008 +#define ARM_7 ARM_6 /* same core instruction set */ +#define ARM_8 ARM_6 /* same core instruction set */ +#define ARM_9 ARM_6 /* same core instruction set */ +#define ARM_CPU_MASK 0x0000000f + +/* The following bitmasks control CPU extensions (ARM7 onwards): */ +#define ARM_LONGMUL 0x00000010 /* allow long multiplies */ +#define ARM_HALFWORD 0x00000020 /* allow half word loads */ +#define ARM_THUMB 0x00000040 /* allow BX instruction */ +#define ARM_EXT_V5 0x00000080 /* allow CLZ etc */ +#define ARM_EXT_V5E 0x00000200 /* "El Segundo" */ + +/* Architectures are the sum of the base and extensions. */ +#define ARM_ARCH_V4 (ARM_7 | ARM_LONGMUL | ARM_HALFWORD) +#define ARM_ARCH_V4T (ARM_ARCH_V4 | ARM_THUMB) +#define ARM_ARCH_V5 (ARM_ARCH_V4 | ARM_EXT_V5) +#define ARM_ARCH_V5T (ARM_ARCH_V5 | ARM_THUMB) + +/* Some useful combinations: */ +#define ARM_ANY 0x00ffffff +#define ARM_2UP (ARM_ANY - ARM_1) +#define ARM_ALL ARM_2UP /* Not arm1 only */ +#define ARM_3UP 0x00fffffc +#define ARM_6UP 0x00fffff8 /* Includes ARM7 */ + +#define FPU_CORE 0x80000000 +#define FPU_FPA10 0x40000000 +#define FPU_FPA11 0x40000000 +#define FPU_NONE 0 + +/* Some useful combinations */ +#define FPU_ALL 0xff000000 /* Note this is ~ARM_ANY */ +#define FPU_MEMMULTI 0x7f000000 /* Not fpu_core */ + + +#ifndef CPU_DEFAULT +#if defined __thumb__ +#define CPU_DEFAULT (ARM_ARCH_V4 | ARM_THUMB) +#else +#define CPU_DEFAULT ARM_ALL +#endif +#endif + +#ifndef FPU_DEFAULT +#define FPU_DEFAULT FPU_ALL +#endif + +#define streq(a, b) (strcmp (a, b) == 0) +#define skip_whitespace(str) while (* (str) == ' ') ++ (str) + +static unsigned long cpu_variant = CPU_DEFAULT | FPU_DEFAULT; +static int target_oabi = 0; + +#if defined OBJ_COFF || defined OBJ_ELF +/* Flags stored in private area of BFD structure */ +static boolean uses_apcs_26 = false; +static boolean support_interwork = false; +static boolean uses_apcs_float = false; +static boolean pic_code = false; +#endif + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful. */ +CONST char comment_chars[] = "@"; + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output. */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that comments like this one will always work. */ +CONST char line_comment_chars[] = "#"; + +#ifdef TE_LINUX +CONST char line_separator_chars[] = ";"; +#else +CONST char line_separator_chars[] = ""; +#endif + +/* Chars that can be used to separate mant + from exp in floating point numbers. */ +CONST char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ + +CONST char FLT_CHARS[] = "rRsSfFdDxXeEpP"; + +/* Prefix characters that indicate the start of an immediate + value. */ +#define is_immediate_prefix(C) ((C) == '#' || (C) == '$') + +#ifdef OBJ_ELF +symbolS * GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ +#endif + +CONST int md_reloc_size = 8; /* Size of relocation record */ + +static int thumb_mode = 0; /* 0: assemble for ARM, 1: assemble for Thumb, + 2: assemble for Thumb even though target cpu + does not support thumb instructions. */ +typedef struct arm_fix +{ + int thumb_mode; +} arm_fix_data; + +struct arm_it +{ + CONST char * error; + unsigned long instruction; + int suffix; + int size; + struct + { + bfd_reloc_code_real_type type; + expressionS exp; + int pc_rel; + } reloc; +}; + +struct arm_it inst; + +struct asm_shift +{ + CONST char * template; + unsigned long value; +}; + +static CONST struct asm_shift shift[] = +{ + {"asl", 0}, + {"lsl", 0}, + {"lsr", 0x00000020}, + {"asr", 0x00000040}, + {"ror", 0x00000060}, + {"rrx", 0x00000060}, + {"ASL", 0}, + {"LSL", 0}, + {"LSR", 0x00000020}, + {"ASR", 0x00000040}, + {"ROR", 0x00000060}, + {"RRX", 0x00000060} +}; + +#define NO_SHIFT_RESTRICT 1 +#define SHIFT_RESTRICT 0 + +#define NUM_FLOAT_VALS 8 + +CONST char * fp_const[] = +{ + "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0 +}; + +/* Number of littlenums required to hold an extended precision number. */ +#define MAX_LITTLENUMS 6 + +LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS]; + +#define FAIL (-1) +#define SUCCESS (0) + +#define SUFF_S 1 +#define SUFF_D 2 +#define SUFF_E 3 +#define SUFF_P 4 + +#define CP_T_X 0x00008000 +#define CP_T_Y 0x00400000 +#define CP_T_Pre 0x01000000 +#define CP_T_UD 0x00800000 +#define CP_T_WB 0x00200000 + +#define CONDS_BIT (0x00100000) +#define LOAD_BIT (0x00100000) +#define TRANS_BIT (0x00200000) + +struct asm_cond +{ + CONST char * template; + unsigned long value; +}; + +/* This is to save a hash look-up in the common case. */ +#define COND_ALWAYS 0xe0000000 + +static CONST struct asm_cond conds[] = +{ + {"eq", 0x00000000}, + {"ne", 0x10000000}, + {"cs", 0x20000000}, {"hs", 0x20000000}, + {"cc", 0x30000000}, {"ul", 0x30000000}, {"lo", 0x30000000}, + {"mi", 0x40000000}, + {"pl", 0x50000000}, + {"vs", 0x60000000}, + {"vc", 0x70000000}, + {"hi", 0x80000000}, + {"ls", 0x90000000}, + {"ge", 0xa0000000}, + {"lt", 0xb0000000}, + {"gt", 0xc0000000}, + {"le", 0xd0000000}, + {"al", 0xe0000000}, + {"nv", 0xf0000000} +}; + +/* Warning: If the top bit of the set_bits is set, then the standard + instruction bitmask is ignored, and the new bitmask is taken from + the set_bits: */ +struct asm_flg +{ + CONST char * template; /* Basic flag string */ + unsigned long set_bits; /* Bits to set */ +}; + +static CONST struct asm_flg s_flag[] = +{ + {"s", CONDS_BIT}, + {NULL, 0} +}; + +static CONST struct asm_flg ldr_flags[] = +{ + {"b", 0x00400000}, + {"t", TRANS_BIT}, + {"bt", 0x00400000 | TRANS_BIT}, + {"h", 0x801000b0}, + {"sh", 0x801000f0}, + {"sb", 0x801000d0}, + {NULL, 0} +}; + +static CONST struct asm_flg str_flags[] = +{ + {"b", 0x00400000}, + {"t", TRANS_BIT}, + {"bt", 0x00400000 | TRANS_BIT}, + {"h", 0x800000b0}, + {NULL, 0} +}; + +static CONST struct asm_flg byte_flag[] = +{ + {"b", 0x00400000}, + {NULL, 0} +}; + +static CONST struct asm_flg cmp_flags[] = +{ + {"s", CONDS_BIT}, + {"p", 0x0010f000}, + {NULL, 0} +}; + +static CONST struct asm_flg ldm_flags[] = +{ + {"ed", 0x01800000}, + {"fd", 0x00800000}, + {"ea", 0x01000000}, + {"fa", 0x08000000}, + {"ib", 0x01800000}, + {"ia", 0x00800000}, + {"db", 0x01000000}, + {"da", 0x08000000}, + {NULL, 0} +}; + +static CONST struct asm_flg stm_flags[] = +{ + {"ed", 0x08000000}, + {"fd", 0x01000000}, + {"ea", 0x00800000}, + {"fa", 0x01800000}, + {"ib", 0x01800000}, + {"ia", 0x00800000}, + {"db", 0x01000000}, + {"da", 0x08000000}, + {NULL, 0} +}; + +static CONST struct asm_flg lfm_flags[] = +{ + {"fd", 0x00800000}, + {"ea", 0x01000000}, + {NULL, 0} +}; + +static CONST struct asm_flg sfm_flags[] = +{ + {"fd", 0x01000000}, + {"ea", 0x00800000}, + {NULL, 0} +}; + +static CONST struct asm_flg round_flags[] = +{ + {"p", 0x00000020}, + {"m", 0x00000040}, + {"z", 0x00000060}, + {NULL, 0} +}; + +/* The implementation of the FIX instruction is broken on some assemblers, + in that it accepts a precision specifier as well as a rounding specifier, + despite the fact that this is meaningless. To be more compatible, we + accept it as well, though of course it does not set any bits. */ +static CONST struct asm_flg fix_flags[] = +{ + {"p", 0x00000020}, + {"m", 0x00000040}, + {"z", 0x00000060}, + {"sp", 0x00000020}, + {"sm", 0x00000040}, + {"sz", 0x00000060}, + {"dp", 0x00000020}, + {"dm", 0x00000040}, + {"dz", 0x00000060}, + {"ep", 0x00000020}, + {"em", 0x00000040}, + {"ez", 0x00000060}, + {NULL, 0} +}; + +static CONST struct asm_flg except_flag[] = +{ + {"e", 0x00400000}, + {NULL, 0} +}; + +static CONST struct asm_flg cplong_flag[] = +{ + {"l", 0x00400000}, + {NULL, 0} +}; + +struct asm_psr +{ + CONST char * template; + boolean cpsr; + unsigned long field; +}; + +#define SPSR_BIT (1 << 22) /* The bit that distnguishes CPSR and SPSR. */ +#define PSR_SHIFT 16 /* How many bits to shift the PSR_xxx bits up by. */ + +#define PSR_c (1 << 0) +#define PSR_x (1 << 1) +#define PSR_s (1 << 2) +#define PSR_f (1 << 3) + +static CONST struct asm_psr psrs[] = +{ + {"CPSR", true, PSR_c | PSR_f}, + {"CPSR_all", true, PSR_c | PSR_f}, + {"SPSR", false, PSR_c | PSR_f}, + {"SPSR_all", false, PSR_c | PSR_f}, + {"CPSR_flg", true, PSR_f}, + {"CPSR_f", true, PSR_f}, + {"SPSR_flg", false, PSR_f}, + {"SPSR_f", false, PSR_f}, + {"CPSR_c", true, PSR_c}, + {"CPSR_ctl", true, PSR_c}, + {"SPSR_c", false, PSR_c}, + {"SPSR_ctl", false, PSR_c}, + {"CPSR_x", true, PSR_x}, + {"CPSR_s", true, PSR_s}, + {"SPSR_x", false, PSR_x}, + {"SPSR_s", false, PSR_s}, + /* For backwards compatability with older toolchain we also + support lower case versions of some of these flags. */ + {"cpsr", true, PSR_c | PSR_f}, + {"cpsr_all", true, PSR_c | PSR_f}, + {"spsr", false, PSR_c | PSR_f}, + {"spsr_all", false, PSR_c | PSR_f}, + {"cpsr_flg", true, PSR_f}, + {"cpsr_f", true, PSR_f}, + {"spsr_flg", false, PSR_f}, + {"spsr_f", false, PSR_f}, + {"cpsr_c", true, PSR_c}, + {"cpsr_ctl", true, PSR_c}, + {"spsr_c", false, PSR_c}, + {"spsr_ctl", false, PSR_c} +}; + +/* Functions called by parser. */ +/* ARM instructions */ +static void do_arit PARAMS ((char *, unsigned long)); +static void do_cmp PARAMS ((char *, unsigned long)); +static void do_mov PARAMS ((char *, unsigned long)); +static void do_ldst PARAMS ((char *, unsigned long)); +static void do_ldmstm PARAMS ((char *, unsigned long)); +static void do_branch PARAMS ((char *, unsigned long)); +static void do_swi PARAMS ((char *, unsigned long)); +/* Pseudo Op codes */ +static void do_adr PARAMS ((char *, unsigned long)); +static void do_adrl PARAMS ((char *, unsigned long)); +static void do_nop PARAMS ((char *, unsigned long)); +/* ARM 2 */ +static void do_mul PARAMS ((char *, unsigned long)); +static void do_mla PARAMS ((char *, unsigned long)); +/* ARM 3 */ +static void do_swap PARAMS ((char *, unsigned long)); +/* ARM 6 */ +static void do_msr PARAMS ((char *, unsigned long)); +static void do_mrs PARAMS ((char *, unsigned long)); +/* ARM 7M */ +static void do_mull PARAMS ((char *, unsigned long)); +/* ARM THUMB */ +static void do_bx PARAMS ((char *, unsigned long)); + + +/* Coprocessor Instructions */ +static void do_cdp PARAMS ((char *, unsigned long)); +static void do_lstc PARAMS ((char *, unsigned long)); +static void do_co_reg PARAMS ((char *, unsigned long)); +static void do_fp_ctrl PARAMS ((char *, unsigned long)); +static void do_fp_ldst PARAMS ((char *, unsigned long)); +static void do_fp_ldmstm PARAMS ((char *, unsigned long)); +static void do_fp_dyadic PARAMS ((char *, unsigned long)); +static void do_fp_monadic PARAMS ((char *, unsigned long)); +static void do_fp_cmp PARAMS ((char *, unsigned long)); +static void do_fp_from_reg PARAMS ((char *, unsigned long)); +static void do_fp_to_reg PARAMS ((char *, unsigned long)); + +static void fix_new_arm PARAMS ((fragS *, int, short, expressionS *, int, int)); +static int arm_reg_parse PARAMS ((char **)); +static CONST struct asm_psr * arm_psr_parse PARAMS ((char **)); +static void symbol_locate PARAMS ((symbolS *, CONST char *, segT, valueT, fragS *)); +static int add_to_lit_pool PARAMS ((void)); +static unsigned validate_immediate PARAMS ((unsigned)); +static unsigned validate_immediate_twopart PARAMS ((unsigned int, unsigned int *)); +static int validate_offset_imm PARAMS ((unsigned int, int)); +static void opcode_select PARAMS ((int)); +static void end_of_line PARAMS ((char *)); +static int reg_required_here PARAMS ((char **, int)); +static int psr_required_here PARAMS ((char **)); +static int co_proc_number PARAMS ((char **)); +static int cp_opc_expr PARAMS ((char **, int, int)); +static int cp_reg_required_here PARAMS ((char **, int)); +static int fp_reg_required_here PARAMS ((char **, int)); +static int cp_address_offset PARAMS ((char **)); +static int cp_address_required_here PARAMS ((char **)); +static int my_get_float_expression PARAMS ((char **)); +static int skip_past_comma PARAMS ((char **)); +static int walk_no_bignums PARAMS ((symbolS *)); +static int negate_data_op PARAMS ((unsigned long *, unsigned long)); +static int data_op2 PARAMS ((char **)); +static int fp_op2 PARAMS ((char **)); +static long reg_list PARAMS ((char **)); +static void thumb_load_store PARAMS ((char *, int, int)); +static int decode_shift PARAMS ((char **, int)); +static int ldst_extend PARAMS ((char **, int)); +static void thumb_add_sub PARAMS ((char *, int)); +static void insert_reg PARAMS ((int)); +static void thumb_shift PARAMS ((char *, int)); +static void thumb_mov_compare PARAMS ((char *, int)); +static void set_constant_flonums PARAMS ((void)); +static valueT md_chars_to_number PARAMS ((char *, int)); +static void insert_reg_alias PARAMS ((char *, int)); +static void output_inst PARAMS ((void)); +#ifdef OBJ_ELF +static bfd_reloc_code_real_type arm_parse_reloc PARAMS ((void)); +#endif + +/* ARM instructions take 4bytes in the object file, Thumb instructions + take 2: */ +#define INSN_SIZE 4 + +/* LONGEST_INST is the longest basic instruction name without conditions or + flags. ARM7M has 4 of length 5. */ + +#define LONGEST_INST 5 + + +struct asm_opcode +{ + CONST char * template; /* Basic string to match */ + unsigned long value; /* Basic instruction code */ + + /* Compulsory suffix that must follow conds. If "", then the + instruction is not conditional and must have no suffix. */ + CONST char * comp_suffix; + + CONST struct asm_flg * flags; /* Bits to toggle if flag 'n' set */ + unsigned long variants; /* Which CPU variants this exists for */ + /* Function to call to parse args */ + void (* parms) PARAMS ((char *, unsigned long)); +}; + +static CONST struct asm_opcode insns[] = +{ +/* ARM Instructions */ + {"and", 0x00000000, NULL, s_flag, ARM_ANY, do_arit}, + {"eor", 0x00200000, NULL, s_flag, ARM_ANY, do_arit}, + {"sub", 0x00400000, NULL, s_flag, ARM_ANY, do_arit}, + {"rsb", 0x00600000, NULL, s_flag, ARM_ANY, do_arit}, + {"add", 0x00800000, NULL, s_flag, ARM_ANY, do_arit}, + {"adc", 0x00a00000, NULL, s_flag, ARM_ANY, do_arit}, + {"sbc", 0x00c00000, NULL, s_flag, ARM_ANY, do_arit}, + {"rsc", 0x00e00000, NULL, s_flag, ARM_ANY, do_arit}, + {"orr", 0x01800000, NULL, s_flag, ARM_ANY, do_arit}, + {"bic", 0x01c00000, NULL, s_flag, ARM_ANY, do_arit}, + {"tst", 0x01000000, NULL, cmp_flags, ARM_ANY, do_cmp}, + {"teq", 0x01200000, NULL, cmp_flags, ARM_ANY, do_cmp}, + {"cmp", 0x01400000, NULL, cmp_flags, ARM_ANY, do_cmp}, + {"cmn", 0x01600000, NULL, cmp_flags, ARM_ANY, do_cmp}, + {"mov", 0x01a00000, NULL, s_flag, ARM_ANY, do_mov}, + {"mvn", 0x01e00000, NULL, s_flag, ARM_ANY, do_mov}, + {"str", 0x04000000, NULL, str_flags, ARM_ANY, do_ldst}, + {"ldr", 0x04100000, NULL, ldr_flags, ARM_ANY, do_ldst}, + {"stm", 0x08000000, NULL, stm_flags, ARM_ANY, do_ldmstm}, + {"ldm", 0x08100000, NULL, ldm_flags, ARM_ANY, do_ldmstm}, + {"swi", 0x0f000000, NULL, NULL, ARM_ANY, do_swi}, +#ifdef TE_WINCE + {"bl", 0x0b000000, NULL, NULL, ARM_ANY, do_branch}, + {"b", 0x0a000000, NULL, NULL, ARM_ANY, do_branch}, +#else + {"bl", 0x0bfffffe, NULL, NULL, ARM_ANY, do_branch}, + {"b", 0x0afffffe, NULL, NULL, ARM_ANY, do_branch}, +#endif + +/* Pseudo ops */ + {"adr", 0x028f0000, NULL, NULL, ARM_ANY, do_adr}, + {"adrl", 0x028f0000, NULL, NULL, ARM_ANY, do_adrl}, + {"nop", 0x01a00000, NULL, NULL, ARM_ANY, do_nop}, + +/* ARM 2 multiplies */ + {"mul", 0x00000090, NULL, s_flag, ARM_2UP, do_mul}, + {"mla", 0x00200090, NULL, s_flag, ARM_2UP, do_mla}, + +/* ARM 3 - swp instructions */ + {"swp", 0x01000090, NULL, byte_flag, ARM_3UP, do_swap}, + +/* ARM 6 Coprocessor instructions */ + {"mrs", 0x010f0000, NULL, NULL, ARM_6UP, do_mrs}, + {"msr", 0x0120f000, NULL, NULL, ARM_6UP, do_msr}, +/* ScottB: our code uses 0x0128f000 for msr. + NickC: but this is wrong because the bits 16 through 19 are + handled by the PSR_xxx defines above. */ + +/* ARM 7M long multiplies - need signed/unsigned flags! */ + {"smull", 0x00c00090, NULL, s_flag, ARM_LONGMUL, do_mull}, + {"umull", 0x00800090, NULL, s_flag, ARM_LONGMUL, do_mull}, + {"smlal", 0x00e00090, NULL, s_flag, ARM_LONGMUL, do_mull}, + {"umlal", 0x00a00090, NULL, s_flag, ARM_LONGMUL, do_mull}, + +/* ARM THUMB interworking */ + {"bx", 0x012fff10, NULL, NULL, ARM_THUMB, do_bx}, + +/* Floating point instructions */ + {"wfs", 0x0e200110, NULL, NULL, FPU_ALL, do_fp_ctrl}, + {"rfs", 0x0e300110, NULL, NULL, FPU_ALL, do_fp_ctrl}, + {"wfc", 0x0e400110, NULL, NULL, FPU_ALL, do_fp_ctrl}, + {"rfc", 0x0e500110, NULL, NULL, FPU_ALL, do_fp_ctrl}, + {"ldf", 0x0c100100, "sdep", NULL, FPU_ALL, do_fp_ldst}, + {"stf", 0x0c000100, "sdep", NULL, FPU_ALL, do_fp_ldst}, + {"lfm", 0x0c100200, NULL, lfm_flags, FPU_MEMMULTI, do_fp_ldmstm}, + {"sfm", 0x0c000200, NULL, sfm_flags, FPU_MEMMULTI, do_fp_ldmstm}, + {"mvf", 0x0e008100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"mnf", 0x0e108100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"abs", 0x0e208100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"rnd", 0x0e308100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"sqt", 0x0e408100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"log", 0x0e508100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"lgn", 0x0e608100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"exp", 0x0e708100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"sin", 0x0e808100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"cos", 0x0e908100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"tan", 0x0ea08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"asn", 0x0eb08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"acs", 0x0ec08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"atn", 0x0ed08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"urd", 0x0ee08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"nrm", 0x0ef08100, "sde", round_flags, FPU_ALL, do_fp_monadic}, + {"adf", 0x0e000100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"suf", 0x0e200100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"rsf", 0x0e300100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"muf", 0x0e100100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"dvf", 0x0e400100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"rdf", 0x0e500100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"pow", 0x0e600100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"rpw", 0x0e700100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"rmf", 0x0e800100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"fml", 0x0e900100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"fdv", 0x0ea00100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"frd", 0x0eb00100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"pol", 0x0ec00100, "sde", round_flags, FPU_ALL, do_fp_dyadic}, + {"cmf", 0x0e90f110, NULL, except_flag, FPU_ALL, do_fp_cmp}, + {"cnf", 0x0eb0f110, NULL, except_flag, FPU_ALL, do_fp_cmp}, +/* The FPA10 data sheet suggests that the 'E' of cmfe/cnfe should not + be an optional suffix, but part of the instruction. To be compatible, + we accept either. */ + {"cmfe", 0x0ed0f110, NULL, NULL, FPU_ALL, do_fp_cmp}, + {"cnfe", 0x0ef0f110, NULL, NULL, FPU_ALL, do_fp_cmp}, + {"flt", 0x0e000110, "sde", round_flags, FPU_ALL, do_fp_from_reg}, + {"fix", 0x0e100110, NULL, fix_flags, FPU_ALL, do_fp_to_reg}, + +/* Generic copressor instructions. */ + {"cdp", 0x0e000000, NULL, NULL, ARM_2UP, do_cdp}, + {"ldc", 0x0c100000, NULL, cplong_flag, ARM_2UP, do_lstc}, + {"stc", 0x0c000000, NULL, cplong_flag, ARM_2UP, do_lstc}, + {"mcr", 0x0e000010, NULL, NULL, ARM_2UP, do_co_reg}, + {"mrc", 0x0e100010, NULL, NULL, ARM_2UP, do_co_reg}, +}; + +/* Defines for various bits that we will want to toggle. */ +#define INST_IMMEDIATE 0x02000000 +#define OFFSET_REG 0x02000000 +#define HWOFFSET_IMM 0x00400000 +#define SHIFT_BY_REG 0x00000010 +#define PRE_INDEX 0x01000000 +#define INDEX_UP 0x00800000 +#define WRITE_BACK 0x00200000 +#define LDM_TYPE_2_OR_3 0x00400000 + +#define LITERAL_MASK 0xf000f000 +#define COND_MASK 0xf0000000 +#define OPCODE_MASK 0xfe1fffff +#define DATA_OP_SHIFT 21 + +/* Codes to distinguish the arithmetic instructions. */ +#define OPCODE_AND 0 +#define OPCODE_EOR 1 +#define OPCODE_SUB 2 +#define OPCODE_RSB 3 +#define OPCODE_ADD 4 +#define OPCODE_ADC 5 +#define OPCODE_SBC 6 +#define OPCODE_RSC 7 +#define OPCODE_TST 8 +#define OPCODE_TEQ 9 +#define OPCODE_CMP 10 +#define OPCODE_CMN 11 +#define OPCODE_ORR 12 +#define OPCODE_MOV 13 +#define OPCODE_BIC 14 +#define OPCODE_MVN 15 + +static void do_t_nop PARAMS ((char *)); +static void do_t_arit PARAMS ((char *)); +static void do_t_add PARAMS ((char *)); +static void do_t_asr PARAMS ((char *)); +static void do_t_branch9 PARAMS ((char *)); +static void do_t_branch12 PARAMS ((char *)); +static void do_t_branch23 PARAMS ((char *)); +static void do_t_bx PARAMS ((char *)); +static void do_t_compare PARAMS ((char *)); +static void do_t_ldmstm PARAMS ((char *)); +static void do_t_ldr PARAMS ((char *)); +static void do_t_ldrb PARAMS ((char *)); +static void do_t_ldrh PARAMS ((char *)); +static void do_t_lds PARAMS ((char *)); +static void do_t_lsl PARAMS ((char *)); +static void do_t_lsr PARAMS ((char *)); +static void do_t_mov PARAMS ((char *)); +static void do_t_push_pop PARAMS ((char *)); +static void do_t_str PARAMS ((char *)); +static void do_t_strb PARAMS ((char *)); +static void do_t_strh PARAMS ((char *)); +static void do_t_sub PARAMS ((char *)); +static void do_t_swi PARAMS ((char *)); +static void do_t_adr PARAMS ((char *)); + +#define T_OPCODE_MUL 0x4340 +#define T_OPCODE_TST 0x4200 +#define T_OPCODE_CMN 0x42c0 +#define T_OPCODE_NEG 0x4240 +#define T_OPCODE_MVN 0x43c0 + +#define T_OPCODE_ADD_R3 0x1800 +#define T_OPCODE_SUB_R3 0x1a00 +#define T_OPCODE_ADD_HI 0x4400 +#define T_OPCODE_ADD_ST 0xb000 +#define T_OPCODE_SUB_ST 0xb080 +#define T_OPCODE_ADD_SP 0xa800 +#define T_OPCODE_ADD_PC 0xa000 +#define T_OPCODE_ADD_I8 0x3000 +#define T_OPCODE_SUB_I8 0x3800 +#define T_OPCODE_ADD_I3 0x1c00 +#define T_OPCODE_SUB_I3 0x1e00 + +#define T_OPCODE_ASR_R 0x4100 +#define T_OPCODE_LSL_R 0x4080 +#define T_OPCODE_LSR_R 0x40c0 +#define T_OPCODE_ASR_I 0x1000 +#define T_OPCODE_LSL_I 0x0000 +#define T_OPCODE_LSR_I 0x0800 + +#define T_OPCODE_MOV_I8 0x2000 +#define T_OPCODE_CMP_I8 0x2800 +#define T_OPCODE_CMP_LR 0x4280 +#define T_OPCODE_MOV_HR 0x4600 +#define T_OPCODE_CMP_HR 0x4500 + +#define T_OPCODE_LDR_PC 0x4800 +#define T_OPCODE_LDR_SP 0x9800 +#define T_OPCODE_STR_SP 0x9000 +#define T_OPCODE_LDR_IW 0x6800 +#define T_OPCODE_STR_IW 0x6000 +#define T_OPCODE_LDR_IH 0x8800 +#define T_OPCODE_STR_IH 0x8000 +#define T_OPCODE_LDR_IB 0x7800 +#define T_OPCODE_STR_IB 0x7000 +#define T_OPCODE_LDR_RW 0x5800 +#define T_OPCODE_STR_RW 0x5000 +#define T_OPCODE_LDR_RH 0x5a00 +#define T_OPCODE_STR_RH 0x5200 +#define T_OPCODE_LDR_RB 0x5c00 +#define T_OPCODE_STR_RB 0x5400 + +#define T_OPCODE_PUSH 0xb400 +#define T_OPCODE_POP 0xbc00 + +#define T_OPCODE_BRANCH 0xe7fe + +static int thumb_reg PARAMS ((char ** str, int hi_lo)); + +#define THUMB_SIZE 2 /* Size of thumb instruction. */ +#define THUMB_REG_LO 0x1 +#define THUMB_REG_HI 0x2 +#define THUMB_REG_ANY 0x3 + +#define THUMB_H1 0x0080 +#define THUMB_H2 0x0040 + +#define THUMB_ASR 0 +#define THUMB_LSL 1 +#define THUMB_LSR 2 + +#define THUMB_MOVE 0 +#define THUMB_COMPARE 1 + +#define THUMB_LOAD 0 +#define THUMB_STORE 1 + +#define THUMB_PP_PC_LR 0x0100 + +/* These three are used for immediate shifts, do not alter. */ +#define THUMB_WORD 2 +#define THUMB_HALFWORD 1 +#define THUMB_BYTE 0 + +struct thumb_opcode +{ + CONST char * template; /* Basic string to match */ + unsigned long value; /* Basic instruction code */ + int size; + unsigned long variants; /* Which CPU variants this exists for */ + void (* parms) PARAMS ((char *)); /* Function to call to parse args */ +}; + +static CONST struct thumb_opcode tinsns[] = +{ + {"adc", 0x4140, 2, ARM_THUMB, do_t_arit}, + {"add", 0x0000, 2, ARM_THUMB, do_t_add}, + {"and", 0x4000, 2, ARM_THUMB, do_t_arit}, + {"asr", 0x0000, 2, ARM_THUMB, do_t_asr}, + {"b", T_OPCODE_BRANCH, 2, ARM_THUMB, do_t_branch12}, + {"beq", 0xd0fe, 2, ARM_THUMB, do_t_branch9}, + {"bne", 0xd1fe, 2, ARM_THUMB, do_t_branch9}, + {"bcs", 0xd2fe, 2, ARM_THUMB, do_t_branch9}, + {"bhs", 0xd2fe, 2, ARM_THUMB, do_t_branch9}, + {"bcc", 0xd3fe, 2, ARM_THUMB, do_t_branch9}, + {"bul", 0xd3fe, 2, ARM_THUMB, do_t_branch9}, + {"blo", 0xd3fe, 2, ARM_THUMB, do_t_branch9}, + {"bmi", 0xd4fe, 2, ARM_THUMB, do_t_branch9}, + {"bpl", 0xd5fe, 2, ARM_THUMB, do_t_branch9}, + {"bvs", 0xd6fe, 2, ARM_THUMB, do_t_branch9}, + {"bvc", 0xd7fe, 2, ARM_THUMB, do_t_branch9}, + {"bhi", 0xd8fe, 2, ARM_THUMB, do_t_branch9}, + {"bls", 0xd9fe, 2, ARM_THUMB, do_t_branch9}, + {"bge", 0xdafe, 2, ARM_THUMB, do_t_branch9}, + {"blt", 0xdbfe, 2, ARM_THUMB, do_t_branch9}, + {"bgt", 0xdcfe, 2, ARM_THUMB, do_t_branch9}, + {"ble", 0xddfe, 2, ARM_THUMB, do_t_branch9}, + {"bal", 0xdefe, 2, ARM_THUMB, do_t_branch9}, + {"bic", 0x4380, 2, ARM_THUMB, do_t_arit}, + {"bl", 0xf7fffffe, 4, ARM_THUMB, do_t_branch23}, + {"bx", 0x4700, 2, ARM_THUMB, do_t_bx}, + {"cmn", T_OPCODE_CMN, 2, ARM_THUMB, do_t_arit}, + {"cmp", 0x0000, 2, ARM_THUMB, do_t_compare}, + {"eor", 0x4040, 2, ARM_THUMB, do_t_arit}, + {"ldmia", 0xc800, 2, ARM_THUMB, do_t_ldmstm}, + {"ldr", 0x0000, 2, ARM_THUMB, do_t_ldr}, + {"ldrb", 0x0000, 2, ARM_THUMB, do_t_ldrb}, + {"ldrh", 0x0000, 2, ARM_THUMB, do_t_ldrh}, + {"ldrsb", 0x5600, 2, ARM_THUMB, do_t_lds}, + {"ldrsh", 0x5e00, 2, ARM_THUMB, do_t_lds}, + {"ldsb", 0x5600, 2, ARM_THUMB, do_t_lds}, + {"ldsh", 0x5e00, 2, ARM_THUMB, do_t_lds}, + {"lsl", 0x0000, 2, ARM_THUMB, do_t_lsl}, + {"lsr", 0x0000, 2, ARM_THUMB, do_t_lsr}, + {"mov", 0x0000, 2, ARM_THUMB, do_t_mov}, + {"mul", T_OPCODE_MUL, 2, ARM_THUMB, do_t_arit}, + {"mvn", T_OPCODE_MVN, 2, ARM_THUMB, do_t_arit}, + {"neg", T_OPCODE_NEG, 2, ARM_THUMB, do_t_arit}, + {"orr", 0x4300, 2, ARM_THUMB, do_t_arit}, + {"pop", 0xbc00, 2, ARM_THUMB, do_t_push_pop}, + {"push", 0xb400, 2, ARM_THUMB, do_t_push_pop}, + {"ror", 0x41c0, 2, ARM_THUMB, do_t_arit}, + {"sbc", 0x4180, 2, ARM_THUMB, do_t_arit}, + {"stmia", 0xc000, 2, ARM_THUMB, do_t_ldmstm}, + {"str", 0x0000, 2, ARM_THUMB, do_t_str}, + {"strb", 0x0000, 2, ARM_THUMB, do_t_strb}, + {"strh", 0x0000, 2, ARM_THUMB, do_t_strh}, + {"swi", 0xdf00, 2, ARM_THUMB, do_t_swi}, + {"sub", 0x0000, 2, ARM_THUMB, do_t_sub}, + {"tst", T_OPCODE_TST, 2, ARM_THUMB, do_t_arit}, + /* Pseudo ops: */ + {"adr", 0x0000, 2, ARM_THUMB, do_t_adr}, + {"nop", 0x46C0, 2, ARM_THUMB, do_t_nop}, /* mov r8,r8 */ +}; + +struct reg_entry +{ + CONST char * name; + int number; +}; + +#define int_register(reg) ((reg) >= 0 && (reg) <= 15) +#define cp_register(reg) ((reg) >= 32 && (reg) <= 47) +#define fp_register(reg) ((reg) >= 16 && (reg) <= 23) + +#define REG_PC 15 +#define REG_LR 14 +#define REG_SP 13 + +/* These are the standard names. Users can add aliases with .req */ +static CONST struct reg_entry reg_table[] = +{ + /* Processor Register Numbers. */ + {"r0", 0}, {"r1", 1}, {"r2", 2}, {"r3", 3}, + {"r4", 4}, {"r5", 5}, {"r6", 6}, {"r7", 7}, + {"r8", 8}, {"r9", 9}, {"r10", 10}, {"r11", 11}, + {"r12", 12}, {"r13", REG_SP},{"r14", REG_LR},{"r15", REG_PC}, + /* APCS conventions. */ + {"a1", 0}, {"a2", 1}, {"a3", 2}, {"a4", 3}, + {"v1", 4}, {"v2", 5}, {"v3", 6}, {"v4", 7}, {"v5", 8}, + {"v6", 9}, {"sb", 9}, {"v7", 10}, {"sl", 10}, + {"fp", 11}, {"ip", 12}, {"sp", REG_SP},{"lr", REG_LR},{"pc", REG_PC}, + /* ATPCS additions to APCS conventions. */ + {"wr", 7}, {"v8", 11}, + /* FP Registers. */ + {"f0", 16}, {"f1", 17}, {"f2", 18}, {"f3", 19}, + {"f4", 20}, {"f5", 21}, {"f6", 22}, {"f7", 23}, + {"c0", 32}, {"c1", 33}, {"c2", 34}, {"c3", 35}, + {"c4", 36}, {"c5", 37}, {"c6", 38}, {"c7", 39}, + {"c8", 40}, {"c9", 41}, {"c10", 42}, {"c11", 43}, + {"c12", 44}, {"c13", 45}, {"c14", 46}, {"c15", 47}, + {"cr0", 32}, {"cr1", 33}, {"cr2", 34}, {"cr3", 35}, + {"cr4", 36}, {"cr5", 37}, {"cr6", 38}, {"cr7", 39}, + {"cr8", 40}, {"cr9", 41}, {"cr10", 42}, {"cr11", 43}, + {"cr12", 44}, {"cr13", 45}, {"cr14", 46}, {"cr15", 47}, + /* ATPCS additions to float register names. */ + {"s0",16}, {"s1",17}, {"s2",18}, {"s3",19}, + {"s4",20}, {"s5",21}, {"s6",22}, {"s7",23}, + {"d0",16}, {"d1",17}, {"d2",18}, {"d3",19}, + {"d4",20}, {"d5",21}, {"d6",22}, {"d7",23}, + /* FIXME: At some point we need to add VFP register names. */ + /* Array terminator. */ + {NULL, 0} +}; + +#define BAD_ARGS _("Bad arguments to instruction") +#define BAD_PC _("r15 not allowed here") +#define BAD_FLAGS _("Instruction should not have flags") +#define BAD_COND _("Instruction is not conditional") + +static struct hash_control * arm_ops_hsh = NULL; +static struct hash_control * arm_tops_hsh = NULL; +static struct hash_control * arm_cond_hsh = NULL; +static struct hash_control * arm_shift_hsh = NULL; +static struct hash_control * arm_reg_hsh = NULL; +static struct hash_control * arm_psr_hsh = NULL; + +/* This table describes all the machine specific pseudo-ops the assembler + has to support. The fields are: + pseudo-op name without dot + function to call to execute this pseudo-op + Integer arg to pass to the function. */ + +static void s_req PARAMS ((int)); +static void s_align PARAMS ((int)); +static void s_bss PARAMS ((int)); +static void s_even PARAMS ((int)); +static void s_ltorg PARAMS ((int)); +static void s_arm PARAMS ((int)); +static void s_thumb PARAMS ((int)); +static void s_code PARAMS ((int)); +static void s_force_thumb PARAMS ((int)); +static void s_thumb_func PARAMS ((int)); +static void s_thumb_set PARAMS ((int)); +static void arm_s_text PARAMS ((int)); +static void arm_s_data PARAMS ((int)); +#ifdef OBJ_ELF +static void arm_s_section PARAMS ((int)); +static void s_arm_elf_cons PARAMS ((int)); +#endif + +static int my_get_expression PARAMS ((expressionS *, char **)); + +CONST pseudo_typeS md_pseudo_table[] = +{ + { "req", s_req, 0 }, /* Never called becasue '.req' does not start line */ + { "bss", s_bss, 0 }, + { "align", s_align, 0 }, + { "arm", s_arm, 0 }, + { "thumb", s_thumb, 0 }, + { "code", s_code, 0 }, + { "force_thumb", s_force_thumb, 0 }, + { "thumb_func", s_thumb_func, 0 }, + { "thumb_set", s_thumb_set, 0 }, + { "even", s_even, 0 }, + { "ltorg", s_ltorg, 0 }, + { "pool", s_ltorg, 0 }, + /* Allow for the effect of section changes. */ + { "text", arm_s_text, 0 }, + { "data", arm_s_data, 0 }, +#ifdef OBJ_ELF + { "section", arm_s_section, 0 }, + { "section.s", arm_s_section, 0 }, + { "sect", arm_s_section, 0 }, + { "sect.s", arm_s_section, 0 }, + { "word", s_arm_elf_cons, 4 }, + { "long", s_arm_elf_cons, 4 }, +#else + { "word", cons, 4}, +#endif + { "extend", float_cons, 'x' }, + { "ldouble", float_cons, 'x' }, + { "packed", float_cons, 'p' }, + { 0, 0, 0 } +}; + +/* Stuff needed to resolve the label ambiguity + As: + ... + label: + may differ from: + ... + label: + +*/ + +symbolS * last_label_seen; +static int label_is_thumb_function_name = false; + +/* Literal stuff */ + +#define MAX_LITERAL_POOL_SIZE 1024 + +typedef struct literalS +{ + struct expressionS exp; + struct arm_it * inst; +} literalT; + +literalT literals[MAX_LITERAL_POOL_SIZE]; +int next_literal_pool_place = 0; /* Next free entry in the pool */ +int lit_pool_num = 1; /* Next literal pool number */ +symbolS * current_poolP = NULL; + +static int +add_to_lit_pool () +{ + int lit_count = 0; + + if (current_poolP == NULL) + current_poolP = symbol_create (FAKE_LABEL_NAME, undefined_section, + (valueT) 0, &zero_address_frag); + + /* Check if this literal value is already in the pool: */ + while (lit_count < next_literal_pool_place) + { + if (literals[lit_count].exp.X_op == inst.reloc.exp.X_op + && inst.reloc.exp.X_op == O_constant + && literals[lit_count].exp.X_add_number + == inst.reloc.exp.X_add_number + && literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned) + break; + lit_count++; + } + + if (lit_count == next_literal_pool_place) /* new entry */ + { + if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE) + { + inst.error = _("Literal Pool Overflow"); + return FAIL; + } + + literals[next_literal_pool_place].exp = inst.reloc.exp; + lit_count = next_literal_pool_place++; + } + + inst.reloc.exp.X_op = O_symbol; + inst.reloc.exp.X_add_number = (lit_count) * 4 - 8; + inst.reloc.exp.X_add_symbol = current_poolP; + + return SUCCESS; +} + +/* Can't use symbol_new here, so have to create a symbol and then at + a later date assign it a value. Thats what these functions do. */ +static void +symbol_locate (symbolP, name, segment, valu, frag) + symbolS * symbolP; + CONST char * name; /* It is copied, the caller can modify */ + segT segment; /* Segment identifier (SEG_) */ + valueT valu; /* Symbol value */ + fragS * frag; /* Associated fragment */ +{ + unsigned int name_length; + char * preserved_copy_of_name; + + name_length = strlen (name) + 1; /* +1 for \0 */ + obstack_grow (¬es, name, name_length); + preserved_copy_of_name = obstack_finish (¬es); +#ifdef STRIP_UNDERSCORE + if (preserved_copy_of_name[0] == '_') + preserved_copy_of_name++; +#endif + +#ifdef tc_canonicalize_symbol_name + preserved_copy_of_name = + tc_canonicalize_symbol_name (preserved_copy_of_name); +#endif + + S_SET_NAME (symbolP, preserved_copy_of_name); + + S_SET_SEGMENT (symbolP, segment); + S_SET_VALUE (symbolP, valu); + symbol_clear_list_pointers(symbolP); + + symbol_set_frag (symbolP, frag); + + /* Link to end of symbol chain. */ + { + extern int symbol_table_frozen; + if (symbol_table_frozen) + abort (); + } + + symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP); + + obj_symbol_new_hook (symbolP); + +#ifdef tc_symbol_new_hook + tc_symbol_new_hook (symbolP); +#endif + +#ifdef DEBUG_SYMS + verify_symbol_chain (symbol_rootP, symbol_lastP); +#endif /* DEBUG_SYMS */ +} + +/* Check that an immediate is valid, and if so, + convert it to the right format. */ +static unsigned int +validate_immediate (val) + unsigned int val; +{ + unsigned int a; + unsigned int i; + +#define rotate_left(v, n) (v << n | v >> (32 - n)) + + for (i = 0; i < 32; i += 2) + if ((a = rotate_left (val, i)) <= 0xff) + return a | (i << 7); /* 12-bit pack: [shift-cnt,const] */ + + return FAIL; +} + +/* Check to see if an immediate can be computed as two seperate immediate + values, added together. We already know that this value cannot be + computed by just one ARM instruction. */ +static unsigned int +validate_immediate_twopart (val, highpart) + unsigned int val; + unsigned int * highpart; +{ + unsigned int a; + unsigned int i; + + for (i = 0; i < 32; i += 2) + if (((a = rotate_left (val, i)) & 0xff) != 0) + { + if (a & 0xff00) + { + if (a & ~ 0xffff) + continue; + * highpart = (a >> 8) | ((i + 24) << 7); + } + else if (a & 0xff0000) + { + if (a & 0xff000000) + continue; + + * highpart = (a >> 16) | ((i + 16) << 7); + } + else + { + assert (a & 0xff000000); + + * highpart = (a >> 24) | ((i + 8) << 7); + } + + return (a & 0xff) | (i << 7); + } + + return FAIL; +} + +static int +validate_offset_imm (val, hwse) + unsigned int val; + int hwse; +{ + if ((hwse && val > 255) || val > 4095) + return FAIL; + return val; +} + + +static void +s_req (a) + int a ATTRIBUTE_UNUSED; +{ + as_bad (_("Invalid syntax for .req directive.")); +} + +static void +s_bss (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + /* We don't support putting frags in the BSS segment, we fake it by + marking in_bss, then looking at s_skip for clues?.. */ + subseg_set (bss_section, 0); + demand_empty_rest_of_line (); +} + +static void +s_even (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + if (!need_pass_2) /* Never make frag if expect extra pass. */ + frag_align (1, 0, 0); + + record_alignment (now_seg, 1); + + demand_empty_rest_of_line (); +} + +static void +s_ltorg (ignored) + int ignored ATTRIBUTE_UNUSED; +{ + int lit_count = 0; + char sym_name[20]; + + if (current_poolP == NULL) + return; + + /* Align pool as you have word accesses */ + /* Only make a frag if we have to ... */ + if (!need_pass_2) + frag_align (2, 0, 0); + + record_alignment (now_seg, 2); + + sprintf (sym_name, "$$lit_\002%x", lit_pool_num++); + + symbol_locate (current_poolP, sym_name, now_seg, + (valueT) frag_now_fix (), frag_now); + symbol_table_insert (current_poolP); + + ARM_SET_THUMB (current_poolP, thumb_mode); + +#if defined OBJ_COFF || defined OBJ_ELF + ARM_SET_INTERWORK (current_poolP, support_interwork); +#endif + + while (lit_count < next_literal_pool_place) + /* First output the expression in the instruction to the pool. */ + emit_expr (&(literals[lit_count++].exp), 4); /* .word */ + + next_literal_pool_place = 0; + current_poolP = NULL; +} + +static void +s_align (unused) /* Same as s_align_ptwo but align 0 => align 2 */ + int unused ATTRIBUTE_UNUSED; +{ + register int temp; + register long temp_fill; + long max_alignment = 15; + + temp = get_absolute_expression (); + if (temp > max_alignment) + as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment); + else if (temp < 0) + { + as_bad (_("Alignment negative. 0 assumed.")); + temp = 0; + } + + if (*input_line_pointer == ',') + { + input_line_pointer++; + temp_fill = get_absolute_expression (); + } + else + temp_fill = 0; + + if (!temp) + temp = 2; + + /* Only make a frag if we HAVE to. . . */ + if (temp && !need_pass_2) + frag_align (temp, (int) temp_fill, 0); + demand_empty_rest_of_line (); + + record_alignment (now_seg, temp); +} + +static void +s_force_thumb (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + /* If we are not already in thumb mode go into it, EVEN if + the target processor does not support thumb instructions. + This is used by gcc/config/arm/lib1funcs.asm for example + to compile interworking support functions even if the + target processor should not support interworking. */ + + if (! thumb_mode) + { + thumb_mode = 2; + + record_alignment (now_seg, 1); + } + + demand_empty_rest_of_line (); +} + +static void +s_thumb_func (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + /* The following label is the name/address of the start of a Thumb function. + We need to know this for the interworking support. */ + + label_is_thumb_function_name = true; + + demand_empty_rest_of_line (); +} + +/* Perform a .set directive, but also mark the alias as + being a thumb function. */ + +static void +s_thumb_set (equiv) + int equiv; +{ + /* XXX the following is a duplicate of the code for s_set() in read.c + We cannot just call that code as we need to get at the symbol that + is created. */ + register char * name; + register char delim; + register char * end_name; + register symbolS * symbolP; + + /* + * Especial apologies for the random logic: + * this just grew, and could be parsed much more simply! + * Dean in haste. + */ + name = input_line_pointer; + delim = get_symbol_end (); + end_name = input_line_pointer; + *end_name = delim; + + SKIP_WHITESPACE (); + + if (*input_line_pointer != ',') + { + *end_name = 0; + as_bad (_("Expected comma after name \"%s\""), name); + *end_name = delim; + ignore_rest_of_line (); + return; + } + + input_line_pointer++; + *end_name = 0; + + if (name[0] == '.' && name[1] == '\0') + { + /* XXX - this should not happen to .thumb_set */ + abort (); + } + + if ((symbolP = symbol_find (name)) == NULL + && (symbolP = md_undefined_symbol (name)) == NULL) + { +#ifndef NO_LISTING + /* When doing symbol listings, play games with dummy fragments living + outside the normal fragment chain to record the file and line info + for this symbol. */ + if (listing & LISTING_SYMBOLS) + { + extern struct list_info_struct * listing_tail; + fragS * dummy_frag = (fragS *) xmalloc (sizeof(fragS)); + memset (dummy_frag, 0, sizeof(fragS)); + dummy_frag->fr_type = rs_fill; + dummy_frag->line = listing_tail; + symbolP = symbol_new (name, undefined_section, 0, dummy_frag); + dummy_frag->fr_symbol = symbolP; + } + else +#endif + symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag); + +#ifdef OBJ_COFF + /* "set" symbols are local unless otherwise specified. */ + SF_SET_LOCAL (symbolP); +#endif /* OBJ_COFF */ + } /* make a new symbol */ + + symbol_table_insert (symbolP); + + * end_name = delim; + + if (equiv + && S_IS_DEFINED (symbolP) + && S_GET_SEGMENT (symbolP) != reg_section) + as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP)); + + pseudo_set (symbolP); + + demand_empty_rest_of_line (); + + /* XXX Now we come to the Thumb specific bit of code. */ + + THUMB_SET_FUNC (symbolP, 1); + ARM_SET_THUMB (symbolP, 1); +#if defined OBJ_ELF || defined OBJ_COFF + ARM_SET_INTERWORK (symbolP, support_interwork); +#endif +} + +/* If we change section we must dump the literal pool first. */ +static void +arm_s_text (ignore) + int ignore; +{ + if (now_seg != text_section) + s_ltorg (0); + +#ifdef OBJ_ELF + obj_elf_text (ignore); +#else + s_text (ignore); +#endif +} + +static void +arm_s_data (ignore) + int ignore; +{ + if (flag_readonly_data_in_text) + { + if (now_seg != text_section) + s_ltorg (0); + } + else if (now_seg != data_section) + s_ltorg (0); + +#ifdef OBJ_ELF + obj_elf_data (ignore); +#else + s_data (ignore); +#endif +} + +#ifdef OBJ_ELF +static void +arm_s_section (ignore) + int ignore; +{ + s_ltorg (0); + + obj_elf_section (ignore); +} +#endif + +static void +opcode_select (width) + int width; +{ + switch (width) + { + case 16: + if (! thumb_mode) + { + if (! (cpu_variant & ARM_THUMB)) + as_bad (_("selected processor does not support THUMB opcodes")); + thumb_mode = 1; + /* No need to force the alignment, since we will have been + coming from ARM mode, which is word-aligned. */ + record_alignment (now_seg, 1); + } + break; + + case 32: + if (thumb_mode) + { + if ((cpu_variant & ARM_ANY) == ARM_THUMB) + as_bad (_("selected processor does not support ARM opcodes")); + thumb_mode = 0; + if (!need_pass_2) + frag_align (2, 0, 0); + record_alignment (now_seg, 1); + } + break; + + default: + as_bad (_("invalid instruction size selected (%d)"), width); + } +} + +static void +s_arm (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + opcode_select (32); + demand_empty_rest_of_line (); +} + +static void +s_thumb (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + opcode_select (16); + demand_empty_rest_of_line (); +} + +static void +s_code (unused) + int unused ATTRIBUTE_UNUSED; +{ + register int temp; + + temp = get_absolute_expression (); + switch (temp) + { + case 16: + case 32: + opcode_select (temp); + break; + + default: + as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp); + } +} + +static void +end_of_line (str) + char * str; +{ + skip_whitespace (str); + + if (* str != '\0') + inst.error = _("Garbage following instruction"); +} + +static int +skip_past_comma (str) + char ** str; +{ + char *p = *str, c; + int comma = 0; + + while ((c = *p) == ' ' || c == ',') + { + p++; + if (c == ',' && comma++) + return FAIL; + } + + if (c == '\0') + return FAIL; + + *str = p; + return comma ? SUCCESS : FAIL; +} + +/* A standard register must be given at this point. + Shift is the place to put it in inst.instruction. + Restores input start point on err. + Returns the reg#, or FAIL. */ +static int +reg_required_here (str, shift) + char ** str; + int shift; +{ + static char buff [128]; /* XXX */ + int reg; + char * start = *str; + + if ((reg = arm_reg_parse (str)) != FAIL && int_register (reg)) + { + if (shift >= 0) + inst.instruction |= reg << shift; + return reg; + } + + /* Restore the start point, we may have got a reg of the wrong class. */ + *str = start; + + /* In the few cases where we might be able to accept something else + this error can be overridden. */ + sprintf (buff, _("Register expected, not '%.100s'"), start); + inst.error = buff; + + return FAIL; +} + +static CONST struct asm_psr * +arm_psr_parse (ccp) + register char ** ccp; +{ + char * start = * ccp; + char c; + char * p; + CONST struct asm_psr * psr; + + p = start; + + /* Skip to the end of the next word in the input stream. */ + do + { + c = *p++; + } + while (isalpha (c) || c == '_'); + + /* Terminate the word. */ + *--p = 0; + + /* Now locate the word in the psr hash table. */ + psr = (CONST struct asm_psr *) hash_find (arm_psr_hsh, start); + + /* Restore the input stream. */ + *p = c; + + /* If we found a valid match, advance the + stream pointer past the end of the word. */ + *ccp = p; + + return psr; +} + +/* Parse the input looking for a PSR flag. */ +static int +psr_required_here (str) + char ** str; +{ + char * start = *str; + CONST struct asm_psr * psr; + + psr = arm_psr_parse (str); + + if (psr) + { + /* If this is the SPSR that is being modified, set the R bit. */ + if (! psr->cpsr) + inst.instruction |= SPSR_BIT; + + /* Set the psr flags in the MSR instruction. */ + inst.instruction |= psr->field << PSR_SHIFT; + + return SUCCESS; + } + + /* In the few cases where we might be able to accept + something else this error can be overridden. */ + inst.error = _("flag for {c}psr instruction expected"); + + /* Restore the start point. */ + *str = start; + return FAIL; +} + +static int +co_proc_number (str) + char ** str; +{ + int processor, pchar; + + skip_whitespace (* str); + + /* The data sheet seems to imply that just a number on its own is valid + here, but the RISC iX assembler seems to accept a prefix 'p'. We will + accept either. */ + if (**str == 'p' || **str == 'P') + (*str)++; + + pchar = *(*str)++; + if (pchar >= '0' && pchar <= '9') + { + processor = pchar - '0'; + if (**str >= '0' && **str <= '9') + { + processor = processor * 10 + *(*str)++ - '0'; + if (processor > 15) + { + inst.error = _("Illegal co-processor number"); + return FAIL; + } + } + } + else + { + inst.error = _("Bad or missing co-processor number"); + return FAIL; + } + + inst.instruction |= processor << 8; + return SUCCESS; +} + +static int +cp_opc_expr (str, where, length) + char ** str; + int where; + int length; +{ + expressionS expr; + + skip_whitespace (* str); + + memset (&expr, '\0', sizeof (expr)); + + if (my_get_expression (&expr, str)) + return FAIL; + if (expr.X_op != O_constant) + { + inst.error = _("bad or missing expression"); + return FAIL; + } + + if ((expr.X_add_number & ((1 << length) - 1)) != expr.X_add_number) + { + inst.error = _("immediate co-processor expression too large"); + return FAIL; + } + + inst.instruction |= expr.X_add_number << where; + return SUCCESS; +} + +static int +cp_reg_required_here (str, where) + char ** str; + int where; +{ + int reg; + char * start = *str; + + if ((reg = arm_reg_parse (str)) != FAIL && cp_register (reg)) + { + reg &= 15; + inst.instruction |= reg << where; + return reg; + } + + /* In the few cases where we might be able to accept something else + this error can be overridden. */ + inst.error = _("Co-processor register expected"); + + /* Restore the start point. */ + *str = start; + return FAIL; +} + +static int +fp_reg_required_here (str, where) + char ** str; + int where; +{ + int reg; + char * start = *str; + + if ((reg = arm_reg_parse (str)) != FAIL && fp_register (reg)) + { + reg &= 7; + inst.instruction |= reg << where; + return reg; + } + + /* In the few cases where we might be able to accept something else + this error can be overridden. */ + inst.error = _("Floating point register expected"); + + /* Restore the start point. */ + *str = start; + return FAIL; +} + +static int +cp_address_offset (str) + char ** str; +{ + int offset; + + skip_whitespace (* str); + + if (! is_immediate_prefix (**str)) + { + inst.error = _("immediate expression expected"); + return FAIL; + } + + (*str)++; + + if (my_get_expression (& inst.reloc.exp, str)) + return FAIL; + + if (inst.reloc.exp.X_op == O_constant) + { + offset = inst.reloc.exp.X_add_number; + + if (offset & 3) + { + inst.error = _("co-processor address must be word aligned"); + return FAIL; + } + + if (offset > 1023 || offset < -1023) + { + inst.error = _("offset too large"); + return FAIL; + } + + if (offset >= 0) + inst.instruction |= INDEX_UP; + else + offset = -offset; + + inst.instruction |= offset >> 2; + } + else + inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; + + return SUCCESS; +} + +static int +cp_address_required_here (str) + char ** str; +{ + char * p = * str; + int pre_inc = 0; + int write_back = 0; + + if (*p == '[') + { + int reg; + + p++; + skip_whitespace (p); + + if ((reg = reg_required_here (& p, 16)) == FAIL) + return FAIL; + + skip_whitespace (p); + + if (*p == ']') + { + p++; + + if (skip_past_comma (& p) == SUCCESS) + { + /* [Rn], #expr */ + write_back = WRITE_BACK; + + if (reg == REG_PC) + { + inst.error = _("pc may not be used in post-increment"); + return FAIL; + } + + if (cp_address_offset (& p) == FAIL) + return FAIL; + } + else + pre_inc = PRE_INDEX | INDEX_UP; + } + else + { + /* '['Rn, #expr']'[!] */ + + if (skip_past_comma (& p) == FAIL) + { + inst.error = _("pre-indexed expression expected"); + return FAIL; + } + + pre_inc = PRE_INDEX; + + if (cp_address_offset (& p) == FAIL) + return FAIL; + + skip_whitespace (p); + + if (*p++ != ']') + { + inst.error = _("missing ]"); + return FAIL; + } + + skip_whitespace (p); + + if (*p == '!') + { + if (reg == REG_PC) + { + inst.error = _("pc may not be used with write-back"); + return FAIL; + } + + p++; + write_back = WRITE_BACK; + } + } + } + else + { + if (my_get_expression (&inst.reloc.exp, &p)) + return FAIL; + + inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; + inst.reloc.exp.X_add_number -= 8; /* PC rel adjust */ + inst.reloc.pc_rel = 1; + inst.instruction |= (REG_PC << 16); + pre_inc = PRE_INDEX; + } + + inst.instruction |= write_back | pre_inc; + *str = p; + return SUCCESS; +} + +static void +do_nop (str, flags) + char * str; + unsigned long flags; +{ + /* Do nothing really. */ + inst.instruction |= flags; /* This is pointless. */ + end_of_line (str); + return; +} + +static void +do_mrs (str, flags) + char *str; + unsigned long flags; +{ + int skip = 0; + + /* Only one syntax. */ + skip_whitespace (str); + + if (reg_required_here (&str, 12) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL) + { + inst.error = _("comma expected after register name"); + return; + } + + skip_whitespace (str); + + if ( strcmp (str, "CPSR") == 0 + || strcmp (str, "SPSR") == 0 + /* Lower case versions for backwards compatability. */ + || strcmp (str, "cpsr") == 0 + || strcmp (str, "spsr") == 0) + skip = 4; + /* This is for backwards compatability with older toolchains. */ + else if (strcmp (str, "cpsr_all") == 0 + || strcmp (str, "spsr_all") == 0) + skip = 7; + else + { + inst.error = _("{C|S}PSR expected"); + return; + } + + if (* str == 's' || * str == 'S') + inst.instruction |= SPSR_BIT; + str += skip; + + inst.instruction |= flags; + end_of_line (str); +} + +/* Two possible forms: + "{C|S}PSR_, Rm", + "{C|S}PSR_f, #expression". */ +static void +do_msr (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + if (psr_required_here (& str) == FAIL) + return; + + if (skip_past_comma (& str) == FAIL) + { + inst.error = _("comma missing after psr flags"); + return; + } + + skip_whitespace (str); + + if (reg_required_here (& str, 0) != FAIL) + { + inst.error = NULL; + inst.instruction |= flags; + end_of_line (str); + return; + } + + if (! is_immediate_prefix (* str)) + { + inst.error = _("only a register or immediate value can follow a psr flag"); + return; + } + + str ++; + inst.error = NULL; + + if (my_get_expression (& inst.reloc.exp, & str)) + { + inst.error = _("only a register or immediate value can follow a psr flag"); + return; + } + + if (inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT)) + { + inst.error = _("can only set flag field with immediate value"); + return; + } + + flags |= INST_IMMEDIATE; + + if (inst.reloc.exp.X_add_symbol) + { + inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE; + inst.reloc.pc_rel = 0; + } + else + { + unsigned value = validate_immediate (inst.reloc.exp.X_add_number); + + if (value == (unsigned) FAIL) + { + inst.error = _("Invalid constant"); + return; + } + + inst.instruction |= value; + } + + inst.error = NULL; + inst.instruction |= flags; + end_of_line (str); +} + +/* Long Multiply Parser + UMULL RdLo, RdHi, Rm, Rs + SMULL RdLo, RdHi, Rm, Rs + UMLAL RdLo, RdHi, Rm, Rs + SMLAL RdLo, RdHi, Rm, Rs +*/ +static void +do_mull (str, flags) + char * str; + unsigned long flags; +{ + int rdlo, rdhi, rm, rs; + + /* Only one format "rdlo, rdhi, rm, rs" */ + skip_whitespace (str); + + if ((rdlo = reg_required_here (&str, 12)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || (rdhi = reg_required_here (&str, 16)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || (rm = reg_required_here (&str, 0)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + /* rdhi, rdlo and rm must all be different */ + if (rdlo == rdhi || rdlo == rm || rdhi == rm) + as_tsktsk (_("rdhi, rdlo and rm must all be different")); + + if (skip_past_comma (&str) == FAIL + || (rs = reg_required_here (&str, 8)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC) + { + inst.error = BAD_PC; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_mul (str, flags) + char * str; + unsigned long flags; +{ + int rd, rm; + + /* Only one format "rd, rm, rs" */ + skip_whitespace (str); + + if ((rd = reg_required_here (&str, 16)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (rd == REG_PC) + { + inst.error = BAD_PC; + return; + } + + if (skip_past_comma (&str) == FAIL + || (rm = reg_required_here (&str, 0)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (rm == REG_PC) + { + inst.error = BAD_PC; + return; + } + + if (rm == rd) + as_tsktsk (_("rd and rm should be different in mul")); + + if (skip_past_comma (&str) == FAIL + || (rm = reg_required_here (&str, 8)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (rm == REG_PC) + { + inst.error = BAD_PC; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_mla (str, flags) + char * str; + unsigned long flags; +{ + int rd, rm; + + /* Only one format "rd, rm, rs, rn" */ + skip_whitespace (str); + + if ((rd = reg_required_here (&str, 16)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (rd == REG_PC) + { + inst.error = BAD_PC; + return; + } + + if (skip_past_comma (&str) == FAIL + || (rm = reg_required_here (&str, 0)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (rm == REG_PC) + { + inst.error = BAD_PC; + return; + } + + if (rm == rd) + as_tsktsk (_("rd and rm should be different in mla")); + + if (skip_past_comma (&str) == FAIL + || (rd = reg_required_here (&str, 8)) == FAIL + || skip_past_comma (&str) == FAIL + || (rm = reg_required_here (&str, 12)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (rd == REG_PC || rm == REG_PC) + { + inst.error = BAD_PC; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +/* Returns the index into fp_values of a floating point number, or -1 if + not in the table. */ +static int +my_get_float_expression (str) + char ** str; +{ + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + char * save_in; + expressionS exp; + int i; + int j; + + memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE)); + /* Look for a raw floating point number */ + if ((save_in = atof_ieee (*str, 'x', words)) != NULL + && (is_end_of_line [(int)(*save_in)] || *save_in == '\0')) + { + for (i = 0; i < NUM_FLOAT_VALS; i++) + { + for (j = 0; j < MAX_LITTLENUMS; j++) + { + if (words[j] != fp_values[i][j]) + break; + } + + if (j == MAX_LITTLENUMS) + { + *str = save_in; + return i; + } + } + } + + /* Try and parse a more complex expression, this will probably fail + unless the code uses a floating point prefix (eg "0f") */ + save_in = input_line_pointer; + input_line_pointer = *str; + if (expression (&exp) == absolute_section + && exp.X_op == O_big + && exp.X_add_number < 0) + { + /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it. + Ditto for 15. */ + if (gen_to_words (words, 5, (long)15) == 0) + { + for (i = 0; i < NUM_FLOAT_VALS; i++) + { + for (j = 0; j < MAX_LITTLENUMS; j++) + { + if (words[j] != fp_values[i][j]) + break; + } + + if (j == MAX_LITTLENUMS) + { + *str = input_line_pointer; + input_line_pointer = save_in; + return i; + } + } + } + } + + *str = input_line_pointer; + input_line_pointer = save_in; + return -1; +} + +/* Return true if anything in the expression is a bignum */ +static int +walk_no_bignums (sp) + symbolS * sp; +{ + if (symbol_get_value_expression (sp)->X_op == O_big) + return 1; + + if (symbol_get_value_expression (sp)->X_add_symbol) + { + return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol) + || (symbol_get_value_expression (sp)->X_op_symbol + && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol))); + } + + return 0; +} + +static int +my_get_expression (ep, str) + expressionS * ep; + char ** str; +{ + char * save_in; + segT seg; + + save_in = input_line_pointer; + input_line_pointer = *str; + seg = expression (ep); + +#ifdef OBJ_AOUT + if (seg != absolute_section + && seg != text_section + && seg != data_section + && seg != bss_section + && seg != undefined_section) + { + inst.error = _("bad_segment"); + *str = input_line_pointer; + input_line_pointer = save_in; + return 1; + } +#endif + + /* Get rid of any bignums now, so that we don't generate an error for which + we can't establish a line number later on. Big numbers are never valid + in instructions, which is where this routine is always called. */ + if (ep->X_op == O_big + || (ep->X_add_symbol + && (walk_no_bignums (ep->X_add_symbol) + || (ep->X_op_symbol + && walk_no_bignums (ep->X_op_symbol))))) + { + inst.error = _("Invalid constant"); + *str = input_line_pointer; + input_line_pointer = save_in; + return 1; + } + + *str = input_line_pointer; + input_line_pointer = save_in; + return 0; +} + +/* unrestrict should be one if is permitted for this + instruction */ + +static int +decode_shift (str, unrestrict) + char ** str; + int unrestrict; +{ + struct asm_shift * shft; + char * p; + char c; + + skip_whitespace (* str); + + for (p = *str; isalpha (*p); p++) + ; + + if (p == *str) + { + inst.error = _("Shift expression expected"); + return FAIL; + } + + c = *p; + *p = '\0'; + shft = (struct asm_shift *) hash_find (arm_shift_hsh, *str); + *p = c; + if (shft) + { + if (!strncmp (*str, "rrx", 3) + || !strncmp (*str, "RRX", 3)) + { + *str = p; + inst.instruction |= shft->value; + return SUCCESS; + } + + skip_whitespace (p); + + if (unrestrict && reg_required_here (&p, 8) != FAIL) + { + inst.instruction |= shft->value | SHIFT_BY_REG; + *str = p; + return SUCCESS; + } + else if (is_immediate_prefix (* p)) + { + inst.error = NULL; + p++; + if (my_get_expression (&inst.reloc.exp, &p)) + return FAIL; + + /* Validate some simple #expressions */ + if (inst.reloc.exp.X_op == O_constant) + { + unsigned num = inst.reloc.exp.X_add_number; + + /* Reject operations greater than 32, or lsl #32 */ + if (num > 32 || (num == 32 && shft->value == 0)) + { + inst.error = _("Invalid immediate shift"); + return FAIL; + } + + /* Shifts of zero should be converted to lsl (which is zero)*/ + if (num == 0) + { + *str = p; + return SUCCESS; + } + + /* Shifts of 32 are encoded as 0, for those shifts that + support it. */ + if (num == 32) + num = 0; + + inst.instruction |= (num << 7) | shft->value; + *str = p; + return SUCCESS; + } + + inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM; + inst.reloc.pc_rel = 0; + inst.instruction |= shft->value; + *str = p; + return SUCCESS; + } + else + { + inst.error = unrestrict ? _("shift requires register or #expression") + : _("shift requires #expression"); + *str = p; + return FAIL; + } + } + + inst.error = _("Shift expression expected"); + return FAIL; +} + +/* Do those data_ops which can take a negative immediate constant */ +/* by altering the instuction. A bit of a hack really */ +/* MOV <-> MVN + AND <-> BIC + ADC <-> SBC + by inverting the second operand, and + ADD <-> SUB + CMP <-> CMN + by negating the second operand. +*/ +static int +negate_data_op (instruction, value) + unsigned long * instruction; + unsigned long value; +{ + int op, new_inst; + unsigned long negated, inverted; + + negated = validate_immediate (-value); + inverted = validate_immediate (~value); + + op = (*instruction >> DATA_OP_SHIFT) & 0xf; + switch (op) + { + /* First negates */ + case OPCODE_SUB: /* ADD <-> SUB */ + new_inst = OPCODE_ADD; + value = negated; + break; + + case OPCODE_ADD: + new_inst = OPCODE_SUB; + value = negated; + break; + + case OPCODE_CMP: /* CMP <-> CMN */ + new_inst = OPCODE_CMN; + value = negated; + break; + + case OPCODE_CMN: + new_inst = OPCODE_CMP; + value = negated; + break; + + /* Now Inverted ops */ + case OPCODE_MOV: /* MOV <-> MVN */ + new_inst = OPCODE_MVN; + value = inverted; + break; + + case OPCODE_MVN: + new_inst = OPCODE_MOV; + value = inverted; + break; + + case OPCODE_AND: /* AND <-> BIC */ + new_inst = OPCODE_BIC; + value = inverted; + break; + + case OPCODE_BIC: + new_inst = OPCODE_AND; + value = inverted; + break; + + case OPCODE_ADC: /* ADC <-> SBC */ + new_inst = OPCODE_SBC; + value = inverted; + break; + + case OPCODE_SBC: + new_inst = OPCODE_ADC; + value = inverted; + break; + + /* We cannot do anything */ + default: + return FAIL; + } + + if (value == (unsigned) FAIL) + return FAIL; + + *instruction &= OPCODE_MASK; + *instruction |= new_inst << DATA_OP_SHIFT; + return value; +} + +static int +data_op2 (str) + char ** str; +{ + int value; + expressionS expr; + + skip_whitespace (* str); + + if (reg_required_here (str, 0) != FAIL) + { + if (skip_past_comma (str) == SUCCESS) + /* Shift operation on register. */ + return decode_shift (str, NO_SHIFT_RESTRICT); + + return SUCCESS; + } + else + { + /* Immediate expression */ + if (is_immediate_prefix (**str)) + { + (*str)++; + inst.error = NULL; + + if (my_get_expression (&inst.reloc.exp, str)) + return FAIL; + + if (inst.reloc.exp.X_add_symbol) + { + inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE; + inst.reloc.pc_rel = 0; + } + else + { + if (skip_past_comma (str) == SUCCESS) + { + /* #x, y -- ie explicit rotation by Y */ + if (my_get_expression (&expr, str)) + return FAIL; + + if (expr.X_op != O_constant) + { + inst.error = _("Constant expression expected"); + return FAIL; + } + + /* Rotate must be a multiple of 2 */ + if (((unsigned) expr.X_add_number) > 30 + || (expr.X_add_number & 1) != 0 + || ((unsigned) inst.reloc.exp.X_add_number) > 255) + { + inst.error = _("Invalid constant"); + return FAIL; + } + inst.instruction |= INST_IMMEDIATE; + inst.instruction |= inst.reloc.exp.X_add_number; + inst.instruction |= expr.X_add_number << 7; + return SUCCESS; + } + + /* Implicit rotation, select a suitable one */ + value = validate_immediate (inst.reloc.exp.X_add_number); + + if (value == FAIL) + { + /* Can't be done, perhaps the code reads something like + "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be ok */ + if ((value = negate_data_op (&inst.instruction, + inst.reloc.exp.X_add_number)) + == FAIL) + { + inst.error = _("Invalid constant"); + return FAIL; + } + } + + inst.instruction |= value; + } + + inst.instruction |= INST_IMMEDIATE; + return SUCCESS; + } + + (*str)++; + inst.error = _("Register or shift expression expected"); + return FAIL; + } +} + +static int +fp_op2 (str) + char ** str; +{ + skip_whitespace (* str); + + if (fp_reg_required_here (str, 0) != FAIL) + return SUCCESS; + else + { + /* Immediate expression */ + if (*((*str)++) == '#') + { + int i; + + inst.error = NULL; + + skip_whitespace (* str); + + /* First try and match exact strings, this is to guarantee that + some formats will work even for cross assembly */ + + for (i = 0; fp_const[i]; i++) + { + if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0) + { + char *start = *str; + + *str += strlen (fp_const[i]); + if (is_end_of_line[(int)**str] || **str == '\0') + { + inst.instruction |= i + 8; + return SUCCESS; + } + *str = start; + } + } + + /* Just because we didn't get a match doesn't mean that the + constant isn't valid, just that it is in a format that we + don't automatically recognize. Try parsing it with + the standard expression routines. */ + if ((i = my_get_float_expression (str)) >= 0) + { + inst.instruction |= i + 8; + return SUCCESS; + } + + inst.error = _("Invalid floating point immediate expression"); + return FAIL; + } + inst.error = _("Floating point register or immediate expression expected"); + return FAIL; + } +} + +static void +do_arit (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + if (reg_required_here (&str, 12) == FAIL + || skip_past_comma (&str) == FAIL + || reg_required_here (&str, 16) == FAIL + || skip_past_comma (&str) == FAIL + || data_op2 (&str) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_adr (str, flags) + char * str; + unsigned long flags; +{ + /* This is a pseudo-op of the form "adr rd, label" to be converted + into a relative address of the form "add rd, pc, #label-.-8". */ + skip_whitespace (str); + + if (reg_required_here (&str, 12) == FAIL + || skip_past_comma (&str) == FAIL + || my_get_expression (&inst.reloc.exp, &str)) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + /* Frag hacking will turn this into a sub instruction if the offset turns + out to be negative. */ + inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE; + inst.reloc.exp.X_add_number -= 8; /* PC relative adjust. */ + inst.reloc.pc_rel = 1; + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_adrl (str, flags) + char * str; + unsigned long flags; +{ + /* This is a pseudo-op of the form "adrl rd, label" to be converted + into a relative address of the form: + add rd, pc, #low(label-.-8)" + add rd, rd, #high(label-.-8)" */ + + skip_whitespace (str); + + if (reg_required_here (& str, 12) == FAIL + || skip_past_comma (& str) == FAIL + || my_get_expression (& inst.reloc.exp, & str)) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + end_of_line (str); + + /* Frag hacking will turn this into a sub instruction if the offset turns + out to be negative. */ + inst.reloc.type = BFD_RELOC_ARM_ADRL_IMMEDIATE; + inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */ + inst.reloc.pc_rel = 1; + inst.instruction |= flags; + inst.size = INSN_SIZE * 2; + + return; +} + +static void +do_cmp (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + if (reg_required_here (&str, 16) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || data_op2 (&str) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + if ((flags & 0x0000f000) == 0) + inst.instruction |= CONDS_BIT; + + end_of_line (str); + return; +} + +static void +do_mov (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + if (reg_required_here (&str, 12) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || data_op2 (&str) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static int +ldst_extend (str, hwse) + char ** str; + int hwse; +{ + int add = INDEX_UP; + + switch (**str) + { + case '#': + case '$': + (*str)++; + if (my_get_expression (& inst.reloc.exp, str)) + return FAIL; + + if (inst.reloc.exp.X_op == O_constant) + { + int value = inst.reloc.exp.X_add_number; + + if ((hwse && (value < -255 || value > 255)) + || (value < -4095 || value > 4095)) + { + inst.error = _("address offset too large"); + return FAIL; + } + + if (value < 0) + { + value = -value; + add = 0; + } + + /* Halfword and signextension instructions have the + immediate value split across bits 11..8 and bits 3..0 */ + if (hwse) + inst.instruction |= add | HWOFFSET_IMM | ((value >> 4) << 8) | (value & 0xF); + else + inst.instruction |= add | value; + } + else + { + if (hwse) + { + inst.instruction |= HWOFFSET_IMM; + inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8; + } + else + inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM; + inst.reloc.pc_rel = 0; + } + return SUCCESS; + + case '-': + add = 0; /* and fall through */ + case '+': + (*str)++; /* and fall through */ + default: + if (reg_required_here (str, 0) == FAIL) + return FAIL; + + if (hwse) + inst.instruction |= add; + else + { + inst.instruction |= add | OFFSET_REG; + if (skip_past_comma (str) == SUCCESS) + return decode_shift (str, SHIFT_RESTRICT); + } + + return SUCCESS; + } +} + +static void +do_ldst (str, flags) + char * str; + unsigned long flags; +{ + int halfword = 0; + int pre_inc = 0; + int conflict_reg; + int value; + + /* This is not ideal, but it is the simplest way of dealing with the + ARM7T halfword instructions (since they use a different + encoding, but the same mnemonic): */ + halfword = (flags & 0x80000000) != 0; + if (halfword) + { + /* This is actually a load/store of a halfword, or a + signed-extension load */ + if ((cpu_variant & ARM_HALFWORD) == 0) + { + inst.error + = _("Processor does not support halfwords or signed bytes"); + return; + } + + inst.instruction = (inst.instruction & COND_MASK) + | (flags & ~COND_MASK); + + flags = 0; + } + + skip_whitespace (str); + + if ((conflict_reg = reg_required_here (& str, 12)) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (& str) == FAIL) + { + inst.error = _("Address expected"); + return; + } + + if (*str == '[') + { + int reg; + + str++; + + skip_whitespace (str); + + if ((reg = reg_required_here (&str, 16)) == FAIL) + return; + + /* Conflicts can occur on stores as well as loads. */ + conflict_reg = (conflict_reg == reg); + + skip_whitespace (str); + + if (*str == ']') + { + str ++; + + if (skip_past_comma (&str) == SUCCESS) + { + /* [Rn],... (post inc) */ + if (ldst_extend (&str, halfword) == FAIL) + return; + if (conflict_reg) + as_warn (_("%s register same as write-back base"), + (inst.instruction & LOAD_BIT) ? _("destination") : _("source") ); + } + else + { + /* [Rn] */ + if (halfword) + inst.instruction |= HWOFFSET_IMM; + + skip_whitespace (str); + + if (*str == '!') + { + if (conflict_reg) + as_warn (_("%s register same as write-back base"), + (inst.instruction & LOAD_BIT) ? _("destination") : _("source") ); + str++; + inst.instruction |= WRITE_BACK; + } + + flags |= INDEX_UP; + if (! (flags & TRANS_BIT)) + pre_inc = 1; + } + } + else + { + /* [Rn,...] */ + if (skip_past_comma (&str) == FAIL) + { + inst.error = _("pre-indexed expression expected"); + return; + } + + pre_inc = 1; + if (ldst_extend (&str, halfword) == FAIL) + return; + + skip_whitespace (str); + + if (*str++ != ']') + { + inst.error = _("missing ]"); + return; + } + + skip_whitespace (str); + + if (*str == '!') + { + if (conflict_reg) + as_warn (_("%s register same as write-back base"), + (inst.instruction & LOAD_BIT) ? _("destination") : _("source") ); + str++; + inst.instruction |= WRITE_BACK; + } + } + } + else if (*str == '=') + { + /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */ + str++; + + skip_whitespace (str); + + if (my_get_expression (&inst.reloc.exp, &str)) + return; + + if (inst.reloc.exp.X_op != O_constant + && inst.reloc.exp.X_op != O_symbol) + { + inst.error = _("Constant expression expected"); + return; + } + + if (inst.reloc.exp.X_op == O_constant + && (value = validate_immediate(inst.reloc.exp.X_add_number)) != FAIL) + { + /* This can be done with a mov instruction */ + inst.instruction &= LITERAL_MASK; + inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT); + inst.instruction |= (flags & COND_MASK) | (value & 0xfff); + end_of_line(str); + return; + } + else + { + /* Insert into literal pool */ + if (add_to_lit_pool () == FAIL) + { + if (!inst.error) + inst.error = _("literal pool insertion failed"); + return; + } + + /* Change the instruction exp to point to the pool */ + if (halfword) + { + inst.instruction |= HWOFFSET_IMM; + inst.reloc.type = BFD_RELOC_ARM_HWLITERAL; + } + else + inst.reloc.type = BFD_RELOC_ARM_LITERAL; + inst.reloc.pc_rel = 1; + inst.instruction |= (REG_PC << 16); + pre_inc = 1; + } + } + else + { + if (my_get_expression (&inst.reloc.exp, &str)) + return; + + if (halfword) + { + inst.instruction |= HWOFFSET_IMM; + inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8; + } + else + inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM; +#ifndef TE_WINCE + inst.reloc.exp.X_add_number -= 8; /* PC rel adjust */ +#endif + inst.reloc.pc_rel = 1; + inst.instruction |= (REG_PC << 16); + pre_inc = 1; + } + + if (pre_inc && (flags & TRANS_BIT)) + inst.error = _("Pre-increment instruction with translate"); + + inst.instruction |= flags | (pre_inc ? PRE_INDEX : 0); + end_of_line (str); + return; +} + +static long +reg_list (strp) + char ** strp; +{ + char * str = *strp; + long range = 0; + int another_range; + + /* We come back here if we get ranges concatenated by '+' or '|' */ + do + { + another_range = 0; + + if (*str == '{') + { + int in_range = 0; + int cur_reg = -1; + + str++; + do + { + int reg; + + skip_whitespace (str); + + if ((reg = reg_required_here (& str, -1)) == FAIL) + return FAIL; + + if (in_range) + { + int i; + + if (reg <= cur_reg) + { + inst.error = _("Bad range in register list"); + return FAIL; + } + + for (i = cur_reg + 1; i < reg; i++) + { + if (range & (1 << i)) + as_tsktsk + (_("Warning: Duplicated register (r%d) in register list"), + i); + else + range |= 1 << i; + } + in_range = 0; + } + + if (range & (1 << reg)) + as_tsktsk (_("Warning: Duplicated register (r%d) in register list"), + reg); + else if (reg <= cur_reg) + as_tsktsk (_("Warning: Register range not in ascending order")); + + range |= 1 << reg; + cur_reg = reg; + } while (skip_past_comma (&str) != FAIL + || (in_range = 1, *str++ == '-')); + str--; + skip_whitespace (str); + + if (*str++ != '}') + { + inst.error = _("Missing `}'"); + return FAIL; + } + } + else + { + expressionS expr; + + if (my_get_expression (&expr, &str)) + return FAIL; + + if (expr.X_op == O_constant) + { + if (expr.X_add_number + != (expr.X_add_number & 0x0000ffff)) + { + inst.error = _("invalid register mask"); + return FAIL; + } + + if ((range & expr.X_add_number) != 0) + { + int regno = range & expr.X_add_number; + + regno &= -regno; + regno = (1 << regno) - 1; + as_tsktsk + (_("Warning: Duplicated register (r%d) in register list"), + regno); + } + + range |= expr.X_add_number; + } + else + { + if (inst.reloc.type != 0) + { + inst.error = _("expression too complex"); + return FAIL; + } + + memcpy (&inst.reloc.exp, &expr, sizeof (expressionS)); + inst.reloc.type = BFD_RELOC_ARM_MULTI; + inst.reloc.pc_rel = 0; + } + } + + skip_whitespace (str); + + if (*str == '|' || *str == '+') + { + str++; + another_range = 1; + } + } while (another_range); + + *strp = str; + return range; +} + +static void +do_ldmstm (str, flags) + char * str; + unsigned long flags; +{ + int base_reg; + long range; + + skip_whitespace (str); + + if ((base_reg = reg_required_here (&str, 16)) == FAIL) + return; + + if (base_reg == REG_PC) + { + inst.error = _("r15 not allowed as base register"); + return; + } + + skip_whitespace (str); + + if (*str == '!') + { + flags |= WRITE_BACK; + str++; + } + + if (skip_past_comma (&str) == FAIL + || (range = reg_list (&str)) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (*str == '^') + { + str++; + flags |= LDM_TYPE_2_OR_3; + } + + inst.instruction |= flags | range; + end_of_line (str); + return; +} + +static void +do_swi (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + /* Allow optional leading '#'. */ + if (is_immediate_prefix (*str)) + str++; + + if (my_get_expression (& inst.reloc.exp, & str)) + return; + + inst.reloc.type = BFD_RELOC_ARM_SWI; + inst.reloc.pc_rel = 0; + inst.instruction |= flags; + + end_of_line (str); + + return; +} + +static void +do_swap (str, flags) + char * str; + unsigned long flags; +{ + int reg; + + skip_whitespace (str); + + if ((reg = reg_required_here (&str, 12)) == FAIL) + return; + + if (reg == REG_PC) + { + inst.error = _("r15 not allowed in swap"); + return; + } + + if (skip_past_comma (&str) == FAIL + || (reg = reg_required_here (&str, 0)) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (reg == REG_PC) + { + inst.error = _("r15 not allowed in swap"); + return; + } + + if (skip_past_comma (&str) == FAIL + || *str++ != '[') + { + inst.error = BAD_ARGS; + return; + } + + skip_whitespace (str); + + if ((reg = reg_required_here (&str, 16)) == FAIL) + return; + + if (reg == REG_PC) + { + inst.error = BAD_PC; + return; + } + + skip_whitespace (str); + + if (*str++ != ']') + { + inst.error = _("missing ]"); + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_branch (str, flags) + char * str; + unsigned long flags ATTRIBUTE_UNUSED; +{ + if (my_get_expression (&inst.reloc.exp, &str)) + return; + +#ifdef OBJ_ELF + { + char * save_in; + + /* ScottB: February 5, 1998 */ + /* Check to see of PLT32 reloc required for the instruction. */ + + /* arm_parse_reloc() works on input_line_pointer. + We actually want to parse the operands to the branch instruction + passed in 'str'. Save the input pointer and restore it later. */ + save_in = input_line_pointer; + input_line_pointer = str; + if (inst.reloc.exp.X_op == O_symbol + && *str == '(' + && arm_parse_reloc () == BFD_RELOC_ARM_PLT32) + { + inst.reloc.type = BFD_RELOC_ARM_PLT32; + inst.reloc.pc_rel = 0; + /* Modify str to point to after parsed operands, otherwise + end_of_line() will complain about the (PLT) left in str. */ + str = input_line_pointer; + } + else + { + inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH; + inst.reloc.pc_rel = 1; + } + input_line_pointer = save_in; + } +#else + inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH; + inst.reloc.pc_rel = 1; +#endif /* OBJ_ELF */ + + end_of_line (str); + return; +} + +static void +do_bx (str, flags) + char * str; + unsigned long flags ATTRIBUTE_UNUSED; +{ + int reg; + + skip_whitespace (str); + + if ((reg = reg_required_here (&str, 0)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (reg == REG_PC) + inst.error = BAD_PC; + + end_of_line (str); +} + +static void +do_cdp (str, flags) + char * str; + unsigned long flags ATTRIBUTE_UNUSED; +{ + /* Co-processor data operation. + Format: CDP{cond} CP#,,CRd,CRn,CRm{,} */ + skip_whitespace (str); + + if (co_proc_number (&str) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_opc_expr (&str, 20,4) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_reg_required_here (&str, 12) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_reg_required_here (&str, 16) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_reg_required_here (&str, 0) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == SUCCESS) + { + if (cp_opc_expr (&str, 5, 3) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + } + + end_of_line (str); + return; +} + +static void +do_lstc (str, flags) + char * str; + unsigned long flags; +{ + /* Co-processor register load/store. + Format: */ + + skip_whitespace (str); + + if (co_proc_number (&str) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_reg_required_here (&str, 12) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_address_required_here (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_co_reg (str, flags) + char * str; + unsigned long flags; +{ + /* Co-processor register transfer. + Format: {cond} CP#,,Rd,CRn,CRm{,} */ + + skip_whitespace (str); + + if (co_proc_number (&str) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_opc_expr (&str, 21, 3) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || reg_required_here (&str, 12) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_reg_required_here (&str, 16) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_reg_required_here (&str, 0) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == SUCCESS) + { + if (cp_opc_expr (&str, 5, 3) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + } + if (flags) + { + inst.error = BAD_COND; + } + + end_of_line (str); + return; +} + +static void +do_fp_ctrl (str, flags) + char * str; + unsigned long flags ATTRIBUTE_UNUSED; +{ + /* FP control registers. + Format: {cond} Rn */ + + skip_whitespace (str); + + if (reg_required_here (&str, 12) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + end_of_line (str); + return; +} + +static void +do_fp_ldst (str, flags) + char * str; + unsigned long flags ATTRIBUTE_UNUSED; +{ + skip_whitespace (str); + + switch (inst.suffix) + { + case SUFF_S: + break; + case SUFF_D: + inst.instruction |= CP_T_X; + break; + case SUFF_E: + inst.instruction |= CP_T_Y; + break; + case SUFF_P: + inst.instruction |= CP_T_X | CP_T_Y; + break; + default: + abort (); + } + + if (fp_reg_required_here (&str, 12) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || cp_address_required_here (&str) == FAIL) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + end_of_line (str); +} + +static void +do_fp_ldmstm (str, flags) + char * str; + unsigned long flags; +{ + int num_regs; + + skip_whitespace (str); + + if (fp_reg_required_here (&str, 12) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + /* Get Number of registers to transfer */ + if (skip_past_comma (&str) == FAIL + || my_get_expression (&inst.reloc.exp, &str)) + { + if (! inst.error) + inst.error = _("constant expression expected"); + return; + } + + if (inst.reloc.exp.X_op != O_constant) + { + inst.error = _("Constant value required for number of registers"); + return; + } + + num_regs = inst.reloc.exp.X_add_number; + + if (num_regs < 1 || num_regs > 4) + { + inst.error = _("number of registers must be in the range [1:4]"); + return; + } + + switch (num_regs) + { + case 1: + inst.instruction |= CP_T_X; + break; + case 2: + inst.instruction |= CP_T_Y; + break; + case 3: + inst.instruction |= CP_T_Y | CP_T_X; + break; + case 4: + break; + default: + abort (); + } + + if (flags) + { + int reg; + int write_back; + int offset; + + /* The instruction specified "ea" or "fd", so we can only accept + [Rn]{!}. The instruction does not really support stacking or + unstacking, so we have to emulate these by setting appropriate + bits and offsets. */ + if (skip_past_comma (&str) == FAIL + || *str != '[') + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + str++; + skip_whitespace (str); + + if ((reg = reg_required_here (&str, 16)) == FAIL) + return; + + skip_whitespace (str); + + if (*str != ']') + { + inst.error = BAD_ARGS; + return; + } + + str++; + if (*str == '!') + { + write_back = 1; + str++; + if (reg == REG_PC) + { + inst.error = _("R15 not allowed as base register with write-back"); + return; + } + } + else + write_back = 0; + + if (flags & CP_T_Pre) + { + /* Pre-decrement */ + offset = 3 * num_regs; + if (write_back) + flags |= CP_T_WB; + } + else + { + /* Post-increment */ + if (write_back) + { + flags |= CP_T_WB; + offset = 3 * num_regs; + } + else + { + /* No write-back, so convert this into a standard pre-increment + instruction -- aesthetically more pleasing. */ + flags = CP_T_Pre | CP_T_UD; + offset = 0; + } + } + + inst.instruction |= flags | offset; + } + else if (skip_past_comma (&str) == FAIL + || cp_address_required_here (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + end_of_line (str); +} + +static void +do_fp_dyadic (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + switch (inst.suffix) + { + case SUFF_S: + break; + case SUFF_D: + inst.instruction |= 0x00000080; + break; + case SUFF_E: + inst.instruction |= 0x00080000; + break; + default: + abort (); + } + + if (fp_reg_required_here (&str, 12) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || fp_reg_required_here (&str, 16) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || fp_op2 (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_fp_monadic (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + switch (inst.suffix) + { + case SUFF_S: + break; + case SUFF_D: + inst.instruction |= 0x00000080; + break; + case SUFF_E: + inst.instruction |= 0x00080000; + break; + default: + abort (); + } + + if (fp_reg_required_here (&str, 12) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || fp_op2 (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_fp_cmp (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + if (fp_reg_required_here (&str, 16) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || fp_op2 (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_fp_from_reg (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + switch (inst.suffix) + { + case SUFF_S: + break; + case SUFF_D: + inst.instruction |= 0x00000080; + break; + case SUFF_E: + inst.instruction |= 0x00080000; + break; + default: + abort (); + } + + if (fp_reg_required_here (&str, 16) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) == FAIL + || reg_required_here (&str, 12) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +static void +do_fp_to_reg (str, flags) + char * str; + unsigned long flags; +{ + skip_whitespace (str); + + if (reg_required_here (&str, 12) == FAIL) + return; + + if (skip_past_comma (&str) == FAIL + || fp_reg_required_here (&str, 0) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.instruction |= flags; + end_of_line (str); + return; +} + +/* Thumb specific routines */ + +/* Parse and validate that a register is of the right form, this saves + repeated checking of this information in many similar cases. + Unlike the 32-bit case we do not insert the register into the opcode + here, since the position is often unknown until the full instruction + has been parsed. */ +static int +thumb_reg (strp, hi_lo) + char ** strp; + int hi_lo; +{ + int reg; + + if ((reg = reg_required_here (strp, -1)) == FAIL) + return FAIL; + + switch (hi_lo) + { + case THUMB_REG_LO: + if (reg > 7) + { + inst.error = _("lo register required"); + return FAIL; + } + break; + + case THUMB_REG_HI: + if (reg < 8) + { + inst.error = _("hi register required"); + return FAIL; + } + break; + + default: + break; + } + + return reg; +} + +/* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode + was SUB. */ +static void +thumb_add_sub (str, subtract) + char * str; + int subtract; +{ + int Rd, Rs, Rn = FAIL; + + skip_whitespace (str); + + if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL + || skip_past_comma (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (is_immediate_prefix (*str)) + { + Rs = Rd; + str++; + if (my_get_expression (&inst.reloc.exp, &str)) + return; + } + else + { + if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) + return; + + if (skip_past_comma (&str) == FAIL) + { + /* Two operand format, shuffle the registers and pretend there + are 3 */ + Rn = Rs; + Rs = Rd; + } + else if (is_immediate_prefix (*str)) + { + str++; + if (my_get_expression (&inst.reloc.exp, &str)) + return; + } + else if ((Rn = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) + return; + } + + /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL; + for the latter case, EXPR contains the immediate that was found. */ + if (Rn != FAIL) + { + /* All register format. */ + if (Rd > 7 || Rs > 7 || Rn > 7) + { + if (Rs != Rd) + { + inst.error = _("dest and source1 must be the same register"); + return; + } + + /* Can't do this for SUB */ + if (subtract) + { + inst.error = _("subtract valid only on lo regs"); + return; + } + + inst.instruction = (T_OPCODE_ADD_HI + | (Rd > 7 ? THUMB_H1 : 0) + | (Rn > 7 ? THUMB_H2 : 0)); + inst.instruction |= (Rd & 7) | ((Rn & 7) << 3); + } + else + { + inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3; + inst.instruction |= Rd | (Rs << 3) | (Rn << 6); + } + } + else + { + /* Immediate expression, now things start to get nasty. */ + + /* First deal with HI regs, only very restricted cases allowed: + Adjusting SP, and using PC or SP to get an address. */ + if ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP)) + || (Rs > 7 && Rs != REG_SP && Rs != REG_PC)) + { + inst.error = _("invalid Hi register with immediate"); + return; + } + + if (inst.reloc.exp.X_op != O_constant) + { + /* Value isn't known yet, all we can do is store all the fragments + we know about in the instruction and let the reloc hacking + work it all out. */ + inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs; + inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD; + } + else + { + int offset = inst.reloc.exp.X_add_number; + + if (subtract) + offset = -offset; + + if (offset < 0) + { + offset = -offset; + subtract = 1; + + /* Quick check, in case offset is MIN_INT */ + if (offset < 0) + { + inst.error = _("immediate value out of range"); + return; + } + } + else + subtract = 0; + + if (Rd == REG_SP) + { + if (offset & ~0x1fc) + { + inst.error = _("invalid immediate value for stack adjust"); + return; + } + inst.instruction = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST; + inst.instruction |= offset >> 2; + } + else if (Rs == REG_PC || Rs == REG_SP) + { + if (subtract + || (offset & ~0x3fc)) + { + inst.error = _("invalid immediate for address calculation"); + return; + } + inst.instruction = (Rs == REG_PC ? T_OPCODE_ADD_PC + : T_OPCODE_ADD_SP); + inst.instruction |= (Rd << 8) | (offset >> 2); + } + else if (Rs == Rd) + { + if (offset & ~0xff) + { + inst.error = _("immediate value out of range"); + return; + } + inst.instruction = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8; + inst.instruction |= (Rd << 8) | offset; + } + else + { + if (offset & ~0x7) + { + inst.error = _("immediate value out of range"); + return; + } + inst.instruction = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3; + inst.instruction |= Rd | (Rs << 3) | (offset << 6); + } + } + } + + end_of_line (str); +} + +static void +thumb_shift (str, shift) + char * str; + int shift; +{ + int Rd, Rs, Rn = FAIL; + + skip_whitespace (str); + + if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL + || skip_past_comma (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (is_immediate_prefix (*str)) + { + /* Two operand immediate format, set Rs to Rd. */ + Rs = Rd; + str ++; + if (my_get_expression (&inst.reloc.exp, &str)) + return; + } + else + { + if ((Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL) + return; + + if (skip_past_comma (&str) == FAIL) + { + /* Two operand format, shuffle the registers and pretend there + are 3 */ + Rn = Rs; + Rs = Rd; + } + else if (is_immediate_prefix (*str)) + { + str++; + if (my_get_expression (&inst.reloc.exp, &str)) + return; + } + else if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL) + return; + } + + /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL; + for the latter case, EXPR contains the immediate that was found. */ + + if (Rn != FAIL) + { + if (Rs != Rd) + { + inst.error = _("source1 and dest must be same register"); + return; + } + + switch (shift) + { + case THUMB_ASR: inst.instruction = T_OPCODE_ASR_R; break; + case THUMB_LSL: inst.instruction = T_OPCODE_LSL_R; break; + case THUMB_LSR: inst.instruction = T_OPCODE_LSR_R; break; + } + + inst.instruction |= Rd | (Rn << 3); + } + else + { + switch (shift) + { + case THUMB_ASR: inst.instruction = T_OPCODE_ASR_I; break; + case THUMB_LSL: inst.instruction = T_OPCODE_LSL_I; break; + case THUMB_LSR: inst.instruction = T_OPCODE_LSR_I; break; + } + + if (inst.reloc.exp.X_op != O_constant) + { + /* Value isn't known yet, create a dummy reloc and let reloc + hacking fix it up */ + + inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT; + } + else + { + unsigned shift_value = inst.reloc.exp.X_add_number; + + if (shift_value > 32 || (shift_value == 32 && shift == THUMB_LSL)) + { + inst.error = _("Invalid immediate for shift"); + return; + } + + /* Shifts of zero are handled by converting to LSL */ + if (shift_value == 0) + inst.instruction = T_OPCODE_LSL_I; + + /* Shifts of 32 are encoded as a shift of zero */ + if (shift_value == 32) + shift_value = 0; + + inst.instruction |= shift_value << 6; + } + + inst.instruction |= Rd | (Rs << 3); + } + + end_of_line (str); +} + +static void +thumb_mov_compare (str, move) + char * str; + int move; +{ + int Rd, Rs = FAIL; + + skip_whitespace (str); + + if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL + || skip_past_comma (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (is_immediate_prefix (*str)) + { + str++; + if (my_get_expression (&inst.reloc.exp, &str)) + return; + } + else if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) + return; + + if (Rs != FAIL) + { + if (Rs < 8 && Rd < 8) + { + if (move == THUMB_MOVE) + /* A move of two lowregs is encoded as ADD Rd, Rs, #0 + since a MOV instruction produces unpredictable results */ + inst.instruction = T_OPCODE_ADD_I3; + else + inst.instruction = T_OPCODE_CMP_LR; + inst.instruction |= Rd | (Rs << 3); + } + else + { + if (move == THUMB_MOVE) + inst.instruction = T_OPCODE_MOV_HR; + else + inst.instruction = T_OPCODE_CMP_HR; + + if (Rd > 7) + inst.instruction |= THUMB_H1; + + if (Rs > 7) + inst.instruction |= THUMB_H2; + + inst.instruction |= (Rd & 7) | ((Rs & 7) << 3); + } + } + else + { + if (Rd > 7) + { + inst.error = _("only lo regs allowed with immediate"); + return; + } + + if (move == THUMB_MOVE) + inst.instruction = T_OPCODE_MOV_I8; + else + inst.instruction = T_OPCODE_CMP_I8; + + inst.instruction |= Rd << 8; + + if (inst.reloc.exp.X_op != O_constant) + inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM; + else + { + unsigned value = inst.reloc.exp.X_add_number; + + if (value > 255) + { + inst.error = _("invalid immediate"); + return; + } + + inst.instruction |= value; + } + } + + end_of_line (str); +} + +static void +thumb_load_store (str, load_store, size) + char * str; + int load_store; + int size; +{ + int Rd, Rb, Ro = FAIL; + + skip_whitespace (str); + + if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL + || skip_past_comma (&str) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (*str == '[') + { + str++; + if ((Rb = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) + return; + + if (skip_past_comma (&str) != FAIL) + { + if (is_immediate_prefix (*str)) + { + str++; + if (my_get_expression (&inst.reloc.exp, &str)) + return; + } + else if ((Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL) + return; + } + else + { + inst.reloc.exp.X_op = O_constant; + inst.reloc.exp.X_add_number = 0; + } + + if (*str != ']') + { + inst.error = _("expected ']'"); + return; + } + str++; + } + else if (*str == '=') + { + /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */ + str++; + + skip_whitespace (str); + + if (my_get_expression (& inst.reloc.exp, & str)) + return; + + end_of_line (str); + + if ( inst.reloc.exp.X_op != O_constant + && inst.reloc.exp.X_op != O_symbol) + { + inst.error = "Constant expression expected"; + return; + } + + if (inst.reloc.exp.X_op == O_constant + && ((inst.reloc.exp.X_add_number & ~0xFF) == 0)) + { + /* This can be done with a mov instruction */ + + inst.instruction = T_OPCODE_MOV_I8 | (Rd << 8); + inst.instruction |= inst.reloc.exp.X_add_number; + return; + } + + /* Insert into literal pool */ + if (add_to_lit_pool () == FAIL) + { + if (!inst.error) + inst.error = "literal pool insertion failed"; + return; + } + + inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET; + inst.reloc.pc_rel = 1; + inst.instruction = T_OPCODE_LDR_PC | (Rd << 8); + inst.reloc.exp.X_add_number += 4; /* Adjust ARM pipeline offset to Thumb */ + + return; + } + else + { + if (my_get_expression (&inst.reloc.exp, &str)) + return; + + inst.instruction = T_OPCODE_LDR_PC | (Rd << 8); + inst.reloc.pc_rel = 1; + inst.reloc.exp.X_add_number -= 4; /* Pipeline offset */ + inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET; + end_of_line (str); + return; + } + + if (Rb == REG_PC || Rb == REG_SP) + { + if (size != THUMB_WORD) + { + inst.error = _("byte or halfword not valid for base register"); + return; + } + else if (Rb == REG_PC && load_store != THUMB_LOAD) + { + inst.error = _("R15 based store not allowed"); + return; + } + else if (Ro != FAIL) + { + inst.error = _("Invalid base register for register offset"); + return; + } + + if (Rb == REG_PC) + inst.instruction = T_OPCODE_LDR_PC; + else if (load_store == THUMB_LOAD) + inst.instruction = T_OPCODE_LDR_SP; + else + inst.instruction = T_OPCODE_STR_SP; + + inst.instruction |= Rd << 8; + if (inst.reloc.exp.X_op == O_constant) + { + unsigned offset = inst.reloc.exp.X_add_number; + + if (offset & ~0x3fc) + { + inst.error = _("invalid offset"); + return; + } + + inst.instruction |= offset >> 2; + } + else + inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET; + } + else if (Rb > 7) + { + inst.error = _("invalid base register in load/store"); + return; + } + else if (Ro == FAIL) + { + /* Immediate offset */ + if (size == THUMB_WORD) + inst.instruction = (load_store == THUMB_LOAD + ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW); + else if (size == THUMB_HALFWORD) + inst.instruction = (load_store == THUMB_LOAD + ? T_OPCODE_LDR_IH : T_OPCODE_STR_IH); + else + inst.instruction = (load_store == THUMB_LOAD + ? T_OPCODE_LDR_IB : T_OPCODE_STR_IB); + + inst.instruction |= Rd | (Rb << 3); + + if (inst.reloc.exp.X_op == O_constant) + { + unsigned offset = inst.reloc.exp.X_add_number; + + if (offset & ~(0x1f << size)) + { + inst.error = _("Invalid offset"); + return; + } + inst.instruction |= (offset >> size) << 6; + } + else + inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET; + } + else + { + /* Register offset */ + if (size == THUMB_WORD) + inst.instruction = (load_store == THUMB_LOAD + ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW); + else if (size == THUMB_HALFWORD) + inst.instruction = (load_store == THUMB_LOAD + ? T_OPCODE_LDR_RH : T_OPCODE_STR_RH); + else + inst.instruction = (load_store == THUMB_LOAD + ? T_OPCODE_LDR_RB : T_OPCODE_STR_RB); + + inst.instruction |= Rd | (Rb << 3) | (Ro << 6); + } + + end_of_line (str); +} + +static void +do_t_nop (str) + char * str; +{ + /* Do nothing */ + end_of_line (str); + return; +} + +/* Handle the Format 4 instructions that do not have equivalents in other + formats. That is, ADC, AND, EOR, SBC, ROR, TST, NEG, CMN, ORR, MUL, + BIC and MVN. */ +static void +do_t_arit (str) + char * str; +{ + int Rd, Rs, Rn; + + skip_whitespace (str); + + if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL + || skip_past_comma (&str) == FAIL + || (Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL) + { + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&str) != FAIL) + { + /* Three operand format not allowed for TST, CMN, NEG and MVN. + (It isn't allowed for CMP either, but that isn't handled by this + function.) */ + if (inst.instruction == T_OPCODE_TST + || inst.instruction == T_OPCODE_CMN + || inst.instruction == T_OPCODE_NEG + || inst.instruction == T_OPCODE_MVN) + { + inst.error = BAD_ARGS; + return; + } + + if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL) + return; + + if (Rs != Rd) + { + inst.error = _("dest and source1 one must be the same register"); + return; + } + Rs = Rn; + } + + if (inst.instruction == T_OPCODE_MUL + && Rs == Rd) + as_tsktsk (_("Rs and Rd must be different in MUL")); + + inst.instruction |= Rd | (Rs << 3); + end_of_line (str); +} + +static void +do_t_add (str) + char * str; +{ + thumb_add_sub (str, 0); +} + +static void +do_t_asr (str) + char * str; +{ + thumb_shift (str, THUMB_ASR); +} + +static void +do_t_branch9 (str) + char * str; +{ + if (my_get_expression (&inst.reloc.exp, &str)) + return; + inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9; + inst.reloc.pc_rel = 1; + end_of_line (str); +} + +static void +do_t_branch12 (str) + char * str; +{ + if (my_get_expression (&inst.reloc.exp, &str)) + return; + inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12; + inst.reloc.pc_rel = 1; + end_of_line (str); +} + +/* Find the real, Thumb encoded start of a Thumb function. */ + +static symbolS * +find_real_start (symbolP) + symbolS * symbolP; +{ + char * real_start; + const char * name = S_GET_NAME (symbolP); + symbolS * new_target; + + /* This definiton must agree with the one in gcc/config/arm/thumb.c */ +#define STUB_NAME ".real_start_of" + + if (name == NULL) + abort(); + + /* Names that start with '.' are local labels, not function entry points. + The compiler may generate BL instructions to these labels because it + needs to perform a branch to a far away location. */ + if (name[0] == '.') + return symbolP; + + real_start = malloc (strlen (name) + strlen (STUB_NAME) + 1); + sprintf (real_start, "%s%s", STUB_NAME, name); + + new_target = symbol_find (real_start); + + if (new_target == NULL) + { + as_warn ("Failed to find real start of function: %s\n", name); + new_target = symbolP; + } + + free (real_start); + + return new_target; +} + + +static void +do_t_branch23 (str) + char * str; +{ + if (my_get_expression (& inst.reloc.exp, & str)) + return; + + inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23; + inst.reloc.pc_rel = 1; + end_of_line (str); + + /* If the destination of the branch is a defined symbol which does not have + the THUMB_FUNC attribute, then we must be calling a function which has + the (interfacearm) attribute. We look for the Thumb entry point to that + function and change the branch to refer to that function instead. */ + if ( inst.reloc.exp.X_op == O_symbol + && inst.reloc.exp.X_add_symbol != NULL + && S_IS_DEFINED (inst.reloc.exp.X_add_symbol) + && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol)) + inst.reloc.exp.X_add_symbol = find_real_start (inst.reloc.exp.X_add_symbol); +} + +static void +do_t_bx (str) + char * str; +{ + int reg; + + skip_whitespace (str); + + if ((reg = thumb_reg (&str, THUMB_REG_ANY)) == FAIL) + return; + + /* This sets THUMB_H2 from the top bit of reg. */ + inst.instruction |= reg << 3; + + /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc + should cause the alignment to be checked once it is known. This is + because BX PC only works if the instruction is word aligned. */ + + end_of_line (str); +} + +static void +do_t_compare (str) + char * str; +{ + thumb_mov_compare (str, THUMB_COMPARE); +} + +static void +do_t_ldmstm (str) + char * str; +{ + int Rb; + long range; + + skip_whitespace (str); + + if ((Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL) + return; + + if (*str != '!') + as_warn (_("Inserted missing '!': load/store multiple always writes back base register")); + else + str++; + + if (skip_past_comma (&str) == FAIL + || (range = reg_list (&str)) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (inst.reloc.type != BFD_RELOC_NONE) + { + /* This really doesn't seem worth it. */ + inst.reloc.type = BFD_RELOC_NONE; + inst.error = _("Expression too complex"); + return; + } + + if (range & ~0xff) + { + inst.error = _("only lo-regs valid in load/store multiple"); + return; + } + + inst.instruction |= (Rb << 8) | range; + end_of_line (str); +} + +static void +do_t_ldr (str) + char * str; +{ + thumb_load_store (str, THUMB_LOAD, THUMB_WORD); +} + +static void +do_t_ldrb (str) + char * str; +{ + thumb_load_store (str, THUMB_LOAD, THUMB_BYTE); +} + +static void +do_t_ldrh (str) + char * str; +{ + thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD); +} + +static void +do_t_lds (str) + char * str; +{ + int Rd, Rb, Ro; + + skip_whitespace (str); + + if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL + || skip_past_comma (&str) == FAIL + || *str++ != '[' + || (Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL + || skip_past_comma (&str) == FAIL + || (Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL + || *str++ != ']') + { + if (! inst.error) + inst.error = _("Syntax: ldrs[b] Rd, [Rb, Ro]"); + return; + } + + inst.instruction |= Rd | (Rb << 3) | (Ro << 6); + end_of_line (str); +} + +static void +do_t_lsl (str) + char * str; +{ + thumb_shift (str, THUMB_LSL); +} + +static void +do_t_lsr (str) + char * str; +{ + thumb_shift (str, THUMB_LSR); +} + +static void +do_t_mov (str) + char * str; +{ + thumb_mov_compare (str, THUMB_MOVE); +} + +static void +do_t_push_pop (str) + char * str; +{ + long range; + + skip_whitespace (str); + + if ((range = reg_list (&str)) == FAIL) + { + if (! inst.error) + inst.error = BAD_ARGS; + return; + } + + if (inst.reloc.type != BFD_RELOC_NONE) + { + /* This really doesn't seem worth it. */ + inst.reloc.type = BFD_RELOC_NONE; + inst.error = _("Expression too complex"); + return; + } + + if (range & ~0xff) + { + if ((inst.instruction == T_OPCODE_PUSH + && (range & ~0xff) == 1 << REG_LR) + || (inst.instruction == T_OPCODE_POP + && (range & ~0xff) == 1 << REG_PC)) + { + inst.instruction |= THUMB_PP_PC_LR; + range &= 0xff; + } + else + { + inst.error = _("invalid register list to push/pop instruction"); + return; + } + } + + inst.instruction |= range; + end_of_line (str); +} + +static void +do_t_str (str) + char * str; +{ + thumb_load_store (str, THUMB_STORE, THUMB_WORD); +} + +static void +do_t_strb (str) + char * str; +{ + thumb_load_store (str, THUMB_STORE, THUMB_BYTE); +} + +static void +do_t_strh (str) + char * str; +{ + thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD); +} + +static void +do_t_sub (str) + char * str; +{ + thumb_add_sub (str, 1); +} + +static void +do_t_swi (str) + char * str; +{ + skip_whitespace (str); + + if (my_get_expression (&inst.reloc.exp, &str)) + return; + + inst.reloc.type = BFD_RELOC_ARM_SWI; + end_of_line (str); + return; +} + +static void +do_t_adr (str) + char * str; +{ + int reg; + + /* This is a pseudo-op of the form "adr rd, label" to be converted + into a relative address of the form "add rd, pc, #label-.-4". */ + skip_whitespace (str); + + /* Store Rd in temporary location inside instruction. */ + if ((reg = reg_required_here (&str, 4)) == FAIL + || (reg > 7) /* For Thumb reg must be r0..r7. */ + || skip_past_comma (&str) == FAIL + || my_get_expression (&inst.reloc.exp, &str)) + { + if (!inst.error) + inst.error = BAD_ARGS; + return; + } + + inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD; + inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */ + inst.reloc.pc_rel = 1; + inst.instruction |= REG_PC; /* Rd is already placed into the instruction. */ + + end_of_line (str); +} + +static void +insert_reg (entry) + int entry; +{ + int len = strlen (reg_table[entry].name) + 2; + char * buf = (char *) xmalloc (len); + char * buf2 = (char *) xmalloc (len); + int i = 0; + +#ifdef REGISTER_PREFIX + buf[i++] = REGISTER_PREFIX; +#endif + + strcpy (buf + i, reg_table[entry].name); + + for (i = 0; buf[i]; i++) + buf2[i] = islower (buf[i]) ? toupper (buf[i]) : buf[i]; + + buf2[i] = '\0'; + + hash_insert (arm_reg_hsh, buf, (PTR) ®_table[entry]); + hash_insert (arm_reg_hsh, buf2, (PTR) ®_table[entry]); +} + +static void +insert_reg_alias (str, regnum) + char *str; + int regnum; +{ + struct reg_entry *new = + (struct reg_entry *)xmalloc (sizeof (struct reg_entry)); + char *name = xmalloc (strlen (str) + 1); + strcpy (name, str); + + new->name = name; + new->number = regnum; + + hash_insert (arm_reg_hsh, name, (PTR) new); +} + +static void +set_constant_flonums () +{ + int i; + + for (i = 0; i < NUM_FLOAT_VALS; i++) + if (atof_ieee ((char *)fp_const[i], 'x', fp_values[i]) == NULL) + abort (); +} + +void +md_begin () +{ + unsigned mach; + unsigned int i; + + if ( (arm_ops_hsh = hash_new ()) == NULL + || (arm_tops_hsh = hash_new ()) == NULL + || (arm_cond_hsh = hash_new ()) == NULL + || (arm_shift_hsh = hash_new ()) == NULL + || (arm_reg_hsh = hash_new ()) == NULL + || (arm_psr_hsh = hash_new ()) == NULL) + as_fatal (_("Virtual memory exhausted")); + + for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++) + hash_insert (arm_ops_hsh, insns[i].template, (PTR) (insns + i)); + for (i = 0; i < sizeof (tinsns) / sizeof (struct thumb_opcode); i++) + hash_insert (arm_tops_hsh, tinsns[i].template, (PTR) (tinsns + i)); + for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++) + hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i)); + for (i = 0; i < sizeof (shift) / sizeof (struct asm_shift); i++) + hash_insert (arm_shift_hsh, shift[i].template, (PTR) (shift + i)); + for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++) + hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i)); + + for (i = 0; reg_table[i].name; i++) + insert_reg (i); + + set_constant_flonums (); + +#if defined OBJ_COFF || defined OBJ_ELF + { + unsigned int flags = 0; + + /* Set the flags in the private structure. */ + if (uses_apcs_26) flags |= F_APCS26; + if (support_interwork) flags |= F_INTERWORK; + if (uses_apcs_float) flags |= F_APCS_FLOAT; + if (pic_code) flags |= F_PIC; + if ((cpu_variant & FPU_ALL) == FPU_NONE) flags |= F_SOFT_FLOAT; + + bfd_set_private_flags (stdoutput, flags); + } +#endif + + /* Record the CPU type as well. */ + switch (cpu_variant & ARM_CPU_MASK) + { + case ARM_2: + mach = bfd_mach_arm_2; + break; + + case ARM_3: /* Also ARM_250. */ + mach = bfd_mach_arm_2a; + break; + + default: + case ARM_6 | ARM_3 | ARM_2: /* Actually no CPU type defined. */ + mach = bfd_mach_arm_4; + break; + + case ARM_7: /* Also ARM_6. */ + mach = bfd_mach_arm_3; + break; + } + + /* Catch special cases. */ + if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT)) + { + if (cpu_variant & (ARM_EXT_V5 & ARM_THUMB)) + mach = bfd_mach_arm_5T; + else if (cpu_variant & ARM_EXT_V5) + mach = bfd_mach_arm_5; + else if (cpu_variant & ARM_THUMB) + mach = bfd_mach_arm_4T; + else if ((cpu_variant & ARM_ARCH_V4) == ARM_ARCH_V4) + mach = bfd_mach_arm_4; + else if (cpu_variant & ARM_LONGMUL) + mach = bfd_mach_arm_3M; + } + + bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach); +} + +/* Turn an integer of n bytes (in val) into a stream of bytes appropriate + for use in the a.out file, and stores them in the array pointed to by buf. + This knows about the endian-ness of the target machine and does + THE RIGHT THING, whatever it is. Possible values for n are 1 (byte) + 2 (short) and 4 (long) Floating numbers are put out as a series of + LITTLENUMS (shorts, here at least). */ +void +md_number_to_chars (buf, val, n) + char * buf; + valueT val; + int n; +{ + if (target_big_endian) + number_to_chars_bigendian (buf, val, n); + else + number_to_chars_littleendian (buf, val, n); +} + +static valueT +md_chars_to_number (buf, n) + char * buf; + int n; +{ + valueT result = 0; + unsigned char * where = (unsigned char *) buf; + + if (target_big_endian) + { + while (n--) + { + result <<= 8; + result |= (*where++ & 255); + } + } + else + { + while (n--) + { + result <<= 8; + result |= (where[n] & 255); + } + } + + return result; +} + +/* Turn a string in input_line_pointer into a floating point constant + of type TYPE, and store the appropriate bytes in *litP. The number + of LITTLENUMS emitted is stored in *sizeP . An error message is + returned, or NULL on OK. + + Note that fp constants aren't represent in the normal way on the ARM. + In big endian mode, things are as expected. However, in little endian + mode fp constants are big-endian word-wise, and little-endian byte-wise + within the words. For example, (double) 1.1 in big endian mode is + the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is + the byte sequence 99 99 f1 3f 9a 99 99 99. + + ??? The format of 12 byte floats is uncertain according to gcc's arm.h. */ + +char * +md_atof (type, litP, sizeP) + char type; + char * litP; + int * sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + char *t; + int i; + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP = 0; + return _("Bad call to MD_ATOF()"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + *sizeP = prec * 2; + + if (target_big_endian) + { + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + } + else + { + /* For a 4 byte float the order of elements in `words' is 1 0. For an + 8 byte float the order is 1 0 3 2. */ + for (i = 0; i < prec; i += 2) + { + md_number_to_chars (litP, (valueT) words[i + 1], 2); + md_number_to_chars (litP + 2, (valueT) words[i], 2); + litP += 4; + } + } + + return 0; +} + +/* The knowledge of the PC's pipeline offset is built into the insns themselves. */ +long +md_pcrel_from (fixP) + fixS * fixP; +{ + if ( fixP->fx_addsy + && S_GET_SEGMENT (fixP->fx_addsy) == undefined_section + && fixP->fx_subsy == NULL) + return 0; + + if (fixP->fx_pcrel && (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_ADD)) + { + /* PC relative addressing on the Thumb is slightly odd + as the bottom two bits of the PC are forced to zero + for the calculation. */ + return (fixP->fx_where + fixP->fx_frag->fr_address) & ~3; + } + +#ifdef TE_WINCE + /* The pattern was adjusted to accomodate CE's off-by-one fixups, + so we un-adjust here to compensate for the accomodation. */ + return fixP->fx_where + fixP->fx_frag->fr_address + 8; +#else + return fixP->fx_where + fixP->fx_frag->fr_address; +#endif +} + +/* Round up a section size to the appropriate boundary. */ +valueT +md_section_align (segment, size) + segT segment ATTRIBUTE_UNUSED; + valueT size; +{ +#ifdef OBJ_ELF + return size; +#else + /* Round all sects to multiple of 4 */ + return (size + 3) & ~3; +#endif +} + +/* Under ELF we need to default _GLOBAL_OFFSET_TABLE. Otherwise + we have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS * +md_undefined_symbol (name) + char * name ATTRIBUTE_UNUSED; +{ +#ifdef OBJ_ELF + if (name[0] == '_' && name[1] == 'G' + && streq (name, GLOBAL_OFFSET_TABLE_NAME)) + { + if (!GOT_symbol) + { + if (symbol_find (name)) + as_bad ("GOT already in the symbol table"); + + GOT_symbol = symbol_new (name, undefined_section, + (valueT)0, & zero_address_frag); + } + + return GOT_symbol; + } +#endif + + return 0; +} + +/* arm_reg_parse () := if it looks like a register, return its token and + advance the pointer. */ + +static int +arm_reg_parse (ccp) + register char ** ccp; +{ + char * start = * ccp; + char c; + char * p; + struct reg_entry * reg; + +#ifdef REGISTER_PREFIX + if (*start != REGISTER_PREFIX) + return FAIL; + p = start + 1; +#else + p = start; +#ifdef OPTIONAL_REGISTER_PREFIX + if (*p == OPTIONAL_REGISTER_PREFIX) + p++, start++; +#endif +#endif + if (!isalpha (*p) || !is_name_beginner (*p)) + return FAIL; + + c = *p++; + while (isalpha (c) || isdigit (c) || c == '_') + c = *p++; + + *--p = 0; + reg = (struct reg_entry *) hash_find (arm_reg_hsh, start); + *p = c; + + if (reg) + { + *ccp = p; + return reg->number; + } + + return FAIL; +} + +int +md_apply_fix3 (fixP, val, seg) + fixS * fixP; + valueT * val; + segT seg; +{ + offsetT value = * val; + offsetT newval; + unsigned int newimm; + unsigned long temp; + int sign; + char * buf = fixP->fx_where + fixP->fx_frag->fr_literal; + arm_fix_data * arm_data = (arm_fix_data *) fixP->tc_fix_data; + + assert (fixP->fx_r_type < BFD_RELOC_UNUSED); + + /* Note whether this will delete the relocation. */ +#if 0 /* patch from REarnshaw to JDavis (disabled for the moment, since it doesn't work fully) */ + if ((fixP->fx_addsy == 0 || symbol_constant_p (fixP->fx_addsy)) + && !fixP->fx_pcrel) +#else + if (fixP->fx_addsy == 0 && !fixP->fx_pcrel) +#endif + fixP->fx_done = 1; + + /* If this symbol is in a different section then we need to leave it for + the linker to deal with. Unfortunately, md_pcrel_from can't tell, + so we have to undo it's effects here. */ + if (fixP->fx_pcrel) + { + if (fixP->fx_addsy != NULL + && S_IS_DEFINED (fixP->fx_addsy) + && S_GET_SEGMENT (fixP->fx_addsy) != seg) + { + if (target_oabi + && (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH + )) + value = 0; + else + value += md_pcrel_from (fixP); + } + } + + fixP->fx_addnumber = value; /* Remember value for emit_reloc. */ + + switch (fixP->fx_r_type) + { + case BFD_RELOC_ARM_IMMEDIATE: + newimm = validate_immediate (value); + temp = md_chars_to_number (buf, INSN_SIZE); + + /* If the instruction will fail, see if we can fix things up by + changing the opcode. */ + if (newimm == (unsigned int) FAIL + && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("invalid constant (%lx) after fixup"), + (unsigned long) value); + break; + } + + newimm |= (temp & 0xfffff000); + md_number_to_chars (buf, (valueT) newimm, INSN_SIZE); + break; + + case BFD_RELOC_ARM_ADRL_IMMEDIATE: + { + unsigned int highpart = 0; + unsigned int newinsn = 0xe1a00000; /* nop */ + newimm = validate_immediate (value); + temp = md_chars_to_number (buf, INSN_SIZE); + + /* If the instruction will fail, see if we can fix things up by + changing the opcode. */ + if (newimm == (unsigned int) FAIL + && (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL) + { + /* No ? OK - try using two ADD instructions to generate the value. */ + newimm = validate_immediate_twopart (value, & highpart); + + /* Yes - then make sure that the second instruction is also an add. */ + if (newimm != (unsigned int) FAIL) + newinsn = temp; + /* Still No ? Try using a negated value. */ + else if (validate_immediate_twopart (- value, & highpart) != (unsigned int) FAIL) + temp = newinsn = (temp & OPCODE_MASK) | OPCODE_SUB << DATA_OP_SHIFT; + /* Otherwise - give up. */ + else + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Unable to compute ADRL instructions for PC offset of 0x%x"), value); + break; + } + + /* Replace the first operand in the 2nd instruction (which is the PC) + with the destination register. We have already added in the PC in the + first instruction and we do not want to do it again. */ + newinsn &= ~ 0xf0000; + newinsn |= ((newinsn & 0x0f000) << 4); + } + + newimm |= (temp & 0xfffff000); + md_number_to_chars (buf, (valueT) newimm, INSN_SIZE); + + highpart |= (newinsn & 0xfffff000); + md_number_to_chars (buf + INSN_SIZE, (valueT) highpart, INSN_SIZE); + } + break; + + case BFD_RELOC_ARM_OFFSET_IMM: + sign = value >= 0; + + if (value < 0) + value = - value; + + if (validate_offset_imm (value, 0) == FAIL) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("bad immediate value for offset (%ld)"), (long) value); + break; + } + + newval = md_chars_to_number (buf, INSN_SIZE); + newval &= 0xff7ff000; + newval |= value | (sign ? INDEX_UP : 0); + md_number_to_chars (buf, newval, INSN_SIZE); + break; + + case BFD_RELOC_ARM_OFFSET_IMM8: + case BFD_RELOC_ARM_HWLITERAL: + sign = value >= 0; + + if (value < 0) + value = - value; + + if (validate_offset_imm (value, 1) == FAIL) + { + if (fixP->fx_r_type == BFD_RELOC_ARM_HWLITERAL) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("invalid literal constant: pool needs to be closer")); + else + as_bad (_("bad immediate value for half-word offset (%ld)"), + (long) value); + break; + } + + newval = md_chars_to_number (buf, INSN_SIZE); + newval &= 0xff7ff0f0; + newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0); + md_number_to_chars (buf, newval, INSN_SIZE); + break; + + case BFD_RELOC_ARM_LITERAL: + sign = value >= 0; + + if (value < 0) + value = - value; + + if (validate_offset_imm (value, 0) == FAIL) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("invalid literal constant: pool needs to be closer")); + break; + } + + newval = md_chars_to_number (buf, INSN_SIZE); + newval &= 0xff7ff000; + newval |= value | (sign ? INDEX_UP : 0); + md_number_to_chars (buf, newval, INSN_SIZE); + break; + + case BFD_RELOC_ARM_SHIFT_IMM: + newval = md_chars_to_number (buf, INSN_SIZE); + if (((unsigned long) value) > 32 + || (value == 32 + && (((newval & 0x60) == 0) || (newval & 0x60) == 0x60))) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("shift expression is too large")); + break; + } + + if (value == 0) + newval &= ~0x60; /* Shifts of zero must be done as lsl */ + else if (value == 32) + value = 0; + newval &= 0xfffff07f; + newval |= (value & 0x1f) << 7; + md_number_to_chars (buf, newval , INSN_SIZE); + break; + + case BFD_RELOC_ARM_SWI: + if (arm_data->thumb_mode) + { + if (((unsigned long) value) > 0xff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid swi expression")); + newval = md_chars_to_number (buf, THUMB_SIZE) & 0xff00; + newval |= value; + md_number_to_chars (buf, newval, THUMB_SIZE); + } + else + { + if (((unsigned long) value) > 0x00ffffff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid swi expression")); + newval = md_chars_to_number (buf, INSN_SIZE) & 0xff000000; + newval |= value; + md_number_to_chars (buf, newval , INSN_SIZE); + } + break; + + case BFD_RELOC_ARM_MULTI: + if (((unsigned long) value) > 0xffff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid expression in load/store multiple")); + newval = value | md_chars_to_number (buf, INSN_SIZE); + md_number_to_chars (buf, newval, INSN_SIZE); + break; + + case BFD_RELOC_ARM_PCREL_BRANCH: + newval = md_chars_to_number (buf, INSN_SIZE); + + /* Sign-extend a 24-bit number. */ +#define SEXT24(x) ((((x) & 0xffffff) ^ (~ 0x7fffff)) + 0x800000) + +#ifdef OBJ_ELF + if (! target_oabi) + value = fixP->fx_offset; +#endif + + /* We are going to store value (shifted right by two) in the + instruction, in a 24 bit, signed field. Thus we need to check + that none of the top 8 bits of the shifted value (top 7 bits of + the unshifted, unsigned value) are set, or that they are all set. */ + if ((value & 0xfe000000UL) != 0 + && ((value & 0xfe000000UL) != 0xfe000000UL)) + { +#ifdef OBJ_ELF + /* Normally we would be stuck at this point, since we cannot store + the absolute address that is the destination of the branch in the + 24 bits of the branch instruction. If however, we happen to know + that the destination of the branch is in the same section as the + branch instruciton itself, then we can compute the relocation for + ourselves and not have to bother the linker with it. + + FIXME: The tests for OBJ_ELF and ! target_oabi are only here + because I have not worked out how to do this for OBJ_COFF or + target_oabi. */ + if (! target_oabi + && fixP->fx_addsy != NULL + && S_IS_DEFINED (fixP->fx_addsy) + && S_GET_SEGMENT (fixP->fx_addsy) == seg) + { + /* Get pc relative value to go into the branch. */ + value = * val; + + /* Permit a backward branch provided that enough bits are set. + Allow a forwards branch, provided that enough bits are clear. */ + if ((value & 0xfe000000UL) == 0xfe000000UL + || (value & 0xfe000000UL) == 0) + fixP->fx_done = 1; + } + + if (! fixP->fx_done) +#endif + as_bad_where (fixP->fx_file, fixP->fx_line, + _("gas can't handle same-section branch dest >= 0x04000000")); + } + + value >>= 2; + value += SEXT24 (newval); + + if ((value & 0xff000000UL) != 0 + && ((value & 0xff000000UL) != 0xff000000UL)) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("out of range branch")); + + newval = (value & 0x00ffffff) | (newval & 0xff000000); + md_number_to_chars (buf, newval, INSN_SIZE); + break; + + + case BFD_RELOC_THUMB_PCREL_BRANCH9: /* conditional branch */ + newval = md_chars_to_number (buf, THUMB_SIZE); + { + addressT diff = (newval & 0xff) << 1; + if (diff & 0x100) + diff |= ~0xff; + + value += diff; + if ((value & ~0xff) && ((value & ~0xff) != ~0xff)) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Branch out of range")); + newval = (newval & 0xff00) | ((value & 0x1ff) >> 1); + } + md_number_to_chars (buf, newval, THUMB_SIZE); + break; + + case BFD_RELOC_THUMB_PCREL_BRANCH12: /* unconditional branch */ + newval = md_chars_to_number (buf, THUMB_SIZE); + { + addressT diff = (newval & 0x7ff) << 1; + if (diff & 0x800) + diff |= ~0x7ff; + + value += diff; + if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff)) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Branch out of range")); + newval = (newval & 0xf800) | ((value & 0xfff) >> 1); + } + md_number_to_chars (buf, newval, THUMB_SIZE); + break; + + case BFD_RELOC_THUMB_PCREL_BRANCH23: + { + offsetT newval2; + addressT diff; + + newval = md_chars_to_number (buf, THUMB_SIZE); + newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE); + diff = ((newval & 0x7ff) << 12) | ((newval2 & 0x7ff) << 1); + if (diff & 0x400000) + diff |= ~0x3fffff; +#ifdef OBJ_ELF + value = fixP->fx_offset; +#endif + value += diff; + if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff)) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Branch with link out of range")); + + newval = (newval & 0xf800) | ((value & 0x7fffff) >> 12); + newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1); + md_number_to_chars (buf, newval, THUMB_SIZE); + md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE); + } + break; + + case BFD_RELOC_8: + if (fixP->fx_done || fixP->fx_pcrel) + md_number_to_chars (buf, value, 1); +#ifdef OBJ_ELF + else if (!target_oabi) + { + value = fixP->fx_offset; + md_number_to_chars (buf, value, 1); + } +#endif + break; + + case BFD_RELOC_16: + if (fixP->fx_done || fixP->fx_pcrel) + md_number_to_chars (buf, value, 2); +#ifdef OBJ_ELF + else if (!target_oabi) + { + value = fixP->fx_offset; + md_number_to_chars (buf, value, 2); + } +#endif + break; + +#ifdef OBJ_ELF + case BFD_RELOC_ARM_GOT32: + case BFD_RELOC_ARM_GOTOFF: + md_number_to_chars (buf, 0, 4); + break; +#endif + + case BFD_RELOC_RVA: + case BFD_RELOC_32: + if (fixP->fx_done || fixP->fx_pcrel) + md_number_to_chars (buf, value, 4); +#ifdef OBJ_ELF + else if (!target_oabi) + { + value = fixP->fx_offset; + md_number_to_chars (buf, value, 4); + } +#endif + break; + +#ifdef OBJ_ELF + case BFD_RELOC_ARM_PLT32: + /* It appears the instruction is fully prepared at this point. */ + break; +#endif + + case BFD_RELOC_ARM_GOTPC: + md_number_to_chars (buf, value, 4); + break; + + case BFD_RELOC_ARM_CP_OFF_IMM: + sign = value >= 0; + if (value < -1023 || value > 1023 || (value & 3)) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Illegal value for co-processor offset")); + if (value < 0) + value = -value; + newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00; + newval |= (value >> 2) | (sign ? INDEX_UP : 0); + md_number_to_chars (buf, newval , INSN_SIZE); + break; + + case BFD_RELOC_ARM_THUMB_OFFSET: + newval = md_chars_to_number (buf, THUMB_SIZE); + /* Exactly what ranges, and where the offset is inserted depends on + the type of instruction, we can establish this from the top 4 bits */ + switch (newval >> 12) + { + case 4: /* PC load */ + /* Thumb PC loads are somewhat odd, bit 1 of the PC is + forced to zero for these loads, so we will need to round + up the offset if the instruction address is not word + aligned (since the final address produced must be, and + we can only describe word-aligned immediate offsets). */ + + if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid offset, target not word aligned (0x%08X)"), + (unsigned int)(fixP->fx_frag->fr_address + fixP->fx_where + value)); + + if ((value + 2) & ~0x3fe) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid offset, value too big (0x%08X)"), value); + + /* Round up, since pc will be rounded down. */ + newval |= (value + 2) >> 2; + break; + + case 9: /* SP load/store */ + if (value & ~0x3fc) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid offset, value too big (0x%08X)"), value); + newval |= value >> 2; + break; + + case 6: /* Word load/store */ + if (value & ~0x7c) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid offset, value too big (0x%08X)"), value); + newval |= value << 4; /* 6 - 2 */ + break; + + case 7: /* Byte load/store */ + if (value & ~0x1f) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid offset, value too big (0x%08X)"), value); + newval |= value << 6; + break; + + case 8: /* Halfword load/store */ + if (value & ~0x3e) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid offset, value too big (0x%08X)"), value); + newval |= value << 5; /* 6 - 1 */ + break; + + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + "Unable to process relocation for thumb opcode: %lx", + (unsigned long) newval); + break; + } + md_number_to_chars (buf, newval, THUMB_SIZE); + break; + + case BFD_RELOC_ARM_THUMB_ADD: + /* This is a complicated relocation, since we use it for all of + the following immediate relocations: + 3bit ADD/SUB + 8bit ADD/SUB + 9bit ADD/SUB SP word-aligned + 10bit ADD PC/SP word-aligned + + The type of instruction being processed is encoded in the + instruction field: + 0x8000 SUB + 0x00F0 Rd + 0x000F Rs + */ + newval = md_chars_to_number (buf, THUMB_SIZE); + { + int rd = (newval >> 4) & 0xf; + int rs = newval & 0xf; + int subtract = newval & 0x8000; + + if (rd == REG_SP) + { + if (value & ~0x1fc) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid immediate for stack address calculation")); + newval = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST; + newval |= value >> 2; + } + else if (rs == REG_PC || rs == REG_SP) + { + if (subtract || + value & ~0x3fc) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid immediate for address calculation (value = 0x%08lX)"), + (unsigned long) value); + newval = (rs == REG_PC ? T_OPCODE_ADD_PC : T_OPCODE_ADD_SP); + newval |= rd << 8; + newval |= value >> 2; + } + else if (rs == rd) + { + if (value & ~0xff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid 8bit immediate")); + newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8; + newval |= (rd << 8) | value; + } + else + { + if (value & ~0x7) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid 3bit immediate")); + newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3; + newval |= rd | (rs << 3) | (value << 6); + } + } + md_number_to_chars (buf, newval , THUMB_SIZE); + break; + + case BFD_RELOC_ARM_THUMB_IMM: + newval = md_chars_to_number (buf, THUMB_SIZE); + switch (newval >> 11) + { + case 0x04: /* 8bit immediate MOV */ + case 0x05: /* 8bit immediate CMP */ + if (value < 0 || value > 255) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid immediate: %ld is too large"), + (long) value); + newval |= value; + break; + + default: + abort (); + } + md_number_to_chars (buf, newval , THUMB_SIZE); + break; + + case BFD_RELOC_ARM_THUMB_SHIFT: + /* 5bit shift value (0..31) */ + if (value < 0 || value > 31) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Illegal Thumb shift value: %ld"), (long) value); + newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f; + newval |= value << 6; + md_number_to_chars (buf, newval , THUMB_SIZE); + break; + + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + return 1; + + case BFD_RELOC_NONE: + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Bad relocation fixup type (%d)"), fixP->fx_r_type); + } + + return 1; +} + +/* Translate internal representation of relocation info to BFD target + format. */ +arelent * +tc_gen_reloc (section, fixp) + asection * section ATTRIBUTE_UNUSED; + fixS * fixp; +{ + arelent * reloc; + bfd_reloc_code_real_type code; + + reloc = (arelent *) xmalloc (sizeof (arelent)); + + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + + /* @@ Why fx_addnumber sometimes and fx_offset other times? */ +#ifndef OBJ_ELF + if (fixp->fx_pcrel == 0) + reloc->addend = fixp->fx_offset; + else + reloc->addend = fixp->fx_offset = reloc->address; +#else /* OBJ_ELF */ + reloc->addend = fixp->fx_offset; +#endif + + switch (fixp->fx_r_type) + { + case BFD_RELOC_8: + if (fixp->fx_pcrel) + { + code = BFD_RELOC_8_PCREL; + break; + } + + case BFD_RELOC_16: + if (fixp->fx_pcrel) + { + code = BFD_RELOC_16_PCREL; + break; + } + + case BFD_RELOC_32: + if (fixp->fx_pcrel) + { + code = BFD_RELOC_32_PCREL; + break; + } + + case BFD_RELOC_ARM_PCREL_BRANCH: + case BFD_RELOC_RVA: + case BFD_RELOC_THUMB_PCREL_BRANCH9: + case BFD_RELOC_THUMB_PCREL_BRANCH12: + case BFD_RELOC_THUMB_PCREL_BRANCH23: + case BFD_RELOC_VTABLE_ENTRY: + case BFD_RELOC_VTABLE_INHERIT: + code = fixp->fx_r_type; + break; + + case BFD_RELOC_ARM_LITERAL: + case BFD_RELOC_ARM_HWLITERAL: + /* If this is called then the a literal has been referenced across + a section boundary - possibly due to an implicit dump */ + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Literal referenced across section boundary (Implicit dump?)")); + return NULL; + +#ifdef OBJ_ELF + case BFD_RELOC_ARM_GOT32: + case BFD_RELOC_ARM_GOTOFF: + case BFD_RELOC_ARM_PLT32: + code = fixp->fx_r_type; + break; +#endif + + case BFD_RELOC_ARM_IMMEDIATE: + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Internal_relocation (type %d) not fixed up (IMMEDIATE)"), + fixp->fx_r_type); + return NULL; + + case BFD_RELOC_ARM_ADRL_IMMEDIATE: + as_bad_where (fixp->fx_file, fixp->fx_line, + _("ADRL used for a symbol not defined in the same file"), + fixp->fx_r_type); + return NULL; + + case BFD_RELOC_ARM_OFFSET_IMM: + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Internal_relocation (type %d) not fixed up (OFFSET_IMM)"), + fixp->fx_r_type); + return NULL; + + default: + { + char * type; + switch (fixp->fx_r_type) + { + case BFD_RELOC_ARM_IMMEDIATE: type = "IMMEDIATE"; break; + case BFD_RELOC_ARM_OFFSET_IMM: type = "OFFSET_IMM"; break; + case BFD_RELOC_ARM_OFFSET_IMM8: type = "OFFSET_IMM8"; break; + case BFD_RELOC_ARM_SHIFT_IMM: type = "SHIFT_IMM"; break; + case BFD_RELOC_ARM_SWI: type = "SWI"; break; + case BFD_RELOC_ARM_MULTI: type = "MULTI"; break; + case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break; + case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break; + case BFD_RELOC_ARM_THUMB_SHIFT: type = "THUMB_SHIFT"; break; + case BFD_RELOC_ARM_THUMB_IMM: type = "THUMB_IMM"; break; + case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break; + default: type = _(""); break; + } + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Can not represent %s relocation in this object file format (%d)"), + type, fixp->fx_pcrel); + return NULL; + } + } + +#ifdef OBJ_ELF + if (code == BFD_RELOC_32_PCREL + && GOT_symbol + && fixp->fx_addsy == GOT_symbol) + { + code = BFD_RELOC_ARM_GOTPC; + reloc->addend = fixp->fx_offset = reloc->address; + } +#endif + + reloc->howto = bfd_reloc_type_lookup (stdoutput, code); + + if (reloc->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Can not represent %s relocation in this object file format"), + bfd_get_reloc_code_name (code)); + return NULL; + } + + /* HACK: Since arm ELF uses Rel instead of Rela, encode the + vtable entry to be used in the relocation's section offset. */ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + reloc->address = fixp->fx_offset; + + return reloc; +} + +int +md_estimate_size_before_relax (fragP, segtype) + fragS * fragP ATTRIBUTE_UNUSED; + segT segtype ATTRIBUTE_UNUSED; +{ + as_fatal (_("md_estimate_size_before_relax\n")); + return 1; +} + +static void +output_inst PARAMS ((void)) +{ + char * to = NULL; + + if (inst.error) + { + as_bad (inst.error); + return; + } + + to = frag_more (inst.size); + + if (thumb_mode && (inst.size > THUMB_SIZE)) + { + assert (inst.size == (2 * THUMB_SIZE)); + md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE); + md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE); + } + else if (inst.size > INSN_SIZE) + { + assert (inst.size == (2 * INSN_SIZE)); + md_number_to_chars (to, inst.instruction, INSN_SIZE); + md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE); + } + else + md_number_to_chars (to, inst.instruction, inst.size); + + if (inst.reloc.type != BFD_RELOC_NONE) + fix_new_arm (frag_now, to - frag_now->fr_literal, + inst.size, & inst.reloc.exp, inst.reloc.pc_rel, + inst.reloc.type); + + return; +} + +void +md_assemble (str) + char * str; +{ + char c; + char * p; + char * q; + char * start; + + /* Align the instruction. + This may not be the right thing to do but ... */ + /* arm_align (2, 0); */ + listing_prev_line (); /* Defined in listing.h */ + + /* Align the previous label if needed. */ + if (last_label_seen != NULL) + { + symbol_set_frag (last_label_seen, frag_now); + S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ()); + S_SET_SEGMENT (last_label_seen, now_seg); + } + + memset (&inst, '\0', sizeof (inst)); + inst.reloc.type = BFD_RELOC_NONE; + + skip_whitespace (str); + + /* Scan up to the end of the op-code, which must end in white space or + end of string. */ + for (start = p = str; *p != '\0'; p++) + if (*p == ' ') + break; + + if (p == str) + { + as_bad (_("No operator -- statement `%s'\n"), str); + return; + } + + if (thumb_mode) + { + CONST struct thumb_opcode * opcode; + + c = *p; + *p = '\0'; + opcode = (CONST struct thumb_opcode *) hash_find (arm_tops_hsh, str); + *p = c; + + if (opcode) + { + /* Check that this instruction is supported for this CPU. */ + if (thumb_mode == 1 && (opcode->variants & cpu_variant) == 0) + { + as_bad (_("selected processor does not support this opcode")); + return; + } + + inst.instruction = opcode->value; + inst.size = opcode->size; + (*opcode->parms)(p); + output_inst (); + return; + } + } + else + { + CONST struct asm_opcode * opcode; + unsigned long cond_code; + + inst.size = INSN_SIZE; + /* p now points to the end of the opcode, probably white space, but we + have to break the opcode up in case it contains condionals and flags; + keep trying with progressively smaller basic instructions until one + matches, or we run out of opcode. */ + q = (p - str > LONGEST_INST) ? str + LONGEST_INST : p; + for (; q != str; q--) + { + c = *q; + *q = '\0'; + opcode = (CONST struct asm_opcode *) hash_find (arm_ops_hsh, str); + *q = c; + + if (opcode && opcode->template) + { + unsigned long flag_bits = 0; + char * r; + + /* Check that this instruction is supported for this CPU. */ + if ((opcode->variants & cpu_variant) == 0) + goto try_shorter; + + inst.instruction = opcode->value; + if (q == p) /* Just a simple opcode. */ + { + if (opcode->comp_suffix) + { + if (*opcode->comp_suffix != '\0') + as_bad (_("Opcode `%s' must have suffix from list: <%s>"), + str, opcode->comp_suffix); + else + /* Not a conditional instruction. */ + (*opcode->parms)(q, 0); + } + else + { + /* A conditional instruction with default condition. */ + inst.instruction |= COND_ALWAYS; + (*opcode->parms)(q, 0); + } + output_inst (); + return; + } + + /* Not just a simple opcode. Check if extra is a conditional. */ + r = q; + if (p - r >= 2) + { + CONST struct asm_cond *cond; + char d = *(r + 2); + + *(r + 2) = '\0'; + cond = (CONST struct asm_cond *) hash_find (arm_cond_hsh, r); + *(r + 2) = d; + if (cond) + { + if (cond->value == 0xf0000000) + as_tsktsk ( +_("Warning: Use of the 'nv' conditional is deprecated\n")); + + cond_code = cond->value; + r += 2; + } + else + cond_code = COND_ALWAYS; + } + else + cond_code = COND_ALWAYS; + + /* Apply the conditional, or complain it's not allowed. */ + if (opcode->comp_suffix && *opcode->comp_suffix == '\0') + { + /* Instruction isn't conditional */ + if (cond_code != COND_ALWAYS) + { + as_bad (_("Opcode `%s' is unconditional\n"), str); + return; + } + } + else + /* Instruction is conditional: set the condition into it. */ + inst.instruction |= cond_code; + + + /* If there is a compulsory suffix, it should come here, before + any optional flags. */ + if (opcode->comp_suffix && *opcode->comp_suffix != '\0') + { + CONST char *s = opcode->comp_suffix; + + while (*s) + { + inst.suffix++; + if (*r == *s) + break; + s++; + } + + if (*s == '\0') + { + as_bad (_("Opcode `%s' must have suffix from <%s>\n"), str, + opcode->comp_suffix); + return; + } + + r++; + } + + /* The remainder, if any should now be flags for the instruction; + Scan these checking each one found with the opcode. */ + if (r != p) + { + char d; + CONST struct asm_flg *flag = opcode->flags; + + if (flag) + { + int flagno; + + d = *p; + *p = '\0'; + + for (flagno = 0; flag[flagno].template; flagno++) + { + if (streq (r, flag[flagno].template)) + { + flag_bits |= flag[flagno].set_bits; + break; + } + } + + *p = d; + if (! flag[flagno].template) + goto try_shorter; + } + else + goto try_shorter; + } + + (*opcode->parms) (p, flag_bits); + output_inst (); + return; + } + + try_shorter: + ; + } + } + + /* It wasn't an instruction, but it might be a register alias of the form + alias .req reg */ + q = p; + skip_whitespace (q); + + c = *p; + *p = '\0'; + + if (*q && !strncmp (q, ".req ", 4)) + { + int reg; + char * copy_of_str = str; + char * r; + + q += 4; + skip_whitespace (q); + + for (r = q; *r != '\0'; r++) + if (*r == ' ') + break; + + if (r != q) + { + int regnum; + char d = *r; + + *r = '\0'; + regnum = arm_reg_parse (& q); + *r = d; + + reg = arm_reg_parse (& str); + + if (reg == FAIL) + { + if (regnum != FAIL) + insert_reg_alias (str, regnum); + else + as_warn (_("register '%s' does not exist\n"), q); + } + else if (regnum != FAIL) + { + if (reg != regnum) + as_warn (_("ignoring redefinition of register alias '%s'"), + copy_of_str ); + + /* Do not warn about redefinitions to the same alias. */ + } + else + as_warn (_("ignoring redefinition of register alias '%s' to non-existant register '%s'"), + copy_of_str, q); + } + else + as_warn (_("ignoring incomplete .req pseuso op")); + + *p = c; + return; + } + + *p = c; + as_bad (_("bad instruction `%s'"), start); +} + +/* + * md_parse_option + * Invocation line includes a switch not recognized by the base assembler. + * See if it's a processor-specific option. These are: + * Cpu variants, the arm part is optional: + * -m[arm]1 Currently not supported. + * -m[arm]2, -m[arm]250 Arm 2 and Arm 250 processor + * -m[arm]3 Arm 3 processor + * -m[arm]6[xx], Arm 6 processors + * -m[arm]7[xx][t][[d]m] Arm 7 processors + * -m[arm]8[10] Arm 8 processors + * -m[arm]9[20][tdmi] Arm 9 processors + * -mstrongarm[110[0]] StrongARM processors + * -m[arm]v[2345[t]] Arm architectures + * -mall All (except the ARM1) + * FP variants: + * -mfpa10, -mfpa11 FPA10 and 11 co-processor instructions + * -mfpe-old (No float load/store multiples) + * -mno-fpu Disable all floating point instructions + * Run-time endian selection: + * -EB big endian cpu + * -EL little endian cpu + * ARM Procedure Calling Standard: + * -mapcs-32 32 bit APCS + * -mapcs-26 26 bit APCS + * -mapcs-float Pass floats in float regs + * -mapcs-reentrant Position independent code + * -mthumb-interwork Code supports Arm/Thumb interworking + * -moabi Old ELF ABI + */ + +CONST char * md_shortopts = "m:k"; +struct option md_longopts[] = +{ +#ifdef ARM_BI_ENDIAN +#define OPTION_EB (OPTION_MD_BASE + 0) + {"EB", no_argument, NULL, OPTION_EB}, +#define OPTION_EL (OPTION_MD_BASE + 1) + {"EL", no_argument, NULL, OPTION_EL}, +#ifdef OBJ_ELF +#define OPTION_OABI (OPTION_MD_BASE +2) + {"oabi", no_argument, NULL, OPTION_OABI}, +#endif +#endif + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof (md_longopts); + +int +md_parse_option (c, arg) + int c; + char * arg; +{ + char * str = arg; + + switch (c) + { +#ifdef ARM_BI_ENDIAN + case OPTION_EB: + target_big_endian = 1; + break; + case OPTION_EL: + target_big_endian = 0; + break; +#endif + + case 'm': + switch (*str) + { + case 'f': + if (streq (str, "fpa10")) + cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA10; + else if (streq (str, "fpa11")) + cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA11; + else if (streq (str, "fpe-old")) + cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_CORE; + else + goto bad; + break; + + case 'n': + if (streq (str, "no-fpu")) + cpu_variant &= ~FPU_ALL; + break; + +#ifdef OBJ_ELF + case 'o': + if (streq (str, "oabi")) + target_oabi = true; + break; +#endif + + case 't': + /* Limit assembler to generating only Thumb instructions: */ + if (streq (str, "thumb")) + { + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB; + cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_NONE; + thumb_mode = 1; + } + else if (streq (str, "thumb-interwork")) + { + if ((cpu_variant & ARM_THUMB) == 0) + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4T; +#if defined OBJ_COFF || defined OBJ_ELF + support_interwork = true; +#endif + } + else + goto bad; + break; + + default: + if (streq (str, "all")) + { + cpu_variant = ARM_ALL | FPU_ALL; + return 1; + } +#if defined OBJ_COFF || defined OBJ_ELF + if (! strncmp (str, "apcs-", 5)) + { + /* GCC passes on all command line options starting "-mapcs-..." + to us, so we must parse them here. */ + + str += 5; + + if (streq (str, "32")) + { + uses_apcs_26 = false; + return 1; + } + else if (streq (str, "26")) + { + uses_apcs_26 = true; + return 1; + } + else if (streq (str, "frame")) + { + /* Stack frames are being generated - does not affect + linkage of code. */ + return 1; + } + else if (streq (str, "stack-check")) + { + /* Stack checking is being performed - does not affect + linkage, but does require that the functions + __rt_stkovf_split_small and __rt_stkovf_split_big be + present in the final link. */ + + return 1; + } + else if (streq (str, "float")) + { + /* Floating point arguments are being passed in the floating + point registers. This does affect linking, since this + version of the APCS is incompatible with the version that + passes floating points in the integer registers. */ + + uses_apcs_float = true; + return 1; + } + else if (streq (str, "reentrant")) + { + /* Reentrant code has been generated. This does affect + linking, since there is no point in linking reentrant/ + position independent code with absolute position code. */ + pic_code = true; + return 1; + } + + as_bad (_("Unrecognised APCS switch -m%s"), arg); + return 0; + } +#endif + /* Strip off optional "arm" */ + if (! strncmp (str, "arm", 3)) + str += 3; + + switch (*str) + { + case '1': + if (streq (str, "1")) + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_1; + else + goto bad; + break; + + case '2': + if (streq (str, "2")) + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2; + else if (streq (str, "250")) + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_250; + else + goto bad; + break; + + case '3': + if (streq (str, "3")) + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3; + else + goto bad; + break; + + case '6': + switch (strtol (str, NULL, 10)) + { + case 6: + case 60: + case 600: + case 610: + case 620: + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_6; + break; + default: + goto bad; + } + break; + + case '7': + switch (strtol (str, & str, 10)) /* Eat the processor name */ + { + case 7: + case 70: + case 700: + case 710: + case 720: + case 7100: + case 7500: + break; + default: + goto bad; + } + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7; + for (; *str; str++) + { + switch (* str) + { + case 't': + cpu_variant |= (ARM_THUMB | ARM_ARCH_V4); + break; + + case 'm': + cpu_variant |= ARM_LONGMUL; + break; + + case 'f': /* fe => fp enabled cpu. */ + if (str[1] == 'e') + ++ str; + else + goto bad; + + case 'c': /* Left over from 710c processor name. */ + case 'd': /* Debug */ + case 'i': /* Embedded ICE */ + /* Included for completeness in ARM processor naming. */ + break; + + default: + goto bad; + } + } + break; + + case '8': + if (streq (str, "8") || streq (str, "810")) + cpu_variant = (cpu_variant & ~ARM_ANY) + | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL; + else + goto bad; + break; + + case '9': + if (streq (str, "9")) + cpu_variant = (cpu_variant & ~ARM_ANY) + | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB; + else if (streq (str, "920")) + cpu_variant = (cpu_variant & ~ARM_ANY) + | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL; + else if (streq (str, "920t")) + cpu_variant = (cpu_variant & ~ARM_ANY) + | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB; + else if (streq (str, "9tdmi")) + cpu_variant = (cpu_variant & ~ARM_ANY) + | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB; + else + goto bad; + break; + + + case 's': + if (streq (str, "strongarm") + || streq (str, "strongarm110") + || streq (str, "strongarm1100")) + cpu_variant = (cpu_variant & ~ARM_ANY) + | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL; + else + goto bad; + break; + + case 'v': + /* Select variant based on architecture rather than processor. */ + switch (*++str) + { + case '2': + switch (*++str) + { + case 'a': + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3; + break; + case 0: + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2; + break; + default: + as_bad (_("Invalid architecture variant -m%s"), arg); + break; + } + break; + + case '3': + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7; + + switch (*++str) + { + case 'm': cpu_variant |= ARM_LONGMUL; break; + case 0: break; + default: + as_bad (_("Invalid architecture variant -m%s"), arg); + break; + } + break; + + case '4': + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4; + + switch (*++str) + { + case 't': cpu_variant |= ARM_THUMB; break; + case 0: break; + default: + as_bad (_("Invalid architecture variant -m%s"), arg); + break; + } + break; + + case '5': + cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V5; + switch (*++str) + { + case 't': cpu_variant |= ARM_THUMB; break; + case 'e': cpu_variant |= ARM_EXT_V5E; break; + case 0: break; + default: + as_bad (_("Invalid architecture variant -m%s"), arg); + break; + } + break; + + default: + as_bad (_("Invalid architecture variant -m%s"), arg); + break; + } + break; + + default: + bad: + as_bad (_("Invalid processor variant -m%s"), arg); + return 0; + } + } + break; + +#if defined OBJ_ELF || defined OBJ_COFF + case 'k': + pic_code = 1; + break; +#endif + + default: + return 0; + } + + return 1; +} + +void +md_show_usage (fp) + FILE * fp; +{ + fprintf (fp, _("\ + ARM Specific Assembler Options:\n\ + -m[arm][] select processor variant\n\ + -m[arm]v[2|2a|3|3m|4|4t|5[t][e]] select architecture variant\n\ + -mthumb only allow Thumb instructions\n\ + -mthumb-interwork mark the assembled code as supporting interworking\n\ + -mall allow any instruction\n\ + -mfpa10, -mfpa11 select floating point architecture\n\ + -mfpe-old don't allow floating-point multiple instructions\n\ + -mno-fpu don't allow any floating-point instructions.\n\ + -k generate PIC code.\n")); +#if defined OBJ_COFF || defined OBJ_ELF + fprintf (fp, _("\ + -mapcs-32, -mapcs-26 specify which ARM Procedure Calling Standard to use\n\ + -mapcs-float floating point args are passed in FP regs\n\ + -mapcs-reentrant the code is position independent/reentrant\n")); + #endif +#ifdef OBJ_ELF + fprintf (fp, _("\ + -moabi support the old ELF ABI\n")); +#endif +#ifdef ARM_BI_ENDIAN + fprintf (fp, _("\ + -EB assemble code for a big endian cpu\n\ + -EL assemble code for a little endian cpu\n")); +#endif +} + +/* We need to be able to fix up arbitrary expressions in some statements. + This is so that we can handle symbols that are an arbitrary distance from + the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask), + which returns part of an address in a form which will be valid for + a data instruction. We do this by pushing the expression into a symbol + in the expr_section, and creating a fix for that. */ + +static void +fix_new_arm (frag, where, size, exp, pc_rel, reloc) + fragS * frag; + int where; + short int size; + expressionS * exp; + int pc_rel; + int reloc; +{ + fixS * new_fix; + arm_fix_data * arm_data; + + switch (exp->X_op) + { + case O_constant: + case O_symbol: + case O_add: + case O_subtract: + new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc); + break; + + default: + new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0, + pc_rel, reloc); + break; + } + + /* Mark whether the fix is to a THUMB instruction, or an ARM instruction */ + arm_data = (arm_fix_data *) obstack_alloc (& notes, sizeof (arm_fix_data)); + new_fix->tc_fix_data = (PTR) arm_data; + arm_data->thumb_mode = thumb_mode; + + return; +} + + +/* This fix_new is called by cons via TC_CONS_FIX_NEW. */ +void +cons_fix_new_arm (frag, where, size, exp) + fragS * frag; + int where; + int size; + expressionS * exp; +{ + bfd_reloc_code_real_type type; + int pcrel = 0; + + /* Pick a reloc. + FIXME: @@ Should look at CPU word size. */ + switch (size) + { + case 1: + type = BFD_RELOC_8; + break; + case 2: + type = BFD_RELOC_16; + break; + case 4: + default: + type = BFD_RELOC_32; + break; + case 8: + type = BFD_RELOC_64; + break; + } + + fix_new_exp (frag, where, (int) size, exp, pcrel, type); +} + +/* A good place to do this, although this was probably not intended + for this kind of use. We need to dump the literal pool before + references are made to a null symbol pointer. */ +void +arm_cleanup () +{ + if (current_poolP == NULL) + return; + + subseg_set (text_section, 0); /* Put it at the end of text section. */ + s_ltorg (0); + listing_prev_line (); +} + +void +arm_start_line_hook () +{ + last_label_seen = NULL; +} + +void +arm_frob_label (sym) + symbolS * sym; +{ + last_label_seen = sym; + + ARM_SET_THUMB (sym, thumb_mode); + +#if defined OBJ_COFF || defined OBJ_ELF + ARM_SET_INTERWORK (sym, support_interwork); +#endif + + if (label_is_thumb_function_name) + { + /* When the address of a Thumb function is taken the bottom + bit of that address should be set. This will allow + interworking between Arm and Thumb functions to work + correctly. */ + + THUMB_SET_FUNC (sym, 1); + + label_is_thumb_function_name = false; + } +} + +/* Adjust the symbol table. This marks Thumb symbols as distinct from + ARM ones. */ + +void +arm_adjust_symtab () +{ +#ifdef OBJ_COFF + symbolS * sym; + + for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym)) + { + if (ARM_IS_THUMB (sym)) + { + if (THUMB_IS_FUNC (sym)) + { + /* Mark the symbol as a Thumb function. */ + if ( S_GET_STORAGE_CLASS (sym) == C_STAT + || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */ + S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC); + + else if (S_GET_STORAGE_CLASS (sym) == C_EXT) + S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC); + else + as_bad (_("%s: unexpected function type: %d"), + S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym)); + } + else switch (S_GET_STORAGE_CLASS (sym)) + { + case C_EXT: + S_SET_STORAGE_CLASS (sym, C_THUMBEXT); + break; + case C_STAT: + S_SET_STORAGE_CLASS (sym, C_THUMBSTAT); + break; + case C_LABEL: + S_SET_STORAGE_CLASS (sym, C_THUMBLABEL); + break; + default: /* do nothing */ + break; + } + } + + if (ARM_IS_INTERWORK (sym)) + coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF; + } +#endif +#ifdef OBJ_ELF + symbolS * sym; + char bind; + + for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym)) + { + if (ARM_IS_THUMB (sym)) + { + elf_symbol_type * elf_sym; + + elf_sym = elf_symbol (symbol_get_bfdsym (sym)); + bind = ELF_ST_BIND (elf_sym); + + /* If it's a .thumb_func, declare it as so, + otherwise tag label as .code 16. */ + if (THUMB_IS_FUNC (sym)) + elf_sym->internal_elf_sym.st_info = + ELF_ST_INFO (bind, STT_ARM_TFUNC); + else + elf_sym->internal_elf_sym.st_info = + ELF_ST_INFO (bind, STT_ARM_16BIT); + } + } +#endif +} + +int +arm_data_in_code () +{ + if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5)) + { + *input_line_pointer = '/'; + input_line_pointer += 5; + *input_line_pointer = 0; + return 1; + } + + return 0; +} + +char * +arm_canonicalize_symbol_name (name) + char * name; +{ + int len; + + if (thumb_mode && (len = strlen (name)) > 5 + && streq (name + len - 5, "/data")) + *(name + len - 5) = 0; + + return name; +} + +boolean +arm_validate_fix (fixP) + fixS * fixP; +{ + /* If the destination of the branch is a defined symbol which does not have + the THUMB_FUNC attribute, then we must be calling a function which has + the (interfacearm) attribute. We look for the Thumb entry point to that + function and change the branch to refer to that function instead. */ + if ( fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23 + && fixP->fx_addsy != NULL + && S_IS_DEFINED (fixP->fx_addsy) + && ! THUMB_IS_FUNC (fixP->fx_addsy)) + { + fixP->fx_addsy = find_real_start (fixP->fx_addsy); + return true; + } + + return false; +} + +#ifdef OBJ_ELF +/* Relocations against Thumb function names must be left unadjusted, + so that the linker can use this information to correctly set the + bottom bit of their addresses. The MIPS version of this function + also prevents relocations that are mips-16 specific, but I do not + know why it does this. + + FIXME: + There is one other problem that ought to be addressed here, but + which currently is not: Taking the address of a label (rather + than a function) and then later jumping to that address. Such + addresses also ought to have their bottom bit set (assuming that + they reside in Thumb code), but at the moment they will not. */ + +boolean +arm_fix_adjustable (fixP) + fixS * fixP; +{ + if (fixP->fx_addsy == NULL) + return 1; + + /* Prevent all adjustments to global symbols. */ + if (S_IS_EXTERN (fixP->fx_addsy)) + return 0; + + if (S_IS_WEAK (fixP->fx_addsy)) + return 0; + + if (THUMB_IS_FUNC (fixP->fx_addsy) + && fixP->fx_subsy == NULL) + return 0; + + /* We need the symbol name for the VTABLE entries */ + if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 0; + + return 1; +} + +const char * +elf32_arm_target_format () +{ + if (target_big_endian) + if (target_oabi) + return "elf32-bigarm-oabi"; + else + return "elf32-bigarm"; + else + if (target_oabi) + return "elf32-littlearm-oabi"; + else + return "elf32-littlearm"; +} + +void +armelf_frob_symbol (symp, puntp) + symbolS * symp; + int * puntp; +{ + elf_frob_symbol (symp, puntp); +} + +int +arm_force_relocation (fixp) + struct fix * fixp; +{ + if ( fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY + || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH + || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23) + return 1; + + return 0; +} + +static bfd_reloc_code_real_type +arm_parse_reloc () +{ + char id[16]; + char * ip; + unsigned int i; + static struct + { + char * str; + int len; + bfd_reloc_code_real_type reloc; + } + reloc_map[] = + { +#define MAP(str,reloc) { str, sizeof (str)-1, reloc } + MAP ("(got)", BFD_RELOC_ARM_GOT32), + MAP ("(gotoff)", BFD_RELOC_ARM_GOTOFF), + /* ScottB: Jan 30, 1998 */ + /* Added support for parsing "var(PLT)" branch instructions */ + /* generated by GCC for PLT relocs */ + MAP ("(plt)", BFD_RELOC_ARM_PLT32), + { NULL, 0, BFD_RELOC_UNUSED } +#undef MAP + }; + + for (i = 0, ip = input_line_pointer; + i < sizeof (id) && (isalnum (*ip) || ispunct (*ip)); + i++, ip++) + id[i] = tolower (*ip); + + for (i = 0; reloc_map[i].str; i++) + if (strncmp (id, reloc_map[i].str, reloc_map[i].len) == 0) + break; + + input_line_pointer += reloc_map[i].len; + + return reloc_map[i].reloc; +} + +static void +s_arm_elf_cons (nbytes) + int nbytes; +{ + expressionS exp; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + +#ifdef md_cons_align + md_cons_align (nbytes); +#endif + + do + { + bfd_reloc_code_real_type reloc; + + expression (& exp); + + if (exp.X_op == O_symbol + && * input_line_pointer == '(' + && (reloc = arm_parse_reloc()) != BFD_RELOC_UNUSED) + { + reloc_howto_type * howto = bfd_reloc_type_lookup (stdoutput, reloc); + int size = bfd_get_reloc_size (howto); + + if (size > nbytes) + as_bad ("%s relocations do not fit in %d bytes", + howto->name, nbytes); + else + { + register char * p = frag_more ((int) nbytes); + int offset = nbytes - size; + + fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, + & exp, 0, reloc); + } + } + else + emit_expr (& exp, (unsigned int) nbytes); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; /* Put terminator back into stream. */ + demand_empty_rest_of_line (); +} + +#endif /* OBJ_ELF */ Property changes on: stable/4/contrib/binutils/gas/config/tc-arm.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/contrib/binutils/gas/doc/c-arm.texi =================================================================== --- stable/4/contrib/binutils/gas/doc/c-arm.texi (nonexistent) +++ stable/4/contrib/binutils/gas/doc/c-arm.texi (revision 62108) @@ -0,0 +1,294 @@ +@c Copyright (C) 1996, 1998, 1999, 2000 Free Software Foundation, Inc. +@c This is part of the GAS manual. +@c For copying conditions, see the file as.texinfo. + +@ifset GENERIC +@page +@node ARM-Dependent +@chapter ARM Dependent Features +@end ifset + +@ifclear GENERIC +@node Machine Dependencies +@chapter ARM Dependent Features +@end ifclear + +@cindex ARM support +@cindex Thumb support +@menu +* ARM Options:: Options +* ARM Syntax:: Syntax +* ARM Floating Point:: Floating Point +* ARM Directives:: ARM Machine Directives +* ARM Opcodes:: Opcodes +@end menu + +@node ARM Options +@section Options +@cindex ARM options (none) +@cindex options for ARM (none) + +@table @code + +@cindex @code{-marm} command line option, ARM +@item -marm@code{[2|250|3|6|60|600|610|620|7|7m|7d|7dm|7di|7dmi|70|700|700i|710|710c|7100|7500|7500fe|7tdmi|8|810|9|9tdmi|920|strongarm|strongarm110|strongarm1100]} +This option specifies the target processor. The assembler will issue an +error message if an attempt is made to assemble an instruction which +will not execute on the target processor. + +@cindex @code{-marmv} command line option, ARM +@item -marmv@code{[2|2a|3|3m|4|4t|5|5t]} +This option specifies the target architecture. The assembler will issue +an error message if an attempt is made to assemble an instruction which +will not execute on the target architecture. + +@cindex @code{-mthumb} command line option, ARM +@item -mthumb +This option specifies that only Thumb instructions should be assembled. + +@cindex @code{-mall} command line option, ARM +@item -mall +This option specifies that any Arm or Thumb instruction should be assembled. + +@cindex @code{-mfpa} command line option, ARM +@item -mfpa @var{[10|11]} +This option specifies the floating point architecture in use on the +target processor. + +@cindex @code{-mfpe-old} command line option, ARM +@item -mfpe-old +Do not allow the assemble of floating point multiple instructions. + +@cindex @code{-mno-fpu} command line option, ARM +@item -mno-fpu +Do not allow the assembly of any floating point instructions. + +@cindex @code{-mthumb-interwork} command line option, ARM +@item -mthumb-interwork +This option specifies that the output generated by the assembler should +be marked as supporting interworking. + +@cindex @code{-mapcs} command line option, ARM +@item -mapcs @var{[26|32]} +This option specifies that the output generated by the assembler should +be marked as supporting the indicated version of the Arm Procedure. +Calling Standard. + +@cindex @code{-mapcs-float} command line option, ARM +@item -mapcs-float +This indicates the the floating point variant of the APCS should be +used. In this variant floating point arguments are passed in FP +registers rather than integer registers. + +@cindex @code{-mapcs-reentrant} command line option, ARM +@item -mapcs-reentrant +This indicates that the reentrant variant of the APCS should be used. +This variant supports position independent code. + +@cindex @code{-EB} command line option, ARM +@item -EB +This option specifies that the output generated by the assembler should +be marked as being encoded for a big-endian processor. + +@cindex @code{-EL} command line option, ARM +@item -EL +This option specifies that the output generated by the assembler should +be marked as being encoded for a little-endian processor. + +@cindex @code{-k} command line option, ARM +@cindex PIC code generation for ARM +@item -k +This option enables the generation of PIC (position independent code). + +@cindex @code{-moabi} command line option, ARM +@item -moabi +This indicates that the code should be assembled using the old ARM ELF +conventions, based on a beta release release of the ARM-ELF +specifications, rather than the default conventions which are based on +the final release of the ARM-ELF specifications. + +@end table + + +@node ARM Syntax +@section Syntax +@menu +* ARM-Chars:: Special Characters +* ARM-Regs:: Register Names +@end menu + +@node ARM-Chars +@subsection Special Characters + +@cindex line comment character, ARM +@cindex ARM line comment character +The presence of a @samp{@@} on a line indicates the start of a comment +that extends to the end of the current line. If a @samp{#} appears as +the first character of a line, the whole line is treated as a comment. + +@cindex line separator, ARM +@cindex statement separator, ARM +@cindex ARM line separator +On ARM systems running the GNU/Linux operating system, @samp{;} can be +used instead of a newline to separate statements. + +@cindex immediate character, ARM +@cindex ARM immediate character +Either @samp{#} or @samp{$} can be used to indicate immediate operands. + +@cindex identifiers, ARM +@cindex ARM identifiers +*TODO* Explain about /data modifier on symbols. + +@node ARM-Regs +@subsection Register Names + +@cindex ARM register names +@cindex register names, ARM +*TODO* Explain about ARM register naming, and the predefined names. + +@node ARM Floating Point +@section Floating Point + +@cindex floating point, ARM (@sc{ieee}) +@cindex ARM floating point (@sc{ieee}) +The ARM family uses @sc{ieee} floating-point numbers. + + + +@node ARM Directives +@section ARM Machine Directives + +@cindex machine directives, ARM +@cindex ARM machine directives +@table @code + +@cindex @code{align} directive, ARM +@item .align @var{expression} [, @var{expression}] +This is the generic @var{.align} directive. For the ARM however if the +first argument is zero (ie no alignment is needed) the assembler will +behave as if the argument had been 2 (ie pad to the next four byte +boundary). This is for compatability with ARM's own assembler. + +@cindex @code{req} directive, ARM +@item @var{name} .req @var{register name} +This creates an alias for @var{register name} called @var{name}. For +example: + +@smallexample + foo .req r0 +@end smallexample + +@cindex @code{code} directive, ARM +@item .code @var{[16|32]} +This directive selects the instruction set being generated. The value 16 +selects Thumb, with the value 32 selecting ARM. + +@cindex @code{thumb} directive, ARM +@item .thumb +This performs the same action as @var{.code 16}. + +@cindex @code{arm} directive, ARM +@item .arm +This performs the same action as @var{.code 32}. + +@cindex @code{force_thumb} directive, ARM +@item .force_thumb +This directive forces the selection of Thumb instructions, even if the +target processor does not support those instructions + +@cindex @code{thumb_func} directive, ARM +@item .thumb_func +This directive specifies that the following symbol is the name of a +Thumb encoded function. This information is necessary in order to allow +the assembler and linker to generate correct code for interworking +between Arm and Thumb instructions and should be used even if +interworking is not going to be performed. + +@cindex @code{thumb_set} directive, ARM +@item .thumb_set +This performs the equivalent of a @code{.set} directive in that it +creates a symbol which is an alias for another symbol (possibly not yet +defined). This directive also has the added property in that it marks +the aliased symbol as being a thumb function entry point, in the same +way that the @code{.thumb_func} directive does. + +@cindex @code{.ltorg} directive, ARM +@item .ltorg +This directive causes the current contents of the literal pool to be +dumped into the current section (which is assumed to be the .text +section) at the current location (aligned to a word boundary). + +@cindex @code{.pool} directive, ARM +@item .pool +This is a synonym for .ltorg. + +@end table + +@node ARM Opcodes +@section Opcodes + +@cindex ARM opcodes +@cindex opcodes for ARM +@code{@value{AS}} implements all the standard ARM opcodes. It also +implements several pseudo opcodes, including several synthetic load +instructions. + +@table @code + +@cindex @code{NOP} pseudo op, ARM +@item NOP +@smallexample + nop +@end smallexample + +This pseudo op will always evaluate to a legal ARM instruction that does +nothing. Currently it will evaluate to MOV r0, r0. + +@cindex @code{LDR reg,=