ARM unwind table linker processing

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

ARM unwind table linker processing

Paul Brook
The patch below implements linker processing of ARM unwinding tables
(SHT_ARM_EXIDX).

ARM exception index tables only define the start address of each region. This
means that code with no unwinding information is effectively covered by the
preceding unwinding table entry.

For normal exceptions that doesn't matter so much - the user should ensure
that any code they throw exceptions through has proper unwinding information.

However these tables are also used for generating backtraces. In this case
it's fairly common for the backtrace to encounter regions without unwinding
information. Picking up incorrect unwinding information is generally fatal,
and causes the unwinder to crash.

The solution is to have the linker insert CANTUNWIND entries for sections that
don't have unwinding information. Obviously we still can't unwind/backtrace
through these regions, but at least the unwinder can fail gracefully.

Now that we're modifying the index table, we also take the opportunity to
coalesce identical adjacent entries.

The actual processing is implemented in two stages. During final linker layout
we scan the index tables, and record what changes we need to make. Then these
changes are applied when writing out the exidx section.

Tested on arm-none-eabi, arm-linux-gnueabi, arm-elf and arm-coff.
Applied to CVS head.

Paul

2009-05-05  Paul Brook  <[hidden email]>

        bfd/
        * bfd-in.h (elf32_arm_fix_exidx_coverage): Add prototype.
        * bfd-in2.h: Regenerate.
        * elf32-arm.c (arm_unwind_edit_type, arm_unwind_table_edit): Define.
        (_arm_elf_section_data): Add text and exidx fields.
        (add_unwind_table_edit, get_arm_elf_section_data, adjust_exidx_size,
        insert_cantunwind_after, elf32_arm_fix_exidx_coverage, offset_prel31,
        copy_exidx_entry): New functions.
        (elf32_arm_write_section): Fixup .ARM.exidx contents.

        ld/
        * emultempl/armelf.em (compare_output_sec_vma): New function.
        (gld${EMULATION_NAME}_finish): Add exidx munging code.

        ld/testsuite/
        * ld-arm/arm.ld: Add .ARM.exidx and .ARM.extab.
        * ld-arm/arm-elf.exp: Add unwind-[1-4].
        * ld-arm/unwind-1.d: New test.
        * ld-arm/unwind-1.s: New test.
        * ld-arm/unwind-2.d: New test.
        * ld-arm/unwind-2.s: New test.
        * ld-arm/unwind-3.d: New test.
        * ld-arm/unwind-3.s: New test.
        * ld-arm/unwind-4.d: New test.
        * ld-arm/unwind-4.s: New test.

Index: ld/emultempl/armelf.em
===================================================================
--- ld/emultempl/armelf.em (revision 248230)
+++ ld/emultempl/armelf.em (working copy)
@@ -258,10 +258,78 @@ build_section_lists (lang_statement_unio
     }
 }
 
