From 137c83d69fad77677cc818593f9399caa777a0c5 Mon Sep 17 00:00:00 2001 From: Andrew Burgess Date: Thu, 8 Jan 2015 21:55:43 +0000 Subject: [PATCH] avr/objdump: Support dumping .avr.prop section. Add support to objdump for dumping the .avr.prop section in a structured way. binutils/ChangeLog: * od-elf32_avr.c: Add elf32-avr.h include. (OPT_AVRPROP): Define. (options[]): Add 'avr-prop' entry. (elf32_avr_help): Add avr-prop help text. (elf32_avr_dump_avr_prop): New function. (elf32_avr_dump): Add check for avr-prop. bfd/ChangeLog: * elf32-avr.h (struct avr_property_header): New strucure. (avr_elf32_load_property_records): Declare. (avr_elf32_property_record_name): Declare. * elf32-avr.c: Add bfd_stdint.h include. (retrieve_local_syms): New function. (get_elf_r_symndx_section): New function. (get_elf_r_symndx_offset): New function. (internal_reloc_compare): New function. (struct avr_find_section_data): New structure. (avr_is_section_for_address): New function. (avr_find_section_for_address): New function. (avr_elf32_load_records_from_section): New function. (avr_elf32_load_property_records): New function. (avr_elf32_property_record_name): New function. gas/testsuite/ChangeLog: * gas/avr/avr-prop-1.d: New file. * gas/avr/avr-prop-1.s: New file. --- bfd/ChangeLog | 17 ++ bfd/elf32-avr.c | 439 +++++++++++++++++++++++++++++++++++++ bfd/elf32-avr.h | 32 +++ binutils/ChangeLog | 9 + binutils/od-elf32_avr.c | 54 +++++ gas/testsuite/ChangeLog | 5 + gas/testsuite/gas/avr/avr-prop-1.d | 26 +++ gas/testsuite/gas/avr/avr-prop-1.s | 29 +++ 8 files changed, 611 insertions(+) create mode 100644 gas/testsuite/gas/avr/avr-prop-1.d create mode 100644 gas/testsuite/gas/avr/avr-prop-1.s diff --git a/bfd/ChangeLog b/bfd/ChangeLog index af2d2ba..2af87a1 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,22 @@ 2015-02-25 Andrew Burgess + * elf32-avr.h (struct avr_property_header): New strucure. + (avr_elf32_load_property_records): Declare. + (avr_elf32_property_record_name): Declare. + * elf32-avr.c: Add bfd_stdint.h include. + (retrieve_local_syms): New function. + (get_elf_r_symndx_section): New function. + (get_elf_r_symndx_offset): New function. + (internal_reloc_compare): New function. + (struct avr_find_section_data): New structure. + (avr_is_section_for_address): New function. + (avr_find_section_for_address): New function. + (avr_elf32_load_records_from_section): New function. + (avr_elf32_load_property_records): New function. + (avr_elf32_property_record_name): New function. + +2015-02-25 Andrew Burgess + * elf32-avr.h (AVR_PROPERTY_RECORD_SECTION_NAME): Define. (AVR_PROPERTY_RECORDS_VERSION): Define. (AVR_PROPERTY_SECTION_HEADER_SIZE): Define. diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c index 255a2c2..b780f8d 100644 --- a/bfd/elf32-avr.c +++ b/bfd/elf32-avr.c @@ -25,6 +25,7 @@ #include "elf-bfd.h" #include "elf/avr.h" #include "elf32-avr.h" +#include "bfd_stdint.h" /* Enable debugging printout at stdout with this variable. */ static bfd_boolean debug_relax = FALSE; @@ -1935,6 +1936,118 @@ elf32_avr_relax_delete_bytes (bfd *abfd, return TRUE; } +static Elf_Internal_Sym * +retrieve_local_syms (bfd *input_bfd) +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *isymbuf; + size_t locsymcount; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + locsymcount = symtab_hdr->sh_info; + + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL && locsymcount != 0) + isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0, + NULL, NULL, NULL); + + /* Save the symbols for this input file so they won't be read again. */ + if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents) + symtab_hdr->contents = (unsigned char *) isymbuf; + + return isymbuf; +} + +/* Get the input section for a given symbol index. + If the symbol is: + . a section symbol, return the section; + . a common symbol, return the common section; + . an undefined symbol, return the undefined section; + . an indirect symbol, follow the links; + . an absolute value, return the absolute section. */ + +static asection * +get_elf_r_symndx_section (bfd *abfd, unsigned long r_symndx) +{ + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + asection *target_sec = NULL; + if (r_symndx < symtab_hdr->sh_info) + { + Elf_Internal_Sym *isymbuf; + unsigned int section_index; + + isymbuf = retrieve_local_syms (abfd); + section_index = isymbuf[r_symndx].st_shndx; + + if (section_index == SHN_UNDEF) + target_sec = bfd_und_section_ptr; + else if (section_index == SHN_ABS) + target_sec = bfd_abs_section_ptr; + else if (section_index == SHN_COMMON) + target_sec = bfd_com_section_ptr; + else + target_sec = bfd_section_from_elf_index (abfd, section_index); + } + else + { + unsigned long indx = r_symndx - symtab_hdr->sh_info; + struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[indx]; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + switch (h->root.type) + { + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + target_sec = h->root.u.def.section; + break; + case bfd_link_hash_common: + target_sec = bfd_com_section_ptr; + break; + case bfd_link_hash_undefined: + case bfd_link_hash_undefweak: + target_sec = bfd_und_section_ptr; + break; + default: /* New indirect warning. */ + target_sec = bfd_und_section_ptr; + break; + } + } + return target_sec; +} + +/* Get the section-relative offset for a symbol number. */ + +static bfd_vma +get_elf_r_symndx_offset (bfd *abfd, unsigned long r_symndx) +{ + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + bfd_vma offset = 0; + + if (r_symndx < symtab_hdr->sh_info) + { + Elf_Internal_Sym *isymbuf; + isymbuf = retrieve_local_syms (abfd); + offset = isymbuf[r_symndx].st_value; + } + else + { + unsigned long indx = r_symndx - symtab_hdr->sh_info; + struct elf_link_hash_entry *h = + elf_sym_hashes (abfd)[indx]; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + offset = h->root.u.def.value; + } + return offset; +} + /* This function handles relaxing for the avr. Many important relaxing opportunities within functions are already realized by the compiler itself. @@ -3347,6 +3460,332 @@ elf32_avr_build_stubs (struct bfd_link_info *info) return TRUE; } +/* Callback used by QSORT to order relocations AP and BP. */ + +static int +internal_reloc_compare (const void *ap, const void *bp) +{ + const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) ap; + const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) bp; + + if (a->r_offset != b->r_offset) + return (a->r_offset - b->r_offset); + + /* We don't need to sort on these criteria for correctness, + but enforcing a more strict ordering prevents unstable qsort + from behaving differently with different implementations. + Without the code below we get correct but different results + on Solaris 2.7 and 2.8. We would like to always produce the + same results no matter the host. */ + + if (a->r_info != b->r_info) + return (a->r_info - b->r_info); + + return (a->r_addend - b->r_addend); +} + +/* Return true if ADDRESS is within the vma range of SECTION from ABFD. */ + +static bfd_boolean +avr_is_section_for_address (bfd *abfd, asection *section, bfd_vma address) +{ + bfd_vma vma; + bfd_size_type size; + + vma = bfd_get_section_vma (abfd, section); + if (address < vma) + return FALSE; + + size = section->size; + if (address >= vma + size) + return FALSE; + + return TRUE; +} + +/* Data structure used by AVR_FIND_SECTION_FOR_ADDRESS. */ + +struct avr_find_section_data +{ + /* The address we're looking for. */ + bfd_vma address; + + /* The section we've found. */ + asection *section; +}; + +/* Helper function to locate the section holding a certain virtual memory + address. This is called via bfd_map_over_sections. The DATA is an + instance of STRUCT AVR_FIND_SECTION_DATA, the address field of which + has been set to the address to search for, and the section field has + been set to NULL. If SECTION from ABFD contains ADDRESS then the + section field in DATA will be set to SECTION. As an optimisation, if + the section field is already non-null then this function does not + perform any checks, and just returns. */ + +static void +avr_find_section_for_address (bfd *abfd, + asection *section, void *data) +{ + struct avr_find_section_data *fs_data + = (struct avr_find_section_data *) data; + + /* Return if already found. */ + if (fs_data->section != NULL) + return; + + /* If this section isn't part of the addressable code content, skip it. */ + if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0 + && (bfd_get_section_flags (abfd, section) & SEC_CODE) == 0) + return; + + if (avr_is_section_for_address (abfd, section, fs_data->address)) + fs_data->section = section; +} + +/* Load all of the property records from SEC, a section from ABFD. Return + a STRUCT AVR_PROPERTY_RECORD_LIST containing all the records. The + memory for the returned structure, and all of the records pointed too by + the structure are allocated with a single call to malloc, so, only the + pointer returned needs to be free'd. */ + +static struct avr_property_record_list * +avr_elf32_load_records_from_section (bfd *abfd, asection *sec) +{ + char *contents = NULL, *ptr; + bfd_size_type size, mem_size; + bfd_byte version, flags; + uint16_t record_count, i; + struct avr_property_record_list *r_list = NULL; + Elf_Internal_Rela *internal_relocs = NULL, *rel, *rel_end; + struct avr_find_section_data fs_data; + + fs_data.section = NULL; + + size = bfd_get_section_size (sec); + contents = bfd_malloc (size); + bfd_get_section_contents (abfd, sec, contents, 0, size); + ptr = contents; + + /* Load the relocations for the '.avr.prop' section if there are any, and + sort them. */ + internal_relocs = (_bfd_elf_link_read_relocs + (abfd, sec, NULL, NULL, FALSE)); + if (internal_relocs) + qsort (internal_relocs, sec->reloc_count, + sizeof (Elf_Internal_Rela), internal_reloc_compare); + + /* There is a header at the start of the property record section SEC, the + format of this header is: + uint8_t : version number + uint8_t : flags + uint16_t : record counter + */ + + /* Check we have at least got a headers worth of bytes. */ + if (size < AVR_PROPERTY_SECTION_HEADER_SIZE) + goto load_failed; + + version = *((bfd_byte *) ptr); + ptr++; + flags = *((bfd_byte *) ptr); + ptr++; + record_count = *((uint16_t *) ptr); + ptr+=2; + BFD_ASSERT (ptr - contents == AVR_PROPERTY_SECTION_HEADER_SIZE); + + /* Now allocate space for the list structure, and all of the list + elements in a single block. */ + mem_size = sizeof (struct avr_property_record_list) + + sizeof (struct avr_property_record) * record_count; + r_list = bfd_malloc (mem_size); + if (r_list == NULL) + goto load_failed; + + r_list->version = version; + r_list->flags = flags; + r_list->section = sec; + r_list->record_count = record_count; + r_list->records = (struct avr_property_record *) (&r_list [1]); + size -= AVR_PROPERTY_SECTION_HEADER_SIZE; + + /* Check that we understand the version number. There is only one + version number right now, anything else is an error. */ + if (r_list->version != AVR_PROPERTY_RECORDS_VERSION) + goto load_failed; + + rel = internal_relocs; + rel_end = rel + sec->reloc_count; + for (i = 0; i < record_count; ++i) + { + bfd_vma address; + + /* Each entry is a 32-bit address, followed by a single byte type. + After that is the type specific data. We must take care to + ensure that we don't read beyond the end of the section data. */ + if (size < 5) + goto load_failed; + + r_list->records [i].section = NULL; + r_list->records [i].offset = 0; + + if (rel) + { + /* The offset of the address within the .avr.prop section. */ + size_t offset = ptr - contents; + + while (rel < rel_end && rel->r_offset < offset) + ++rel; + + if (rel == rel_end) + rel = NULL; + else if (rel->r_offset == offset) + { + /* Find section and section offset. */ + unsigned long r_symndx; + + asection * rel_sec; + bfd_vma sec_offset; + + r_symndx = ELF32_R_SYM (rel->r_info); + rel_sec = get_elf_r_symndx_section (abfd, r_symndx); + sec_offset = get_elf_r_symndx_offset (abfd, r_symndx) + + rel->r_addend; + + r_list->records [i].section = rel_sec; + r_list->records [i].offset = sec_offset; + } + } + + address = *((uint32_t *) ptr); + ptr += 4; + size -= 4; + + if (r_list->records [i].section == NULL) + { + /* Try to find section and offset from address. */ + if (fs_data.section != NULL + && !avr_is_section_for_address (abfd, fs_data.section, + address)) + fs_data.section = NULL; + + if (fs_data.section == NULL) + { + fs_data.address = address; + bfd_map_over_sections (abfd, avr_find_section_for_address, + &fs_data); + } + + if (fs_data.section == NULL) + { + fprintf (stderr, "Failed to find matching section.\n"); + goto load_failed; + } + + r_list->records [i].section = fs_data.section; + r_list->records [i].offset + = address - bfd_get_section_vma (abfd, fs_data.section); + } + + r_list->records [i].type = *((bfd_byte *) ptr); + ptr += 1; + size -= 1; + + switch (r_list->records [i].type) + { + case RECORD_ORG: + /* Nothing else to load. */ + break; + case RECORD_ORG_AND_FILL: + /* Just a 4-byte fill to load. */ + if (size < 4) + goto load_failed; + r_list->records [i].data.org.fill = *((uint32_t *) ptr); + ptr += 4; + size -= 4; + break; + case RECORD_ALIGN: + /* Just a 4-byte alignment to load. */ + if (size < 4) + goto load_failed; + r_list->records [i].data.align.bytes = *((uint32_t *) ptr); + ptr += 4; + size -= 4; + /* Just initialise PRECEDING_DELETED field, this field is + used during linker relaxation. */ + r_list->records [i].data.align.preceding_deleted = 0; + break; + case RECORD_ALIGN_AND_FILL: + /* A 4-byte alignment, and a 4-byte fill to load. */ + if (size < 8) + goto load_failed; + r_list->records [i].data.align.bytes = *((uint32_t *) ptr); + ptr += 4; + r_list->records [i].data.align.fill = *((uint32_t *) ptr); + ptr += 4; + size -= 8; + /* Just initialise PRECEDING_DELETED field, this field is + used during linker relaxation. */ + r_list->records [i].data.align.preceding_deleted = 0; + break; + default: + goto load_failed; + } + } + + free (contents); + free (internal_relocs); + return r_list; + + load_failed: + free (internal_relocs); + free (contents); + free (r_list); + return NULL; +} + +/* Load all of the property records from ABFD. See + AVR_ELF32_LOAD_RECORDS_FROM_SECTION for details of the return value. */ + +struct avr_property_record_list * +avr_elf32_load_property_records (bfd *abfd) +{ + asection *sec; + + /* Find the '.avr.prop' section and load the contents into memory. */ + sec = bfd_get_section_by_name (abfd, AVR_PROPERTY_RECORD_SECTION_NAME); + if (sec == NULL) + return NULL; + return avr_elf32_load_records_from_section (abfd, sec); +} + +const char * +avr_elf32_property_record_name (struct avr_property_record *rec) +{ + const char *str; + + switch (rec->type) + { + case RECORD_ORG: + str = "ORG"; + break; + case RECORD_ORG_AND_FILL: + str = "ORG+FILL"; + break; + case RECORD_ALIGN: + str = "ALIGN"; + break; + case RECORD_ALIGN_AND_FILL: + str = "ALIGN+FILL"; + break; + default: + str = "unknown"; + } + + return str; +} + + #define ELF_ARCH bfd_arch_avr #define ELF_TARGET_ID AVR_ELF_DATA #define ELF_MACHINE_CODE EM_AVR diff --git a/bfd/elf32-avr.h b/bfd/elf32-avr.h index 688b706..0ddb562 100644 --- a/bfd/elf32-avr.h +++ b/bfd/elf32-avr.h @@ -88,3 +88,35 @@ struct avr_property_record } align; } data; }; + +struct avr_property_record_list +{ + /* The version number tells us the structure of the property record data + within the section. See AVR_PROPERTY_RECORDS_VERSION. */ + bfd_byte version; + + /* The flags field is currently unused. This should be set to 0. */ + bfd_byte flags; + + /* The number of property records. This is stored as a 2-byte value in + the section contents. */ + unsigned long record_count; + + /* The section from which the property records were loaded. This is the + actual section containing the records, not the section(s) to which the + records apply. */ + asection *section; + + /* The actual property records. */ + struct avr_property_record *records; +}; + +/* Load the property records from ABFD, return NULL if there are non + found, otherwise return pointer to dynamically allocated memory. The + memory for the header and all of the records are allocated in a single + block, as such only the header needs to be freed. */ + +extern struct avr_property_record_list *avr_elf32_load_property_records (bfd *abfd); + +/* Return a string that is the name of the property record pointed to by REC. */ +extern const char *avr_elf32_property_record_name (struct avr_property_record *rec); diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 421fbe3..c61a2bd 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,12 @@ +2015-02-25 Andrew Burgess + + * od-elf32_avr.c: Add elf32-avr.h include. + (OPT_AVRPROP): Define. + (options[]): Add 'avr-prop' entry. + (elf32_avr_help): Add avr-prop help text. + (elf32_avr_dump_avr_prop): New function. + (elf32_avr_dump): Add check for avr-prop. + 2015-02-24 Nick Clifton * readelf.c (get_machine_flags): Remove deprecated V850 machine diff --git a/binutils/od-elf32_avr.c b/binutils/od-elf32_avr.c index 5e828fb..5635964 100644 --- a/binutils/od-elf32_avr.c +++ b/binutils/od-elf32_avr.c @@ -31,14 +31,17 @@ #include "bfd.h" #include "elf/external.h" #include "elf/internal.h" +#include "elf32-avr.h" /* Index of the options in the options[] array. */ #define OPT_MEMUSAGE 0 +#define OPT_AVRPROP 1 /* List of actions. */ static struct objdump_private_option options[] = { { "mem-usage", 0 }, + { "avr-prop", 0}, { NULL, 0 } }; @@ -50,6 +53,7 @@ elf32_avr_help (FILE *stream) fprintf (stream, _("\ For AVR ELF files:\n\ mem-usage Display memory usage\n\ + avr-prop Display contents of .avr.prop section\n\ ")); } @@ -234,10 +238,60 @@ elf32_avr_dump_mem_usage (bfd *abfd) } static void +elf32_avr_dump_avr_prop (bfd *abfd) +{ + struct avr_property_record_list *r_list; + unsigned int i; + + r_list = avr_elf32_load_property_records (abfd); + if (r_list == NULL) + return; + + printf ("\nContents of `%s' section:\n\n", r_list->section->name); + + printf (" Version: %d\n", r_list->version); + printf (" Flags: %#x\n\n", r_list->flags); + + for (i = 0; i < r_list->record_count; ++i) + { + printf (" %d %s @ %s + %#08lx (%#08lx)\n", + i, + avr_elf32_property_record_name (&r_list->records [i]), + r_list->records [i].section->name, + r_list->records [i].offset, + (bfd_get_section_vma (abfd, r_list->records [i].section) + + r_list->records [i].offset)); + switch (r_list->records [i].type) + { + case RECORD_ORG: + /* Nothing else to print. */ + break; + case RECORD_ORG_AND_FILL: + printf (" Fill: %#08lx\n", + r_list->records [i].data.org.fill); + break; + case RECORD_ALIGN: + printf (" Align: %#08lx\n", + r_list->records [i].data.align.bytes); + break; + case RECORD_ALIGN_AND_FILL: + printf (" Align: %#08lx, Fill: %#08lx\n", + r_list->records [i].data.align.bytes, + r_list->records [i].data.org.fill); + break; + } + } + + free (r_list); +} + +static void elf32_avr_dump (bfd *abfd) { if (options[OPT_MEMUSAGE].selected) elf32_avr_dump_mem_usage (abfd); + if (options[OPT_AVRPROP].selected) + elf32_avr_dump_avr_prop (abfd); } const struct objdump_private_desc objdump_private_desc_elf32_avr = diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index 1cf803e..96697eb 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2015-02-25 Andrew Burgess + + * gas/avr/avr-prop-1.d: New file. + * gas/avr/avr-prop-1.s: New file. + 2015-02-25 Kaz Kojima Oleg Endo diff --git a/gas/testsuite/gas/avr/avr-prop-1.d b/gas/testsuite/gas/avr/avr-prop-1.d new file mode 100644 index 0000000..b140ae6 --- /dev/null +++ b/gas/testsuite/gas/avr/avr-prop-1.d @@ -0,0 +1,26 @@ +#name: AVR '.avr.prop' test 1 +#as: -mmcu=avrxmega2 -mlink-relax +#objdump: -P avr-prop +#source: avr-prop-1.s +#target: avr-*-* + +.*: file format elf32-avr + +Contents of `\.avr\.prop' section: + + Version: 1 + Flags: 0 + + 0 ORG @ \.text\.1 \+ 0x000020 \(0x000020\) + 1 ORG @ \.text\.1 \+ 0x000044 \(0x000044\) + 2 ORG @ \.text\.2 \+ 0x000020 \(0x000020\) + 3 ALIGN @ \.text\.2 \+ 0x000020 \(0x000020\) + Align: 0x000004 + 4 ALIGN @ \.text\.2 \+ 0x000030 \(0x000030\) + Align: 0x000004 + 5 ORG @ \.text\.2 \+ 0x000200 \(0x000200\) + 6 ALIGN @ \.text\.2 \+ 0x000200 \(0x000200\) + Align: 0x000004 + 7 ALIGN @ \.text\.3 \+ 0x000100 \(0x000100\) + Align: 0x000008 + diff --git a/gas/testsuite/gas/avr/avr-prop-1.s b/gas/testsuite/gas/avr/avr-prop-1.s new file mode 100644 index 0000000..6e50cf1 --- /dev/null +++ b/gas/testsuite/gas/avr/avr-prop-1.s @@ -0,0 +1,29 @@ + .section ".text.1", "ax" + .global _start +_start: + .org 0x20 + nop + .org 0x44 + nop + + + .section ".text.2", "ax" + .global test2 +text2: + .org 0x20 + nop + .align 4 + nop + .align 4 + nop + .org 0x200 + nop + + .section ".text.3", "ax" + .global test3 +text3: + .org 0x0 + nop + nop + .align 8 + nop -- 2.7.4