diff mbox

Support -fuse-ld=bfd and -fuse-ld=gold

Message ID 20121129041841.GA14005@gmail.com
State New
Headers show

Commit Message

H.J. Lu Nov. 29, 2012, 4:18 a.m. UTC
From: "H.J. Lu" <hjl.tools@gmail.com>
To: gcc-patches@gcc.gnu.org
Cc: Joseph Myers  <joseph@codesourcery.com>, Paolo Bonzini <bonzini@gnu.org>
Bcc: 
Subject: [PATCH] Support -fuse-ld=bfd and -fuse-ld=gold
Reply-To: 

Hi,

Binutils supports 2 linkers, ld.gold and ld.bfd.  One of them is
configured as the default linker, ld, which is used by GCC.  Sometimes,
we want to use the alternate linker with GCC at run-time.  This patch
adds -fuse-ld=bfd and -fuse-ld=gold options to GCC driver.  It changes
collect2.c to pick either ld.bfd or ld.gold.  It also adds
ORIGINAL_LD_BFD_FOR_TARGET and ORIGINAL_LD_GOLD_FOR_TARGET to
exec-tool.in to add -fuse-ld=bfd and -fuse-ld=gold support to
collect-ld.  Since ld.bfd nor ld.gold know the new options, you
will get

# ./xgcc -B./  /tmp/x.c -fuse-ld=gold -v
...
./collect-ld --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -fuse-ld=gold /lib/../lib64/crt1.o /lib/../lib64/crti.o ./crtbegin.o -L. -L/lib/../lib64 -L/usr/lib/../lib64 /tmp/cclVWcGz.o -v -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed ./crtend.o /lib/../lib64/crtn.o
GNU gold (Linux/GNU Binutils 2.23.51.0.7.20121127) 1.11
/usr/local/bin/ld.gold: fatal error: -f/--auxiliary may not be used without -shared
collect2: error: ld returned 1 exit status

This is because we pass everything to ld and ld.bfd/ld.gold doesn't
understand -fuse-ld=bfd/-fuse-ld=gold.  It isn't a problem for collect2
since it will filter-out -fuse-ld=bfd/-fuse-ld=gold.  I will submit a
binutils patch to ignore -fuse-ld=bfd/-fuse-ld=gold, similar to -flto
options.

OK to install?

Thanks.


H.J.
---
2012-11-28   Nick Clifton  <nickc@redhat.com>
	     Matthias Klose <doko@debian.org>
	     Doug Kwan  <dougkwan@google.com>
	     H.J. Lu  <hongjiu.lu@intel.com>

	PR driver/55470
	* collect2.c (main): Support -fuse-ld=bfd and -fuse-ld=gold.

	* common.opt: Add fuse-ld=bfd and fuse-ld=gold.

	* configure.ac (ORIGINAL_LD_BFD_FOR_TARGET): New AC_PATH_PROG.
	(ORIGINAL_LD_GOLD_FOR_TARGET): Likewise.
	* configure: Regenerated.

	* exec-tool.in (ORIGINAL_LD_BFD_FOR_TARGET): New variable.
	(ORIGINAL_LD_GOLD_FOR_TARGET): Likewise.
	Use $ORIGINAL_LD_BFD_FOR_TARGET for collect-ld when seeing
	-fuse-ld=bfd.  Use =$ORIGINAL_LD_GOLD_FOR_TARGET for collect-ld
	when seeing -fuse-ld=gold.  Issue an error if linker isn't
	found.

	* gcc.c (LINK_COMMAND_SPEC): Pass -fuse-ld=* to collect2.

	* opts.c (comman_handle_option): Ignore -fuse-ld=bfd and
	-fuse-ld=gold.

	* doc/invoke.texi: Document -fuse-ld=bfd and -fuse-ld=gold.

Comments

Richard Biener Nov. 29, 2012, 8:43 a.m. UTC | #1
On Thu, Nov 29, 2012 at 5:18 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> From: "H.J. Lu" <hjl.tools@gmail.com>
> To: gcc-patches@gcc.gnu.org
> Cc: Joseph Myers  <joseph@codesourcery.com>, Paolo Bonzini <bonzini@gnu.org>
> Bcc:
> Subject: [PATCH] Support -fuse-ld=bfd and -fuse-ld=gold
> Reply-To:
>
> Hi,
>
> Binutils supports 2 linkers, ld.gold and ld.bfd.  One of them is
> configured as the default linker, ld, which is used by GCC.  Sometimes,
> we want to use the alternate linker with GCC at run-time.  This patch
> adds -fuse-ld=bfd and -fuse-ld=gold options to GCC driver.  It changes
> collect2.c to pick either ld.bfd or ld.gold.  It also adds
> ORIGINAL_LD_BFD_FOR_TARGET and ORIGINAL_LD_GOLD_FOR_TARGET to
> exec-tool.in to add -fuse-ld=bfd and -fuse-ld=gold support to
> collect-ld.  Since ld.bfd nor ld.gold know the new options, you
> will get
>
> # ./xgcc -B./  /tmp/x.c -fuse-ld=gold -v
> ...
> ./collect-ld --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -fuse-ld=gold /lib/../lib64/crt1.o /lib/../lib64/crti.o ./crtbegin.o -L. -L/lib/../lib64 -L/usr/lib/../lib64 /tmp/cclVWcGz.o -v -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed ./crtend.o /lib/../lib64/crtn.o
> GNU gold (Linux/GNU Binutils 2.23.51.0.7.20121127) 1.11
> /usr/local/bin/ld.gold: fatal error: -f/--auxiliary may not be used without -shared
> collect2: error: ld returned 1 exit status
>
> This is because we pass everything to ld and ld.bfd/ld.gold doesn't
> understand -fuse-ld=bfd/-fuse-ld=gold.  It isn't a problem for collect2
> since it will filter-out -fuse-ld=bfd/-fuse-ld=gold.  I will submit a
> binutils patch to ignore -fuse-ld=bfd/-fuse-ld=gold, similar to -flto
> options.
>
> OK to install?