+static int
+compare_output_sec_vma (const void *a, const void *b)
+{
+  asection *asec = *(asection **) a, *bsec = *(asection **) b;
+  asection *aout = asec->output_section, *bout = bsec->output_section;
+  bfd_vma avma, bvma;
+  
+  /* If there's no output section for some reason, compare equal.  */
+  if (!aout || !bout)
+    return 0;
+  
+  avma = aout->vma + asec->output_offset;
+  bvma = bout->vma + bsec->output_offset;
+  
+  if (avma > bvma)
+    return 1;
+  else if (avma < bvma)
+    return -1;
+  
+  return 0;
+}
+
 static void
 gld${EMULATION_NAME}_finish (void)
 {
   struct bfd_link_hash_entry * h;
+  unsigned int list_size = 10;
+  asection **sec_list = xmalloc (list_size * sizeof (asection *));
+  unsigned int sec_count = 0;
+
+  if (!link_info.relocatable)
+    {
+      /* Build a sorted list of input text sections, then use that to process
+ the unwind table index.  */
+      LANG_FOR_EACH_INPUT_STATEMENT (is)
+ {
+  bfd *abfd = is->the_bfd;
+  asection *sec;
+  
+  if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
+    continue;
+  
+  for (sec = abfd->sections; sec != NULL; sec = sec->next)
+    {
+      asection *out_sec = sec->output_section;
+
+      if (out_sec
+  && elf_section_type (sec) == SHT_PROGBITS
+  && (elf_section_flags (sec) & SHF_EXECINSTR) != 0
+  && (sec->flags & SEC_EXCLUDE) == 0
+  && sec->sec_info_type != ELF_INFO_TYPE_JUST_SYMS
+  && out_sec != bfd_abs_section_ptr)
+ {
+  if (sec_count == list_size)
+    {
+      list_size *= 2;
+      sec_list = xrealloc (sec_list,
+   list_size * sizeof (asection *));
+    }
+
+  sec_list[sec_count++] = sec;
+ }
+    }
+ }
+
+      qsort (sec_list, sec_count, sizeof (asection *), &compare_output_sec_vma);
+      
+      if (elf32_arm_fix_exidx_coverage (sec_list, sec_count, &link_info))
+ need_laying_out = 1;
+      
+      free (sec_list);
+    }
 
   /* bfd_elf32_discard_info just plays with debugging sections,
      ie. doesn't affect any code, so we can delay resizing the
Index: ld/testsuite/ld-arm/unwind-3.d
===================================================================
--- ld/testsuite/ld-arm/unwind-3.d (revision 0)
+++ ld/testsuite/ld-arm/unwind-3.d (revision 0)
@@ -0,0 +1,11 @@
+#ld: -T arm.ld
+#objdump: -s
+
+.*:     file format.*
+
+#...
+Contents of section .ARM.exidx:
+ 800c (f4ffff7f b0b0a880 f0ffff7f 01000000|7ffffff4 80a8b0b0 7ffffff0 00000001)  .*
+ 801c (ecffff7f b0b0a880 e8ffff7f 01000000|7fffffec 80a8b0b0 7fffffe8 00000001)  .*
+Contents of section .far:
+#...
Index: ld/testsuite/ld-arm/unwind-3.s
===================================================================
--- ld/testsuite/ld-arm/unwind-3.s (revision 0)
+++ ld/testsuite/ld-arm/unwind-3.s (revision 0)
@@ -0,0 +1,29 @@
+ .syntax unified
+ .text
+ @ section without unwind info
+ .global _start
+ .type _start, %function
+_start:
+ bl _before
+
+ @ Section that will be placed first
+ .section .before, "xa"
+ .type _before, %function
+_before:
+ .fnstart
+ .save {r4, lr}
+ bx lr
+ .fnend
+
+ @ section that will be placed last
+ .section .after, "xa"
+ .global __aeabi_unwind_cpp_pr0
+ .type __aeabi_unwind_cpp_pr0, %function
+__aeabi_unwind_cpp_pr0:
+ .fnstart
+ .save {r4, lr}
+ bx lr
+ .fnend
+
+ .section .far
+ .word 0
Index: ld/testsuite/ld-arm/unwind-4.d
===================================================================
--- ld/testsuite/ld-arm/unwind-4.d (revision 0)
+++ ld/testsuite/ld-arm/unwind-4.d (revision 0)
@@ -0,0 +1,11 @@
+#ld: -T arm.ld
+#objdump: -s
+
+.*:     file format.*
+
+#...
+Contents of section .ARM.exidx:
+ 8020 (e0ffff7f b0b0a880 dcffff7f e8ffff7f|7fffffe0 80a8b0b0 7fffffdc 7fffffe8)  .*
+ 8030 (d8ffff7f b0b0a880 d8ffff7f 01000000|7fffffd8 80a8b0b0 7fffffd8 00000001)  .*
+Contents of section .far:
+#...
Index: ld/testsuite/ld-arm/unwind-4.s
===================================================================
--- ld/testsuite/ld-arm/unwind-4.s (revision 0)
+++ ld/testsuite/ld-arm/unwind-4.s (revision 0)
@@ -0,0 +1,49 @@
+ .syntax unified
+ .text
+ @ out of line table entry
+ .global _start
+ .type _start, %function
+_start:
+ .fnstart
+ .save {r4, lr}
+ .vsave {d0}
+ .vsave {d4}
+ bl _before
+ .fnend
+
+ @ entry that can be merged
+ .fnstart
+ .save {r4, lr}
+ bx lr
+ .fnend
+
+ @ Section that will be placed first
+ .section .before, "xa"
+ .type _before, %function
+_before:
+ .fnstart
+ .save {r4, lr}
+ bx lr
+ .fnend
+
+ @ section that will be placed last
+ .section .after, "xa"
+ .global __aeabi_unwind_cpp_pr0
+ .type __aeabi_unwind_cpp_pr0, %function
+__aeabi_unwind_cpp_pr0:
+ .fnstart
+ .save {r4, lr}
+ bx lr
+ .fnend
+ @ final function is cantunwind, so output table size is smaller
+ @ than sum of input sections
+ .global __aeabi_unwind_cpp_pr1
+ .type __aeabi_unwind_cpp_pr1, %function
+__aeabi_unwind_cpp_pr1:
+ .fnstart
+ .cantunwind
+ bx lr
+ .fnend
+
+ .section .far
+ .word 0
Index: ld/testsuite/ld-arm/unwind-1.d
===================================================================
--- ld/testsuite/ld-arm/unwind-1.d (revision 0)
+++ ld/testsuite/ld-arm/unwind-1.d (revision 0)
@@ -0,0 +1,10 @@
+#ld: -T arm.ld
+#objdump: -s
+
+.*:     file format.*
+
+#...
+Contents of section .ARM.exidx:
+ 8008 (f8ffff7f b0b0a880 f4ffff7f 01000000|7ffffff8 80a8b0b0 7ffffff4 00000001)  .*
+Contents of section .far:
+#...
Index: ld/testsuite/ld-arm/unwind-1.s
===================================================================
--- ld/testsuite/ld-arm/unwind-1.s (revision 0)
+++ ld/testsuite/ld-arm/unwind-1.s (revision 0)
@@ -0,0 +1,19 @@
+ .syntax unified
+ .text
+ .global _start
+ .type _start, %function
+_start:
+ .fnstart
+ .save {r4, lr}
+ bx lr
+ .fnend
+
+ @ Section with no unwinding information.  Linker should insert a cantunwind entry.
+ .section .after, "xa"
+ .global __aeabi_unwind_cpp_pr0
+ .type __aeabi_unwind_cpp_pr0, %function
+__aeabi_unwind_cpp_pr0:
+ bx lr
+
+ .section .far
+ .word 0
Index: ld/testsuite/ld-arm/arm.ld
===================================================================
--- ld/testsuite/ld-arm/arm.ld (revision 248230)
+++ ld/testsuite/ld-arm/arm.ld (working copy)
@@ -10,9 +10,11 @@ SECTIONS
     *(.before)
     *(.text)
     *(.after)
+    *(.ARM.extab*)
     *(.glue_7)
     *(.v4_bx)
   } =0
+  .ARM.exidx : { *(.ARM.exidx*) }
   . = 0x9000;
   .got            : { *(.got) *(.got.plt)}
   . = 0x12340000;
Index: ld/testsuite/ld-arm/arm-elf.exp
===================================================================
--- ld/testsuite/ld-arm/arm-elf.exp (revision 248230)
+++ ld/testsuite/ld-arm/arm-elf.exp (working copy)
@@ -392,3 +392,7 @@ run_dump_test "attr-merge-unknown-1"
 run_dump_test "attr-merge-unknown-2"
 run_dump_test "attr-merge-unknown-2r"
 run_dump_test "attr-merge-unknown-3"
+run_dump_test "unwind-1"
+run_dump_test "unwind-2"
+run_dump_test "unwind-3"
+run_dump_test "unwind-4"
Index: ld/testsuite/ld-arm/unwind-2.d
===================================================================
--- ld/testsuite/ld-arm/unwind-2.d (revision 0)
+++ ld/testsuite/ld-arm/unwind-2.d (revision 0)
@@ -0,0 +1,10 @@
+#ld: -T arm.ld
+#objdump: -s
+
+.*:     file format.*
+
+#...
+Contents of section .ARM.exidx:
+ 8004 (fcffff7f b0b0a880 f8ffff7f 01000000|7ffffffc 80a8b0b0 7ffffff8 00000001)  .*
+Contents of section .far:
+#...
Index: ld/testsuite/ld-arm/unwind-2.s
===================================================================
--- ld/testsuite/ld-arm/unwind-2.s (revision 0)
+++ ld/testsuite/ld-arm/unwind-2.s (revision 0)
@@ -0,0 +1,19 @@
+ .syntax unified
+ .text
+
+ .global __aeabi_unwind_cpp_pr0
+ .type __aeabi_unwind_cpp_pr0, %function
+__aeabi_unwind_cpp_pr0:
+ .global _start
+ .type _start, %function
+_start:
+ .fnstart
+ .save {r4, lr}
+ bx lr
+ .fnend
+
+ @ last text section has unwind information. Linker should append a
+ @ terminating cantunwind entry.
+
+ .section .far
+ .word 0
Index: bfd/bfd-in.h
===================================================================
--- bfd/bfd-in.h (revision 248230)
+++ bfd/bfd-in.h (working copy)
@@ -898,7 +898,11 @@ extern bfd_boolean elf32_arm_size_stubs
    struct bfd_section * (*) (const char *, struct bfd_section *), void (*) (void));
 extern bfd_boolean elf32_arm_build_stubs
   (struct bfd_link_info *);
-  
+
+/* ARM unwind section editing support.  */
+extern bfd_boolean elf32_arm_fix_exidx_coverage
+  (struct bfd_section **, unsigned int, struct bfd_link_info *);
+
 /* TI COFF load page support.  */
 extern void bfd_ticoff_set_section_load_page
   (struct bfd_section *, int);
Index: bfd/bfd-in2.h
===================================================================
--- bfd/bfd-in2.h (revision 248230)
+++ bfd/bfd-in2.h (working copy)
@@ -905,7 +905,11 @@ extern bfd_boolean elf32_arm_size_stubs
    struct bfd_section * (*) (const char *, struct bfd_section *), void (*) (void));
 extern bfd_boolean elf32_arm_build_stubs
   (struct bfd_link_info *);
