Detect linker information at runtime. Fixes Trac #6063
authorAustin Seipp <aseipp@pobox.com>
Sat, 4 May 2013 21:07:22 +0000 (16:07 -0500)
committerAustin Seipp <aseipp@pobox.com>
Mon, 17 Jun 2013 01:01:25 +0000 (20:01 -0500)
Previously, we did ./configure time checks to see if 'GNU ld' supported
certain options. If it does, we bake those options into the link step.
See Trac #5240.

Unfortunately, the linker we use at runtime can change for several
reasons. One is that the user specifies -pgml 'foo'. The other is if
/usr/bin/ld or whatnot changes from when GHC was built.  Those options
mentioned earlier are specific to GNU ld, but many systems support GNU
gold too. This is Trac #6063.

So we need to check at runtime what linker we're using. This is actually
a little bit complicated because we normally use the C compiler as our
linker. Windows and OS X are also special here.

Finally, this patch also unconditionally gives '--hash-size=31' and
'--reduce-memory-overheads' to the system linker if it's GNU ld. These
options have been supported for 8+ years from what I can see, and there
are probably a lot of other reasons why GHC would not work with such an
ancient binutils, all things considered.

See Note [Run-time linker info] in SysTools for details. There are
plenty of comments as well in the surrounding code.

Signed-off-by: Austin Seipp <aseipp@pobox.com>
aclocal.m4
compiler/main/DynFlags.hs
compiler/main/SysTools.lhs
configure.ac
distrib/configure.ac.in

index 2ab4ad5..0283206 100644 (file)
@@ -535,18 +535,6 @@ AC_DEFUN([FPTOOLS_SET_C_LD_FLAGS],
         $2="$$2 -fno-stack-protector"
     fi
 
-    # Reduce memory usage when linking. See trac #5240.
-    if test -n "$LdHashSize31"
-    then
-        $3="$$3 -Wl,$LdHashSize31"
-        $4="$$4     $LdHashSize31"
-    fi
-    if test -n "$LdReduceMemoryOverheads"
-    then
-        $3="$$3 -Wl,$LdReduceMemoryOverheads"
-        $4="$$4     $LdReduceMemoryOverheads"
-    fi
-
     rm -f conftest.c conftest.o
     AC_MSG_RESULT([done])
 ])
@@ -921,27 +909,6 @@ $2=$fp_cv_$2
 ])# FP_PROG_LD_FLAG
 
 
-# FP_PROG_LD_HashSize31
-# ------------
-# Sets the output variable LdHashSize31 to --hash-size=31 if ld supports
-# this flag. Otherwise the variable's value is empty.
-AC_DEFUN([FP_PROG_LD_HashSize31],
-[
-FP_PROG_LD_FLAG([--hash-size=31],[LdHashSize31])
-])# FP_PROG_LD_HashSize31
-
-
-# FP_PROG_LD_ReduceMemoryOverheads
-# ------------
-# Sets the output variable LdReduceMemoryOverheads to
-# --reduce-memory-overheads if ld supports this flag.
-# Otherwise the variable's value is empty.
-AC_DEFUN([FP_PROG_LD_ReduceMemoryOverheads],
-[
-FP_PROG_LD_FLAG([--reduce-memory-overheads],[LdReduceMemoryOverheads])
-])# FP_PROG_LD_ReduceMemoryOverheads
-
-
 # FP_PROG_LD_BUILD_ID
 # ------------
 
index 0e96a20..64ae9b5 100644 (file)
@@ -129,6 +129,9 @@ module DynFlags (
         -- * SSE
         isSse2Enabled,
         isSse4_2Enabled,
+
+        -- * Linker information
+        LinkerInfo(..),
   ) where
 
 #include "HsVersions.h"
@@ -742,7 +745,10 @@ data DynFlags = DynFlags {
   nextWrapperNum        :: IORef Int,
 
   -- | Machine dependant flags (-m<blah> stuff)
-  sseVersion            :: Maybe (Int, Int)  -- (major, minor)
+  sseVersion            :: Maybe (Int, Int),  -- (major, minor)
+
+  -- | Run-time linker information (what options we need, etc.)
+  rtldFlags             :: IORef (Maybe LinkerInfo)
  }
 
 class HasDynFlags m where
