[binutils-gdb] Add a "-O 0" option to the linker for a faster, but less effective link.

Nick Clifton nickc@sourceware.org
Mon Apr 13 08:32:37 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=4fe7dd9a10d15928834d8a20ec91f2b1436c8108

commit 4fe7dd9a10d15928834d8a20ec91f2b1436c8108
Author: Nick Clifton <nickc@redhat.com>
Date:   Mon Apr 13 09:32:09 2026 +0100

    Add a "-O 0" option to the linker for a faster, but less effective link.

Diff:
---
 include/bfdlink.h              |  7 ++++++-
 ld/NEWS                        |  5 +++++
 ld/ld.texi                     | 10 ++++++++++
 ld/ldlang.c                    | 19 +++++++++++--------
 ld/lexsup.c                    | 20 +++++++++++++++++++-
 ld/testsuite/ld-elf/merge5.a.s |  4 ++++
 ld/testsuite/ld-elf/merge5.b.s |  4 ++++
 ld/testsuite/ld-elf/merge5.d   | 10 ++++++++++
 8 files changed, 69 insertions(+), 10 deletions(-)

diff --git a/include/bfdlink.h b/include/bfdlink.h
index eac72b8c388..a412f9c241a 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -551,10 +551,15 @@ struct bfd_link_info
      an executable stack.  */
   unsigned int default_execstack : 1;
   
-  /* TRUE if we want to produced optimized output files.  This might
+  /* TRUE if we want to produce optimized output files.  This might
      need much more time and therefore must be explicitly selected.  */
   unsigned int optimize: 1;
 
+  /* TRUE if we want to skip optional steps during linking.  This can
+     include section merging for example.  This is effectively the opposite
+     of the 'optimize' field, and both should not be TRUE at the same time.  */
+  unsigned int skip_optional: 1;
+
   /* TRUE if user should be informed of removed unreferenced sections.  */
   unsigned int print_gc_sections: 1;
 
diff --git a/ld/NEWS b/ld/NEWS
index c4f89fd389f..aa7a9251821 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,5 +1,10 @@
 -*- text -*-
 
+* Add a linker optimization level of zero.  Using "-O 0" on the linker command
+  line will stop it from combining the contents of mergeable sections.  This
+  can significantly speed up the link process at the cost of producing larger
+  binaries.
+
 Changes in 2.47:
 
 * Add linker support for --start-lib/--end-lib options and LIB linker script
diff --git a/ld/ld.texi b/ld/ld.texi
index 90d19c27d57..273c80b356b 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1043,6 +1043,16 @@ the linker may make more use of this option.  Also currently there is
 no difference in the linker's behaviour for different non-zero values
 of this option.  Again this may change with future releases.
 
