ld patch that greatly speeds up linking large programs with debug symbols

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

ld patch that greatly speeds up linking large programs with debug symbols

Aaron Miller-2
Hi All,

I was experiencing ~8 minute linking times for a large C++ application
I have been working on when running -current on amd64. It turns out
that the decade-old version of ld in the OpenBSD source tree has a bug
that causes quadratic complexity for some linking operations when
debug symbols are present. I found a patch from 2006 in a mailing list
archive that fixes this; I adapted it to work with OpenBSD by editing
files that are normally regenerated (I couldn't figure out how to do
automatically regenerate them).

Here is the original patch:
https://sourceware.org/ml/binutils/2006-08/msg00334.html

I have rebuilt the kernel and system with this ld and everything runs
identically to the system linked with the unpatched ld.

Please test this and let me know if this could get into the tree, and
if not, what changes I need to make. Thanks!

Index: bfd/bfd-in2.h
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/bfd-in2.h,v
retrieving revision 1.4
diff -u -p -r1.4 bfd-in2.h
--- bfd/bfd-in2.h    25 May 2015 14:56:26 -0000    1.4
+++ bfd/bfd-in2.h    17 Apr 2016 05:14:15 -0000
@@ -5068,7 +5068,7 @@ typedef struct bfd_target

   /* Check if SEC has been already linked during a reloceatable or
      final link.  */
-  void (*_section_already_linked) (bfd *, struct bfd_section *);
+  void (*_section_already_linked) (bfd *, struct bfd_section *,
struct bfd_link_info *);

   /* Routines to handle dynamic symbols and relocs.  */
 #define BFD_JUMP_TABLE_DYNAMIC(NAME) \
@@ -5128,10 +5128,10 @@ bfd_boolean bfd_link_split_section (bfd
 #define bfd_link_split_section(abfd, sec) \
        BFD_SEND (abfd, _bfd_link_split_section, (abfd, sec))

-void bfd_section_already_linked (bfd *abfd, asection *sec);
+void bfd_section_already_linked (bfd *abfd, asection *sec, struct
bfd_link_info *);

-#define bfd_section_already_linked(abfd, sec) \
-       BFD_SEND (abfd, _section_already_linked, (abfd, sec))
+#define bfd_section_already_linked(abfd, sec, info) \
+       BFD_SEND (abfd, _section_already_linked, (abfd, sec, info))

 /* Extracted from simple.c.  */
 bfd_byte *bfd_simple_get_relocated_section_contents
Index: bfd/elf-bfd.h
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elf-bfd.h,v
retrieving revision 1.4
diff -u -p -r1.4 elf-bfd.h
--- bfd/elf-bfd.h    25 Aug 2015 02:24:49 -0000    1.4
+++ bfd/elf-bfd.h    17 Apr 2016 05:14:16 -0000
@@ -1371,6 +1371,9 @@ struct elf_obj_tdata

   /* Used to determine if we are creating an executable.  */
   bfd_boolean executable;
+
+  /* Symbol buffer.  */
+  Elf_Internal_Sym *symbuf;
 };

 #define elf_tdata(bfd)        ((bfd) -> tdata.elf_obj_data)
@@ -1503,11 +1506,11 @@ extern bfd_boolean _bfd_elf_match_sectio
 extern bfd_boolean bfd_elf_is_group_section
   (bfd *, const struct bfd_section *);
 extern void _bfd_elf_section_already_linked
-  (bfd *, struct bfd_section *);
+  (bfd *, struct bfd_section *, struct bfd_link_info *);
 extern void bfd_elf_set_group_contents
   (bfd *, asection *, void *);
 extern asection *_bfd_elf_check_kept_section
-  (asection *);
+  (asection *, struct bfd_link_info *);
 extern void _bfd_elf_link_just_syms
   (asection *, struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_copy_private_header_data
@@ -1703,7 +1706,7 @@ extern bfd_boolean _bfd_elf_symbol_refs_
   (struct elf_link_hash_entry *, struct bfd_link_info *, bfd_boolean);

 extern bfd_boolean bfd_elf_match_symbols_in_sections
-  (asection *sec1, asection *sec2);
+  (asection *, asection *, struct bfd_link_info *);

 extern bfd_boolean _bfd_elf_setup_sections
   (bfd *);
Index: bfd/elf.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elf.c,v
retrieving revision 1.7
diff -u -p -r1.7 elf.c
--- bfd/elf.c    13 Jan 2015 20:05:43 -0000    1.7
+++ bfd/elf.c    17 Apr 2016 05:14:18 -0000
@@ -3101,7 +3101,7 @@ assign_section_numbers (bfd *abfd, struc
              s, s->owner);
               /* Point to the kept section if it has the same
              size as the discarded one.  */
-              kept = _bfd_elf_check_kept_section (s);
+              kept = _bfd_elf_check_kept_section (s, link_info);
               if (kept == NULL)
             {
               bfd_set_error (bfd_error_bad_value);
@@ -8674,7 +8674,8 @@ elf_sym_name_compare (const void *arg1,
    symbols.  */

 bfd_boolean
-bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
+          struct bfd_link_info *info)
 {
   bfd *bfd1, *bfd2;
   const struct elf_backend_data *bed1, *bed2;
@@ -8732,21 +8733,37 @@ bfd_elf_match_symbols_in_sections (asect
   if (symcount1 == 0 || symcount2 == 0)
     return FALSE;

-  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
-                   NULL, NULL, NULL);
-  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
-                   NULL, NULL, NULL);
-
   result = FALSE;
-  if (isymbuf1 == NULL || isymbuf2 == NULL)
-    goto done;
+  isymbuf1 = elf_tdata (bfd1)->symbuf;
+  isymbuf2 = elf_tdata (bfd2)->symbuf;

-  /* Sort symbols by binding and section. Global definitions are at
-     the beginning.  */
-  qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
-     elf_sort_elf_symbol);
-  qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
-     elf_sort_elf_symbol);
+  if (isymbuf1 == NULL)
+    {
+      isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
+              NULL, NULL, NULL);
+      if (isymbuf1 == NULL)
+        goto done;
+      /* Sort symbols by binding and section. Global definitions are at
+         the beginning.  */
+      qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
+      elf_sort_elf_symbol);
+      if (!info->reduce_memory_overheads)
+        elf_tdata (bfd1)->symbuf = isymbuf1;
+    }
+
+  if (isymbuf2 == NULL)
+    {
+      isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
+              NULL, NULL, NULL);
+      if (isymbuf2 == NULL)
+        goto done;
+      /* Sort symbols by binding and section. Global definitions are at
+         the beginning.  */
+      qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
+      elf_sort_elf_symbol);
+      if (!info->reduce_memory_overheads)
+        elf_tdata (bfd2)->symbuf = isymbuf2;
+    }

   /* Count definitions in the section.  */
   count1 = 0;
@@ -8830,10 +8847,13 @@ done:
     free (symtable1);
   if (symtable2)
     free (symtable2);