-  
+
+/* ARM unwind section editing support.  */
+extern bfd_boolean elf32_arm_fix_exidx_coverage
+  (struct bfd_section **, unsigned int, struct bfd_link_info *);
+
 /* TI COFF load page support.  */
 extern void bfd_ticoff_set_section_load_page
   (struct bfd_section *, int);
Index: bfd/elf32-arm.c
===================================================================
--- bfd/elf32-arm.c (revision 248230)
+++ bfd/elf32-arm.c (working copy)
@@ -19,6 +19,8 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
    MA 02110-1301, USA.  */
 
+#include <limits.h>
+
 #include "sysdep.h"
 #include "bfd.h"
 #include "libiberty.h"
@@ -2266,14 +2268,52 @@ typedef struct elf32_vfp11_erratum_list
 }
 elf32_vfp11_erratum_list;
 
+typedef enum
+{
+  DELETE_EXIDX_ENTRY,
+  INSERT_EXIDX_CANTUNWIND_AT_END
+}
+arm_unwind_edit_type;
+
+/* A (sorted) list of edits to apply to an unwind table.  */
+typedef struct arm_unwind_table_edit
+{
+  arm_unwind_edit_type type;
+  /* Note: we sometimes want to insert an unwind entry corresponding to a
+     section different from the one we're currently writing out, so record the
+     (text) section this edit relates to here.  */
+  asection *linked_section;
+  unsigned int index;
+  struct arm_unwind_table_edit *next;
+}
+arm_unwind_table_edit;
+
 typedef struct _arm_elf_section_data
 {
+  /* Information about mapping symbols.  */
   struct bfd_elf_section_data elf;
   unsigned int mapcount;
   unsigned int mapsize;
   elf32_arm_section_map *map;
+  /* Information about CPU errata.  */
   unsigned int erratumcount;
   elf32_vfp11_erratum_list *erratumlist;
+  /* Information about unwind tables.  */
+  union
+  {
+    /* Unwind info attached to a text section.  */
+    struct
+    {
+      asection *arm_exidx_sec;
+    } text;
+
+    /* Unwind info attached to an .ARM.exidx section.  */
+    struct
+    {
+      arm_unwind_table_edit *unwind_edit_list;
+      arm_unwind_table_edit *unwind_edit_tail;
+    } exidx;
+  } u;
 }
 _arm_elf_section_data;
 