Do we need to consider GNU ld and gold coming from different binutils versions
and thus do we need two sets of linker feature tests at configure
time?  Eventually
also if GNU ld and gold are not in feature-parity for the same binutils version?

That is, isn't this going to create hard to debug issues for users?

Thanks,
Richard.

> Thanks.
>
>
> H.J.
> ---
> 2012-11-28   Nick Clifton  <nickc@redhat.com>
>              Matthias Klose <doko@debian.org>
>              Doug Kwan  <dougkwan@google.com>
>              H.J. Lu  <hongjiu.lu@intel.com>
>
>         PR driver/55470
>         * collect2.c (main): Support -fuse-ld=bfd and -fuse-ld=gold.
>
>         * common.opt: Add fuse-ld=bfd and fuse-ld=gold.
>
>         * configure.ac (ORIGINAL_LD_BFD_FOR_TARGET): New AC_PATH_PROG.
>         (ORIGINAL_LD_GOLD_FOR_TARGET): Likewise.
>         * configure: Regenerated.
>
>         * exec-tool.in (ORIGINAL_LD_BFD_FOR_TARGET): New variable.
>         (ORIGINAL_LD_GOLD_FOR_TARGET): Likewise.
>         Use $ORIGINAL_LD_BFD_FOR_TARGET for collect-ld when seeing
>         -fuse-ld=bfd.  Use =$ORIGINAL_LD_GOLD_FOR_TARGET for collect-ld
>         when seeing -fuse-ld=gold.  Issue an error if linker isn't
>         found.
>
>         * gcc.c (LINK_COMMAND_SPEC): Pass -fuse-ld=* to collect2.
>
>         * opts.c (comman_handle_option): Ignore -fuse-ld=bfd and
>         -fuse-ld=gold.
>
>         * doc/invoke.texi: Document -fuse-ld=bfd and -fuse-ld=gold.
> diff --git a/gcc/collect2.c b/gcc/collect2.c
> index 49c4030..4e8cdf0 100644
> --- a/gcc/collect2.c
> +++ b/gcc/collect2.c
> @@ -842,8 +842,21 @@ maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
>  int
>  main (int argc, char **argv)
>  {
> -  static const char *const ld_suffix   = "ld";
> -  static const char *const plugin_ld_suffix = PLUGIN_LD_SUFFIX;
> +  enum linker_select
> +    {
> +      USE_DEFAULT_LD,
> +      USE_PLUGIN_LD,
> +      USE_GOLD_LD,
> +      USE_BFD_LD,
> +      USE_LD_MAX
> +    } selected_linker = USE_DEFAULT_LD;
> +  static const char *const ld_suffixes[USE_LD_MAX] =
> +    {
> +      "ld",
> +      PLUGIN_LD_SUFFIX,
> +      "ld.gold",
> +      "ld.bfd"
> +    };
>    static const char *const real_ld_suffix = "real-ld";
>    static const char *const collect_ld_suffix = "collect-ld";
>    static const char *const nm_suffix   = "nm";
> @@ -854,16 +867,13 @@ main (int argc, char **argv)
>    static const char *const strip_suffix = "strip";
>    static const char *const gstrip_suffix = "gstrip";
>
> +  const char *full_ld_suffixes[USE_LD_MAX];
>  #ifdef CROSS_DIRECTORY_STRUCTURE
>    /* If we look for a program in the compiler directories, we just use
>       the short name, since these directories are already system-specific.
>       But it we look for a program in the system directories, we need to
>       qualify the program name with the target machine.  */
>
> -  const char *const full_ld_suffix =
> -    concat(target_machine, "-", ld_suffix, NULL);
> -  const char *const full_plugin_ld_suffix =
> -    concat(target_machine, "-", plugin_ld_suffix, NULL);
>    const char *const full_nm_suffix =
>      concat (target_machine, "-", nm_suffix, NULL);
>    const char *const full_gnm_suffix =
> @@ -877,13 +887,11 @@ main (int argc, char **argv)
>    const char *const full_gstrip_suffix =
>      concat (target_machine, "-", gstrip_suffix, NULL);
>  #else
> -  const char *const full_ld_suffix     = ld_suffix;
> -  const char *const full_plugin_ld_suffix = plugin_ld_suffix;
> -  const char *const full_nm_suffix     = nm_suffix;
> -  const char *const full_gnm_suffix    = gnm_suffix;
>  #ifdef LDD_SUFFIX
>    const char *const full_ldd_suffix    = ldd_suffix;
>  #endif
> +  const char *const full_nm_suffix     = nm_suffix;
> +  const char *const full_gnm_suffix    = gnm_suffix;
>    const char *const full_strip_suffix  = strip_suffix;
>    const char *const full_gstrip_suffix = gstrip_suffix;
>  #endif /* CROSS_DIRECTORY_STRUCTURE */
> @@ -900,6 +908,7 @@ main (int argc, char **argv)
>    char **ld1_argv;
>    const char **ld1;
>    bool use_plugin = false;
> +  bool use_collect_ld = false;
>
>    /* The kinds of symbols we will have to consider when scanning the
>       outcome of a first pass link.  This is ALL to start with, then might
> @@ -919,6 +928,15 @@ main (int argc, char **argv)
>    int first_file;
>    int num_c_args;
>    char **old_argv;
> +  int i;
> +
> +  for (i = 0; i < USE_LD_MAX; i++)
> +    full_ld_suffixes[i]
> +#ifdef CROSS_DIRECTORY_STRUCTURE
> +      = concat(target_machine, "-", ld_suffixes[i], NULL);
> +#else
> +      = ld_suffixes[i];
> +#endif
>
>    p = argv[0] + strlen (argv[0]);
>    while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
> @@ -980,7 +998,6 @@ main (int argc, char **argv)
>       are called.  We also look for the -flto or -flto-partition=none flag to know
>       what LTO mode we are in.  */
>    {
> -    int i;
>      bool no_partition = false;
>
>      for (i = 1; argv[i] != NULL; i ++)
> @@ -998,7 +1015,14 @@ main (int argc, char **argv)
>           {
>             use_plugin = true;
>             lto_mode = LTO_MODE_NONE;
> +           if (selected_linker == USE_DEFAULT_LD)
> +             selected_linker = USE_PLUGIN_LD;
>           }
> +       else if (strcmp (argv[i], "-fuse-ld=bfd") == 0)
> +         selected_linker = USE_BFD_LD;
> +       else if (strcmp (argv[i], "-fuse-ld=gold") == 0)
> +         selected_linker = USE_GOLD_LD;
> +
>  #ifdef COLLECT_EXPORT_LIST
>         /* since -brtl, -bexport, -b64 are not position dependent
>            also check for them here */
> @@ -1095,21 +1119,18 @@ main (int argc, char **argv)
>    ld_file_name = find_a_file (&cpath, real_ld_suffix);
>    /* Likewise for `collect-ld'.  */
>    if (ld_file_name == 0)
> -    ld_file_name = find_a_file (&cpath, collect_ld_suffix);
> +    {
> +      ld_file_name = find_a_file (&cpath, collect_ld_suffix);
> +      use_collect_ld = ld_file_name != 0;
> +    }
>    /* Search the compiler directories for `ld'.  We have protection against
>       recursive calls in find_a_file.  */
>    if (ld_file_name == 0)
> -    ld_file_name = find_a_file (&cpath,
> -                               use_plugin
> -                               ? plugin_ld_suffix
> -                               : ld_suffix);
> +    ld_file_name = find_a_file (&cpath, ld_suffixes[selected_linker]);
>    /* Search the ordinary system bin directories
>       for `ld' (if native linking) or `TARGET-ld' (if cross).  */
>    if (ld_file_name == 0)
> -    ld_file_name = find_a_file (&path,
> -                               use_plugin
> -                               ? full_plugin_ld_suffix
> -                               : full_ld_suffix);
> +    ld_file_name = find_a_file (&path, full_ld_suffixes[selected_linker]);
>
>  #ifdef REAL_NM_FILE_NAME
>    nm_file_name = find_a_file (&path, REAL_NM_FILE_NAME);
> @@ -1266,6 +1287,13 @@ main (int argc, char **argv)
>                          "configuration");
>  #endif
>                 }
> +             else if (!use_collect_ld
> +                      && strncmp (arg, "-fuse-ld=", 9) == 0)
> +               {
> +                 /* Do not pass -fuse-ld={bfd|gold} to the linker. */
> +                 ld1--;
> +                 ld2--;
> +               }
>  #ifdef TARGET_AIX_VERSION
>               else
>                 {
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 4c8bd11..7b36ba3 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -2171,6 +2171,12 @@ funwind-tables
>  Common Report Var(flag_unwind_tables) Optimization
>  Just generate unwind tables for exception handling
>
> +fuse-ld=bfd
> +Common Var(flag_use_ld_bfd) Negative(fuse-ld=gold) Undocumented
> +
> +fuse-ld=gold
> +Common Var(flag_use_ld_gold) Negative(fuse-ld=bfd) Undocumented
> +
>  fuse-linker-plugin
>  Common Undocumented
>
> diff --git a/gcc/configure.ac b/gcc/configure.ac
> index c6f57bd..14f0974 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -2095,9 +2095,14 @@ changequote(,)dnl
>         gcc_cv_gld_major_version=`expr "$gcc_cv_gld_version" : "VERSION=\([0-9]*\)"`
>         gcc_cv_gld_minor_version=`expr "$gcc_cv_gld_version" : "VERSION=[0-9]*\.\([0-9]*\)"`
>  changequote([,])dnl
> +       ORIGINAL_LD_BFD_FOR_TARGET=../ld/ld-new$build_exeext
> +       ORIGINAL_LD_GOLD_FOR_TARGET=../gold/ld-new$build_exeext
>  else
>         AC_MSG_RESULT($gcc_cv_ld)
>         in_tree_ld=no
> +       gcc_cvs_ld_program=`basename $gcc_cv_ld $host_exeext`
> +       AC_PATH_PROG(ORIGINAL_LD_BFD_FOR_TARGET, ${gcc_cvs_ld_program}.bfd)
> +       AC_PATH_PROG(ORIGINAL_LD_GOLD_FOR_TARGET, ${gcc_cvs_ld_program}.gold)
>  fi
>
>  # Figure out what nm we will be using.
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 51b6e85..3558f43 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -425,7 +425,7 @@ Objective-C and Objective-C++ Dialects}.
>  -funit-at-a-time -funroll-all-loops -funroll-loops @gol
>  -funsafe-loop-optimizations -funsafe-math-optimizations -funswitch-loops @gol
>  -fvariable-expansion-in-unroller -fvect-cost-model -fvpt -fweb @gol
> --fwhole-program -fwpa -fuse-linker-plugin @gol
> +-fwhole-program -fwpa -fuse-ld=@var{linker} -fuse-linker-plugin @gol
>  --param @var{name}=@var{value}
>  -O  -O0  -O1  -O2  -O3  -Os -Ofast -Og}
>
> @@ -8419,6 +8419,12 @@ the comparison operation before register allocation is complete.
>
>  Enabled at levels @option{-O}, @option{-O2}, @option{-O3}, @option{-Os}.
>
> +@item -fuse-ld=gold
> +Use the @command{gold} linker instead of the default linker.
> +
> +@item -fuse-ld=bfd
> +Use the @command{ld.bfd} linker instead of the default linker.
> +
>  @item -fcprop-registers
>  @opindex fcprop-registers
>  After register allocation and post-register allocation instruction splitting,
> diff --git a/gcc/exec-tool.in b/gcc/exec-tool.in
> index 8a10775..e6a96d8 100644
> --- a/gcc/exec-tool.in
> +++ b/gcc/exec-tool.in
> @@ -21,6 +21,8 @@
>
>  ORIGINAL_AS_FOR_TARGET="@ORIGINAL_AS_FOR_TARGET@"
>  ORIGINAL_LD_FOR_TARGET="@ORIGINAL_LD_FOR_TARGET@"
> +ORIGINAL_LD_BFD_FOR_TARGET="@ORIGINAL_LD_BFD_FOR_TARGET@"
> +ORIGINAL_LD_GOLD_FOR_TARGET="@ORIGINAL_LD_GOLD_FOR_TARGET@"
>  ORIGINAL_PLUGIN_LD_FOR_TARGET="@ORIGINAL_PLUGIN_LD_FOR_TARGET@"
>  ORIGINAL_NM_FOR_TARGET="@ORIGINAL_NM_FOR_TARGET@"
>  exeext=@host_exeext@
> @@ -36,12 +38,27 @@ case "$invoked" in
>      dir=gas
>      ;;
>    collect-ld)
> -    # when using a linker plugin, gcc will always pass '-plugin' as the
> -    # first or second option to the linker.
> -    if test x"$1" = "x-plugin" || test x"$2" = "x-plugin"; then
> -      original=$ORIGINAL_PLUGIN_LD_FOR_TARGET
> -    else
> -      original=$ORIGINAL_LD_FOR_TARGET
> +    # Check -fuse-ld=bfd and -fuse-ld=gold
> +    case " $* " in
> +      *\ -fuse-ld=bfd\ *)
> +       original=$ORIGINAL_LD_BFD_FOR_TARGET
> +       ;;
> +      *\ -fuse-ld=gold\ *)
> +       original=$ORIGINAL_LD_GOLD_FOR_TARGET
> +       ;;
> +      *)
> +       # when using a linker plugin, gcc will always pass '-plugin' as the
> +       # first or second option to the linker.
> +       if test x"$1" = "x-plugin" || test x"$2" = "x-plugin"; then
> +         original=$ORIGINAL_PLUGIN_LD_FOR_TARGET
> +       else
> +         original=$ORIGINAL_LD_FOR_TARGET
> +       fi
> +       ;;
> +    esac
> +    if test ! -x "${original}"; then
> +      echo "fatal error: cannot find 'ld'"
> +      exit 1
>      fi
>      prog=ld-new$exeext
>      dir=ld
> diff --git a/gcc/gcc.c b/gcc/gcc.c
> index 13e93e5..e0fee40 100644
> --- a/gcc/gcc.c
> +++ b/gcc/gcc.c
> @@ -705,7 +705,8 @@ proper position among the other output files.  */
>      LINK_PLUGIN_SPEC \
>      "%{flto|flto=*:%<fcompare-debug*} \
>      %{flto} %{flto=*} %l " LINK_PIE_SPEC \
> -   "%X %{o*} %{e*} %{N} %{n} %{r}\
> +   "%{fuse-ld=*:-fuse-ld=%*}\
> +    %X %{o*} %{e*} %{N} %{n} %{r}\
>      %{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!nostartfiles:%S}}\
>      %{static:} %{L*} %(mfwrap) %(link_libgcc) %o\
>      %{fopenmp|ftree-parallelize-loops=*:%:include(libgomp.spec)%(link_gomp)}\
> diff --git a/gcc/opts.c b/gcc/opts.c
> index b3a9afe..ff1b51e 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -1753,6 +1753,8 @@ common_handle_option (struct gcc_options *opts,
>        dc->max_errors = value;
>        break;
>
> +    case OPT_fuse_ld_bfd:
> +    case OPT_fuse_ld_gold:
>      case OPT_fuse_linker_plugin:
>        /* No-op. Used by the driver and passed to us because it starts with f.*/
>        break;
> --
> 1.7.11.7
>
Markus Trippelsdorf Nov. 29, 2012, 9:10 a.m. UTC | #2
On 2012.11.29 at 09:43 +0100, Richard Biener wrote:
> On Thu, Nov 29, 2012 at 5:18 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> > From: "H.J. Lu" <hjl.tools@gmail.com>
> > To: gcc-patches@gcc.gnu.org
> > Cc: Joseph Myers  <joseph@codesourcery.com>, Paolo Bonzini <bonzini@gnu.org>
> > Bcc:
> > Subject: [PATCH] Support -fuse-ld=bfd and -fuse-ld=gold
> > Reply-To:
> >
> > Hi,
> >
> > Binutils supports 2 linkers, ld.gold and ld.bfd.  One of them is
> > configured as the default linker, ld, which is used by GCC.  Sometimes,
> > we want to use the alternate linker with GCC at run-time.  This patch
> > adds -fuse-ld=bfd and -fuse-ld=gold options to GCC driver.  It changes
> > collect2.c to pick either ld.bfd or ld.gold.  It also adds
> > ORIGINAL_LD_BFD_FOR_TARGET and ORIGINAL_LD_GOLD_FOR_TARGET to
> > exec-tool.in to add -fuse-ld=bfd and -fuse-ld=gold support to
> > collect-ld.  Since ld.bfd nor ld.gold know the new options, you
> > will get
> >
> > # ./xgcc -B./  /tmp/x.c -fuse-ld=gold -v
> > ...
> > ./collect-ld --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -fuse-ld=gold /lib/../lib64/crt1.o /lib/../lib64/crti.o ./crtbegin.o -L. -L/lib/../lib64 -L/usr/lib/../lib64 /tmp/cclVWcGz.o -v -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed ./crtend.o /lib/../lib64/crtn.o
> > GNU gold (Linux/GNU Binutils 2.23.51.0.7.20121127) 1.11
> > /usr/local/bin/ld.gold: fatal error: -f/--auxiliary may not be used without -shared
> > collect2: error: ld returned 1 exit status
> >
> > This is because we pass everything to ld and ld.bfd/ld.gold doesn't
> > understand -fuse-ld=bfd/-fuse-ld=gold.  It isn't a problem for collect2
> > since it will filter-out -fuse-ld=bfd/-fuse-ld=gold.  I will submit a
> > binutils patch to ignore -fuse-ld=bfd/-fuse-ld=gold, similar to -flto
> > options.
> >
> > OK to install?
> 
> Do we need to consider GNU ld and gold coming from different binutils versions
> and thus do we need two sets of linker feature tests at configure
> time?  Eventually
> also if GNU ld and gold are not in feature-parity for the same binutils version?
> 
> That is, isn't this going to create hard to debug issues for users?

Additionally, what is the rationale for this patch? IOW why isn't the following
enough?

x4 ~ # ln -f /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld.gold  /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld
x4 ~ # ld -v
GNU gold (GNU Binutils 2.23.51.20121126) 1.11
x4 ~ # ln -f /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld.bfd  /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld
x4 ~ # ld -v
GNU ld (GNU Binutils) 2.23.51.20121126

Or is this meant as a temporary workaround hack for gold bugs?
H.J. Lu Nov. 29, 2012, 1:55 p.m. UTC | #3
On Thu, Nov 29, 2012 at 1:10 AM, Markus Trippelsdorf
<markus@trippelsdorf.de> wrote:
> On 2012.11.29 at 09:43 +0100, Richard Biener wrote:
>> On Thu, Nov 29, 2012 at 5:18 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> > From: "H.J. Lu" <hjl.tools@gmail.com>
>> > To: gcc-patches@gcc.gnu.org
>> > Cc: Joseph Myers  <joseph@codesourcery.com>, Paolo Bonzini <bonzini@gnu.org>
>> > Bcc:
>> > Subject: [PATCH] Support -fuse-ld=bfd and -fuse-ld=gold
>> > Reply-To:
>> >
>> > Hi,
>> >
>> > Binutils supports 2 linkers, ld.gold and ld.bfd.  One of them is
>> > configured as the default linker, ld, which is used by GCC.  Sometimes,
>> > we want to use the alternate linker with GCC at run-time.  This patch
>> > adds -fuse-ld=bfd and -fuse-ld=gold options to GCC driver.  It changes
>> > collect2.c to pick either ld.bfd or ld.gold.  It also adds
>> > ORIGINAL_LD_BFD_FOR_TARGET and ORIGINAL_LD_GOLD_FOR_TARGET to
>> > exec-tool.in to add -fuse-ld=bfd and -fuse-ld=gold support to
>> > collect-ld.  Since ld.bfd nor ld.gold know the new options, you
>> > will get
>> >
>> > # ./xgcc -B./  /tmp/x.c -fuse-ld=gold -v
>> > ...
>> > ./collect-ld --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -fuse-ld=gold /lib/../lib64/crt1.o /lib/../lib64/crti.o ./crtbegin.o -L. -L/lib/../lib64 -L/usr/lib/../lib64 /tmp/cclVWcGz.o -v -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed ./crtend.o /lib/../lib64/crtn.o
>> > GNU gold (Linux/GNU Binutils 2.23.51.0.7.20121127) 1.11
>> > /usr/local/bin/ld.gold: fatal error: -f/--auxiliary may not be used without -shared
>> > collect2: error: ld returned 1 exit status
>> >
>> > This is because we pass everything to ld and ld.bfd/ld.gold doesn't
>> > understand -fuse-ld=bfd/-fuse-ld=gold.  It isn't a problem for collect2
>> > since it will filter-out -fuse-ld=bfd/-fuse-ld=gold.  I will submit a
>> > binutils patch to ignore -fuse-ld=bfd/-fuse-ld=gold, similar to -flto
>> > options.
>> >
>> > OK to install?
>>
>> Do we need to consider GNU ld and gold coming from different binutils versions
>> and thus do we need two sets of linker feature tests at configure
>> time?  Eventually
>> also if GNU ld and gold are not in feature-parity for the same binutils version?

This is no different from run-time linker vs configure-time linker.
If it is a link-time failure, we can check:

[hjl@gnu-6 tmp]$ gcc x.o -v -Wl,-v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.7.2/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info
--with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap
--enable-shared --enable-threads=posix --enable-checking=release
--disable-build-with-cxx --disable-build-poststage1-with-cxx
--with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-gnu-unique-object
--enable-linker-build-id --with-linker-hash-style=gnu
--enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto
--enable-plugin --enable-initfini-array --enable-java-awt=gtk
--disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre
--enable-libgcj-multifile --enable-java-maintainer-mode
--with-ecj-jar=/usr/share/java/eclipse-ecj.jar
--disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic
--with-arch_32=i686 --with-multilib-list=m32,m64,mx32
--build=x86_64-redhat-linux
Thread model: posix
gcc version 4.7.2 20120921 (Red Hat 4.7.2-2) (GCC)
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.7.2/:/usr/libexec/gcc/x86_64-redhat-linux/4.7.2/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.7.2/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.7.2/:/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.7.2/collect2 --build-id
--no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64
-dynamic-linker /lib64/ld-linux-x86-64.so.2
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crt1.o
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crti.o
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/crtbegin.o
-L/usr/lib/gcc/x86_64-redhat-linux/4.7.2
-L/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64
-L/lib/../lib64 -L/usr/lib/../lib64
-L/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../.. x.o -v -lgcc
--as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s
--no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.7.2/crtend.o
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crtn.o
collect2 version 4.7.2 20120921 (Red Hat 4.7.2-2)
/usr/local/bin/ld --build-id --no-add-needed --eh-frame-hdr
--hash-style=gnu -m elf_x86_64 -dynamic-linker
/lib64/ld-linux-x86-64.so.2
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crt1.o
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crti.o
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/crtbegin.o
-L/usr/lib/gcc/x86_64-redhat-linux/4.7.2
-L/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64
-L/lib/../lib64 -L/usr/lib/../lib64
-L/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../.. x.o -v -lgcc
--as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s
--no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.7.2/crtend.o
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crtn.o
GNU ld (GNU Binutils) 2.23.51.20121128
[hjl@gnu-6 tmp]$

>> That is, isn't this going to create hard to debug issues for users?

User uses it by choice. You can check which linker is used.

> Additionally, what is the rationale for this patch? IOW why isn't the following
> enough?
>
> x4 ~ # ln -f /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld.gold  /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld
> x4 ~ # ld -v
> GNU gold (GNU Binutils 2.23.51.20121126) 1.11
> x4 ~ # ln -f /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld.bfd  /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld
> x4 ~ # ld -v
> GNU ld (GNU Binutils) 2.23.51.20121126
>
> Or is this meant as a temporary workaround hack for gold bugs?
>

Yes, it is used as a temporary workaround for linker bugs.

---
H.J.
Matthias Klose Nov. 29, 2012, 3:49 p.m. UTC | #4
Am 29.11.2012 09:43, schrieb Richard Biener:
> Do we need to consider GNU ld and gold coming from different binutils versions
> and thus do we need two sets of linker feature tests at configure
> time?

both GNU ld and gold are built from the same sources. So it is likely that they
come from the same binutils version (at least that is the current situation).

> Eventually
> also if GNU ld and gold are not in feature-parity for the same binutils version?
> 
> That is, isn't this going to create hard to debug issues for users?

How would that be different than today's issues? If both versions are built,
then any of them can be picked up once these are installed.

How is today's situation? Debian/Ubuntu do build both linkers (and I assume
other distributions do as well).

