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 via email to