@@ -1201,6 +1207,7 @@ initDynFlags dflags = do
  refFilesToNotIntermediateClean <- newIORef []
  refGeneratedDumps <- newIORef Set.empty
  refLlvmVersion <- newIORef 28
+ refRtldFlags <- newIORef Nothing
  wrapperNum <- newIORef 0
  canUseUnicodeQuotes <- do let enc = localeEncoding
                                str = "‛’"
@@ -1216,7 +1223,8 @@ initDynFlags dflags = do
         generatedDumps = refGeneratedDumps,
         llvmVersion    = refLlvmVersion,
         nextWrapperNum = wrapperNum,
-        useUnicodeQuotes = canUseUnicodeQuotes
+        useUnicodeQuotes = canUseUnicodeQuotes,
+        rtldFlags      = refRtldFlags
         }
 
 -- | The normal 'DynFlags'. Note that they is not suitable for use in this form
@@ -1349,7 +1357,8 @@ defaultDynFlags mySettings =
         llvmVersion = panic "defaultDynFlags: No llvmVersion",
         interactivePrint = Nothing,
         nextWrapperNum = panic "defaultDynFlags: No nextWrapperNum",
-        sseVersion = Nothing
+        sseVersion = Nothing,
+        rtldFlags = panic "defaultDynFlags: no rtldFlags"
       }
 
 defaultWays :: Settings -> [Way]
@@ -3519,3 +3528,14 @@ isSse2Enabled dflags = case platformArch (targetPlatform dflags) of
 
 isSse4_2Enabled :: DynFlags -> Bool
 isSse4_2Enabled dflags = sseVersion dflags >= Just (4,2)
+
+-- -----------------------------------------------------------------------------
+-- Linker information
+
+-- LinkerInfo contains any extra options needed by the system linker.
+data LinkerInfo
+  = GnuLD    [Option]
+  | GnuGold  [Option]
+  | DarwinLD [Option]
+  | UnknownLD
+  deriving Eq
index 5926114..d43826a 100644 (file)
@@ -24,6 +24,8 @@ module SysTools (
         figureLlvmVersion,
         readElfSection,
 
+        getLinkerInfo,
+
         linkDynLib,
 
         askCc,
@@ -596,13 +598,122 @@ figureLlvmVersion dflags = do
                           text "Make sure you have installed LLVM"]
                 return Nothing)
   return ver
