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.
This commit is contained in:
Andrew Burgess 2015-01-08 21:55:43 +00:00
parent fdd410ac7a
commit 137c83d69f
8 changed files with 611 additions and 0 deletions

View File

@ -1,3 +1,20 @@
2015-02-25 Andrew Burgess <andrew.burgess@embecosm.com>
* 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 <andrew.burgess@embecosm.com>
* elf32-avr.h (AVR_PROPERTY_RECORD_SECTION_NAME): Define.

View File

@ -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

View File

@ -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);

View File

@ -1,3 +1,12 @@
2015-02-25 Andrew Burgess <andrew.burgess@embecosm.com>
* 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 <nickc@redhat.com>
* readelf.c (get_machine_flags): Remove deprecated V850 machine

View File

@ -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\
"));
}
@ -233,11 +237,61 @@ 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 =

View File

@ -1,3 +1,8 @@
2015-02-25 Andrew Burgess <andrew.burgess@embecosm.com>
* gas/avr/avr-prop-1.d: New file.
* gas/avr/avr-prop-1.s: New file.
2015-02-25 Kaz Kojima <kkojima@rr.iij4u.or.jp>
Oleg Endo <olegendo@gcc.gnu.org>

View File

@ -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

View File

@ -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