One is the default (currently GNU ld), and you can make the other the system
wide default using diversions (a distribution mechanism to change /usr/bin/ld).

I have:

$ ls -l /usr/lib/*-ld
/usr/lib/compat-ld:
lrwxrwxrwx 1 root root 16 Nov 26 08:13 ld -> ../../bin/ld.bfd

/usr/lib/gold-ld:
lrwxrwxrwx 1 root root 17 Nov 26 08:13 ld -> ../../bin/ld.gold

so to explicitly use one linker you have to know the location, and then pass the
appropriate -B option. Having this option makes this choice independent of the
location. Having this location independence is my rationale for this patch.

  Matthias
Matthias Klose Nov. 29, 2012, 3:52 p.m. UTC | #5
Am 29.11.2012 10:10, schrieb Markus Trippelsdorf:
> Additionally, what is the rationale for this patch? IOW why isn't the following
> enough?
> 
> x4 ~ # ln -f /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld.gold  /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld
> x4 ~ # ld -v
> GNU gold (GNU Binutils 2.23.51.20121126) 1.11
> x4 ~ # ln -f /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld.bfd  /usr/x86_64-pc-linux-gnu/binutils-bin/git/ld
> x4 ~ # ld -v
> GNU ld (GNU Binutils) 2.23.51.20121126

because you have to find out about these hard-coded path for every single
installation of binutils.
H.J. Lu Nov. 29, 2012, 3:55 p.m. UTC | #6
On Thu, Nov 29, 2012 at 12:43 AM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Thu, Nov 29, 2012 at 5:18 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> From: "H.J. Lu" <hjl.tools@gmail.com>
>> To: gcc-patches@gcc.gnu.org
>> Cc: Joseph Myers  <joseph@codesourcery.com>, Paolo Bonzini <bonzini@gnu.org>
>> Bcc:
>> Subject: [PATCH] Support -fuse-ld=bfd and -fuse-ld=gold
>> Reply-To:
>>
>> Hi,
>>
>> Binutils supports 2 linkers, ld.gold and ld.bfd.  One of them is
>> configured as the default linker, ld, which is used by GCC.  Sometimes,
>> we want to use the alternate linker with GCC at run-time.  This patch
>> adds -fuse-ld=bfd and -fuse-ld=gold options to GCC driver.  It changes
>> collect2.c to pick either ld.bfd or ld.gold.  It also adds
>> ORIGINAL_LD_BFD_FOR_TARGET and ORIGINAL_LD_GOLD_FOR_TARGET to
>> exec-tool.in to add -fuse-ld=bfd and -fuse-ld=gold support to
>> collect-ld.  Since ld.bfd nor ld.gold know the new options, you
>> will get
>>
>> # ./xgcc -B./  /tmp/x.c -fuse-ld=gold -v
>> ...
>> ./collect-ld --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -fuse-ld=gold /lib/../lib64/crt1.o /lib/../lib64/crti.o ./crtbegin.o -L. -L/lib/../lib64 -L/usr/lib/../lib64 /tmp/cclVWcGz.o -v -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed ./crtend.o /lib/../lib64/crtn.o
>> GNU gold (Linux/GNU Binutils 2.23.51.0.7.20121127) 1.11
>> /usr/local/bin/ld.gold: fatal error: -f/--auxiliary may not be used without -shared
>> collect2: error: ld returned 1 exit status
>>
>> This is because we pass everything to ld and ld.bfd/ld.gold doesn't
>> understand -fuse-ld=bfd/-fuse-ld=gold.  It isn't a problem for collect2
>> since it will filter-out -fuse-ld=bfd/-fuse-ld=gold.  I will submit a
>> binutils patch to ignore -fuse-ld=bfd/-fuse-ld=gold, similar to -flto
>> options.
>>
>> OK to install?
>
> Do we need to consider GNU ld and gold coming from different binutils versions
> and thus do we need two sets of linker feature tests at configure
> time?  Eventually
> also if GNU ld and gold are not in feature-parity for the same binutils version?
>
> That is, isn't this going to create hard to debug issues for users?
>

Actually, it helps to debug issues for users.  At the moment, we don't
know which linker is the default linker.  With -fuse-ld, we can specify
which linker to use.
diff mbox

Patch

diff --git a/gcc/collect2.c b/gcc/collect2.c
index 49c4030..4e8cdf0 100644
--- a/gcc/collect2.c
+++ b/gcc/collect2.c
@@ -842,8 +842,21 @@  maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
 int
 main (int argc, char **argv)
 {
-  static const char *const ld_suffix	= "ld";
-  static const char *const plugin_ld_suffix = PLUGIN_LD_SUFFIX;
+  enum linker_select
+    {
+      USE_DEFAULT_LD,
+      USE_PLUGIN_LD,
+      USE_GOLD_LD,
+      USE_BFD_LD,
+      USE_LD_MAX
+    } selected_linker = USE_DEFAULT_LD;
+  static const char *const ld_suffixes[USE_LD_MAX] =
+    {
+      "ld",
+      PLUGIN_LD_SUFFIX,
+      "ld.gold",
+      "ld.bfd"
+    };
   static const char *const real_ld_suffix = "real-ld";
   static const char *const collect_ld_suffix = "collect-ld";
   static const char *const nm_suffix	= "nm";
@@ -854,16 +867,13 @@  main (int argc, char **argv)
   static const char *const strip_suffix = "strip";
   static const char *const gstrip_suffix = "gstrip";
 
+  const char *full_ld_suffixes[USE_LD_MAX];
 #ifdef CROSS_DIRECTORY_STRUCTURE
   /* If we look for a program in the compiler directories, we just use
      the short name, since these directories are already system-specific.
      But it we look for a program in the system directories, we need to
      qualify the program name with the target machine.  */
 