@@ -8180,6 +8220,245 @@ elf32_arm_relocate_section (bfd *      
   return TRUE;
 }
 
+/* Add a new unwind edit to the list described by HEAD, TAIL.  If INDEX is zero,
+   adds the edit to the start of the list.  (The list must be built in order of
+   ascending INDEX: the function's callers are primarily responsible for
+   maintaining that condition).  */
+
+static void
+add_unwind_table_edit (arm_unwind_table_edit **head,
+       arm_unwind_table_edit **tail,
+       arm_unwind_edit_type type,
+       asection *linked_section,
+       unsigned int index)
+{
+  arm_unwind_table_edit *new_edit = xmalloc (sizeof (arm_unwind_table_edit));
+  
+  new_edit->type = type;
+  new_edit->linked_section = linked_section;
+  new_edit->index = index;
+  
+  if (index > 0)
+    {
+      new_edit->next = NULL;
+
+      if (*tail)
+ (*tail)->next = new_edit;
+
+      (*tail) = new_edit;
+
+      if (!*head)
+ (*head) = new_edit;
+    }
+  else
+    {
+      new_edit->next = *head;
+
+      if (!*tail)
+ *tail = new_edit;
+
+      *head = new_edit;
+    }
+}
+
+static _arm_elf_section_data *get_arm_elf_section_data (asection *);
+
+/* Increase the size of EXIDX_SEC by ADJUST bytes.  ADJUST mau be negative.  */
+static void
+adjust_exidx_size(asection *exidx_sec, int adjust)
+{
+  asection *out_sec;
+
+  if (!exidx_sec->rawsize)
+    exidx_sec->rawsize = exidx_sec->size;
+
+  bfd_set_section_size (exidx_sec->owner, exidx_sec, exidx_sec->size + adjust);
+  out_sec = exidx_sec->output_section;
+  /* Adjust size of output section.  */
+  bfd_set_section_size (out_sec->owner, out_sec, out_sec->size +adjust);
+}
+
+/* Insert an EXIDX_CANTUNWIND marker at the end of a section.  */
+static void
+insert_cantunwind_after(asection *text_sec, asection *exidx_sec)
+{
+  struct _arm_elf_section_data *exidx_arm_data;
+
+  exidx_arm_data = get_arm_elf_section_data (exidx_sec);
+  add_unwind_table_edit (
+    &exidx_arm_data->u.exidx.unwind_edit_list,
+    &exidx_arm_data->u.exidx.unwind_edit_tail,
+    INSERT_EXIDX_CANTUNWIND_AT_END, text_sec, UINT_MAX);
+
+  adjust_exidx_size(exidx_sec, 8);
+}
+
+/* Scan .ARM.exidx tables, and create a list describing edits which should be
+   made to those tables, such that:
+  
+     1. Regions without unwind data are marked with EXIDX_CANTUNWIND entries.
+     2. Duplicate entries are merged together (EXIDX_CANTUNWIND, or unwind
+        codes which have been inlined into the index).
+
+   The edits are applied when the tables are written
+   (in elf32_arm_write_section).
+*/
+
+bfd_boolean
+elf32_arm_fix_exidx_coverage (asection **text_section_order,
+      unsigned int num_text_sections,
+      struct bfd_link_info *info)
+{
+  bfd *inp;
+  unsigned int last_second_word = 0, i;
+  asection *last_exidx_sec = NULL;
+  asection *last_text_sec = NULL;
+  int last_unwind_type = -1;
+
+  /* Walk over all EXIDX sections, and create backlinks from the corrsponding
+     text sections.  */
+  for (inp = info->input_bfds; inp != NULL; inp = inp->link_next)
+    {
+      asection *sec;
+      
+      for (sec = inp->sections; sec != NULL; sec = sec->next)
+        {
+  struct bfd_elf_section_data *elf_sec = elf_section_data (sec);
+  Elf_Internal_Shdr *hdr = &elf_sec->this_hdr;
+  
+  if (hdr->sh_type != SHT_ARM_EXIDX)
+    continue;
+  
+  if (elf_sec->linked_to)
+    {
+      Elf_Internal_Shdr *linked_hdr
+        = &elf_section_data (elf_sec->linked_to)->this_hdr;
+      struct _arm_elf_section_data *linked_sec_arm_data
+        = get_arm_elf_section_data (linked_hdr->bfd_section);
+
+      if (linked_sec_arm_data == NULL)
+        continue;
+
+      /* Link this .ARM.exidx section back from the text section it
+         describes.  */
+      linked_sec_arm_data->u.text.arm_exidx_sec = sec;
+    }
+ }
+    }
+
+  /* Walk all text sections in order of increasing VMA.  Eilminate duplicate
+     index table entries (EXIDX_CANTUNWIND and inlined unwind opcodes),
+     and add EXIDX_CANTUNWIND entries for sections with no unwind table data.
+   */
+
+  for (i = 0; i < num_text_sections; i++)
+    {
+      asection *sec = text_section_order[i];
+      asection *exidx_sec;
+      struct _arm_elf_section_data *arm_data = get_arm_elf_section_data (sec);
+      struct _arm_elf_section_data *exidx_arm_data;
+      bfd_byte *contents = NULL;
+      int deleted_exidx_bytes = 0;
+      bfd_vma j;
+      arm_unwind_table_edit *unwind_edit_head = NULL;
+      arm_unwind_table_edit *unwind_edit_tail = NULL;
+      Elf_Internal_Shdr *hdr;
+      bfd *ibfd;
+
+      if (arm_data == NULL)
+        continue;
+
+      exidx_sec = arm_data->u.text.arm_exidx_sec;
+      if (exidx_sec == NULL)
+ {
+  /* Section has no unwind data.  */
+  if (last_unwind_type == 0 || !last_exidx_sec)
+    continue;
+
+  /* Ignore zero sized sections.  */
+  if (sec->size == 0)
+    continue;
+
+  insert_cantunwind_after(last_text_sec, last_exidx_sec);
+  last_unwind_type = 0;
+  continue;
+ }
+
+      hdr = &elf_section_data (exidx_sec)->this_hdr;
+      if (hdr->sh_type != SHT_ARM_EXIDX)
+        continue;
+      
+      exidx_arm_data = get_arm_elf_section_data (exidx_sec);
+      if (exidx_arm_data == NULL)
+        continue;
+      
+      ibfd = exidx_sec->owner;
+  
+      if (hdr->contents != NULL)
+ contents = hdr->contents;
+      else if (! bfd_malloc_and_get_section (ibfd, exidx_sec, &contents))
+ /* An error?  */
+ continue;
+
+      for (j = 0; j < hdr->sh_size; j += 8)
+ {
+  unsigned int second_word = bfd_get_32 (ibfd, contents + j + 4);
+  int unwind_type;
+  int elide = 0;
+
+  /* An EXIDX_CANTUNWIND entry.  */
+  if (second_word == 1)
+    {
+      if (last_unwind_type == 0)
+ elide = 1;
+      unwind_type = 0;
+    }
+  /* Inlined unwinding data.  Merge if equal to previous.  */
+  else if ((second_word & 0x80000000) != 0)
+    {
+      if (last_second_word == second_word && last_unwind_type == 1)
+ elide = 1;
+      unwind_type = 1;
+      last_second_word = second_word;
+    }
+  /* Normal table entry.  In theory we could merge these too,
+     but duplicate entries are likely to be much less common.  */
+  else
+    unwind_type = 2;
+
+  if (elide)
+    {
+      add_unwind_table_edit (&unwind_edit_head, &unwind_edit_tail,
+     DELETE_EXIDX_ENTRY, NULL, j / 8);
+
+      deleted_exidx_bytes += 8;
+    }
+
+  last_unwind_type = unwind_type;
+ }
+
+      /* Free contents if we allocated it ourselves.  */
+      if (contents != hdr->contents)
+        free (contents);
+
+      /* Record edits to be applied later (in elf32_arm_write_section).  */
+      exidx_arm_data->u.exidx.unwind_edit_list = unwind_edit_head;
+      exidx_arm_data->u.exidx.unwind_edit_tail = unwind_edit_tail;
+  
+      if (deleted_exidx_bytes > 0)
+ adjust_exidx_size(exidx_sec, -deleted_exidx_bytes);
+
+      last_exidx_sec = exidx_sec;
+      last_text_sec = sec;
+    }
+
+  /* Add terminating CANTUNWIND entry.  */
+  if (last_exidx_sec && last_unwind_type != 0)
+    insert_cantunwind_after(last_text_sec, last_exidx_sec);
+
+  return TRUE;
+}
+
 static bfd_boolean
 elf32_arm_output_glue_section (struct bfd_link_info *info, bfd *obfd,
        bfd *ibfd, const char *name)
@@ -12163,6 +12442,35 @@ elf32_arm_compare_mapping (const void *
     return 0;
 }
 
+/* Add OFFSET to lower 31 bits of ADDR, leaving other bits unmodified.  */
+
+static unsigned long
+offset_prel31 (unsigned long addr, bfd_vma offset)
+{
+  return (addr & ~0x7ffffffful) | ((addr + offset) & 0x7ffffffful);
+}
+
+/* Copy an .ARM.exidx table entry, adding OFFSET to (applied) PREL31
+   relocations.  */
+
+static void
+copy_exidx_entry (bfd *output_bfd, bfd_byte *to, bfd_byte *from, bfd_vma offset)
+{
+  unsigned long first_word = bfd_get_32 (output_bfd, from);
+  unsigned long second_word = bfd_get_32 (output_bfd, from + 4);
+  
+  /* High bit of first word is supposed to be zero.  */
+  if ((first_word & 0x80000000ul) == 0)
+    first_word = offset_prel31 (first_word, offset);
+  
+  /* If the high bit of the first word is clear, and the bit pattern is not 0x1
+     (EXIDX_CANTUNWIND), this is an offset to an .ARM.extab entry.  */
+  if ((second_word != 0x1) && ((second_word & 0x80000000ul) == 0))
+    second_word = offset_prel31 (second_word, offset);
+  
+  bfd_put_32 (output_bfd, first_word, to);
+  bfd_put_32 (output_bfd, second_word, to + 4);
+}
 
 /* Do code byteswapping.  Return FALSE afterwards so that the section is
    written out as normal.  */
