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;
>