-  const char *const full_ld_suffix =
-    concat(target_machine, "-", ld_suffix, NULL);
-  const char *const full_plugin_ld_suffix =
-    concat(target_machine, "-", plugin_ld_suffix, NULL);
   const char *const full_nm_suffix =
     concat (target_machine, "-", nm_suffix, NULL);
   const char *const full_gnm_suffix =
@@ -877,13 +887,11 @@  main (int argc, char **argv)
   const char *const full_gstrip_suffix =
     concat (target_machine, "-", gstrip_suffix, NULL);
 #else
-  const char *const full_ld_suffix	= ld_suffix;
-  const char *const full_plugin_ld_suffix = plugin_ld_suffix;
-  const char *const full_nm_suffix	= nm_suffix;
-  const char *const full_gnm_suffix	= gnm_suffix;
 #ifdef LDD_SUFFIX
   const char *const full_ldd_suffix	= ldd_suffix;
 #endif
+  const char *const full_nm_suffix	= nm_suffix;
+  const char *const full_gnm_suffix	= gnm_suffix;
   const char *const full_strip_suffix	= strip_suffix;
   const char *const full_gstrip_suffix	= gstrip_suffix;
 #endif /* CROSS_DIRECTORY_STRUCTURE */
@@ -900,6 +908,7 @@  main (int argc, char **argv)
   char **ld1_argv;
   const char **ld1;
   bool use_plugin = false;