-  if (isymbuf1)
-    free (isymbuf1);
-  if (isymbuf2)
-    free (isymbuf2);
+  if (info->reduce_memory_overheads)
+    {
+      if (isymbuf1)
+        free (isymbuf1);
+      if (isymbuf2)
+        free (isymbuf2);
+    }

   return result;
 }
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c,v
retrieving revision 1.12
diff -u -p -r1.12 elflink.c
--- bfd/elflink.c    28 Nov 2015 11:32:33 -0000    1.12
+++ bfd/elflink.c    17 Apr 2016 05:14:20 -0000
@@ -6766,14 +6766,15 @@ _bfd_elf_default_action_discarded (asect
 /* Find a match between a section and a member of a section group.  */

 static asection *
-match_group_member (asection *sec, asection *group)
+match_group_member (asection *sec, asection *group,
+       struct bfd_link_info *info)
 {
   asection *first = elf_next_in_group (group);
   asection *s = first;

   while (s != NULL)
     {
-      if (bfd_elf_match_symbols_in_sections (s, sec))
+      if (bfd_elf_match_symbols_in_sections (s, sec, info))
     return s;

       s = elf_next_in_group (s);
@@ -6789,7 +6790,7 @@ match_group_member (asection *sec, asect
    NULL. */

 asection *
-_bfd_elf_check_kept_section (asection *sec)
+_bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info)
 {
   asection *kept;

@@ -6797,7 +6798,7 @@ _bfd_elf_check_kept_section (asection *s
   if (kept != NULL)
     {
       if (elf_sec_group (sec) != NULL)
-    kept = match_group_member (sec, kept);
+    kept = match_group_member (sec, kept, info);
       if (kept != NULL && sec->size != kept->size)
     kept = NULL;
     }
@@ -7150,7 +7151,8 @@ elf_link_input_bfd (struct elf_final_lin
             {
               asection *kept;

-              kept = _bfd_elf_check_kept_section (sec);
+              kept = _bfd_elf_check_kept_section (sec,
+                                                              finfo->info);
               if (kept != NULL)
                 {
                   *ps = kept;
@@ -8280,6 +8282,15 @@ bfd_elf_final_link (bfd *abfd, struct bf
     }
     }

+  /* Free symbol buffer if needed.  */
+  if (!info->reduce_memory_overheads)
+    {
+      for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+       if (elf_tdata (sub)->symbuf)
+         free (elf_tdata (sub)->symbuf);
+    }
+
+
   /* Output any global symbols that got converted to local in a
      version script or due to symbol visibility.  We do this in a
      separate step since ELF requires all local symbols to appear
@@ -9724,7 +9735,8 @@ bfd_elf_discard_info (bfd *output_bfd, s
 }

 void
-_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section * sec)
+_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
+                                struct bfd_link_info *info)
 {
   flagword flags;
   const char *name, *p;
@@ -9890,7 +9902,8 @@ _bfd_elf_section_already_linked (bfd *ab
     if ((l->sec->flags & SEC_GROUP) == 0
         && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL
         && bfd_elf_match_symbols_in_sections (l->sec,
-                          elf_next_in_group (sec)))
+                          elf_next_in_group (sec),
+                                                  info))
       {
         elf_next_in_group (sec)->output_section = bfd_abs_section_ptr;
         elf_next_in_group (sec)->kept_section = l->sec;
@@ -9911,7 +9924,7 @@ _bfd_elf_section_already_linked (bfd *ab

       if (first != NULL
           && elf_next_in_group (first) == first
-          && bfd_elf_match_symbols_in_sections (first, sec))
+          && bfd_elf_match_symbols_in_sections (first, sec, info))
         {
           sec->output_section = bfd_abs_section_ptr;
           sec->kept_section = l->sec;
Index: bfd/libbfd-in.h
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/libbfd-in.h,v
retrieving revision 1.3
diff -u -p -r1.3 libbfd-in.h
--- bfd/libbfd-in.h    10 May 2015 20:41:19 -0000    1.3
+++ bfd/libbfd-in.h    17 Apr 2016 05:14:20 -0000
@@ -408,7 +408,7 @@ extern bfd_boolean _bfd_generic_set_sect
 #define _bfd_nolink_bfd_link_split_section \
   ((bfd_boolean (*) (bfd *, struct bfd_section *)) bfd_false)
 #define _bfd_nolink_section_already_linked \
-  ((void (*) (bfd *, struct bfd_section *)) bfd_void)
+  ((void (*) (bfd *, struct bfd_section *, struct bfd_link_info *)) bfd_void)

 /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
    have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
@@ -522,7 +522,7 @@ extern bfd_boolean _bfd_generic_link_spl
   (bfd *, struct bfd_section *);

 extern void _bfd_generic_section_already_linked
-  (bfd *, struct bfd_section *);
+  (bfd *, struct bfd_section *, struct bfd_link_info *);

 /* Generic reloc_link_order processing routine.  */
 extern bfd_boolean _bfd_generic_reloc_link_order
Index: bfd/libbfd.h
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/libbfd.h,v
retrieving revision 1.4
diff -u -p -r1.4 libbfd.h
--- bfd/libbfd.h    25 May 2015 14:56:26 -0000    1.4
+++ bfd/libbfd.h    17 Apr 2016 05:14:21 -0000
@@ -413,7 +413,7 @@ extern bfd_boolean _bfd_generic_set_sect
 #define _bfd_nolink_bfd_link_split_section \
   ((bfd_boolean (*) (bfd *, struct bfd_section *)) bfd_false)
 #define _bfd_nolink_section_already_linked \
-  ((void (*) (bfd *, struct bfd_section *)) bfd_void)
+  ((void (*) (bfd *, struct bfd_section *, struct bfd_link_info *)) bfd_void)

 /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
    have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
@@ -527,7 +527,7 @@ extern bfd_boolean _bfd_generic_link_spl
   (bfd *, struct bfd_section *);

 extern void _bfd_generic_section_already_linked
-  (bfd *, struct bfd_section *);
+  (bfd *, struct bfd_section *, struct bfd_link_info *);

 /* Generic reloc_link_order processing routine.  */
 extern bfd_boolean _bfd_generic_reloc_link_order
Index: bfd/linker.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/linker.c,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 linker.c
--- bfd/linker.c    24 Apr 2011 20:14:42 -0000    1.1.1.1
+++ bfd/linker.c    17 Apr 2016 05:14:22 -0000
@@ -2877,14 +2877,15 @@ FUNCTION
     bfd_section_already_linked

 SYNOPSIS
-        void bfd_section_already_linked (bfd *abfd, asection *sec);
+        void bfd_section_already_linked (bfd *abfd, asection *sec,
+                                         struct bfd_link_info *info);

 DESCRIPTION
     Check if @var{sec} has been already linked during a reloceatable
     or final link.

-.#define bfd_section_already_linked(abfd, sec) \
-.       BFD_SEND (abfd, _section_already_linked, (abfd, sec))
+.#define bfd_section_already_linked(abfd, sec, info) \
+.       BFD_SEND (abfd, _section_already_linked, (abfd, sec, info))
 .

 */
@@ -2970,7 +2971,8 @@ bfd_section_already_linked_table_free (v
 /* This is used on non-ELF inputs.  */

 void
-_bfd_generic_section_already_linked (bfd *abfd, asection *sec)
+_bfd_generic_section_already_linked (bfd *abfd, asection *sec,
+                                    struct bfd_link_info *info
ATTRIBUTE_UNUSED)
 {
   flagword flags;
   const char *name;
Index: bfd/targets.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/targets.c,v
retrieving revision 1.3
diff -u -p -r1.3 targets.c
--- bfd/targets.c    6 Apr 2015 18:30:22 -0000    1.3
+++ bfd/targets.c    17 Apr 2016 05:14:22 -0000
@@ -481,7 +481,8 @@ BFD_JUMP_TABLE macros.
 .
 .  {* Check if SEC has been already linked during a reloceatable or
 .     final link.  *}
-.  void (*_section_already_linked) (bfd *, struct bfd_section *);
+.  void (*_section_already_linked) (bfd *, struct bfd_section *,
+.                                  struct bfd_link_info *);
 .
 .  {* Routines to handle dynamic symbols and relocs.  *}
 .#define BFD_JUMP_TABLE_DYNAMIC(NAME) \
Index: include/bfdlink.h
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/include/bfdlink.h,v
retrieving revision 1.3
diff -u -p -r1.3 bfdlink.h
--- include/bfdlink.h    25 Aug 2015 02:24:50 -0000    1.3
+++ include/bfdlink.h    17 Apr 2016 05:14:23 -0000
@@ -324,6 +324,11 @@ struct bfd_link_info
   /* TRUE if unreferenced sections should be removed.  */
   unsigned int gc_sections: 1;

+  /* If TRUE reduce memory overheads, at the expense of speed. This will
+     cause map file generation to use an O(N^2) algorithm and disable
+     caching ELF symbol buffer.  */
+  unsigned int reduce_memory_overheads: 1;
+
   /* What to do with unresolved symbols in an object file.
      When producing executables the default is GENERATE_ERROR.
      When producing shared libraries the default is IGNORE.  The
Index: ld/ld.h
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ld.h,v
retrieving revision 1.2
diff -u -p -r1.2 ld.h
--- ld/ld.h    24 Apr 2011 20:19:25 -0000    1.2
+++ ld/ld.h    17 Apr 2016 05:14:23 -0000
@@ -200,11 +200,6 @@ typedef struct {
      behaviour of the linker.  The new default behaviour is to reject such
      input files.  */
   bfd_boolean accept_unknown_input_arch;
-
-  /* If TRUE reduce memory overheads, at the expense of speed.
-     This will cause map file generation to use an O(N^2) algorithm.  */
-  bfd_boolean reduce_memory_overheads;
-
 } args_type;

 extern args_type command_line;
Index: ld/ldlang.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ldlang.c,v
retrieving revision 1.3
diff -u -p -r1.3 ldlang.c
--- ld/ldlang.c    25 Aug 2015 02:24:50 -0000    1.3
+++ ld/ldlang.c    17 Apr 2016 05:14:24 -0000
@@ -1670,7 +1670,7 @@ lang_map (void)

   fprintf (config.map_file, _("\nLinker script and memory map\n\n"));

-  if (! command_line.reduce_memory_overheads)
+  if (! link_info.reduce_memory_overheads)
     {
       obstack_begin (&map_obstack, 1000);
       for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link_next)
@@ -1746,7 +1746,7 @@ init_os (lang_output_section_statement_t
     }
   s->bfd_section->output_section = s->bfd_section;
   s->bfd_section->output_offset = 0;
-  if (!command_line.reduce_memory_overheads)
+  if (!link_info.reduce_memory_overheads)
     {
       fat_section_userdata_type *new
     = stat_alloc (sizeof (fat_section_userdata_type));
@@ -1840,7 +1840,7 @@ section_already_linked (bfd *abfd, asect
     }

   if (!(abfd->flags & DYNAMIC))
-    bfd_section_already_linked (abfd, sec);
+    bfd_section_already_linked (abfd, sec, &link_info);
 }

 /* The wild routines.
@@ -3564,7 +3564,7 @@ print_input_section (asection *i)

       if (i->output_section != NULL && i->output_section->owner == output_bfd)
     {
-      if (command_line.reduce_memory_overheads)
+      if (link_info.reduce_memory_overheads)
         bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
       else
         print_all_symbols (i);
Index: ld/ldmain.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ldmain.c,v
retrieving revision 1.7
diff -u -p -r1.7 ldmain.c
--- ld/ldmain.c    13 Nov 2015 16:14:37 -0000    1.7
+++ ld/ldmain.c    17 Apr 2016 05:14:25 -0000
@@ -261,7 +261,6 @@ main (int argc, char **argv)
   command_line.warn_mismatch = TRUE;
   command_line.check_section_addresses = TRUE;
   command_line.accept_unknown_input_arch = FALSE;
-  command_line.reduce_memory_overheads = FALSE;

   sort_section = none;

@@ -325,6 +324,7 @@ main (int argc, char **argv)
   link_info.relax_pass = 1;
   link_info.warn_shared_textrel = FALSE;
   link_info.gc_sections = FALSE;
+  link_info.reduce_memory_overheads = FALSE;

   ldfile_add_arch ("");

Index: ld/lexsup.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/lexsup.c,v
retrieving revision 1.7
diff -u -p -r1.7 lexsup.c
--- ld/lexsup.c    25 Aug 2015 02:24:50 -0000    1.7
+++ ld/lexsup.c    17 Apr 2016 05:14:25 -0000
@@ -1350,7 +1350,7 @@ parse_args (unsigned argc, char **argv)
       break;

     case OPTION_REDUCE_MEMORY_OVERHEADS:
-      command_line.reduce_memory_overheads = TRUE;
+      link_info.reduce_memory_overheads = TRUE;
       if (config.hash_table_size == 0)
         config.hash_table_size = 1021;
       break;

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Martin Pieuchot
On 27/04/16(Wed) 21:12, Aaron Miller wrote:

> Hi All,
>
> I was experiencing ~8 minute linking times for a large C++ application
> I have been working on when running -current on amd64. It turns out
> that the decade-old version of ld in the OpenBSD source tree has a bug
> that causes quadratic complexity for some linking operations when
> debug symbols are present. I found a patch from 2006 in a mailing list
> archive that fixes this; I adapted it to work with OpenBSD by editing
> files that are normally regenerated (I couldn't figure out how to do
> automatically regenerate them).

Do you see a difference when linking a kernel built with DWARF sections,
i.e. by setting DEBUG to -g?

> Here is the original patch:
> https://sourceware.org/ml/binutils/2006-08/msg00334.html
>
> I have rebuilt the kernel and system with this ld and everything runs
> identically to the system linked with the unpatched ld.

Could you make sure ports bulk builds run without regression.  You can
ping aja@ and landry@ to have it tested on various architectures.

> Please test this and let me know if this could get into the tree, and
> if not, what changes I need to make. Thanks!

I'd be nice if your changes are not lost when re-generating files.  I
don't know how to check that myself but maybe somebody could comment
on that.

> Index: bfd/bfd-in2.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/bfd-in2.h,v
> retrieving revision 1.4
> diff -u -p -r1.4 bfd-in2.h
> --- bfd/bfd-in2.h    25 May 2015 14:56:26 -0000    1.4
> +++ bfd/bfd-in2.h    17 Apr 2016 05:14:15 -0000
> @@ -5068,7 +5068,7 @@ typedef struct bfd_target
>
>    /* Check if SEC has been already linked during a reloceatable or
>       final link.  */
> -  void (*_section_already_linked) (bfd *, struct bfd_section *);
> +  void (*_section_already_linked) (bfd *, struct bfd_section *,
> struct bfd_link_info *);
>
>    /* Routines to handle dynamic symbols and relocs.  */
>  #define BFD_JUMP_TABLE_DYNAMIC(NAME) \
> @@ -5128,10 +5128,10 @@ bfd_boolean bfd_link_split_section (bfd
>  #define bfd_link_split_section(abfd, sec) \
>         BFD_SEND (abfd, _bfd_link_split_section, (abfd, sec))
>
> -void bfd_section_already_linked (bfd *abfd, asection *sec);
> +void bfd_section_already_linked (bfd *abfd, asection *sec, struct
> bfd_link_info *);
>
> -#define bfd_section_already_linked(abfd, sec) \
> -       BFD_SEND (abfd, _section_already_linked, (abfd, sec))
> +#define bfd_section_already_linked(abfd, sec, info) \
> +       BFD_SEND (abfd, _section_already_linked, (abfd, sec, info))
>
>  /* Extracted from simple.c.  */
>  bfd_byte *bfd_simple_get_relocated_section_contents
> Index: bfd/elf-bfd.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elf-bfd.h,v
> retrieving revision 1.4
> diff -u -p -r1.4 elf-bfd.h
> --- bfd/elf-bfd.h    25 Aug 2015 02:24:49 -0000    1.4
> +++ bfd/elf-bfd.h    17 Apr 2016 05:14:16 -0000
> @@ -1371,6 +1371,9 @@ struct elf_obj_tdata
>
>    /* Used to determine if we are creating an executable.  */
>    bfd_boolean executable;
> +
> +  /* Symbol buffer.  */
> +  Elf_Internal_Sym *symbuf;
>  };
>
>  #define elf_tdata(bfd)        ((bfd) -> tdata.elf_obj_data)
> @@ -1503,11 +1506,11 @@ extern bfd_boolean _bfd_elf_match_sectio
>  extern bfd_boolean bfd_elf_is_group_section
>    (bfd *, const struct bfd_section *);
>  extern void _bfd_elf_section_already_linked
> -  (bfd *, struct bfd_section *);
> +  (bfd *, struct bfd_section *, struct bfd_link_info *);
>  extern void bfd_elf_set_group_contents
>    (bfd *, asection *, void *);
>  extern asection *_bfd_elf_check_kept_section
> -  (asection *);
> +  (asection *, struct bfd_link_info *);
>  extern void _bfd_elf_link_just_syms
>    (asection *, struct bfd_link_info *);
>  extern bfd_boolean _bfd_elf_copy_private_header_data
> @@ -1703,7 +1706,7 @@ extern bfd_boolean _bfd_elf_symbol_refs_
>    (struct elf_link_hash_entry *, struct bfd_link_info *, bfd_boolean);
>
>  extern bfd_boolean bfd_elf_match_symbols_in_sections
> -  (asection *sec1, asection *sec2);
> +  (asection *, asection *, struct bfd_link_info *);
>
>  extern bfd_boolean _bfd_elf_setup_sections
>    (bfd *);
> Index: bfd/elf.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elf.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 elf.c
> --- bfd/elf.c    13 Jan 2015 20:05:43 -0000    1.7
> +++ bfd/elf.c    17 Apr 2016 05:14:18 -0000
> @@ -3101,7 +3101,7 @@ assign_section_numbers (bfd *abfd, struc
>               s, s->owner);
>                /* Point to the kept section if it has the same
>               size as the discarded one.  */
> -              kept = _bfd_elf_check_kept_section (s);
> +              kept = _bfd_elf_check_kept_section (s, link_info);
>                if (kept == NULL)
>              {
>                bfd_set_error (bfd_error_bad_value);
> @@ -8674,7 +8674,8 @@ elf_sym_name_compare (const void *arg1,
>     symbols.  */
>
>  bfd_boolean
> -bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
> +bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
> +          struct bfd_link_info *info)
>  {
>    bfd *bfd1, *bfd2;
>    const struct elf_backend_data *bed1, *bed2;
> @@ -8732,21 +8733,37 @@ bfd_elf_match_symbols_in_sections (asect
>    if (symcount1 == 0 || symcount2 == 0)
>      return FALSE;
>
> -  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
> -                   NULL, NULL, NULL);
> -  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
> -                   NULL, NULL, NULL);
> -
>    result = FALSE;
> -  if (isymbuf1 == NULL || isymbuf2 == NULL)
> -    goto done;
> +  isymbuf1 = elf_tdata (bfd1)->symbuf;
> +  isymbuf2 = elf_tdata (bfd2)->symbuf;
>
> -  /* Sort symbols by binding and section. Global definitions are at
> -     the beginning.  */
> -  qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
> -     elf_sort_elf_symbol);
> -  qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
> -     elf_sort_elf_symbol);
> +  if (isymbuf1 == NULL)
> +    {
> +      isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
> +              NULL, NULL, NULL);
> +      if (isymbuf1 == NULL)
> +        goto done;
> +      /* Sort symbols by binding and section. Global definitions are at
> +         the beginning.  */
> +      qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
> +      elf_sort_elf_symbol);
> +      if (!info->reduce_memory_overheads)
> +        elf_tdata (bfd1)->symbuf = isymbuf1;
> +    }
> +
> +  if (isymbuf2 == NULL)
> +    {
> +      isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
> +              NULL, NULL, NULL);
> +      if (isymbuf2 == NULL)
> +        goto done;
> +      /* Sort symbols by binding and section. Global definitions are at
> +         the beginning.  */
> +      qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
> +      elf_sort_elf_symbol);
> +      if (!info->reduce_memory_overheads)
> +        elf_tdata (bfd2)->symbuf = isymbuf2;
> +    }
>
>    /* Count definitions in the section.  */
>    count1 = 0;
> @@ -8830,10 +8847,13 @@ done:
>      free (symtable1);
>    if (symtable2)
>      free (symtable2);
> -  if (isymbuf1)
> -    free (isymbuf1);
> -  if (isymbuf2)
> -    free (isymbuf2);
> +  if (info->reduce_memory_overheads)
> +    {
> +      if (isymbuf1)
> +        free (isymbuf1);
> +      if (isymbuf2)
> +        free (isymbuf2);
> +    }
>
>    return result;
>  }
> Index: bfd/elflink.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c,v
> retrieving revision 1.12
> diff -u -p -r1.12 elflink.c
> --- bfd/elflink.c    28 Nov 2015 11:32:33 -0000    1.12
> +++ bfd/elflink.c    17 Apr 2016 05:14:20 -0000
> @@ -6766,14 +6766,15 @@ _bfd_elf_default_action_discarded (asect
>  /* Find a match between a section and a member of a section group.  */
>
>  static asection *
> -match_group_member (asection *sec, asection *group)
> +match_group_member (asection *sec, asection *group,
> +       struct bfd_link_info *info)
>  {
>    asection *first = elf_next_in_group (group);
>    asection *s = first;
>
>    while (s != NULL)
>      {
> -      if (bfd_elf_match_symbols_in_sections (s, sec))
> +      if (bfd_elf_match_symbols_in_sections (s, sec, info))
>      return s;
>
>        s = elf_next_in_group (s);
> @@ -6789,7 +6790,7 @@ match_group_member (asection *sec, asect
>     NULL. */
>
>  asection *
> -_bfd_elf_check_kept_section (asection *sec)
> +_bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info)
>  {
>    asection *kept;
>
> @@ -6797,7 +6798,7 @@ _bfd_elf_check_kept_section (asection *s
>    if (kept != NULL)
>      {
>        if (elf_sec_group (sec) != NULL)
> -    kept = match_group_member (sec, kept);
> +    kept = match_group_member (sec, kept, info);
>        if (kept != NULL && sec->size != kept->size)
>      kept = NULL;
>      }
> @@ -7150,7 +7151,8 @@ elf_link_input_bfd (struct elf_final_lin
>              {
>                asection *kept;
>
> -              kept = _bfd_elf_check_kept_section (sec);
> +              kept = _bfd_elf_check_kept_section (sec,
> +                                                              finfo->info);
>                if (kept != NULL)
>                  {
>                    *ps = kept;
> @@ -8280,6 +8282,15 @@ bfd_elf_final_link (bfd *abfd, struct bf
>      }
>      }
>
> +  /* Free symbol buffer if needed.  */
> +  if (!info->reduce_memory_overheads)
> +    {
> +      for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> +       if (elf_tdata (sub)->symbuf)
> +         free (elf_tdata (sub)->symbuf);
> +    }
> +
> +
>    /* Output any global symbols that got converted to local in a
>       version script or due to symbol visibility.  We do this in a
>       separate step since ELF requires all local symbols to appear
> @@ -9724,7 +9735,8 @@ bfd_elf_discard_info (bfd *output_bfd, s
>  }
>
>  void
> -_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section * sec)
> +_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
> +                                struct bfd_link_info *info)
>  {
>    flagword flags;
>    const char *name, *p;
> @@ -9890,7 +9902,8 @@ _bfd_elf_section_already_linked (bfd *ab
>      if ((l->sec->flags & SEC_GROUP) == 0
>          && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL
>          && bfd_elf_match_symbols_in_sections (l->sec,
> -                          elf_next_in_group (sec)))
> +                          elf_next_in_group (sec),
> +                                                  info))
>        {
>          elf_next_in_group (sec)->output_section = bfd_abs_section_ptr;
>          elf_next_in_group (sec)->kept_section = l->sec;
> @@ -9911,7 +9924,7 @@ _bfd_elf_section_already_linked (bfd *ab
>
>        if (first != NULL
>            && elf_next_in_group (first) == first
> -          && bfd_elf_match_symbols_in_sections (first, sec))
> +          && bfd_elf_match_symbols_in_sections (first, sec, info))
>          {
>            sec->output_section = bfd_abs_section_ptr;
>            sec->kept_section = l->sec;
> Index: bfd/libbfd-in.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/libbfd-in.h,v
> retrieving revision 1.3
> diff -u -p -r1.3 libbfd-in.h
> --- bfd/libbfd-in.h    10 May 2015 20:41:19 -0000    1.3
> +++ bfd/libbfd-in.h    17 Apr 2016 05:14:20 -0000
> @@ -408,7 +408,7 @@ extern bfd_boolean _bfd_generic_set_sect
>  #define _bfd_nolink_bfd_link_split_section \
>    ((bfd_boolean (*) (bfd *, struct bfd_section *)) bfd_false)
>  #define _bfd_nolink_section_already_linked \
> -  ((void (*) (bfd *, struct bfd_section *)) bfd_void)
> +  ((void (*) (bfd *, struct bfd_section *, struct bfd_link_info *)) bfd_void)
>
>  /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
>     have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
> @@ -522,7 +522,7 @@ extern bfd_boolean _bfd_generic_link_spl
>    (bfd *, struct bfd_section *);
>
>  extern void _bfd_generic_section_already_linked
> -  (bfd *, struct bfd_section *);
> +  (bfd *, struct bfd_section *, struct bfd_link_info *);
>
>  /* Generic reloc_link_order processing routine.  */
>  extern bfd_boolean _bfd_generic_reloc_link_order
> Index: bfd/libbfd.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/libbfd.h,v
> retrieving revision 1.4
> diff -u -p -r1.4 libbfd.h
> --- bfd/libbfd.h    25 May 2015 14:56:26 -0000    1.4
> +++ bfd/libbfd.h    17 Apr 2016 05:14:21 -0000
> @@ -413,7 +413,7 @@ extern bfd_boolean _bfd_generic_set_sect
>  #define _bfd_nolink_bfd_link_split_section \
>    ((bfd_boolean (*) (bfd *, struct bfd_section *)) bfd_false)
>  #define _bfd_nolink_section_already_linked \
> -  ((void (*) (bfd *, struct bfd_section *)) bfd_void)
> +  ((void (*) (bfd *, struct bfd_section *, struct bfd_link_info *)) bfd_void)
>
>  /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
>     have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
> @@ -527,7 +527,7 @@ extern bfd_boolean _bfd_generic_link_spl
>    (bfd *, struct bfd_section *);
>
>  extern void _bfd_generic_section_already_linked
> -  (bfd *, struct bfd_section *);
> +  (bfd *, struct bfd_section *, struct bfd_link_info *);
>
>  /* Generic reloc_link_order processing routine.  */
>  extern bfd_boolean _bfd_generic_reloc_link_order
> Index: bfd/linker.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/linker.c,v
> retrieving revision 1.1.1.1
> diff -u -p -r1.1.1.1 linker.c
> --- bfd/linker.c    24 Apr 2011 20:14:42 -0000    1.1.1.1
> +++ bfd/linker.c    17 Apr 2016 05:14:22 -0000
> @@ -2877,14 +2877,15 @@ FUNCTION
>      bfd_section_already_linked
>
>  SYNOPSIS
> -        void bfd_section_already_linked (bfd *abfd, asection *sec);
> +        void bfd_section_already_linked (bfd *abfd, asection *sec,
> +                                         struct bfd_link_info *info);
>
>  DESCRIPTION
>      Check if @var{sec} has been already linked during a reloceatable
>      or final link.
>
> -.#define bfd_section_already_linked(abfd, sec) \
> -.       BFD_SEND (abfd, _section_already_linked, (abfd, sec))
> +.#define bfd_section_already_linked(abfd, sec, info) \
> +.       BFD_SEND (abfd, _section_already_linked, (abfd, sec, info))
>  .
>
>  */
> @@ -2970,7 +2971,8 @@ bfd_section_already_linked_table_free (v
>  /* This is used on non-ELF inputs.  */
>
>  void
> -_bfd_generic_section_already_linked (bfd *abfd, asection *sec)
> +_bfd_generic_section_already_linked (bfd *abfd, asection *sec,
> +                                    struct bfd_link_info *info
> ATTRIBUTE_UNUSED)
>  {
>    flagword flags;
>    const char *name;
> Index: bfd/targets.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/targets.c,v
> retrieving revision 1.3
> diff -u -p -r1.3 targets.c
> --- bfd/targets.c    6 Apr 2015 18:30:22 -0000    1.3
> +++ bfd/targets.c    17 Apr 2016 05:14:22 -0000
> @@ -481,7 +481,8 @@ BFD_JUMP_TABLE macros.
>  .
>  .  {* Check if SEC has been already linked during a reloceatable or
>  .     final link.  *}
> -.  void (*_section_already_linked) (bfd *, struct bfd_section *);
> +.  void (*_section_already_linked) (bfd *, struct bfd_section *,
> +.                                  struct bfd_link_info *);
>  .
>  .  {* Routines to handle dynamic symbols and relocs.  *}
>  .#define BFD_JUMP_TABLE_DYNAMIC(NAME) \
> Index: include/bfdlink.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/include/bfdlink.h,v
> retrieving revision 1.3
> diff -u -p -r1.3 bfdlink.h
> --- include/bfdlink.h    25 Aug 2015 02:24:50 -0000    1.3
> +++ include/bfdlink.h    17 Apr 2016 05:14:23 -0000
> @@ -324,6 +324,11 @@ struct bfd_link_info
>    /* TRUE if unreferenced sections should be removed.  */
>    unsigned int gc_sections: 1;
>
> +  /* If TRUE reduce memory overheads, at the expense of speed. This will
> +     cause map file generation to use an O(N^2) algorithm and disable
> +     caching ELF symbol buffer.  */
> +  unsigned int reduce_memory_overheads: 1;
> +
>    /* What to do with unresolved symbols in an object file.
>       When producing executables the default is GENERATE_ERROR.
>       When producing shared libraries the default is IGNORE.  The
> Index: ld/ld.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ld.h,v
> retrieving revision 1.2
> diff -u -p -r1.2 ld.h
> --- ld/ld.h    24 Apr 2011 20:19:25 -0000    1.2
> +++ ld/ld.h    17 Apr 2016 05:14:23 -0000
> @@ -200,11 +200,6 @@ typedef struct {
>       behaviour of the linker.  The new default behaviour is to reject such
>       input files.  */
>    bfd_boolean accept_unknown_input_arch;
> -
> -  /* If TRUE reduce memory overheads, at the expense of speed.
> -     This will cause map file generation to use an O(N^2) algorithm.  */
> -  bfd_boolean reduce_memory_overheads;
> -
>  } args_type;
>
>  extern args_type command_line;
> Index: ld/ldlang.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ldlang.c,v
> retrieving revision 1.3
> diff -u -p -r1.3 ldlang.c
> --- ld/ldlang.c    25 Aug 2015 02:24:50 -0000    1.3
> +++ ld/ldlang.c    17 Apr 2016 05:14:24 -0000
> @@ -1670,7 +1670,7 @@ lang_map (void)
>
>    fprintf (config.map_file, _("\nLinker script and memory map\n\n"));
>
> -  if (! command_line.reduce_memory_overheads)
> +  if (! link_info.reduce_memory_overheads)
>      {
>        obstack_begin (&map_obstack, 1000);
>        for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link_next)
> @@ -1746,7 +1746,7 @@ init_os (lang_output_section_statement_t
>      }
>    s->bfd_section->output_section = s->bfd_section;
>    s->bfd_section->output_offset = 0;
> -  if (!command_line.reduce_memory_overheads)
> +  if (!link_info.reduce_memory_overheads)
>      {
>        fat_section_userdata_type *new
>      = stat_alloc (sizeof (fat_section_userdata_type));
> @@ -1840,7 +1840,7 @@ section_already_linked (bfd *abfd, asect
>      }
>
>    if (!(abfd->flags & DYNAMIC))
> -    bfd_section_already_linked (abfd, sec);
> +    bfd_section_already_linked (abfd, sec, &link_info);
>  }
>
>  /* The wild routines.
> @@ -3564,7 +3564,7 @@ print_input_section (asection *i)
>
>        if (i->output_section != NULL && i->output_section->owner == output_bfd)
>      {
> -      if (command_line.reduce_memory_overheads)
> +      if (link_info.reduce_memory_overheads)
>          bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
>        else
>          print_all_symbols (i);
> Index: ld/ldmain.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ldmain.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 ldmain.c
> --- ld/ldmain.c    13 Nov 2015 16:14:37 -0000    1.7
> +++ ld/ldmain.c    17 Apr 2016 05:14:25 -0000
> @@ -261,7 +261,6 @@ main (int argc, char **argv)
>    command_line.warn_mismatch = TRUE;
>    command_line.check_section_addresses = TRUE;
>    command_line.accept_unknown_input_arch = FALSE;
> -  command_line.reduce_memory_overheads = FALSE;
>
>    sort_section = none;
>
> @@ -325,6 +324,7 @@ main (int argc, char **argv)
>    link_info.relax_pass = 1;
>    link_info.warn_shared_textrel = FALSE;
>    link_info.gc_sections = FALSE;
> +  link_info.reduce_memory_overheads = FALSE;
>
>    ldfile_add_arch ("");
>
> Index: ld/lexsup.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/lexsup.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 lexsup.c
> --- ld/lexsup.c    25 Aug 2015 02:24:50 -0000    1.7
> +++ ld/lexsup.c    17 Apr 2016 05:14:25 -0000
> @@ -1350,7 +1350,7 @@ parse_args (unsigned argc, char **argv)
>        break;
>
>      case OPTION_REDUCE_MEMORY_OVERHEADS:
> -      command_line.reduce_memory_overheads = TRUE;
> +      link_info.reduce_memory_overheads = TRUE;
>        if (config.hash_table_size == 0)
>          config.hash_table_size = 1021;
>        break;
>

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Stefan Kempf-2
In reply to this post by Aaron Miller-2
Aaron Miller wrote:

> Hi All,
>
> I was experiencing ~8 minute linking times for a large C++ application
> I have been working on when running -current on amd64. It turns out
> that the decade-old version of ld in the OpenBSD source tree has a bug
> that causes quadratic complexity for some linking operations when
> debug symbols are present. I found a patch from 2006 in a mailing list
> archive that fixes this; I adapted it to work with OpenBSD by editing
> files that are normally regenerated (I couldn't figure out how to do
> automatically regenerate them).
>
> Here is the original patch:
> https://sourceware.org/ml/binutils/2006-08/msg00334.html
>
> I have rebuilt the kernel and system with this ld and everything runs
> identically to the system linked with the unpatched ld.
>
> Please test this and let me know if this could get into the tree, and
> if not, what changes I need to make. Thanks!

Interesting!

About the autogenerated files: it looks you already made the additions
to bfd/linker.c, bfd/targets.c and bfd/libbfd.h that should end up
in bfd-in2.h and libbfd-in.h.

To regenerate them, make sure you first did a "make obj" in the root of
your source tree. Then run "make depend" from gnu/usr.bin. Afterwards,
to to gnu/usr.bin/binutils-2.17/obj/bfd and run "make headers".
headers". That did the trick for me.

PS: It appears that gmail mangled your diff. It does not apply cleanly
here. You probably need to use a different mail client or try sending the
diff as a plain text attachment. Can you please resend an un-mangled
diff?
 

> Index: bfd/bfd-in2.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/bfd-in2.h,v
> retrieving revision 1.4
> diff -u -p -r1.4 bfd-in2.h
> --- bfd/bfd-in2.h    25 May 2015 14:56:26 -0000    1.4
> +++ bfd/bfd-in2.h    17 Apr 2016 05:14:15 -0000
> @@ -5068,7 +5068,7 @@ typedef struct bfd_target
>
>    /* Check if SEC has been already linked during a reloceatable or
>       final link.  */
> -  void (*_section_already_linked) (bfd *, struct bfd_section *);
> +  void (*_section_already_linked) (bfd *, struct bfd_section *,
> struct bfd_link_info *);
>
>    /* Routines to handle dynamic symbols and relocs.  */
>  #define BFD_JUMP_TABLE_DYNAMIC(NAME) \
> @@ -5128,10 +5128,10 @@ bfd_boolean bfd_link_split_section (bfd
>  #define bfd_link_split_section(abfd, sec) \
>         BFD_SEND (abfd, _bfd_link_split_section, (abfd, sec))
>
> -void bfd_section_already_linked (bfd *abfd, asection *sec);
> +void bfd_section_already_linked (bfd *abfd, asection *sec, struct
> bfd_link_info *);
>
> -#define bfd_section_already_linked(abfd, sec) \
> -       BFD_SEND (abfd, _section_already_linked, (abfd, sec))
> +#define bfd_section_already_linked(abfd, sec, info) \
> +       BFD_SEND (abfd, _section_already_linked, (abfd, sec, info))
>
>  /* Extracted from simple.c.  */
>  bfd_byte *bfd_simple_get_relocated_section_contents
> Index: bfd/elf-bfd.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elf-bfd.h,v
> retrieving revision 1.4
> diff -u -p -r1.4 elf-bfd.h
> --- bfd/elf-bfd.h    25 Aug 2015 02:24:49 -0000    1.4
> +++ bfd/elf-bfd.h    17 Apr 2016 05:14:16 -0000
> @@ -1371,6 +1371,9 @@ struct elf_obj_tdata
>
>    /* Used to determine if we are creating an executable.  */
>    bfd_boolean executable;
> +
> +  /* Symbol buffer.  */
> +  Elf_Internal_Sym *symbuf;
>  };
>
>  #define elf_tdata(bfd)        ((bfd) -> tdata.elf_obj_data)
> @@ -1503,11 +1506,11 @@ extern bfd_boolean _bfd_elf_match_sectio
>  extern bfd_boolean bfd_elf_is_group_section
>    (bfd *, const struct bfd_section *);
>  extern void _bfd_elf_section_already_linked
> -  (bfd *, struct bfd_section *);
> +  (bfd *, struct bfd_section *, struct bfd_link_info *);
>  extern void bfd_elf_set_group_contents
>    (bfd *, asection *, void *);
>  extern asection *_bfd_elf_check_kept_section
> -  (asection *);
> +  (asection *, struct bfd_link_info *);
>  extern void _bfd_elf_link_just_syms
>    (asection *, struct bfd_link_info *);
>  extern bfd_boolean _bfd_elf_copy_private_header_data
> @@ -1703,7 +1706,7 @@ extern bfd_boolean _bfd_elf_symbol_refs_
>    (struct elf_link_hash_entry *, struct bfd_link_info *, bfd_boolean);
>
>  extern bfd_boolean bfd_elf_match_symbols_in_sections
> -  (asection *sec1, asection *sec2);
> +  (asection *, asection *, struct bfd_link_info *);
>
>  extern bfd_boolean _bfd_elf_setup_sections
>    (bfd *);
> Index: bfd/elf.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elf.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 elf.c
> --- bfd/elf.c    13 Jan 2015 20:05:43 -0000    1.7
> +++ bfd/elf.c    17 Apr 2016 05:14:18 -0000
> @@ -3101,7 +3101,7 @@ assign_section_numbers (bfd *abfd, struc
>               s, s->owner);
>                /* Point to the kept section if it has the same
>               size as the discarded one.  */
> -              kept = _bfd_elf_check_kept_section (s);
> +              kept = _bfd_elf_check_kept_section (s, link_info);
>                if (kept == NULL)
>              {
>                bfd_set_error (bfd_error_bad_value);
> @@ -8674,7 +8674,8 @@ elf_sym_name_compare (const void *arg1,
>     symbols.  */
>
>  bfd_boolean
> -bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
> +bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
> +          struct bfd_link_info *info)
>  {
>    bfd *bfd1, *bfd2;
>    const struct elf_backend_data *bed1, *bed2;
> @@ -8732,21 +8733,37 @@ bfd_elf_match_symbols_in_sections (asect
>    if (symcount1 == 0 || symcount2 == 0)
>      return FALSE;
>
> -  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
> -                   NULL, NULL, NULL);
> -  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
> -                   NULL, NULL, NULL);
> -
>    result = FALSE;
> -  if (isymbuf1 == NULL || isymbuf2 == NULL)
> -    goto done;
> +  isymbuf1 = elf_tdata (bfd1)->symbuf;
> +  isymbuf2 = elf_tdata (bfd2)->symbuf;
>
> -  /* Sort symbols by binding and section. Global definitions are at
> -     the beginning.  */
> -  qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
> -     elf_sort_elf_symbol);
> -  qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
> -     elf_sort_elf_symbol);
> +  if (isymbuf1 == NULL)
> +    {
> +      isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
> +              NULL, NULL, NULL);
> +      if (isymbuf1 == NULL)
> +        goto done;
> +      /* Sort symbols by binding and section. Global definitions are at
> +         the beginning.  */
> +      qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
> +      elf_sort_elf_symbol);
> +      if (!info->reduce_memory_overheads)
> +        elf_tdata (bfd1)->symbuf = isymbuf1;
> +    }
> +
> +  if (isymbuf2 == NULL)
> +    {
> +      isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
> +              NULL, NULL, NULL);
> +      if (isymbuf2 == NULL)
> +        goto done;
> +      /* Sort symbols by binding and section. Global definitions are at
> +         the beginning.  */
> +      qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
> +      elf_sort_elf_symbol);
> +      if (!info->reduce_memory_overheads)
> +        elf_tdata (bfd2)->symbuf = isymbuf2;
> +    }
>
>    /* Count definitions in the section.  */
>    count1 = 0;
> @@ -8830,10 +8847,13 @@ done:
>      free (symtable1);
>    if (symtable2)
>      free (symtable2);
> -  if (isymbuf1)
> -    free (isymbuf1);
> -  if (isymbuf2)
> -    free (isymbuf2);
> +  if (info->reduce_memory_overheads)
> +    {
> +      if (isymbuf1)
> +        free (isymbuf1);
> +      if (isymbuf2)
> +        free (isymbuf2);
> +    }
>
>    return result;
>  }
> Index: bfd/elflink.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c,v
> retrieving revision 1.12
> diff -u -p -r1.12 elflink.c
> --- bfd/elflink.c    28 Nov 2015 11:32:33 -0000    1.12
> +++ bfd/elflink.c    17 Apr 2016 05:14:20 -0000
> @@ -6766,14 +6766,15 @@ _bfd_elf_default_action_discarded (asect
>  /* Find a match between a section and a member of a section group.  */
>
>  static asection *
> -match_group_member (asection *sec, asection *group)
> +match_group_member (asection *sec, asection *group,
> +       struct bfd_link_info *info)
>  {
>    asection *first = elf_next_in_group (group);
>    asection *s = first;
>
>    while (s != NULL)
>      {
> -      if (bfd_elf_match_symbols_in_sections (s, sec))
> +      if (bfd_elf_match_symbols_in_sections (s, sec, info))
>      return s;
>
>        s = elf_next_in_group (s);
> @@ -6789,7 +6790,7 @@ match_group_member (asection *sec, asect
>     NULL. */
>
>  asection *
> -_bfd_elf_check_kept_section (asection *sec)
> +_bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info)
>  {
>    asection *kept;
>
> @@ -6797,7 +6798,7 @@ _bfd_elf_check_kept_section (asection *s
>    if (kept != NULL)
>      {
>        if (elf_sec_group (sec) != NULL)
> -    kept = match_group_member (sec, kept);
> +    kept = match_group_member (sec, kept, info);
>        if (kept != NULL && sec->size != kept->size)
>      kept = NULL;
>      }
> @@ -7150,7 +7151,8 @@ elf_link_input_bfd (struct elf_final_lin
>              {
>                asection *kept;
>
> -              kept = _bfd_elf_check_kept_section (sec);
> +              kept = _bfd_elf_check_kept_section (sec,
> +                                                              finfo->info);
>                if (kept != NULL)
>                  {
>                    *ps = kept;
> @@ -8280,6 +8282,15 @@ bfd_elf_final_link (bfd *abfd, struct bf
>      }
>      }
>
> +  /* Free symbol buffer if needed.  */
> +  if (!info->reduce_memory_overheads)
> +    {
> +      for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> +       if (elf_tdata (sub)->symbuf)
> +         free (elf_tdata (sub)->symbuf);
> +    }
> +
> +
>    /* Output any global symbols that got converted to local in a
>       version script or due to symbol visibility.  We do this in a
>       separate step since ELF requires all local symbols to appear
> @@ -9724,7 +9735,8 @@ bfd_elf_discard_info (bfd *output_bfd, s
>  }
>
>  void
> -_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section * sec)
> +_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
> +                                struct bfd_link_info *info)
>  {
>    flagword flags;
>    const char *name, *p;
> @@ -9890,7 +9902,8 @@ _bfd_elf_section_already_linked (bfd *ab
>      if ((l->sec->flags & SEC_GROUP) == 0
>          && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL
>          && bfd_elf_match_symbols_in_sections (l->sec,
> -                          elf_next_in_group (sec)))
> +                          elf_next_in_group (sec),
> +                                                  info))
>        {
>          elf_next_in_group (sec)->output_section = bfd_abs_section_ptr;
>          elf_next_in_group (sec)->kept_section = l->sec;
> @@ -9911,7 +9924,7 @@ _bfd_elf_section_already_linked (bfd *ab
>
>        if (first != NULL
>            && elf_next_in_group (first) == first
> -          && bfd_elf_match_symbols_in_sections (first, sec))
> +          && bfd_elf_match_symbols_in_sections (first, sec, info))
>          {
>            sec->output_section = bfd_abs_section_ptr;
>            sec->kept_section = l->sec;
> Index: bfd/libbfd-in.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/libbfd-in.h,v
> retrieving revision 1.3
> diff -u -p -r1.3 libbfd-in.h
> --- bfd/libbfd-in.h    10 May 2015 20:41:19 -0000    1.3
> +++ bfd/libbfd-in.h    17 Apr 2016 05:14:20 -0000
> @@ -408,7 +408,7 @@ extern bfd_boolean _bfd_generic_set_sect
>  #define _bfd_nolink_bfd_link_split_section \
>    ((bfd_boolean (*) (bfd *, struct bfd_section *)) bfd_false)
>  #define _bfd_nolink_section_already_linked \
> -  ((void (*) (bfd *, struct bfd_section *)) bfd_void)
> +  ((void (*) (bfd *, struct bfd_section *, struct bfd_link_info *)) bfd_void)
>
>  /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
>     have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
> @@ -522,7 +522,7 @@ extern bfd_boolean _bfd_generic_link_spl
>    (bfd *, struct bfd_section *);
>
>  extern void _bfd_generic_section_already_linked
> -  (bfd *, struct bfd_section *);
> +  (bfd *, struct bfd_section *, struct bfd_link_info *);
>
>  /* Generic reloc_link_order processing routine.  */
>  extern bfd_boolean _bfd_generic_reloc_link_order
> Index: bfd/libbfd.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/libbfd.h,v
> retrieving revision 1.4
> diff -u -p -r1.4 libbfd.h
> --- bfd/libbfd.h    25 May 2015 14:56:26 -0000    1.4
> +++ bfd/libbfd.h    17 Apr 2016 05:14:21 -0000
> @@ -413,7 +413,7 @@ extern bfd_boolean _bfd_generic_set_sect
>  #define _bfd_nolink_bfd_link_split_section \
>    ((bfd_boolean (*) (bfd *, struct bfd_section *)) bfd_false)
>  #define _bfd_nolink_section_already_linked \
> -  ((void (*) (bfd *, struct bfd_section *)) bfd_void)
> +  ((void (*) (bfd *, struct bfd_section *, struct bfd_link_info *)) bfd_void)
>
>  /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
>     have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
> @@ -527,7 +527,7 @@ extern bfd_boolean _bfd_generic_link_spl
>    (bfd *, struct bfd_section *);
>
>  extern void _bfd_generic_section_already_linked
> -  (bfd *, struct bfd_section *);
> +  (bfd *, struct bfd_section *, struct bfd_link_info *);
>
>  /* Generic reloc_link_order processing routine.  */
>  extern bfd_boolean _bfd_generic_reloc_link_order
> Index: bfd/linker.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/linker.c,v
> retrieving revision 1.1.1.1
> diff -u -p -r1.1.1.1 linker.c
> --- bfd/linker.c    24 Apr 2011 20:14:42 -0000    1.1.1.1
> +++ bfd/linker.c    17 Apr 2016 05:14:22 -0000
> @@ -2877,14 +2877,15 @@ FUNCTION
>      bfd_section_already_linked
>
>  SYNOPSIS
> -        void bfd_section_already_linked (bfd *abfd, asection *sec);
> +        void bfd_section_already_linked (bfd *abfd, asection *sec,
> +                                         struct bfd_link_info *info);
>
>  DESCRIPTION
>      Check if @var{sec} has been already linked during a reloceatable
>      or final link.
>
> -.#define bfd_section_already_linked(abfd, sec) \
> -.       BFD_SEND (abfd, _section_already_linked, (abfd, sec))
> +.#define bfd_section_already_linked(abfd, sec, info) \
> +.       BFD_SEND (abfd, _section_already_linked, (abfd, sec, info))
>  .
>
>  */
> @@ -2970,7 +2971,8 @@ bfd_section_already_linked_table_free (v
>  /* This is used on non-ELF inputs.  */
>
>  void
> -_bfd_generic_section_already_linked (bfd *abfd, asection *sec)
> +_bfd_generic_section_already_linked (bfd *abfd, asection *sec,
> +                                    struct bfd_link_info *info
> ATTRIBUTE_UNUSED)
>  {
>    flagword flags;
>    const char *name;
> Index: bfd/targets.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/targets.c,v
> retrieving revision 1.3
> diff -u -p -r1.3 targets.c
> --- bfd/targets.c    6 Apr 2015 18:30:22 -0000    1.3
> +++ bfd/targets.c    17 Apr 2016 05:14:22 -0000
> @@ -481,7 +481,8 @@ BFD_JUMP_TABLE macros.
>  .
>  .  {* Check if SEC has been already linked during a reloceatable or
>  .     final link.  *}
> -.  void (*_section_already_linked) (bfd *, struct bfd_section *);
> +.  void (*_section_already_linked) (bfd *, struct bfd_section *,
> +.                                  struct bfd_link_info *);
>  .
>  .  {* Routines to handle dynamic symbols and relocs.  *}
>  .#define BFD_JUMP_TABLE_DYNAMIC(NAME) \
> Index: include/bfdlink.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/include/bfdlink.h,v
> retrieving revision 1.3
> diff -u -p -r1.3 bfdlink.h
> --- include/bfdlink.h    25 Aug 2015 02:24:50 -0000    1.3
> +++ include/bfdlink.h    17 Apr 2016 05:14:23 -0000
> @@ -324,6 +324,11 @@ struct bfd_link_info
>    /* TRUE if unreferenced sections should be removed.  */
>    unsigned int gc_sections: 1;
>
> +  /* If TRUE reduce memory overheads, at the expense of speed. This will
> +     cause map file generation to use an O(N^2) algorithm and disable
> +     caching ELF symbol buffer.  */
> +  unsigned int reduce_memory_overheads: 1;
> +
>    /* What to do with unresolved symbols in an object file.
>       When producing executables the default is GENERATE_ERROR.
>       When producing shared libraries the default is IGNORE.  The
> Index: ld/ld.h
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ld.h,v
> retrieving revision 1.2
> diff -u -p -r1.2 ld.h
> --- ld/ld.h    24 Apr 2011 20:19:25 -0000    1.2
> +++ ld/ld.h    17 Apr 2016 05:14:23 -0000
> @@ -200,11 +200,6 @@ typedef struct {
>       behaviour of the linker.  The new default behaviour is to reject such
>       input files.  */
>    bfd_boolean accept_unknown_input_arch;
> -
> -  /* If TRUE reduce memory overheads, at the expense of speed.
> -     This will cause map file generation to use an O(N^2) algorithm.  */
> -  bfd_boolean reduce_memory_overheads;
> -
>  } args_type;
>
>  extern args_type command_line;
> Index: ld/ldlang.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ldlang.c,v
> retrieving revision 1.3
> diff -u -p -r1.3 ldlang.c
> --- ld/ldlang.c    25 Aug 2015 02:24:50 -0000    1.3
> +++ ld/ldlang.c    17 Apr 2016 05:14:24 -0000
> @@ -1670,7 +1670,7 @@ lang_map (void)
>
>    fprintf (config.map_file, _("\nLinker script and memory map\n\n"));
>
> -  if (! command_line.reduce_memory_overheads)
> +  if (! link_info.reduce_memory_overheads)
>      {
>        obstack_begin (&map_obstack, 1000);
>        for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link_next)
> @@ -1746,7 +1746,7 @@ init_os (lang_output_section_statement_t
>      }
>    s->bfd_section->output_section = s->bfd_section;
>    s->bfd_section->output_offset = 0;
> -  if (!command_line.reduce_memory_overheads)
> +  if (!link_info.reduce_memory_overheads)
>      {
>        fat_section_userdata_type *new
>      = stat_alloc (sizeof (fat_section_userdata_type));
> @@ -1840,7 +1840,7 @@ section_already_linked (bfd *abfd, asect
>      }
>
>    if (!(abfd->flags & DYNAMIC))
> -    bfd_section_already_linked (abfd, sec);
> +    bfd_section_already_linked (abfd, sec, &link_info);
>  }
>
>  /* The wild routines.
> @@ -3564,7 +3564,7 @@ print_input_section (asection *i)
>
>        if (i->output_section != NULL && i->output_section->owner == output_bfd)
>      {
> -      if (command_line.reduce_memory_overheads)
> +      if (link_info.reduce_memory_overheads)
>          bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
>        else
>          print_all_symbols (i);
> Index: ld/ldmain.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ldmain.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 ldmain.c
> --- ld/ldmain.c    13 Nov 2015 16:14:37 -0000    1.7
> +++ ld/ldmain.c    17 Apr 2016 05:14:25 -0000
> @@ -261,7 +261,6 @@ main (int argc, char **argv)
>    command_line.warn_mismatch = TRUE;
>    command_line.check_section_addresses = TRUE;
>    command_line.accept_unknown_input_arch = FALSE;
> -  command_line.reduce_memory_overheads = FALSE;
>
>    sort_section = none;
>
> @@ -325,6 +324,7 @@ main (int argc, char **argv)
>    link_info.relax_pass = 1;
>    link_info.warn_shared_textrel = FALSE;
>    link_info.gc_sections = FALSE;
> +  link_info.reduce_memory_overheads = FALSE;
>
>    ldfile_add_arch ("");
>
> Index: ld/lexsup.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/lexsup.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 lexsup.c
> --- ld/lexsup.c    25 Aug 2015 02:24:50 -0000    1.7
> +++ ld/lexsup.c    17 Apr 2016 05:14:25 -0000
> @@ -1350,7 +1350,7 @@ parse_args (unsigned argc, char **argv)
>        break;
>
>      case OPTION_REDUCE_MEMORY_OVERHEADS:
> -      command_line.reduce_memory_overheads = TRUE;
> +      link_info.reduce_memory_overheads = TRUE;
>        if (config.hash_table_size == 0)
>          config.hash_table_size = 1021;
>        break;
>

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Jeremie Courreges-Anglas-2
On Sat, May 07 2016, Stefan Kempf <[hidden email]> wrote:

> Aaron Miller wrote:
>> Hi All,
>>
>> I was experiencing ~8 minute linking times for a large C++ application
>> I have been working on when running -current on amd64. It turns out
>> that the decade-old version of ld in the OpenBSD source tree has a bug
>> that causes quadratic complexity for some linking operations when
>> debug symbols are present. I found a patch from 2006 in a mailing list
>> archive that fixes this; I adapted it to work with OpenBSD by editing
>> files that are normally regenerated (I couldn't figure out how to do
>> automatically regenerate them).
>>
>> Here is the original patch:
>> https://sourceware.org/ml/binutils/2006-08/msg00334.html
>>
>> I have rebuilt the kernel and system with this ld and everything runs
>> identically to the system linked with the unpatched ld.
>>
>> Please test this and let me know if this could get into the tree, and
>> if not, what changes I need to make. Thanks!
>
> Interesting!
>
> About the autogenerated files: it looks you already made the additions
> to bfd/linker.c, bfd/targets.c and bfd/libbfd.h that should end up
> in bfd-in2.h and libbfd-in.h.
>
> To regenerate them, make sure you first did a "make obj" in the root of
> your source tree. Then run "make depend" from gnu/usr.bin. Afterwards,
> to to gnu/usr.bin/binutils-2.17/obj/bfd and run "make headers".
> headers". That did the trick for me.

A bit late to the party,

I think this diff breaks ld.bfd(1) operations in some special cases.

  http://build-failures.rhaalovely.net//sparc64/2019-02-03/textproc/mupdf.log

shows a double free on sparc64.  A similar crash can be reproduced on
amd64 using MALLOC_OPTIONS:

--8<--
russell /usr/ports/pobj/mupdf-1.14.0/mupdf-1.14.0-source$ MALLOC_OPTIONS=S egdb --args ld.bfd -r -b binary -o build/release/resources/fonts/urw/NimbusMonoPS-Bold.cff.o resources/fonts/urw/NimbusMonoPS-Bold.cff
GNU gdb (GDB) 7.12.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-openbsd6.4".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ld.bfd...done.
(gdb) r
Starting program: /usr/bin/ld.bfd -r -b binary -o build/release/resources/fonts/urw/NimbusMonoPS-Bold.cff.o resources/fonts/urw/NimbusMonoPS-Bold.cff
ld.bfd(20556) in free(): bogus pointer (double free?) 0xdbdbdbdbdbdbdbdb

Program received signal SIGABRT, Aborted.
thrkill () at -:3
3       -: No such file or directory.
(gdb) bt
#0  thrkill () at -:3
#1  0x000007d40bd157fe in _libc_abort () at /usr/src/lib/libc/stdlib/abort.c:51
#2  0x000007d40bca4909 in wrterror (d=0x7d3ab4b9be0, msg=0x7d40bc82ed6 "bogus pointer (double free?) %p") at /usr/src/lib/libc/stdlib/malloc.c:297
#3  0x000007d40bca7970 in findpool (p=0xdbdbdbdbdbdbdbdb, argpool=<optimized out>, foundpool=0x7f7ffffcb180, saved_function=0x7f7ffffcb178) at /usr/src/lib/libc/stdlib/malloc.c:1323
#4  0x000007d40bca4af0 in ofree (argpool=0x7f7ffffcb1d8, p=0xdbdbdbdbdbdbdbdb, clear=0, check=0, argsz=0) at /usr/src/lib/libc/stdlib/malloc.c:1337
#5  0x000007d40bca4a10 in free (ptr=0xdbdbdbdbdbdbdbdb) at /usr/src/lib/libc/stdlib/malloc.c:1451
#6  0x000007d16b97be4a in bfd_elf_final_link (abfd=0x7d4006f0a00, info=0x7d16b9dc1f8 <link_info>) at /usr/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c:8623
#7  0x000007d16b8fe7f2 in ldwrite () at /usr/src/gnu/usr.bin/binutils-2.17/ld/ldwrite.c:557
#8  0x000007d16b8fbde7 in main (argc=7, argv=0x7f7ffffcb778) at /usr/src/gnu/usr.bin/binutils-2.17/ld/ldmain.c:496
(gdb) frame 6
#6  0x000007d16b97be4a in bfd_elf_final_link (abfd=0x7d4006f0a00, info=0x7d16b9dc1f8 <link_info>) at /usr/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c:8623
8623              free (elf_tdata (sub)->symbuf);
(gdb) l
8618      /* Free symbol buffer if needed.  */
8619      if (!info->reduce_memory_overheads)
8620        {
8621          for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
8622            if (elf_tdata (sub)->symbuf)
8623              free (elf_tdata (sub)->symbuf);
8624        }
8625    
8626      /* Output any global symbols that got converted to local in a
8627         version script or due to symbol visibility.  We do this in a
(gdb)
-->8--

This is not specific to mupdf, any source file will do.  I have no idea
yet where elf_tdata(sub) gets allocated in this case.

Using --reduce-memory-overheads appears to work around the problem.

--
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Aaron Miller-2


On February 23, 2019 2:50:46 AM PST, Jeremie Courreges-Anglas <[hidden email]> wrote:

>On Sat, May 07 2016, Stefan Kempf <[hidden email]> wrote:
>> Aaron Miller wrote:
>>> Hi All,
>>>
>>> I was experiencing ~8 minute linking times for a large C++
>application
>>> I have been working on when running -current on amd64. It turns out
>>> that the decade-old version of ld in the OpenBSD source tree has a
>bug
>>> that causes quadratic complexity for some linking operations when
>>> debug symbols are present. I found a patch from 2006 in a mailing
>list
>>> archive that fixes this; I adapted it to work with OpenBSD by
>editing
>>> files that are normally regenerated (I couldn't figure out how to do
>>> automatically regenerate them).
>>>
>>> Here is the original patch:
>>> https://sourceware.org/ml/binutils/2006-08/msg00334.html
>>>
>>> I have rebuilt the kernel and system with this ld and everything
>runs
>>> identically to the system linked with the unpatched ld.
>>>
>>> Please test this and let me know if this could get into the tree,
>and
>>> if not, what changes I need to make. Thanks!
>>
>> Interesting!
>>
>> About the autogenerated files: it looks you already made the
>additions
>> to bfd/linker.c, bfd/targets.c and bfd/libbfd.h that should end up
>> in bfd-in2.h and libbfd-in.h.
>>
>> To regenerate them, make sure you first did a "make obj" in the root
>of
>> your source tree. Then run "make depend" from gnu/usr.bin.
>Afterwards,
>> to to gnu/usr.bin/binutils-2.17/obj/bfd and run "make headers".
>> headers". That did the trick for me.
>
>A bit late to the party,
>
>I think this diff breaks ld.bfd(1) operations in some special cases.
>
>http://build-failures.rhaalovely.net//sparc64/2019-02-03/textproc/mupdf.log
>
>shows a double free on sparc64.  A similar crash can be reproduced on
>amd64 using MALLOC_OPTIONS:
>
>--8<--
>russell /usr/ports/pobj/mupdf-1.14.0/mupdf-1.14.0-source$
>MALLOC_OPTIONS=S egdb --args ld.bfd -r -b binary -o
>build/release/resources/fonts/urw/NimbusMonoPS-Bold.cff.o
>resources/fonts/urw/NimbusMonoPS-Bold.cff
>GNU gdb (GDB) 7.12.1
>Copyright (C) 2017 Free Software Foundation, Inc.
>License GPLv3+: GNU GPL version 3 or later
><http://gnu.org/licenses/gpl.html>
>This is free software: you are free to change and redistribute it.
>There is NO WARRANTY, to the extent permitted by law.  Type "show
>copying"
>and "show warranty" for details.
>This GDB was configured as "x86_64-unknown-openbsd6.4".
>Type "show configuration" for configuration details.
>For bug reporting instructions, please see:
><http://www.gnu.org/software/gdb/bugs/>.
>Find the GDB manual and other documentation resources online at:
><http://www.gnu.org/software/gdb/documentation/>.
>For help, type "help".
>Type "apropos word" to search for commands related to "word"...
>Reading symbols from ld.bfd...done.
>(gdb) r
>Starting program: /usr/bin/ld.bfd -r -b binary -o
>build/release/resources/fonts/urw/NimbusMonoPS-Bold.cff.o
>resources/fonts/urw/NimbusMonoPS-Bold.cff
>ld.bfd(20556) in free(): bogus pointer (double free?)
>0xdbdbdbdbdbdbdbdb
>
>Program received signal SIGABRT, Aborted.
>thrkill () at -:3
>3       -: No such file or directory.
>(gdb) bt
>#0  thrkill () at -:3
>#1  0x000007d40bd157fe in _libc_abort () at
>/usr/src/lib/libc/stdlib/abort.c:51
>#2  0x000007d40bca4909 in wrterror (d=0x7d3ab4b9be0, msg=0x7d40bc82ed6
>"bogus pointer (double free?) %p") at
>/usr/src/lib/libc/stdlib/malloc.c:297
>#3  0x000007d40bca7970 in findpool (p=0xdbdbdbdbdbdbdbdb,
>argpool=<optimized out>, foundpool=0x7f7ffffcb180,
>saved_function=0x7f7ffffcb178) at
>/usr/src/lib/libc/stdlib/malloc.c:1323
>#4  0x000007d40bca4af0 in ofree (argpool=0x7f7ffffcb1d8,
>p=0xdbdbdbdbdbdbdbdb, clear=0, check=0, argsz=0) at
>/usr/src/lib/libc/stdlib/malloc.c:1337
>#5  0x000007d40bca4a10 in free (ptr=0xdbdbdbdbdbdbdbdb) at
>/usr/src/lib/libc/stdlib/malloc.c:1451
>#6  0x000007d16b97be4a in bfd_elf_final_link (abfd=0x7d4006f0a00,
>info=0x7d16b9dc1f8 <link_info>) at
>/usr/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c:8623
>#7  0x000007d16b8fe7f2 in ldwrite () at
>/usr/src/gnu/usr.bin/binutils-2.17/ld/ldwrite.c:557
>#8  0x000007d16b8fbde7 in main (argc=7, argv=0x7f7ffffcb778) at
>/usr/src/gnu/usr.bin/binutils-2.17/ld/ldmain.c:496
>(gdb) frame 6
>#6  0x000007d16b97be4a in bfd_elf_final_link (abfd=0x7d4006f0a00,
>info=0x7d16b9dc1f8 <link_info>) at
>/usr/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c:8623
>8623              free (elf_tdata (sub)->symbuf);
>(gdb) l
>8618      /* Free symbol buffer if needed.  */
>8619      if (!info->reduce_memory_overheads)
>8620        {
>8621          for (sub = info->input_bfds; sub != NULL; sub =
>sub->link_next)
>8622            if (elf_tdata (sub)->symbuf)
>8623              free (elf_tdata (sub)->symbuf);
>8624        }
>8625    
>8626      /* Output any global symbols that got converted to local in a
>8627         version script or due to symbol visibility.  We do this in
>a
>(gdb)
>-->8--
>
>This is not specific to mupdf, any source file will do.  I have no idea
>yet where elf_tdata(sub) gets allocated in this case.
>
>Using --reduce-memory-overheads appears to work around the problem.

Hi Jeremie,

That is concerning. I'm on my phone and haven't had a chance to investigate, but from the code in the gdb output above, it looks like the author of the diff forgot to set the pointer to NULL after freeing. For example:
            if (elf_tdata (sub)->symbuf) {
              free (elf_tdata (sub)->symbuf);
              elf_tdata (sub)->symbuf = NULL;
            }

This is not tested at all. I hope this works!

--Aaron

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Jeremie Courreges-Anglas-2
On Sat, Feb 23 2019, Aaron Miller <[hidden email]> wrote:
> On February 23, 2019 2:50:46 AM PST, Jeremie Courreges-Anglas <[hidden email]> wrote:
>>On Sat, May 07 2016, Stefan Kempf <[hidden email]> wrote:

[...]

> Hi Jeremie,
>
> That is concerning. I'm on my phone and haven't had a chance to investigate, but from the code in the gdb output above, it looks like the author of the diff forgot to set the pointer to NULL after freeing. For example:
>             if (elf_tdata (sub)->symbuf) {
>               free (elf_tdata (sub)->symbuf);
>               elf_tdata (sub)->symbuf = NULL;
>             }
>
> This is not tested at all. I hope this works!

It doesn't, which is consistent with the error seen with
MALLOC_OPTIONS=S: "free (ptr=0xdbdbdbdbdbdbdbdb)" points out that the
code uses uninitialized memory (0xdb).  The 0xdf pattern in the sparc64
build failure is likely newly allocated, uninitialized memory which
had previously been junked by free(3).

--
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Jeremie Courreges-Anglas-2

+cc sthen since ports/textproc/mupdf is affected by the bug:

  http://build-failures.rhaalovely.net//sparc64/2019-02-03/textproc/mupdf.log

On Sun, Feb 24 2019, Jeremie Courreges-Anglas <[hidden email]> wrote:

> On Sat, Feb 23 2019, Aaron Miller <[hidden email]> wrote:
>> On February 23, 2019 2:50:46 AM PST, Jeremie Courreges-Anglas <[hidden email]> wrote:
>>>On Sat, May 07 2016, Stefan Kempf <[hidden email]> wrote:
>
> [...]
>
>> Hi Jeremie,
>>
>> That is concerning. I'm on my phone and haven't had a chance to investigate, but from the code in the gdb output above, it looks like the author of the diff forgot to set the pointer to NULL after freeing. For example:
>>             if (elf_tdata (sub)->symbuf) {
>>               free (elf_tdata (sub)->symbuf);
>>               elf_tdata (sub)->symbuf = NULL;
>>             }
>>
>> This is not tested at all. I hope this works!
>
> It doesn't, which is consistent with the error seen with
> MALLOC_OPTIONS=S: "free (ptr=0xdbdbdbdbdbdbdbdb)" points out that the
> code uses uninitialized memory (0xdb).  The 0xdf pattern in the sparc64
> build failure is likely newly allocated, uninitialized memory which
> had previously been junked by free(3).

The following diff fixes the issue here.  The "bfd_get_flavour (sub) ==
bfd_target_elf_flavour" check is the important part, it mirrors the
checks done in bfd_elf_match_symbols_in_sections().

The "symbuf = NULL" part is not needed to avoid the crash, but if it can
avoid someone another dive in this codebase, I think it's worth it. ;)

Quick way to test the diff:

  MALLOC_OPTIONS=S ld.bfd -r -b binary ~/.profile -o /tmp/garbage

ok?


Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c,v
retrieving revision 1.22
diff -u -p -r1.22 elflink.c
--- bfd/elflink.c 3 Dec 2018 02:59:51 -0000 1.22
+++ bfd/elflink.c 25 Feb 2019 09:49:05 -0000
@@ -8619,8 +8619,13 @@ bfd_elf_final_link (bfd *abfd, struct bf
   if (!info->reduce_memory_overheads)
     {
       for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
- if (elf_tdata (sub)->symbuf)
-  free (elf_tdata (sub)->symbuf);
+        {
+          if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
+            {
+              free (elf_tdata (sub)->symbuf);
+              elf_tdata (sub)->symbuf = NULL;
+            }
+        }
     }
 
   /* Output any global symbols that got converted to local in a


--
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Jeremie Courreges-Anglas-2
On Mon, Feb 25 2019, Jeremie Courreges-Anglas <[hidden email]> wrote:

> +cc sthen since ports/textproc/mupdf is affected by the bug:
>
>   http://build-failures.rhaalovely.net//sparc64/2019-02-03/textproc/mupdf.log
>
> On Sun, Feb 24 2019, Jeremie Courreges-Anglas <[hidden email]> wrote:
>> On Sat, Feb 23 2019, Aaron Miller <[hidden email]> wrote:
>>> On February 23, 2019 2:50:46 AM PST, Jeremie Courreges-Anglas <[hidden email]> wrote:
>>>>On Sat, May 07 2016, Stefan Kempf <[hidden email]> wrote:
>>
>> [...]
>>
>>> Hi Jeremie,
>>>
>>> That is concerning. I'm on my phone and haven't had a chance to investigate, but from the code in the gdb output above, it looks like the author of the diff forgot to set the pointer to NULL after freeing. For example:
>>>             if (elf_tdata (sub)->symbuf) {
>>>               free (elf_tdata (sub)->symbuf);
>>>               elf_tdata (sub)->symbuf = NULL;
>>>             }
>>>
>>> This is not tested at all. I hope this works!
>>
>> It doesn't, which is consistent with the error seen with
>> MALLOC_OPTIONS=S: "free (ptr=0xdbdbdbdbdbdbdbdb)" points out that the
>> code uses uninitialized memory (0xdb).  The 0xdf pattern in the sparc64
>> build failure is likely newly allocated, uninitialized memory which
>> had previously been junked by free(3).
>
> The following diff fixes the issue here.  The "bfd_get_flavour (sub) ==
> bfd_target_elf_flavour" check is the important part, it mirrors the
> checks done in bfd_elf_match_symbols_in_sections().
>
> The "symbuf = NULL" part is not needed to avoid the crash, but if it can
> avoid someone another dive in this codebase, I think it's worth it. ;)
>
> Quick way to test the diff:
>
>   MALLOC_OPTIONS=S ld.bfd -r -b binary ~/.profile -o /tmp/garbage
>
> ok?

Still looking for feedback.  textproc/mupdf being broken knocks out cups
and its consumers in bulk builds, this is not nice.  I'll commit this on
sunday unless I hear objections.

> Index: bfd/elflink.c
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c,v
> retrieving revision 1.22
> diff -u -p -r1.22 elflink.c
> --- bfd/elflink.c 3 Dec 2018 02:59:51 -0000 1.22
> +++ bfd/elflink.c 25 Feb 2019 09:49:05 -0000
> @@ -8619,8 +8619,13 @@ bfd_elf_final_link (bfd *abfd, struct bf
>    if (!info->reduce_memory_overheads)
>      {
>        for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> - if (elf_tdata (sub)->symbuf)
> -  free (elf_tdata (sub)->symbuf);
> +        {
> +          if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
> +            {
> +              free (elf_tdata (sub)->symbuf);
> +              elf_tdata (sub)->symbuf = NULL;
> +            }
> +        }
>      }
>  
>    /* Output any global symbols that got converted to local in a

--
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Theo Buehler-3
On Fri, Mar 01, 2019 at 04:10:17PM +0100, Jeremie Courreges-Anglas wrote:

> On Mon, Feb 25 2019, Jeremie Courreges-Anglas <[hidden email]> wrote:
> > +cc sthen since ports/textproc/mupdf is affected by the bug:
> >
> >   http://build-failures.rhaalovely.net//sparc64/2019-02-03/textproc/mupdf.log
> >
> > On Sun, Feb 24 2019, Jeremie Courreges-Anglas <[hidden email]> wrote:
> >> On Sat, Feb 23 2019, Aaron Miller <[hidden email]> wrote:
> >>> On February 23, 2019 2:50:46 AM PST, Jeremie Courreges-Anglas <[hidden email]> wrote:
> >>>>On Sat, May 07 2016, Stefan Kempf <[hidden email]> wrote:
> >>
> >> [...]
> >>
> >>> Hi Jeremie,
> >>>
> >>> That is concerning. I'm on my phone and haven't had a chance to investigate, but from the code in the gdb output above, it looks like the author of the diff forgot to set the pointer to NULL after freeing. For example:
> >>>             if (elf_tdata (sub)->symbuf) {
> >>>               free (elf_tdata (sub)->symbuf);
> >>>               elf_tdata (sub)->symbuf = NULL;
> >>>             }
> >>>
> >>> This is not tested at all. I hope this works!
> >>
> >> It doesn't, which is consistent with the error seen with
> >> MALLOC_OPTIONS=S: "free (ptr=0xdbdbdbdbdbdbdbdb)" points out that the
> >> code uses uninitialized memory (0xdb).  The 0xdf pattern in the sparc64
> >> build failure is likely newly allocated, uninitialized memory which
> >> had previously been junked by free(3).
> >
> > The following diff fixes the issue here.  The "bfd_get_flavour (sub) ==
> > bfd_target_elf_flavour" check is the important part, it mirrors the
> > checks done in bfd_elf_match_symbols_in_sections().
> >
> > The "symbuf = NULL" part is not needed to avoid the crash, but if it can
> > avoid someone another dive in this codebase, I think it's worth it. ;)
> >
> > Quick way to test the diff:
> >
> >   MALLOC_OPTIONS=S ld.bfd -r -b binary ~/.profile -o /tmp/garbage
> >
> > ok?
>
> Still looking for feedback.  textproc/mupdf being broken knocks out cups
> and its consumers in bulk builds, this is not nice.  I'll commit this on
> sunday unless I hear objections.

ok tb

>
> > Index: bfd/elflink.c
> > ===================================================================
> > RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c,v
> > retrieving revision 1.22
> > diff -u -p -r1.22 elflink.c
> > --- bfd/elflink.c 3 Dec 2018 02:59:51 -0000 1.22
> > +++ bfd/elflink.c 25 Feb 2019 09:49:05 -0000
> > @@ -8619,8 +8619,13 @@ bfd_elf_final_link (bfd *abfd, struct bf
> >    if (!info->reduce_memory_overheads)
> >      {
> >        for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> > - if (elf_tdata (sub)->symbuf)
> > -  free (elf_tdata (sub)->symbuf);
> > +        {
> > +          if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
> > +            {
> > +              free (elf_tdata (sub)->symbuf);
> > +              elf_tdata (sub)->symbuf = NULL;
> > +            }
> > +        }
> >      }
> >  
> >    /* Output any global symbols that got converted to local in a
>
> --
> jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE
>

Reply | Threaded
Open this post in threaded view
|

Re: ld patch that greatly speeds up linking large programs with debug symbols

Stuart Henderson-6
In reply to this post by Jeremie Courreges-Anglas-2
On 2019/03/01 16:10, Jeremie Courreges-Anglas wrote:

> On Mon, Feb 25 2019, Jeremie Courreges-Anglas <[hidden email]> wrote:
> > +cc sthen since ports/textproc/mupdf is affected by the bug:
> >
> >   http://build-failures.rhaalovely.net//sparc64/2019-02-03/textproc/mupdf.log
> >
> > On Sun, Feb 24 2019, Jeremie Courreges-Anglas <[hidden email]> wrote:
> >> On Sat, Feb 23 2019, Aaron Miller <[hidden email]> wrote:
> >>> On February 23, 2019 2:50:46 AM PST, Jeremie Courreges-Anglas <[hidden email]> wrote:
> >>>>On Sat, May 07 2016, Stefan Kempf <[hidden email]> wrote:
> >>
> >> [...]
> >>
> >>> Hi Jeremie,
> >>>
> >>> That is concerning. I'm on my phone and haven't had a chance to investigate, but from the code in the gdb output above, it looks like the author of the diff forgot to set the pointer to NULL after freeing. For example:
> >>>             if (elf_tdata (sub)->symbuf) {
> >>>               free (elf_tdata (sub)->symbuf);
> >>>               elf_tdata (sub)->symbuf = NULL;
> >>>             }
> >>>
> >>> This is not tested at all. I hope this works!
> >>
> >> It doesn't, which is consistent with the error seen with
> >> MALLOC_OPTIONS=S: "free (ptr=0xdbdbdbdbdbdbdbdb)" points out that the
> >> code uses uninitialized memory (0xdb).  The 0xdf pattern in the sparc64
> >> build failure is likely newly allocated, uninitialized memory which
> >> had previously been junked by free(3).
> >
> > The following diff fixes the issue here.  The "bfd_get_flavour (sub) ==
> > bfd_target_elf_flavour" check is the important part, it mirrors the
> > checks done in bfd_elf_match_symbols_in_sections().
> >
> > The "symbuf = NULL" part is not needed to avoid the crash, but if it can
> > avoid someone another dive in this codebase, I think it's worth it. ;)
> >
> > Quick way to test the diff:
> >
> >   MALLOC_OPTIONS=S ld.bfd -r -b binary ~/.profile -o /tmp/garbage
> >
> > ok?
>
> Still looking for feedback.  textproc/mupdf being broken knocks out cups
> and its consumers in bulk builds, this is not nice.  I'll commit this on
> sunday unless I hear objections.

I'm not exactly the best person to comment on binutils diffs(!) but
as far as I'm concerned it's OK with me.

> > Index: bfd/elflink.c
> > ===================================================================
> > RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elflink.c,v
> > retrieving revision 1.22
> > diff -u -p -r1.22 elflink.c
> > --- bfd/elflink.c 3 Dec 2018 02:59:51 -0000 1.22
> > +++ bfd/elflink.c 25 Feb 2019 09:49:05 -0000
> > @@ -8619,8 +8619,13 @@ bfd_elf_final_link (bfd *abfd, struct bf
> >    if (!info->reduce_memory_overheads)
> >      {
> >        for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> > - if (elf_tdata (sub)->symbuf)
> > -  free (elf_tdata (sub)->symbuf);
> > +        {
> > +          if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
> > +            {
> > +              free (elf_tdata (sub)->symbuf);
> > +              elf_tdata (sub)->symbuf = NULL;
> > +            }
> > +        }
> >      }
> >  
> >    /* Output any global symbols that got converted to local in a
>
> --
> jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE