aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-avr.c
diff options
context:
space:
mode:
authorAndrew Burgess <andrew.burgess@embecosm.com>2015-01-08 20:55:10 +0000
committerAndrew Burgess <andrew.burgess@embecosm.com>2015-02-25 23:15:02 +0000
commitfdd410ac7a07dfb47dcb992201000582a280d8b2 (patch)
tree1a68c9b9d5cf73d3e9f227ee5a46183652dcd498 /gas/config/tc-avr.c
parentac99436572d903781c124fa3cc72d83360202b76 (diff)
downloadgdb-fdd410ac7a07dfb47dcb992201000582a280d8b2.zip
gdb-fdd410ac7a07dfb47dcb992201000582a280d8b2.tar.gz
gdb-fdd410ac7a07dfb47dcb992201000582a280d8b2.tar.bz2
avr/gas: Write out data to track .org/.align usage.
Adds support to the assembler to write out data for tracking the use of .org and .align directives. This data is collected within the assembler and written out to a section ".avr.prop" (if there's anything to write out). This patch does not add any tests. The next patch in this series will add a better mechanism for visualising the contents of .avr.prop which will make writing tests much easier. This patch also does not make any use of this collected data, that will also come along in a later patch; the intended consumer is the linker, during linker relaxation this information will be used to ensure that the .org and .align directives are honoured. bfd/ChangeLog: * elf32-avr.h (AVR_PROPERTY_RECORD_SECTION_NAME): Define. (AVR_PROPERTY_RECORDS_VERSION): Define. (AVR_PROPERTY_SECTION_HEADER_SIZE): Define. (struct avr_property_record): New structure. gas/ChangeLog: * config/tc-avr.c: Add elf32-avr.h include. (struct avr_property_record_link): New structure. (avr_output_property_section_header): New function. (avr_record_size): New function. (avr_output_property_record): New function. (avr_create_property_section): New function. (avr_handle_align): New function. (exclude_section_from_property_tables): New function. (create_record_for_frag): New function. (append_records_for_section): New function. (avr_create_and_fill_property_section): New function. (avr_post_relax_hook): New function. * config/tc-avr.h (md_post_relax_hook): Define. (avr_post_relax_hook): Declare. (HANDLE_ALIGN): Define. (avr_handle_align): Declare. (strut avr_frag_data): New structure. (TC_FRAG_TYPE): Define.
Diffstat (limited to 'gas/config/tc-avr.c')
-rw-r--r--gas/config/tc-avr.c337
1 files changed, 337 insertions, 0 deletions
diff --git a/gas/config/tc-avr.c b/gas/config/tc-avr.c
index 5a0b405..cc0328d 100644
--- a/gas/config/tc-avr.c
+++ b/gas/config/tc-avr.c
@@ -26,6 +26,14 @@
#include "dwarf2dbg.h"
#include "dw2gencfi.h"
#include "elf/avr.h"
+#include "elf32-avr.h"
+
+/* For building a linked list of AVR_PROPERTY_RECORD structures. */
+struct avr_property_record_link
+{
+ struct avr_property_record record;
+ struct avr_property_record_link *next;
+};
struct avr_opcodes_s
{
@@ -1864,3 +1872,332 @@ avr_elf_final_processing (void)
if (linkrelax)
elf_elfheader (stdoutput)->e_flags |= EF_AVR_LINKRELAX_PREPARED;
}
+
+/* Write out the header of a .avr.prop section into the area pointed to by
+ DATA. The RECORD_COUNT will be placed in the header as the number of
+ records that are to follow.
+ The area DATA must be big enough the receive the header, which is
+ AVR_PROPERTY_SECTION_HEADER_SIZE bytes long. */
+
+static char *
+avr_output_property_section_header (char *data,
+ unsigned int record_count)
+{
+ char *orig_data = data;
+
+ md_number_to_chars (data, AVR_PROPERTY_RECORDS_VERSION, 1);
+ data++;
+ /* There's space for a single byte flags field, but right now there's
+ nothing to go in here, so just set the value to zero. */
+ md_number_to_chars (data, 0, 1);
+ data++;
+ md_number_to_chars (data, record_count, 2);
+ data+=2;
+
+ gas_assert (data - orig_data == AVR_PROPERTY_SECTION_HEADER_SIZE);
+
+ return data;
+}
+
+/* Return the number of bytes required to store RECORD into the .avr.prop
+ section. The size returned is the compressed size that corresponds to
+ how the record will be written out in AVR_OUTPUT_PROPERTY_RECORD. */
+
+static int
+avr_record_size (const struct avr_property_record *record)
+{
+ /* The first 5 bytes are a 4-byte address, followed by a 1-byte type
+ identifier. */
+ int size = 5;
+
+ switch (record->type)
+ {
+ case RECORD_ORG:
+ size += 0; /* No extra information. */
+ break;
+
+ case RECORD_ORG_AND_FILL:
+ size += 4; /* A 4-byte fill value. */
+ break;
+
+ case RECORD_ALIGN:
+ size += 4; /* A 4-byte alignment value. */
+ break;
+
+ case RECORD_ALIGN_AND_FILL:
+ size += 8; /* A 4-byte alignment, and 4-byte fill value. */
+ break;
+
+ default:
+ as_fatal (_("unknown record type %d (in %s)"),
+ record->type, __PRETTY_FUNCTION__);
+ }
+
+ return size;
+}
+
+/* Write out RECORD. FRAG_BASE points to the start of the data area setup
+ to hold all of the .avr.prop content, FRAG_PTR points to the next
+ writable location. The data area must be big enough to hold all of the
+ records. The size of the data written out for this RECORD must match
+ the size from AVR_RECORD_SIZE. */
+
+static char *
+avr_output_property_record (char * const frag_base, char *frag_ptr,
+ const struct avr_property_record *record)
+{
+ fixS *fix;
+ int where;
+ char *init_frag_ptr = frag_ptr;
+
+ where = frag_ptr - frag_base;
+ fix = fix_new (frag_now, where, 4,
+ section_symbol (record->section),
+ record->offset, FALSE, BFD_RELOC_32);
+ fix->fx_file = "<internal>";
+ fix->fx_line = 0;
+ frag_ptr += 4;
+
+ md_number_to_chars (frag_ptr, (bfd_byte) record->type, 1);
+ frag_ptr += 1;
+
+ /* Write out the rest of the data. */
+ switch (record->type)
+ {
+ case RECORD_ORG:
+ break;
+
+ case RECORD_ORG_AND_FILL:
+ md_number_to_chars (frag_ptr, record->data.org.fill, 4);
+ frag_ptr += 4;
+ break;
+
+ case RECORD_ALIGN:
+ md_number_to_chars (frag_ptr, record->data.align.bytes, 4);
+ frag_ptr += 4;
+ break;
+
+ case RECORD_ALIGN_AND_FILL:
+ md_number_to_chars (frag_ptr, record->data.align.bytes, 4);
+ md_number_to_chars (frag_ptr, record->data.align.fill, 4);
+ frag_ptr += 8;
+ break;
+
+ default:
+ as_fatal (_("unknown record type %d (in %s)"),
+ record->type, __PRETTY_FUNCTION__);
+ }
+
+ gas_assert (frag_ptr - init_frag_ptr == avr_record_size (record));
+
+ return frag_ptr;
+}
+
+/* Create the section to hold the AVR property information. Return the
+ section. */
+
+static asection *
+avr_create_property_section (void)
+{
+ asection *sec;
+ flagword flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY);
+ const char *section_name = AVR_PROPERTY_RECORD_SECTION_NAME;
+
+ sec = bfd_make_section (stdoutput, section_name);
+ if (sec == NULL)
+ as_fatal (_("Failed to create property section `%s'\n"), section_name);
+ bfd_set_section_flags (stdoutput, sec, flags);
+ sec->output_section = sec;
+ return sec;
+}
+
+/* This hook is called when alignment is performed, and allows us to
+ capture the details of both .org and .align directives. */
+
+void
+avr_handle_align (fragS *fragP)
+{
+ if (linkrelax)
+ {
+ /* Ignore alignment requests at FR_ADDRESS 0, these are at the very
+ start of a section, and will be handled by the standard section
+ alignment mechanism. */
+ if ((fragP->fr_type == rs_align
+ || fragP->fr_type == rs_align_code)
+ && fragP->fr_address > 0
+ && fragP->fr_offset > 0)
+ {
+ fragP->tc_frag_data.is_align = TRUE;
+ fragP->tc_frag_data.alignment = fragP->fr_offset;
+ }
+
+ if (fragP->fr_type == rs_org && fragP->fr_offset > 0)
+ {
+ char *p = fragP->fr_literal + fragP->fr_fix;
+
+ fragP->tc_frag_data.is_org = TRUE;
+ fragP->tc_frag_data.fill = *p;
+ fragP->tc_frag_data.has_fill = (fragP->tc_frag_data.fill != 0);
+ }
+ }
+}
+
+/* Return TRUE if this section is not one for which we need to record
+ information in the avr property section. */
+
+static bfd_boolean
+exclude_section_from_property_tables (segT sec)
+{
+ /* Only generate property information for sections on which linker
+ relaxation could be performed. */
+ return !relaxable_section (sec);
+}
+
+/* Create a property record for fragment FRAGP from section SEC and place
+ it into an AVR_PROPERTY_RECORD_LINK structure, which can then formed
+ into a linked list by the caller. */
+
+static struct avr_property_record_link *
+create_record_for_frag (segT sec, fragS *fragP)
+{
+ struct avr_property_record_link *link;
+
+ link = xmalloc (sizeof (struct avr_property_record_link));
+ memset (link, 0, sizeof (*link));
+
+ if (fragP->tc_frag_data.is_org)
+ {
+ link->record.offset = fragP->fr_next->fr_address;
+ link->record.section = sec;
+
+ if (fragP->tc_frag_data.has_fill)
+ {
+ link->record.data.org.fill = fragP->tc_frag_data.fill;
+ link->record.type = RECORD_ORG_AND_FILL;
+ }
+ else
+ link->record.type = RECORD_ORG;
+ }
+ else
+ {
+ link->record.offset = fragP->fr_address;
+ link->record.section = sec;
+
+ gas_assert (fragP->tc_frag_data.is_align);
+ if (fragP->tc_frag_data.has_fill)
+ {
+ link->record.data.align.fill = fragP->tc_frag_data.fill;
+ link->record.type = RECORD_ALIGN_AND_FILL;
+ }
+ else
+ link->record.type = RECORD_ALIGN;
+ link->record.data.align.bytes = fragP->tc_frag_data.alignment;
+ }
+
+ return link;
+}
+
+/* Build a list of AVR_PROPERTY_RECORD_LINK structures for section SEC, and
+ merged them onto the list pointed to by NEXT_PTR. Return a pointer to
+ the last list item created. */
+
+static struct avr_property_record_link **
+append_records_for_section (segT sec,
+ struct avr_property_record_link **next_ptr)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragP;
+
+ if (seginfo && seginfo->frchainP)
+ {
+ for (fragP = seginfo->frchainP->frch_root;
+ fragP;
+ fragP = fragP->fr_next)
+ {
+ if (fragP->tc_frag_data.is_align
+ || fragP->tc_frag_data.is_org)
+ {
+ /* Create a single new entry. */
+ struct avr_property_record_link *new_link
+ = create_record_for_frag (sec, fragP);
+
+ *next_ptr = new_link;
+ next_ptr = &new_link->next;
+ }
+ }
+ }
+
+ return next_ptr;
+}
+
+/* Create the AVR property section and fill it with records of .org and
+ .align directives that were used. The section is only created if it
+ will actually have any content. */
+
+static void
+avr_create_and_fill_property_section (void)
+{
+ segT *seclist;
+ asection *prop_sec;
+ struct avr_property_record_link *r_list, **next_ptr;
+ char *frag_ptr, *frag_base;
+ bfd_size_type sec_size;
+ struct avr_property_record_link *rec;
+ unsigned int record_count;
+
+ /* First walk over all sections. For sections on which linker
+ relaxation could be applied, extend the record list. The record list
+ holds information that the linker will need to know. */
+
+ prop_sec = NULL;
+ r_list = NULL;
+ next_ptr = &r_list;
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+
+ if (exclude_section_from_property_tables (sec))
+ continue;
+
+ next_ptr = append_records_for_section (sec, next_ptr);
+ }
+
+ /* Create property section and ensure the size is correct. We've already
+ passed the point where gas could size this for us. */
+ sec_size = AVR_PROPERTY_SECTION_HEADER_SIZE;
+ record_count = 0;
+ for (rec = r_list; rec != NULL; rec = rec->next)
+ {
+ record_count++;
+ sec_size += avr_record_size (&rec->record);
+ }
+
+ if (record_count == 0)
+ return;
+
+ prop_sec = avr_create_property_section ();
+ bfd_set_section_size (stdoutput, prop_sec, sec_size);
+
+ subseg_set (prop_sec, 0);
+ frag_base = frag_more (sec_size);
+
+ frag_ptr =
+ avr_output_property_section_header (frag_base, record_count);
+
+ for (rec = r_list; rec != NULL; rec = rec->next)
+ frag_ptr = avr_output_property_record (frag_base, frag_ptr, &rec->record);
+
+ frag_wane (frag_now);
+ frag_new (0);
+ frag_wane (frag_now);
+}
+
+/* We're using this hook to build up the AVR property section. It's called
+ late in the assembly process which suits our needs. */
+void
+avr_post_relax_hook (void)
+{
+ avr_create_and_fill_property_section ();
+}