+  bool use_collect_ld = false;
 
   /* The kinds of symbols we will have to consider when scanning the
      outcome of a first pass link.  This is ALL to start with, then might
@@ -919,6 +928,15 @@  main (int argc, char **argv)
   int first_file;
   int num_c_args;
   char **old_argv;
+  int i;
+
+  for (i = 0; i < USE_LD_MAX; i++)
+    full_ld_suffixes[i]
+#ifdef CROSS_DIRECTORY_STRUCTURE
+      = concat(target_machine, "-", ld_suffixes[i], NULL);
+#else
+      = ld_suffixes[i];
+#endif
 
   p = argv[0] + strlen (argv[0]);
   while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
@@ -980,7 +998,6 @@  main (int argc, char **argv)
      are called.  We also look for the -flto or -flto-partition=none flag to know
      what LTO mode we are in.  */
   {
-    int i;
     bool no_partition = false;
 
     for (i = 1; argv[i] != NULL; i ++)
@@ -998,7 +1015,14 @@  main (int argc, char **argv)
 	  {
 	    use_plugin = true;
 	    lto_mode = LTO_MODE_NONE;
+	    if (selected_linker == USE_DEFAULT_LD)
+	      selected_linker = USE_PLUGIN_LD;
 	  }
+	else if (strcmp (argv[i], "-fuse-ld=bfd") == 0)
+	  selected_linker = USE_BFD_LD;
+	else if (strcmp (argv[i], "-fuse-ld=gold") == 0)
+	  selected_linker = USE_GOLD_LD;
+
 #ifdef COLLECT_EXPORT_LIST
 	/* since -brtl, -bexport, -b64 are not position dependent
 	   also check for them here */
@@ -1095,21 +1119,18 @@  main (int argc, char **argv)
   ld_file_name = find_a_file (&cpath, real_ld_suffix);
   /* Likewise for `collect-ld'.  */
   if (ld_file_name == 0)
-    ld_file_name = find_a_file (&cpath, collect_ld_suffix);
+    {
+      ld_file_name = find_a_file (&cpath, collect_ld_suffix);
+      use_collect_ld = ld_file_name != 0;
+    }
   /* Search the compiler directories for `ld'.  We have protection against
      recursive calls in find_a_file.  */
   if (ld_file_name == 0)
-    ld_file_name = find_a_file (&cpath,
-				use_plugin
-				? plugin_ld_suffix
-				: ld_suffix);
+    ld_file_name = find_a_file (&cpath, ld_suffixes[selected_linker]);
   /* Search the ordinary system bin directories
      for `ld' (if native linking) or `TARGET-ld' (if cross).  */
   if (ld_file_name == 0)
-    ld_file_name = find_a_file (&path,
-				use_plugin
-				? full_plugin_ld_suffix
-				: full_ld_suffix);
+    ld_file_name = find_a_file (&path, full_ld_suffixes[selected_linker]);
 
 #ifdef REAL_NM_FILE_NAME
   nm_file_name = find_a_file (&path, REAL_NM_FILE_NAME);
@@ -1266,6 +1287,13 @@  main (int argc, char **argv)
 			 "configuration");
 #endif
 		}
