avr/gas: Write out data to track .org/.align usage.
authorAndrew Burgess <andrew.burgess@embecosm.com>
Thu, 8 Jan 2015 20:55:10 +0000 (20:55 +0000)
committerAndrew Burgess <andrew.burgess@embecosm.com>
Wed, 25 Feb 2015 23:15:02 +0000 (23:15 +0000)
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.

bfd/ChangeLog
bfd/elf32-avr.h
gas/ChangeLog
gas/config/tc-avr.c
gas/config/tc-avr.h

index 57b7fd1801774a4a63f572e30cdfff7e31f3b95a..af2d2bafae439ba891ddb139846c82ad000f8600 100644 (file)
@@ -1,3 +1,10 @@
+2015-02-25  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * 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.
+
 2015-02-24  Nick Clifton  <nickc@redhat.com>
 
        * elf32-v850.c (v850_set_note): New function.  Creates a Renesas
index 6eb31541101ef39642669da272ac7f6499021ab8..688b70685415f854108b70e4e1f82a6778a19a3e 100644 (file)
@@ -36,3 +36,55 @@ elf32_avr_size_stubs (bfd *, struct bfd_link_info *, bfd_boolean);
 
 extern bfd_boolean
 elf32_avr_build_stubs (struct bfd_link_info *);
+
+/* The name of the section into which the property records are stored.  */
+#define AVR_PROPERTY_RECORD_SECTION_NAME ".avr.prop"
+
+/* The current version number for the format of the property records.  */
+#define AVR_PROPERTY_RECORDS_VERSION 1
+
+/* The size of the header that is written to the property record section
+   before the property records are written out.  */
+#define AVR_PROPERTY_SECTION_HEADER_SIZE 4
+
+/* This holds a single property record in memory, the structure of this
+   data when written out to the ELF section is more compressed.  */
+
+struct avr_property_record
+{
+  /* The section and offset for this record.  */
+  asection *section;
+  bfd_vma offset;
+
+  /* The type of this record.  */
+  enum {
+    RECORD_ORG = 0,
+    RECORD_ORG_AND_FILL = 1,
+    RECORD_ALIGN = 2,
+    RECORD_ALIGN_AND_FILL = 3
+  } type;
+
+  /* Type specific data.  */
+  union
+  {
+    /* RECORD_ORG and RECORD_ORG_AND_FILL.  */
+    struct
+    {
+      unsigned long fill;
+    } org;
+
+    /* RECORD_ALIGN and RECORD_ALIGN_AND_FILL.  */
+    struct
+    {
+      unsigned long bytes;
+      unsigned long fill;
+
+      /* This field is used during linker relaxation to track the number of
+         bytes that have been opened up before this alignment directive.
+         When we have enough bytes available it is possible to move the
+         re-align this directive backwards while still maintaining the
+         alignment requirement.  */
+      unsigned long preceding_deleted;
+    } align;
+  } data;
+};
index 3c51b8a9a3124b194e71d0459a00b124f48e7f0f..bac200238f9d6a4091f3986c81c833c63911d8c9 100644 (file)
@@ -1,3 +1,24 @@
+2015-02-25  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * 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.
+
 2015-02-25  Matthew Wahab  <matthew.wahab@arm.com>
 
        * doc/c-arm.texi (-mcpu=): Add cortex-a53, cortex-a57 and
index 5a0b405eee2150f81d638fca962dbed8764a0e56..cc0328d0299f2ec8f99101ca3d860170e3795798 100644 (file)
 #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 ();
+}
index c096ce39f65889ad639ea276efe7885ad079a132..21471c8bb3874254efa6de038beece9b538fcea9 100644 (file)
@@ -216,3 +216,20 @@ extern bfd_boolean avr_allow_local_subtract (expressionS *, expressionS *, segT)
 
 #define elf_tc_final_processing        avr_elf_final_processing
 extern void avr_elf_final_processing (void);
+
+#define md_post_relax_hook avr_post_relax_hook ()
+extern void avr_post_relax_hook (void);
+
+#define HANDLE_ALIGN(fragP) avr_handle_align (fragP)
+extern void avr_handle_align (fragS *fragP);
+
+struct avr_frag_data
+{
+  unsigned is_org : 1;
+  unsigned is_align : 1;
+  unsigned has_fill : 1;
+
+  char fill;
+  offsetT alignment;
+};
+#define TC_FRAG_TYPE                   struct avr_frag_data