@@ -12269,6 +12577,94 @@ elf32_arm_write_section (bfd *output_bfd
         }
     }
 
+  if (arm_data->elf.this_hdr.sh_type == SHT_ARM_EXIDX)
+    {
+      arm_unwind_table_edit *edit_node
+        = arm_data->u.exidx.unwind_edit_list;
+      /* Now, sec->size is the size of the section we will write.  The original
+         size (before we merged duplicate entries and inserted EXIDX_CANTUNWIND
+ markers) was sec->rawsize.  (This isn't the case if we perform no
+ edits, then rawsize will be zero and we should use size).  */
+      bfd_byte *edited_contents = bfd_malloc (sec->size);
+      unsigned int input_size = sec->rawsize ? sec->rawsize : sec->size;
+      unsigned int in_index, out_index;
+      bfd_vma add_to_offsets = 0;
+
+      for (in_index = 0, out_index = 0; in_index * 8 < input_size || edit_node;)
+        {
+  if (edit_node)
+    {
+      unsigned int edit_index = edit_node->index;
+      
+      if (in_index < edit_index && in_index * 8 < input_size)
+        {
+  copy_exidx_entry (output_bfd, edited_contents + out_index * 8,
+    contents + in_index * 8, add_to_offsets);
+  out_index++;
+  in_index++;
+ }
+      else if (in_index == edit_index
+       || (in_index * 8 >= input_size
+   && edit_index == UINT_MAX))
+        {
+  switch (edit_node->type)
+    {
+    case DELETE_EXIDX_ENTRY:
+      in_index++;
+      add_to_offsets += 8;
+      break;
+    
+    case INSERT_EXIDX_CANTUNWIND_AT_END:
+      {
+        asection *text_sec = edit_node->linked_section;
+ bfd_vma text_offset = text_sec->output_section->vma
+      + text_sec->output_offset
+      + text_sec->size;
+ bfd_vma exidx_offset = offset + out_index * 8;
+        unsigned long prel31_offset;
+
+ /* Note: this is meant to be equivalent to an
+   R_ARM_PREL31 relocation.  These synthetic
+   EXIDX_CANTUNWIND markers are not relocated by the
+   usual BFD method.  */
+ prel31_offset = (text_offset - exidx_offset)
+ & 0x7ffffffful;
+
+ /* First address we can't unwind.  */
+ bfd_put_32 (output_bfd, prel31_offset,
+    &edited_contents[out_index * 8]);
+
+ /* Code for EXIDX_CANTUNWIND.  */
+ bfd_put_32 (output_bfd, 0x1,
+    &edited_contents[out_index * 8 + 4]);
+
+ out_index++;
+ add_to_offsets -= 8;
+      }
+      break;
+    }
+  
+  edit_node = edit_node->next;
+ }
+    }
+  else
+    {
+      /* No more edits, copy remaining entries verbatim.  */
+      copy_exidx_entry (output_bfd, edited_contents + out_index * 8,
+ contents + in_index * 8, add_to_offsets);
+      out_index++;
+      in_index++;
+    }
+ }
+
+      if (!(sec->flags & SEC_EXCLUDE) && !(sec->flags & SEC_NEVER_LOAD))
+ bfd_set_section_contents (output_bfd, sec->output_section,
+  edited_contents,
+  (file_ptr) sec->output_offset, sec->size);
+
+      return TRUE;
+    }
+
   if (mapcount == 0)
     return FALSE;
 