+	      else if (!use_collect_ld
+		       && strncmp (arg, "-fuse-ld=", 9) == 0)
+		{
+		  /* Do not pass -fuse-ld={bfd|gold} to the linker. */
+		  ld1--;
+		  ld2--;
+		}
 #ifdef TARGET_AIX_VERSION
 	      else
 		{
diff --git a/gcc/common.opt b/gcc/common.opt
index 4c8bd11..7b36ba3 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2171,6 +2171,12 @@  funwind-tables
 Common Report Var(flag_unwind_tables) Optimization
 Just generate unwind tables for exception handling
 
+fuse-ld=bfd
+Common Var(flag_use_ld_bfd) Negative(fuse-ld=gold) Undocumented
+
+fuse-ld=gold
+Common Var(flag_use_ld_gold) Negative(fuse-ld=bfd) Undocumented
+
 fuse-linker-plugin
 Common Undocumented
 
diff --git a/gcc/configure.ac b/gcc/configure.ac
index c6f57bd..14f0974 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -2095,9 +2095,14 @@  changequote(,)dnl
 	gcc_cv_gld_major_version=`expr "$gcc_cv_gld_version" : "VERSION=\([0-9]*\)"`
 	gcc_cv_gld_minor_version=`expr "$gcc_cv_gld_version" : "VERSION=[0-9]*\.\([0-9]*\)"`
 changequote([,])dnl
+	ORIGINAL_LD_BFD_FOR_TARGET=../ld/ld-new$build_exeext
+	ORIGINAL_LD_GOLD_FOR_TARGET=../gold/ld-new$build_exeext
 else
 	AC_MSG_RESULT($gcc_cv_ld)
 	in_tree_ld=no
+	gcc_cvs_ld_program=`basename $gcc_cv_ld $host_exeext`
+	AC_PATH_PROG(ORIGINAL_LD_BFD_FOR_TARGET, ${gcc_cvs_ld_program}.bfd)
+	AC_PATH_PROG(ORIGINAL_LD_GOLD_FOR_TARGET, ${gcc_cvs_ld_program}.gold)
 fi
 
 # Figure out what nm we will be using.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 51b6e85..3558f43 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -425,7 +425,7 @@  Objective-C and Objective-C++ Dialects}.
 -funit-at-a-time -funroll-all-loops -funroll-loops @gol
 -funsafe-loop-optimizations -funsafe-math-optimizations -funswitch-loops @gol
 -fvariable-expansion-in-unroller -fvect-cost-model -fvpt -fweb @gol
--fwhole-program -fwpa -fuse-linker-plugin @gol
+-fwhole-program -fwpa -fuse-ld=@var{linker} -fuse-linker-plugin @gol
 --param @var{name}=@var{value}
 -O  -O0  -O1  -O2  -O3  -Os -Ofast -Og}
 
