This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH 2/3] avr/objdump: Support dumping .avr.prop section.
- From: Andrew Burgess <andrew dot burgess at embecosm dot com>
- To: binutils at sourceware dot org
- Cc: Andrew Burgess <andrew dot burgess at embecosm dot com>
- Date: Fri, 9 Jan 2015 23:58:22 +0000
- Subject: [PATCH 2/3] avr/objdump: Support dumping .avr.prop section.
- Authentication-results: sourceware.org; auth=none
- References: <cover dot 1420847083 dot git dot andrew dot burgess at embecosm dot com>
- References: <cover dot 1420847083 dot git dot andrew dot burgess at embecosm dot com>
This includes tests that cover patch 1 and 2 of this series.
Feedback welcome,
thanks,
Andrew
---
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 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/elf32-avr.c | 440 +++++++++++++++++++++++++++++++++++++
bfd/elf32-avr.h | 32 +++
binutils/od-elf32_avr.c | 54 +++++
gas/testsuite/gas/avr/avr-prop-1.d | 26 +++
gas/testsuite/gas/avr/avr-prop-1.s | 29 +++
5 files changed, 581 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/elf32-avr.c b/bfd/elf32-avr.c
index adcf1a3..af5fd8a 100644
--- a/bfd/elf32-avr.c
+++ b/bfd/elf32-avr.c
@@ -26,6 +26,8 @@
#include "elf/avr.h"
#include "elf32-avr.h"
+#include <stdint.h>
+
/* Enable debugging printout at stdout with this variable. */
static bfd_boolean debug_relax = FALSE;
@@ -1935,6 +1937,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 +3461,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/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/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
--
1.9.3