Reply | Threaded
Open this post in threaded view
|

Re: ARM unwind table linker processing

Matthias Klose-6
Paul Brook schrieb:

> The patch below implements linker processing of ARM unwinding tables
> (SHT_ARM_EXIDX).
>
> 2009-05-05  Paul Brook  <[hidden email]>
>
> bfd/
> * bfd-in.h (elf32_arm_fix_exidx_coverage): Add prototype.
> * bfd-in2.h: Regenerate.
> * elf32-arm.c (arm_unwind_edit_type, arm_unwind_table_edit): Define.
> (_arm_elf_section_data): Add text and exidx fields.
> (add_unwind_table_edit, get_arm_elf_section_data, adjust_exidx_size,
> insert_cantunwind_after, elf32_arm_fix_exidx_coverage, offset_prel31,
> copy_exidx_entry): New functions.
> (elf32_arm_write_section): Fixup .ARM.exidx contents.
>
> ld/
> * emultempl/armelf.em (compare_output_sec_vma): New function.
> (gld${EMULATION_NAME}_finish): Add exidx munging code.

This breaks kernel builds on arm-linux-gnueabi. Checked by reverting this
change. command line used and object files at https://launchpad.net/bugs/375991

  Matthias

Program received signal SIGSEGV, Segmentation fault.
bfd_set_section_size (abfd=0x0, ptr=0x7e5f8, val=8) at ../../bfd/section.c:1297
1297      if (abfd->output_has_begun)
(gdb) bt
#0  bfd_set_section_size (abfd=0x0, ptr=0x7e5f8, val=8) at ../../bfd/section.c:1297
#1  0x4005c60c in elf32_arm_fix_exidx_coverage (text_section_order=<value
optimized out>, num_text_sections=4,
    info=<value optimized out>) at ../../bfd/elf32-arm.c:8425