@@ -8419,6 +8419,12 @@  the comparison operation before register allocation is complete.
 
 Enabled at levels @option{-O}, @option{-O2}, @option{-O3}, @option{-Os}.
 
+@item -fuse-ld=gold
+Use the @command{gold} linker instead of the default linker.
+
+@item -fuse-ld=bfd
+Use the @command{ld.bfd} linker instead of the default linker.
+
 @item -fcprop-registers
 @opindex fcprop-registers
 After register allocation and post-register allocation instruction splitting,
diff --git a/gcc/exec-tool.in b/gcc/exec-tool.in
index 8a10775..e6a96d8 100644
--- a/gcc/exec-tool.in
+++ b/gcc/exec-tool.in
@@ -21,6 +21,8 @@ 
 
 ORIGINAL_AS_FOR_TARGET="@ORIGINAL_AS_FOR_TARGET@"
 ORIGINAL_LD_FOR_TARGET="@ORIGINAL_LD_FOR_TARGET@"
+ORIGINAL_LD_BFD_FOR_TARGET="@ORIGINAL_LD_BFD_FOR_TARGET@"
+ORIGINAL_LD_GOLD_FOR_TARGET="@ORIGINAL_LD_GOLD_FOR_TARGET@"
 ORIGINAL_PLUGIN_LD_FOR_TARGET="@ORIGINAL_PLUGIN_LD_FOR_TARGET@"
 ORIGINAL_NM_FOR_TARGET="@ORIGINAL_NM_FOR_TARGET@"
 exeext=@host_exeext@
