From f496c9550098ffaa3bf25a3447c138626d79bae0 Mon Sep 17 00:00:00 2001 From: Adam Sandberg Ericsson <adam@sandbergericsson.se> Date: Sun, 3 May 2020 11:49:46 +0100 Subject: [PATCH] add -flink-rts flag to link the rts when linking a shared or static library #18072 By default we don't link the RTS when linking shared libraries because in the most usual mode a shared library is an intermediary product, for example a Haskell library, that will be linked into some executable in the end. So we wish to defer the RTS flavour to link to the final link. However sometimes the final product is the shared library, for example when writing a plugin for some other system, so we do wish the shared library to link the RTS. For consistency we also make -staticlib honor this flag and its inversion. -staticlib currently implies -flink-shared. --- compiler/GHC/Driver/Flags.hs | 1 + compiler/GHC/Driver/Pipeline.hs | 9 ++++++++- compiler/GHC/Driver/Session.hs | 5 +++-- compiler/GHC/SysTools.hs | 4 +++- docs/users_guide/8.12.1-notes.rst | 3 +++ docs/users_guide/phases.rst | 19 ++++++++++++++++++ docs/users_guide/shared_libs.rst | 21 +++++++++++++------- testsuite/tests/dynlibs/Makefile | 32 ++++++++++++++++++++++++++++++- testsuite/tests/dynlibs/T18072.hs | 10 ++++++++++ testsuite/tests/dynlibs/all.T | 9 +++++++++ 10 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 testsuite/tests/dynlibs/T18072.hs diff --git a/compiler/GHC/Driver/Flags.hs b/compiler/GHC/Driver/Flags.hs index 93748bbc0689..a827ffe3150e 100644 --- a/compiler/GHC/Driver/Flags.hs +++ b/compiler/GHC/Driver/Flags.hs @@ -274,6 +274,7 @@ data GeneralFlag | Opt_KeepCAFs | Opt_KeepGoing | Opt_ByteCode + | Opt_LinkRts -- output style opts | Opt_ErrorSpans -- Include full span info in error messages, diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs index 15cce2f11d20..83e637401e28 100644 --- a/compiler/GHC/Driver/Pipeline.hs +++ b/compiler/GHC/Driver/Pipeline.hs @@ -1937,7 +1937,14 @@ linkStaticLib dflags o_files dep_units = do output_exists <- doesFileExist full_output_fn (when output_exists) $ removeFile full_output_fn - pkg_cfgs <- getPreloadUnitsAnd dflags dep_units + pkg_cfgs_init <- getPreloadUnitsAnd dflags dep_units + + let pkg_cfgs + | gopt Opt_LinkRts dflags + = pkg_cfgs_init + | otherwise + = filter ((/= rtsUnitId) . unitId) pkg_cfgs_init + archives <- concatMapM (collectArchives dflags) pkg_cfgs ar <- foldl mappend diff --git a/compiler/GHC/Driver/Session.hs b/compiler/GHC/Driver/Session.hs index 85eca17aed17..a558ceae96bd 100644 --- a/compiler/GHC/Driver/Session.hs +++ b/compiler/GHC/Driver/Session.hs @@ -2435,7 +2435,7 @@ dynamic_flags_deps = [ , make_ord_flag defGhcFlag "shared" (noArg (\d -> d { ghcLink=LinkDynLib })) , make_ord_flag defGhcFlag "staticlib" - (noArg (\d -> d { ghcLink=LinkStaticLib })) + (noArg (\d -> setGeneralFlag' Opt_LinkRts (d { ghcLink=LinkStaticLib }))) , make_ord_flag defGhcFlag "dynload" (hasArg parseDynLibLoaderMode) , make_ord_flag defGhcFlag "dylib-install-name" (hasArg setDylibInstallName) @@ -3599,7 +3599,8 @@ fFlagsDeps = [ flagSpec "hide-source-paths" Opt_HideSourcePaths, flagSpec "show-loaded-modules" Opt_ShowLoadedModules, flagSpec "whole-archive-hs-libs" Opt_WholeArchiveHsLibs, - flagSpec "keep-cafs" Opt_KeepCAFs + flagSpec "keep-cafs" Opt_KeepCAFs, + flagSpec "link-rts" Opt_LinkRts ] ++ fHoleFlags diff --git a/compiler/GHC/SysTools.hs b/compiler/GHC/SysTools.hs index ab83b3bf2a94..0b71e8ccdebf 100644 --- a/compiler/GHC/SysTools.hs +++ b/compiler/GHC/SysTools.hs @@ -274,7 +274,9 @@ linkDynLib dflags0 o_files dep_packages pkgs_no_rts = case os of OSMinGW32 -> pkgs - _ -> + _ | gopt Opt_LinkRts dflags -> + pkgs + | otherwise -> filter ((/= rtsUnitId) . unitId) pkgs let pkg_link_opts = let (package_hs_libs, extra_libs, other_flags) = collectLinkOpts dflags pkgs_no_rts in package_hs_libs ++ extra_libs ++ other_flags diff --git a/docs/users_guide/8.12.1-notes.rst b/docs/users_guide/8.12.1-notes.rst index 1eb577c36e7a..b7f0444d6989 100644 --- a/docs/users_guide/8.12.1-notes.rst +++ b/docs/users_guide/8.12.1-notes.rst @@ -216,6 +216,9 @@ Language Compiler ~~~~~~~~ +- A new flag :ghc-flag:`-flink-rts` to enable linking the RTS when linking + shared libraries. + GHCi ~~~~ diff --git a/docs/users_guide/phases.rst b/docs/users_guide/phases.rst index 5975962370d1..c7a17f6475bc 100644 --- a/docs/users_guide/phases.rst +++ b/docs/users_guide/phases.rst @@ -735,6 +735,8 @@ for example). :type: dynamic :category: linking + :implies: :ghc-flag:`-flink-rts` + Link all passed files into a static library suitable for linking. To control the name, use the :ghc-flag:`-o ⟨file⟩` option as usual. The default name is ``liba.a``. @@ -826,6 +828,23 @@ for example). libraries at runtime. See :ref:`finding-shared-libs` for a description of each mode. +.. ghc-flag:: -flink-rts + :shortdesc: Link the runtime when generating a shared or static library + :type: dynamic + :category: linking + + When linking shared libraries (:ghc-flag:`-shared`) GHC does not + automatically link the RTS. This is to allow choosing the RTS flavour + (:ghc-flag:`-threaded`, :ghc-flag:`-eventlog`, etc) when linking an + executable. + However when the shared library is the intended product it is useful to be + able to reverse this default. See :ref:`shared-libraries-c-api` for an + usage example. + + When linking a static library (:ghc-flag:`-staticlib`) GHC links the RTS + automatically, you can reverse this behaviour by reversing this flag: + ``-fno-link-rts``. + .. ghc-flag:: -main-is ⟨thing⟩ :shortdesc: Set main module and function :type: dynamic diff --git a/docs/users_guide/shared_libs.rst b/docs/users_guide/shared_libs.rst index 7e525019ca37..4c9ad621f7cb 100644 --- a/docs/users_guide/shared_libs.rst +++ b/docs/users_guide/shared_libs.rst @@ -92,6 +92,8 @@ files to use the extension ``.dyn_hi``. The other requirements are the same as for C libraries and are described below, in particular the use of the flags :ghc-flag:`-dynamic`, :ghc-flag:`-fPIC` and :ghc-flag:`-shared`. +.. _shared-libraries-c-api: + Shared libraries that export a C API ------------------------------------ @@ -115,19 +117,24 @@ the :ghc-flag:`-dynamic`, :ghc-flag:`-fPIC` and :ghc-flag:`-shared` flags: .. code-block:: none - ghc --make -dynamic -shared -fPIC Foo.hs -o libfoo.so + ghc --make -dynamic -shared -fPIC -flink-rts Foo.hs -o libfoo.so As before, the :ghc-flag:`-dynamic` flag specifies that this library links -against the shared library versions of the ``rts`` and ``base`` package. The -:ghc-flag:`-fPIC` flag is required for all code that will end up in a shared -library. The :ghc-flag:`-shared` flag specifies to make a shared library rather -than a program. To make this clearer we can break this down into separate -compilation and link steps: +against the shared library versions of the ``base`` package. +:ghc-flag:`-flink-rts` additionally links against the shared library version of +the ``rts`` package (linking against the ``rts`` package is not enabled by +default when building shared libraries). You may also omit ``-flink-rts`` +and link the RTS library into your final executable. + +The :ghc-flag:`-fPIC` flag is required for all code that will end up in a +shared library. The :ghc-flag:`-shared` flag specifies to make a shared library +rather than a program. To make this clearer we can break this down into +separate compilation and link steps: .. code-block:: none ghc -dynamic -fPIC -c Foo.hs - ghc -dynamic -shared Foo.o -o libfoo.so + ghc -dynamic -shared -flink-rts Foo.o -o libfoo.so In principle you can use :ghc-flag:`-shared` without :ghc-flag:`-dynamic` in the link step. That means to statically link the runtime system and all of the base diff --git a/testsuite/tests/dynlibs/Makefile b/testsuite/tests/dynlibs/Makefile index 2a530a5aebf8..19009605eac7 100644 --- a/testsuite/tests/dynlibs/Makefile +++ b/testsuite/tests/dynlibs/Makefile @@ -14,7 +14,7 @@ T3807: # libraries. This is done to allow the RTS flavour to be chosen later (i.e. # when linking an executable). # Hence we must explicitly linking with the RTS here. - '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 --make -dynamic -fPIC -shared T3807Export.hs T3807-export.c -o T3807test.so -lHSrts-ghc`'$(TEST_HC)' $(TEST_HC_OPTS) --numeric-version` + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 --make -dynamic -fPIC -shared T3807Export.hs T3807-export.c -o T3807test.so -flink-rts '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -no-auto-link-packages -no-hs-main T3807-load.c -o T3807-load -ldl ./T3807-load @@ -61,3 +61,33 @@ T5373: T13702: '$(TEST_HC)' -v0 -dynamic -rdynamic -fPIC -pie T13702.hs ./T13702 + +.PHONY: T18072 +T18072: + $(RM) -rf T18072/ + mkdir T18072 + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072 \ + -dynamic -fPIC -c T18072.hs + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072 \ + -dynamic -shared -flink-rts T18072/T18072.o -o T18072/T18072.so + ldd T18072/T18072.so | grep libHSrts >/dev/null + +.PHONY: T18072debug +T18072debug: + $(RM) -rf T18072debug/ + mkdir T18072debug + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072debug \ + -dynamic -fPIC -c T18072.hs + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072debug \ + -dynamic -shared -flink-rts -debug T18072debug/T18072.o -o T18072debug/T18072.so + ldd T18072debug/T18072.so | grep libHSrts | grep _debug >/dev/null + +.PHONY: T18072static +T18072static: + $(RM) -rf T18072static/ + mkdir T18072static + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072static \ + -c T18072.hs + '$(TEST_HC)' $(filter-out -rtsopts,$(TEST_HC_OPTS)) -v0 -outputdir T18072static \ + -staticlib -fno-link-rts T18072static/T18072.o -o T18072static/T18072.a + ar t T18072static/T18072.a | grep RtsSymbols.o > /dev/null && exit 1 || exit 0 diff --git a/testsuite/tests/dynlibs/T18072.hs b/testsuite/tests/dynlibs/T18072.hs new file mode 100644 index 000000000000..39a174f2aef6 --- /dev/null +++ b/testsuite/tests/dynlibs/T18072.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE ForeignFunctionInterface #-} + +module T18072 where + +import Foreign.C + +foo :: IO CInt +foo = return 3 + +foreign export ccall foo :: IO CInt diff --git a/testsuite/tests/dynlibs/all.T b/testsuite/tests/dynlibs/all.T index aaa7a6277479..b7272d4bac26 100644 --- a/testsuite/tests/dynlibs/all.T +++ b/testsuite/tests/dynlibs/all.T @@ -7,3 +7,12 @@ test('T5373', [req_shared_libs], makefile_test, []) # It's not clear exactly what platforms we can expect this to succeed on. test('T13702', unless(opsys('linux'), skip), makefile_test, []) + +# test that -shared and -flink-rts actually links the rts +test('T18072', [req_shared_libs, unless(opsys('linux'), skip)], makefile_test, []) + +# test that -shared and -flink-rts respects alternative RTS flavours +test('T18072debug', [extra_files(['T18072.hs']), req_shared_libs, unless(opsys('linux'), skip)], makefile_test, []) + +# check that -staticlib and -fno-link-rts results in an archive without the RTR libary +test('T18072static', [extra_files(['T18072.hs']), unless(opsys('linux'), skip)], makefile_test, []) -- GitLab