#2  0x00026980 in gldarmelf_linux_eabi_finish () at earmelf_linux_eabi.c:420
#3  0x00021ea8 in ldemul_finish () at ../../ld/ldemul.c:90
#4  0x0001ac08 in lang_process () at ../../ld/ldlang.c:6282
#5  0x0001f154 in main (argc=10000, argv=0x12) at ../../ld/ldmain.c:479

Reply | Threaded
Open this post in threaded view
|

Re: ARM unwind table linker processing

Paul Brook
In reply to this post by Paul Brook
> This breaks kernel builds on arm-linux-gnueabi.

Argh. We'd already found and fixed this once, but I missed it when merging the
patch. Fixed as below.

Paul

2009-05-15  Andrew Stubbs  <[hidden email]>
        Paul Brook  <[hidden email]>

        bfd/
        * elf32-arm.c (elf32_arm_fix_exidx_coverage): Don't attempt to
        fix discarded sections.

        ld/testsuite/
        * ld-arm/arm-elf.exp: Add unwind-5.
        * ld-arm/discard-unwind.ld: New file.
        * ld-arm/unwind-5.d: New test.
        * ld-arm/unwind-5.s: New test.

Index: ld/testsuite/ld-arm/discard-unwind.ld
===================================================================
--- ld/testsuite/ld-arm/discard-unwind.ld (revision 0)
+++ ld/testsuite/ld-arm/discard-unwind.ld (revision 0)
@@ -0,0 +1,19 @@
+/* Script for ld testsuite */
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  PROVIDE (__executable_start = 0x8000); . = 0x8000;
+  .text           :
+  {
+    *(.before)
+    *(.text)
+    *(.after)
+    *(.ARM.extab*)
+    *(.glue_7)
+    *(.v4_bx)
+  } =0
+  /DISCARD/ : { *(.ARM.exidx*) }
+  .ARM.attribues 0 : { *(.ARM.atttributes) }
+}
Index: ld/testsuite/ld-arm/unwind-5.d
===================================================================
--- ld/testsuite/ld-arm/unwind-5.d (revision 0)
+++ ld/testsuite/ld-arm/unwind-5.d (revision 0)
@@ -0,0 +1,7 @@
+#ld: -T discard-unwind.ld
+#objdump: -s
+
+.*:     file format.*
+
+# Check we don't crash when discarding unwind info.
+#...
Index: ld/testsuite/ld-arm/unwind-5.s
===================================================================
--- ld/testsuite/ld-arm/unwind-5.s (revision 0)
+++ ld/testsuite/ld-arm/unwind-5.s (revision 0)
@@ -0,0 +1,12 @@
+ .syntax unified
+ .text
+ .global __aeabi_unwind_cpp_pr0
+ .type __aeabi_unwind_cpp_pr0, %function
+__aeabi_unwind_cpp_pr0:
+ .global _start
+ .type _start, %function
+_start:
+ .fnstart
+ .save {r4, lr}
+ bx lr
+ .fnend
Index: ld/testsuite/ld-arm/arm-elf.exp
===================================================================
--- ld/testsuite/ld-arm/arm-elf.exp (revision 249367)
+++ ld/testsuite/ld-arm/arm-elf.exp (working copy)
@@ -396,3 +396,4 @@ run_dump_test "unwind-1"
 run_dump_test "unwind-2"
 run_dump_test "unwind-3"
 run_dump_test "unwind-4"
+run_dump_test "unwind-5"
Index: bfd/elf32-arm.c
===================================================================
--- bfd/elf32-arm.c (revision 249367)
+++ bfd/elf32-arm.c (working copy)
@@ -8352,6 +8352,10 @@ elf32_arm_fix_exidx_coverage (asection *
   continue;
  }
 
+      /* Skip /DISCARD/ sections.  */
+      if (bfd_is_abs_section (exidx_sec->output_section))
+ continue;
+
       hdr = &elf_section_data (exidx_sec)->this_hdr;
       if (hdr->sh_type != SHT_ARM_EXIDX)
         continue;


Reply | Threaded
Open this post in threaded view
|

Re: ARM unwind table linker processing

Matthias Klose-6
On 15.05.2009 02:19, Paul Brook wrote:
>> This breaks kernel builds on arm-linux-gnueabi.
>
> Argh. We'd already found and fixed this once, but I missed it when merging the
> patch. Fixed as below.

Found out, that this breaks the uno2cpp bridge in OpenOffice as well.
Filed PR ld/10695.

   Matthias