-  
+
+
+{- Note [Run-time linker info]
+
+See also: Trac #5240, Trac #6063
+
+Before 'runLink', we need to be sure to get the relevant information
+about the linker we're using at runtime to see if we need any extra
+options. For example, GNU ld requires '--reduce-memory-overheads' and
+'--hash-size=31' in order to use reasonable amounts of memory (see
+trac #5240.) But this isn't supported in GNU gold.
+
+Generally, the linker changing from what was detected at ./configure
+time has always been possible using -pgml, but on Linux it can happen
+'transparently' by installing packages like binutils-gold, which
+change what /usr/bin/ld actually points to.
+
+Clang vs GCC notes:
+
+For gcc, 'gcc -Wl,--version' gives a bunch of output about how to
+invoke the linker before the version information string. For 'clang',
+the version information for 'ld' is all that's output. For this
+reason, we typically need to slurp up all of the standard error output
+and look through it.
+
+Other notes:
+
+We cache the LinkerInfo inside DynFlags, since clients may link
+multiple times. The definition of LinkerInfo is there to avoid a
+circular dependency.
+
+-}
+
+
+neededLinkArgs :: LinkerInfo -> [Option]
+neededLinkArgs (GnuLD o)     = o
+neededLinkArgs (GnuGold o)   = o
+neededLinkArgs (DarwinLD o)  = o
+neededLinkArgs UnknownLD     = []
+
+-- Grab linker info and cache it in DynFlags.
+getLinkerInfo :: DynFlags -> IO LinkerInfo
+getLinkerInfo dflags = do
+  info <- readIORef (rtldFlags dflags)
+  case info of
+    Just v  -> return v
+    Nothing -> do
+      v <- getLinkerInfo' dflags
+      writeIORef (rtldFlags dflags) (Just v)
+      return v
+
+-- See Note [Run-time linker info].
+getLinkerInfo' :: DynFlags -> IO LinkerInfo
+getLinkerInfo' dflags = do
+  let platform = targetPlatform dflags
+      os = platformOS platform
+      (pgm,_) = pgm_l dflags
+
+      -- Try to grab the info from the process output.
+      parseLinkerInfo stdo _stde _exitc
+        | any ("GNU ld" `isPrefixOf`) stdo =
+          -- GNU ld specifically needs to use less memory. This especially
+          -- hurts on small object files. Trac #5240.
+          return (GnuLD $ map Option ["-Wl,--hash-size=31",
+                                      "-Wl,--reduce-memory-overheads"])
+
+        | any ("GNU gold" `isPrefixOf`) stdo =
+          -- GNU gold does not require any special arguments.
+          return (GnuGold [])
+
+         -- Unknown linker.
+        | otherwise = fail "invalid --version output, or linker is unsupported"
+
+  -- Process the executable call
+  info <- catchIO (do
+             case os of
+               OSDarwin ->
+                 -- Darwin has neither GNU Gold or GNU LD, but a strange linker
+                 -- that doesn't support --version. We can just assume that's
+                 -- what we're using.
+                 return $ DarwinLD []
+               OSMinGW32 ->
+                 -- GHC doesn't support anything but GNU ld on Windows anyway.
+                 -- Process creation is also fairly expensive on win32, so
+                 -- we short-circuit here.
+                 return $ GnuLD $ map Option ["-Wl,--hash-size=31",
+                                              "-Wl,--reduce-memory-overheads"]
+               _ -> do
+                 -- In practice, we use the compiler as the linker here. Pass
+                 -- -Wl,--version to get linker version info.
+                 (exitc, stdo, stde) <- readProcessWithExitCode pgm
+                                        ["-Wl,--version"] ""
+                 -- Split the output by lines to make certain kinds
+                 -- of processing easier. In particular, 'clang' and 'gcc'
+                 -- have slightly different outputs for '-Wl,--version', but
+                 -- it's still easy to figure out.
+                 parseLinkerInfo (lines stdo) (lines stde) exitc
+            )
+            (\err -> do
+                debugTraceMsg dflags 2
+                    (text "Error (figuring out linker information):" <+>
+                     text (show err))
+                errorMsg dflags $ hang (text "Warning:") 9 $
+                  text "Couldn't figure out linker information!" $$
+                  text "Make sure you're using GNU ld, GNU gold" <+>
+                  text "or the built in OS X linker, etc."
+                return UnknownLD)
+  return info
 
 runLink :: DynFlags -> [Option] -> IO ()
 runLink dflags args = do
+  -- See Note [Run-time linker info]
+  linkargs <- neededLinkArgs `fmap` getLinkerInfo dflags
   let (p,args0) = pgm_l dflags
-      args1 = map Option (getOpts dflags opt_l)
-      args2 = args0 ++ args1 ++ args
+      args1     = map Option (getOpts dflags opt_l)
+      args2     = args0 ++ args1 ++ args ++ linkargs
   mb_env <- getGccEnv args2
   runSomethingFiltered dflags id "Linker" p args2 mb_env
 
index 5fccdbe..7bbeca6 100644 (file)
@@ -553,8 +553,6 @@ dnl ** look to see if we have a C compiler using an llvm back end.
 dnl
 FP_CC_LLVM_BACKEND
 
-FP_PROG_LD_HashSize31
-FP_PROG_LD_ReduceMemoryOverheads
 FP_PROG_LD_IS_GNU
 FP_PROG_LD_BUILD_ID
 FP_PROG_LD_NO_COMPACT_UNWIND
index 6b20f84..4a6944f 100644 (file)
@@ -72,8 +72,6 @@ AC_SUBST([LdCmd])
 FP_GCC_VERSION
 AC_PROG_CPP
 
-FP_PROG_LD_HashSize31
-FP_PROG_LD_ReduceMemoryOverheads
 FP_PROG_LD_IS_GNU
 FP_PROG_LD_BUILD_ID
 FP_PROG_LD_NO_COMPACT_UNWIND