[Git][ghc/ghc][wip/mangoiv/ghc-9.12-bp] linker: add --optimistic-linking flag
Magnus pushed to branch wip/mangoiv/ghc-9.12-bp at Glasgow Haskell Compiler / GHC Commits: 11ce0b56 by doyougnu at 2026-05-25T14:42:22+02:00 linker: add --optimistic-linking flag This patch adds: - the --optimistic-linking flag which binds unknown symbols in the runtime linker to 0xDEADBEEF instead of exiting with failure - The test T25240 which tests these flags using dead code in the FFI system. - closes #25240 This patch is part of the upstreaming haskell.nix patches project. (cherry picked from commit 25121dbc57724498776c0d46f810612f4ca81cc4) - - - - - 20 changed files: - docs/users_guide/runtime_control.rst - libraries/ghc-internal/src/GHC/Internal/RTS/Flags.hsc - rts/Linker.c - rts/RtsFlags.c - rts/include/rts/Flags.h - rts/linker/elf_got.c - + testsuite/tests/ghci/linking/T25240/Makefile - + testsuite/tests/ghci/linking/T25240/T25240.hs - + testsuite/tests/ghci/linking/T25240/T25240.stderr - + testsuite/tests/ghci/linking/T25240/T25240a.hs - + testsuite/tests/ghci/linking/T25240/all.T - testsuite/tests/ghci/should_run/T18064.stderr - testsuite/tests/interface-stability/base-exports.stdout - testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs - testsuite/tests/interface-stability/base-exports.stdout-mingw32 - testsuite/tests/interface-stability/base-exports.stdout-ws-32 - testsuite/tests/rts/linker/T11223/T11223_link_order_a_b_2_fail.stderr - testsuite/tests/rts/linker/T11223/T11223_link_order_a_b_2_fail.stderr-ws-64-mingw32 - testsuite/tests/rts/linker/T11223/T11223_simple_duplicate_lib.stderr - testsuite/tests/rts/linker/T11223/T11223_simple_duplicate_lib.stderr-ws-64-mingw32 Changes: ===================================== docs/users_guide/runtime_control.rst ===================================== @@ -397,6 +397,11 @@ Miscellaneous RTS options heap larger than 1T. ``-xr`` is a no-op if GHC is configured with ``--disable-large-address-space`` or if the platform is 32-bit. +.. rts-flag:: --optimistic-linking + + If given, instruct the runtime linker to try to continue linking in the + presence of an unresolved symbol. + .. _rts-options-gc: RTS options to control the garbage collector ===================================== libraries/ghc-internal/src/GHC/Internal/RTS/Flags.hsc ===================================== @@ -162,6 +162,7 @@ data MiscFlags = MiscFlags , disableDelayedOsMemoryReturn :: Bool , internalCounters :: Bool , linkerAlwaysPic :: Bool + , linkerOptimistic :: Bool , linkerMemBase :: Word -- ^ address to ask the OS for memory for the linker, 0 ==> off , ioManager :: IoManagerFlag @@ -536,6 +537,8 @@ getMiscFlags = do (#{peek MISC_FLAGS, internalCounters} ptr :: IO CBool)) <*> (toBool <$> (#{peek MISC_FLAGS, linkerAlwaysPic} ptr :: IO CBool)) + <*> (toBool <$> + (#{peek MISC_FLAGS, linkerOptimistic} ptr :: IO CBool)) <*> #{peek MISC_FLAGS, linkerMemBase} ptr <*> (toEnum . fromIntegral <$> (#{peek MISC_FLAGS, ioManager} ptr :: IO Word32)) ===================================== rts/Linker.c ===================================== @@ -980,10 +980,20 @@ SymbolAddr* lookupSymbol( SymbolName* lbl ) // lookupDependentSymbol directly. SymbolAddr* r = lookupDependentSymbol(lbl, NULL, NULL); if (!r) { - errorBelch("^^ Could not load '%s', dependency unresolved. " - "See top entry above.\n", lbl); - IF_DEBUG(linker, printLoadedObjects()); - fflush(stderr); + if (!RtsFlags.MiscFlags.linkerOptimistic) { + errorBelch("^^ Could not load '%s', dependency unresolved. " + "See top entry above. You might consider using --optimistic-linking\n", + lbl); + IF_DEBUG(linker, printLoadedObjects()); + fflush(stderr); + } else { + // if --optimistic-linking is passed into the RTS we allow the linker + // to optimistically continue + errorBelch("^^ Could not load '%s', dependency unresolved, " + "optimistically continuing\n", + lbl); + r = (void*) 0xDEADBEEF; + } } if (!runPendingInitializers()) { ===================================== rts/RtsFlags.c ===================================== @@ -269,6 +269,7 @@ void initRtsFlagsDefaults(void) RtsFlags.MiscFlags.disableDelayedOsMemoryReturn = false; RtsFlags.MiscFlags.internalCounters = false; RtsFlags.MiscFlags.linkerAlwaysPic = DEFAULT_LINKER_ALWAYS_PIC; + RtsFlags.MiscFlags.linkerOptimistic = false; RtsFlags.MiscFlags.linkerMemBase = 0; RtsFlags.MiscFlags.ioManager = IO_MNGR_FLAG_AUTO; #if defined(THREADED_RTS) && defined(mingw32_HOST_OS) @@ -998,6 +999,11 @@ error = true; OPTION_UNSAFE; RtsFlags.MiscFlags.generate_dump_file = true; } + else if (strequal("optimistic-linking", + &rts_argv[arg][2])) { + OPTION_UNSAFE; + RtsFlags.MiscFlags.linkerOptimistic = true; + } else if (strequal("null-eventlog-writer", &rts_argv[arg][2])) { OPTION_UNSAFE; ===================================== rts/include/rts/Flags.h ===================================== @@ -267,6 +267,7 @@ typedef struct _MISC_FLAGS { there as well. */ bool internalCounters; /* See Note [Internal Counters Stats] */ bool linkerAlwaysPic; /* Assume the object code is always PIC */ + bool linkerOptimistic; /* Should the runtime linker optimistically continue */ StgWord linkerMemBase; /* address to ask the OS for memory * for the linker, NULL ==> off */ IO_MANAGER_FLAG ioManager; /* The I/O manager to use. */ ===================================== rts/linker/elf_got.c ===================================== @@ -97,9 +97,22 @@ fillGot(ObjectCode * oc) { if(0 == strncmp(symbol->name,"_GLOBAL_OFFSET_TABLE_",21)) { symbol->addr = oc->info->got_start; } else { - errorBelch("Failed to lookup symbol: %s\n", + errorBelch("Failed to lookup symbol: %s," + " you might consider using --optimistic-linking\n", symbol->name); - return EXIT_FAILURE; + + // if --optimistic-linking is passed into the + // RTS we allow the linker to optimistically + // continue + if (RtsFlags.MiscFlags.linkerOptimistic) { + errorBelch("Failed to lookup symbol: %s," + " optimistically continuing.\n", + symbol->name); + symbol->addr = (void*) 0xDEADBEEF; + } else { + return EXIT_FAILURE; + } + } } } else { ===================================== testsuite/tests/ghci/linking/T25240/Makefile ===================================== @@ -0,0 +1,7 @@ +TOP=../../../.. +include $(TOP)/mk/boilerplate.mk +include $(TOP)/mk/test.mk + +.PHONY: T25240 +T25240: + "$(TEST_HC)" $(TEST_HC_OPTS_INTERACTIVE) T25240a.hs T25240.hs +RTS --optimistic-linking -RTS ===================================== testsuite/tests/ghci/linking/T25240/T25240.hs ===================================== @@ -0,0 +1,17 @@ +{-# LANGUAGE TemplateHaskell #-} + +{- + +When the RTS linker loads the T25240a module to run the pure foo splice, it +tries to resolve the func symbol even if this function isn't required to run the +splice code, i.e., its dead code. This test checks that by passing the +--optimistic-linking flag the RTS linker continues to link even in the presence +of unknown symbols. + +-} + +module T25240 where + +import T25240a + +$(pure foo) ===================================== testsuite/tests/ghci/linking/T25240/T25240.stderr ===================================== @@ -0,0 +1,2 @@ +ghc: ^^ Could not load 'func', dependency unresolved, optimistically continuing + ===================================== testsuite/tests/ghci/linking/T25240/T25240a.hs ===================================== @@ -0,0 +1,13 @@ +{-# LANGUAGE TemplateHaskell #-} + + +module T25240a + ( foo, func + ) where + + +foo :: [a] +foo = [] + +foreign import ccall "func" + func :: Int -> Int ===================================== testsuite/tests/ghci/linking/T25240/all.T ===================================== @@ -0,0 +1,3 @@ +# skip on darwin because the leading underscores will make the test fail +test('T25240', [when(leading_underscore(),skip), req_rts_linker, extra_files(['T25240a.hs'])], + makefile_test, ['T25240']) ===================================== testsuite/tests/ghci/should_run/T18064.stderr ===================================== @@ -1,2 +1,2 @@ -<interactive>: ^^ Could not load 'blah', dependency unresolved. See top entry above. +<interactive>: ^^ Could not load 'blah', dependency unresolved. See top entry above. You might consider using --optimistic-linking ===================================== testsuite/tests/interface-stability/base-exports.stdout ===================================== @@ -9120,7 +9120,7 @@ module GHC.RTS.Flags where type IoSubSystem :: * data IoSubSystem = IoPOSIX | IoNative type MiscFlags :: * - data MiscFlags = MiscFlags {tickInterval :: RtsTime, installSignalHandlers :: GHC.Types.Bool, installSEHHandlers :: GHC.Types.Bool, generateCrashDumpFile :: GHC.Types.Bool, generateStackTrace :: GHC.Types.Bool, machineReadable :: GHC.Types.Bool, disableDelayedOsMemoryReturn :: GHC.Types.Bool, internalCounters :: GHC.Types.Bool, linkerAlwaysPic :: GHC.Types.Bool, linkerMemBase :: GHC.Types.Word, ioManager :: IoManagerFlag, numIoWorkerThreads :: GHC.Internal.Word.Word32} + data MiscFlags = MiscFlags {tickInterval :: RtsTime, installSignalHandlers :: GHC.Types.Bool, installSEHHandlers :: GHC.Types.Bool, generateCrashDumpFile :: GHC.Types.Bool, generateStackTrace :: GHC.Types.Bool, machineReadable :: GHC.Types.Bool, disableDelayedOsMemoryReturn :: GHC.Types.Bool, internalCounters :: GHC.Types.Bool, linkerAlwaysPic :: GHC.Types.Bool, linkerOptimistic :: GHC.Types.Bool, linkerMemBase :: GHC.Types.Word, ioManager :: IoManagerFlag, numIoWorkerThreads :: GHC.Internal.Word.Word32} type ParFlags :: * data ParFlags = ParFlags {nCapabilities :: GHC.Internal.Word.Word32, migrate :: GHC.Types.Bool, maxLocalSparks :: GHC.Internal.Word.Word32, parGcEnabled :: GHC.Types.Bool, parGcGen :: GHC.Internal.Word.Word32, parGcLoadBalancingEnabled :: GHC.Types.Bool, parGcLoadBalancingGen :: GHC.Internal.Word.Word32, parGcNoSyncWithIdle :: GHC.Internal.Word.Word32, parGcThreads :: GHC.Internal.Word.Word32, setAffinity :: GHC.Types.Bool} type ProfFlags :: * ===================================== testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs ===================================== @@ -12162,7 +12162,7 @@ module GHC.RTS.Flags where type IoSubSystem :: * data IoSubSystem = IoPOSIX | IoNative type MiscFlags :: * - data MiscFlags = MiscFlags {tickInterval :: RtsTime, installSignalHandlers :: GHC.Types.Bool, installSEHHandlers :: GHC.Types.Bool, generateCrashDumpFile :: GHC.Types.Bool, generateStackTrace :: GHC.Types.Bool, machineReadable :: GHC.Types.Bool, disableDelayedOsMemoryReturn :: GHC.Types.Bool, internalCounters :: GHC.Types.Bool, linkerAlwaysPic :: GHC.Types.Bool, linkerMemBase :: GHC.Types.Word, ioManager :: IoManagerFlag, numIoWorkerThreads :: GHC.Internal.Word.Word32} + data MiscFlags = MiscFlags {tickInterval :: RtsTime, installSignalHandlers :: GHC.Types.Bool, installSEHHandlers :: GHC.Types.Bool, generateCrashDumpFile :: GHC.Types.Bool, generateStackTrace :: GHC.Types.Bool, machineReadable :: GHC.Types.Bool, disableDelayedOsMemoryReturn :: GHC.Types.Bool, internalCounters :: GHC.Types.Bool, linkerAlwaysPic :: GHC.Types.Bool, linkerOptimistic :: GHC.Types.Bool, linkerMemBase :: GHC.Types.Word, ioManager :: IoManagerFlag, numIoWorkerThreads :: GHC.Internal.Word.Word32} type ParFlags :: * data ParFlags = ParFlags {nCapabilities :: GHC.Internal.Word.Word32, migrate :: GHC.Types.Bool, maxLocalSparks :: GHC.Internal.Word.Word32, parGcEnabled :: GHC.Types.Bool, parGcGen :: GHC.Internal.Word.Word32, parGcLoadBalancingEnabled :: GHC.Types.Bool, parGcLoadBalancingGen :: GHC.Internal.Word.Word32, parGcNoSyncWithIdle :: GHC.Internal.Word.Word32, parGcThreads :: GHC.Internal.Word.Word32, setAffinity :: GHC.Types.Bool} type ProfFlags :: * ===================================== testsuite/tests/interface-stability/base-exports.stdout-mingw32 ===================================== @@ -9344,7 +9344,7 @@ module GHC.RTS.Flags where type IoSubSystem :: * data IoSubSystem = IoPOSIX | IoNative type MiscFlags :: * - data MiscFlags = MiscFlags {tickInterval :: RtsTime, installSignalHandlers :: GHC.Types.Bool, installSEHHandlers :: GHC.Types.Bool, generateCrashDumpFile :: GHC.Types.Bool, generateStackTrace :: GHC.Types.Bool, machineReadable :: GHC.Types.Bool, disableDelayedOsMemoryReturn :: GHC.Types.Bool, internalCounters :: GHC.Types.Bool, linkerAlwaysPic :: GHC.Types.Bool, linkerMemBase :: GHC.Types.Word, ioManager :: IoManagerFlag, numIoWorkerThreads :: GHC.Internal.Word.Word32} + data MiscFlags = MiscFlags {tickInterval :: RtsTime, installSignalHandlers :: GHC.Types.Bool, installSEHHandlers :: GHC.Types.Bool, generateCrashDumpFile :: GHC.Types.Bool, generateStackTrace :: GHC.Types.Bool, machineReadable :: GHC.Types.Bool, disableDelayedOsMemoryReturn :: GHC.Types.Bool, internalCounters :: GHC.Types.Bool, linkerAlwaysPic :: GHC.Types.Bool, linkerOptimistic :: GHC.Types.Bool, linkerMemBase :: GHC.Types.Word, ioManager :: IoManagerFlag, numIoWorkerThreads :: GHC.Internal.Word.Word32} type ParFlags :: * data ParFlags = ParFlags {nCapabilities :: GHC.Internal.Word.Word32, migrate :: GHC.Types.Bool, maxLocalSparks :: GHC.Internal.Word.Word32, parGcEnabled :: GHC.Types.Bool, parGcGen :: GHC.Internal.Word.Word32, parGcLoadBalancingEnabled :: GHC.Types.Bool, parGcLoadBalancingGen :: GHC.Internal.Word.Word32, parGcNoSyncWithIdle :: GHC.Internal.Word.Word32, parGcThreads :: GHC.Internal.Word.Word32, setAffinity :: GHC.Types.Bool} type ProfFlags :: * ===================================== testsuite/tests/interface-stability/base-exports.stdout-ws-32 ===================================== @@ -9120,7 +9120,7 @@ module GHC.RTS.Flags where type IoSubSystem :: * data IoSubSystem = IoPOSIX | IoNative type MiscFlags :: * - data MiscFlags = MiscFlags {tickInterval :: RtsTime, installSignalHandlers :: GHC.Types.Bool, installSEHHandlers :: GHC.Types.Bool, generateCrashDumpFile :: GHC.Types.Bool, generateStackTrace :: GHC.Types.Bool, machineReadable :: GHC.Types.Bool, disableDelayedOsMemoryReturn :: GHC.Types.Bool, internalCounters :: GHC.Types.Bool, linkerAlwaysPic :: GHC.Types.Bool, linkerMemBase :: GHC.Types.Word, ioManager :: IoManagerFlag, numIoWorkerThreads :: GHC.Internal.Word.Word32} + data MiscFlags = MiscFlags {tickInterval :: RtsTime, installSignalHandlers :: GHC.Types.Bool, installSEHHandlers :: GHC.Types.Bool, generateCrashDumpFile :: GHC.Types.Bool, generateStackTrace :: GHC.Types.Bool, machineReadable :: GHC.Types.Bool, disableDelayedOsMemoryReturn :: GHC.Types.Bool, internalCounters :: GHC.Types.Bool, linkerAlwaysPic :: GHC.Types.Bool, linkerOptimistic :: GHC.Types.Bool, linkerMemBase :: GHC.Types.Word, ioManager :: IoManagerFlag, numIoWorkerThreads :: GHC.Internal.Word.Word32} type ParFlags :: * data ParFlags = ParFlags {nCapabilities :: GHC.Internal.Word.Word32, migrate :: GHC.Types.Bool, maxLocalSparks :: GHC.Internal.Word.Word32, parGcEnabled :: GHC.Types.Bool, parGcGen :: GHC.Internal.Word.Word32, parGcLoadBalancingEnabled :: GHC.Types.Bool, parGcLoadBalancingGen :: GHC.Internal.Word.Word32, parGcNoSyncWithIdle :: GHC.Internal.Word.Word32, parGcThreads :: GHC.Internal.Word.Word32, setAffinity :: GHC.Types.Bool} type ProfFlags :: * ===================================== testsuite/tests/rts/linker/T11223/T11223_link_order_a_b_2_fail.stderr ===================================== @@ -9,7 +9,7 @@ This could be caused by: * Specifying the same object file twice on the GHCi command line * An incorrect `package.conf' entry, causing some object to be loaded twice. -ghc-stage2: ^^ Could not load 'c', dependency unresolved. See top entry above. +ghc-stage2: ^^ Could not load 'c', dependency unresolved. See top entry above. You might consider using --optimistic-linking GHC.ByteCode.Linker: can't find label ===================================== testsuite/tests/rts/linker/T11223/T11223_link_order_a_b_2_fail.stderr-ws-64-mingw32 ===================================== @@ -9,7 +9,7 @@ This could be caused by: * Specifying the same object file twice on the GHCi command line * An incorrect `package.conf' entry, causing some object to be loaded twice. -ghc-stage2.exe: ^^ Could not load 'c', dependency unresolved. See top entry above. +ghc-stage2.exe: ^^ Could not load 'c', dependency unresolved. See top entry above. You might consider using --optimistic-linking GHC.ByteCode.Linker: can't find label ===================================== testsuite/tests/rts/linker/T11223/T11223_simple_duplicate_lib.stderr ===================================== @@ -9,7 +9,7 @@ This could be caused by: * Specifying the same object file twice on the GHCi command line * An incorrect `package.conf' entry, causing some object to be loaded twice. -ghc-stage2: ^^ Could not load 'c', dependency unresolved. See top entry above. +ghc-stage2: ^^ Could not load 'c', dependency unresolved. See top entry above. You might consider using --optimistic-linking GHC.ByteCode.Linker: can't find label ===================================== testsuite/tests/rts/linker/T11223/T11223_simple_duplicate_lib.stderr-ws-64-mingw32 ===================================== @@ -9,7 +9,7 @@ This could be caused by: * Specifying the same object file twice on the GHCi command line * An incorrect `package.conf' entry, causing some object to be loaded twice. -ghc-stage2.exe: ^^ Could not load 'c', dependency unresolved. See top entry above. +ghc-stage2.exe: ^^ Could not load 'c', dependency unresolved. See top entry above. You might consider using --optimistic-linking GHC.ByteCode.Linker: can't find label View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/11ce0b56c671b0939625cb208a48e7cf... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/11ce0b56c671b0939625cb208a48e7cf... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Magnus (@MangoIV)