@@ -36,12 +38,27 @@  case "$invoked" in
     dir=gas
     ;;
   collect-ld)
-    # when using a linker plugin, gcc will always pass '-plugin' as the
-    # first or second option to the linker.
-    if test x"$1" = "x-plugin" || test x"$2" = "x-plugin"; then
-      original=$ORIGINAL_PLUGIN_LD_FOR_TARGET
-    else
-      original=$ORIGINAL_LD_FOR_TARGET
+    # Check -fuse-ld=bfd and -fuse-ld=gold
+    case " $* " in
+      *\ -fuse-ld=bfd\ *)
+	original=$ORIGINAL_LD_BFD_FOR_TARGET
+	;;
+      *\ -fuse-ld=gold\ *)
+	original=$ORIGINAL_LD_GOLD_FOR_TARGET
+	;;
+      *)
+	# when using a linker plugin, gcc will always pass '-plugin' as the
+	# first or second option to the linker.
+	if test x"$1" = "x-plugin" || test x"$2" = "x-plugin"; then
+	  original=$ORIGINAL_PLUGIN_LD_FOR_TARGET
+	else
+	  original=$ORIGINAL_LD_FOR_TARGET
+	fi
+	;;
+    esac
+    if test ! -x "${original}"; then
+      echo "fatal error: cannot find 'ld'"
+      exit 1
     fi
     prog=ld-new$exeext
     dir=ld
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 13e93e5..e0fee40 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -705,7 +705,8 @@  proper position among the other output files.  */
     LINK_PLUGIN_SPEC \
     "%{flto|flto=*:%<fcompare-debug*} \
     %{flto} %{flto=*} %l " LINK_PIE_SPEC \
-   "%X %{o*} %{e*} %{N} %{n} %{r}\
+   "%{fuse-ld=*:-fuse-ld=%*}\
+    %X %{o*} %{e*} %{N} %{n} %{r}\
     %{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!nostartfiles:%S}}\
     %{static:} %{L*} %(mfwrap) %(link_libgcc) %o\
     %{fopenmp|ftree-parallelize-loops=*:%:include(libgomp.spec)%(link_gomp)}\
diff --git a/gcc/opts.c b/gcc/opts.c
index b3a9afe..ff1b51e 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1753,6 +1753,8 @@  common_handle_option (struct gcc_options *opts,
       dc->max_errors = value;
       break;
 
+    case OPT_fuse_ld_bfd:
+    case OPT_fuse_ld_gold:
     case OPT_fuse_linker_plugin:
       /* No-op. Used by the driver and passed to us because it starts with f.*/
       break;