+If @var{level} is zero or the string @samp{fast} then @command{ld}
+instead skips operations that are optional, resulting in a faster link
+but producing slower, larger binaries.  Currently the only operation
+that is skipped is the combining of mergeable sections, but this may
+change in the future.
+
+If @var{level} is the string @samp{default} or just the @samp{-}
+character then the linker's default behaviour of neither optimizing
+nor skipping operations is selected.
+
 @kindex -plugin @var{name}
 @item -plugin @var{name}
 Involve a plugin in the linking process.  The @var{name} parameter is
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 656edeb4981..98adc90e58a 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -8820,16 +8820,19 @@ lang_process (void)
     {
       asection *found;
 
-      ld_start_phase (PHASE_MERGE);
+      if (! link_info.skip_optional)
+	{
+	  ld_start_phase (PHASE_MERGE);
 
-      /* Merge SEC_MERGE sections.  This has to be done after GC of
-	 sections, so that GCed sections are not merged, but before
-	 assigning dynamic symbols, since removing whole input sections
-	 is hard then.  */
-      if (!bfd_merge_sections (link_info.output_bfd, &link_info))
-	fatal (_("%P: bfd_merge_sections failed: %E\n"));
+	  /* Merge SEC_MERGE sections.  This has to be done after GC of
+	     sections, so that GCed sections are not merged, but before
+	     assigning dynamic symbols, since removing whole input sections
+	     is hard then.  */
+	  if (!bfd_merge_sections (link_info.output_bfd, &link_info))
+	    fatal (_("%P: bfd_merge_sections failed: %E\n"));
 
-      ld_stop_phase (PHASE_MERGE);
+	  ld_stop_phase (PHASE_MERGE);
+	}
 
       /* Look for a text section and set the readonly attribute in it.  */
       found = bfd_get_section_by_name (link_info.output_bfd, ".text");
diff --git a/ld/lexsup.c b/ld/lexsup.c
index 4e89feffb09..a55b0e81b2f 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -177,7 +177,7 @@ static const struct ld_option ld_options[] =
   { {"output", required_argument, NULL, 'o'},
     'o', N_("FILE"), N_("Set output file name"), EXACTLY_TWO_DASHES },
   { {NULL, required_argument, NULL, '\0'},
-    'O', NULL, N_("Optimize output file"), ONE_DASH },
+    'O', N_("LEVEL"), N_("Optimize output file"), ONE_DASH },
   { {"out-implib", required_argument, NULL, OPTION_OUT_IMPLIB},
     '\0', N_("FILE"), N_("Generate import library"), TWO_DASHES },
   { {"plugin", required_argument, NULL, OPTION_PLUGIN},
@@ -1211,6 +1211,24 @@ parse_args (unsigned argc, char **argv)
 
 	  /* Enable optimizations of output files.  */
 	  link_info.optimize = strtoul (optarg, NULL, 0) != 0;
+	  if (link_info.optimize == 0)
+	    {
+	      /* "-O -" or "-O default" means restore the linker's default behaviour.  */
+	      if ((optarg[0] == '-' && optarg[1] == 0) || strcmp (optarg, "default") == 0)
+		{
+		  link_info.optimize = link_info.skip_optional = 0;
+		}
+	      /* "-O 0" or "-O fast" means disable optimizations and skip optional features.  */
+	      else if ((optarg[0] == '0' && optarg[1] == 0) || strcmp (optarg, "fast") == 0)
+		{
+		  link_info.optimize = 0;
+		  link_info.skip_optional = 1;
+		}
+	      else
+		fatal (_("%P: -O requires a numerical argument or '-' or 'fast' or 'default'"));
+	    }
+	  else
+	    link_info.skip_optional = 0;
 	  break;
 	case 'o':
 	  lang_add_output (optarg, 0);
diff --git a/ld/testsuite/ld-elf/merge5.a.s b/ld/testsuite/ld-elf/merge5.a.s
new file mode 100644
index 00000000000..8fe6a6967f2
--- /dev/null
+++ b/ld/testsuite/ld-elf/merge5.a.s
@@ -0,0 +1,4 @@
+.section .rodata.str,"aMS","progbits",1 
+.global .merge1
+.merge1: 
+	.asciz "abc" 
diff --git a/ld/testsuite/ld-elf/merge5.b.s b/ld/testsuite/ld-elf/merge5.b.s
new file mode 100644
index 00000000000..79ac2fc4e6c
--- /dev/null
+++ b/ld/testsuite/ld-elf/merge5.b.s
@@ -0,0 +1,4 @@
+.section .rodata.str,"aMS","progbits",1 
+.global .merge2
+.merge2: 
+	.asciz "abc" 
diff --git a/ld/testsuite/ld-elf/merge5.d b/ld/testsuite/ld-elf/merge5.d
new file mode 100644
index 00000000000..38c6754670c
--- /dev/null
+++ b/ld/testsuite/ld-elf/merge5.d
@@ -0,0 +1,10 @@
+# Checks to see that linking with "-O 0" does not merge strings.
+# The various other -O options are there just to make sure that the parser handles them.
+#source: merge5.a.s
+#source: merge5.b.s
+#ld: -T merge.ld -O 2 -O - -O 99 -O default -O fast -O 0
+#nm: -s
+
+0+1100 R .merge1
+0+1104 R .merge2
+#pass


More information about the Binutils-cvs mailing list