From 9d6837d595719904720e5ff68ec1f1a2665bdc2f Mon Sep 17 00:00:00 2001 From: Michael Klemm Date: Mon, 25 Dec 2023 19:15:00 +0100 Subject: [PATCH] [flang][driver] Remove Fortain_main static library from linking stages (#75816) At present, when building static or shared libraries, Flang adds `-lFortran_main.a` (or `/WHOLEARCHIVE:Fortran.*.lib` pon Windows) to the link line. This leads to the problem that `_QQmain` and `_QQEnvironmentDefaults` (as of the time of this PR) are symbols marked as used, while `main` is being defined. This should not happen and this PR fixes this by detecting if `-shared` or `-static` is used on the Flang command line and removing the static `Fortran_main` library. --------- Co-authored-by: kkwli --- clang/lib/Driver/ToolChains/CommonArgs.cpp | 20 +++++++++++ flang/docs/FlangDriver.md | 57 ++++++++++++++++++++++++++++++ flang/test/Driver/dynamic-linker.f90 | 8 +++-- 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 6eb0ed8f3fed..3b29e1bc7585 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1133,6 +1133,16 @@ static bool isWholeArchivePresent(const ArgList &Args) { return WholeArchiveActive; } +/// Determine if driver is invoked to create a shared object library (-static) +static bool isSharedLinkage(const ArgList &Args) { + return Args.hasArg(options::OPT_shared); +} + +/// Determine if driver is invoked to create a static object library (-shared) +static bool isStaticLinkage(const ArgList &Args) { + return Args.hasArg(options::OPT_static); +} + /// Add Fortran runtime libs for MSVC static void addFortranRuntimeLibsMSVC(const ArgList &Args, llvm::opt::ArgStringList &CmdArgs) { @@ -1164,6 +1174,16 @@ static void addFortranRuntimeLibsMSVC(const ArgList &Args, // Add FortranMain runtime lib static void addFortranMain(const ToolChain &TC, const ArgList &Args, llvm::opt::ArgStringList &CmdArgs) { + // 0. Shared-library linkage + // If we are attempting to link a library, we should not add + // -lFortran_main.a to the link line, as the `main` symbol is not + // required for a library and should also be provided by one of + // the translation units of the code that this shared library + // will be linked against eventually. + if (isSharedLinkage(Args) || isStaticLinkage(Args)) { + return; + } + // 1. MSVC if (TC.getTriple().isKnownWindowsMSVCEnvironment()) { addFortranRuntimeLibsMSVC(Args, CmdArgs); diff --git a/flang/docs/FlangDriver.md b/flang/docs/FlangDriver.md index 5231e78335f6..fa39889927e0 100644 --- a/flang/docs/FlangDriver.md +++ b/flang/docs/FlangDriver.md @@ -163,6 +163,63 @@ forward compiler options to the frontend driver, `flang-new -fc1`. You can read more on the design of `clangDriver` in Clang's [Driver Design & Internals](https://clang.llvm.org/docs/DriverInternals.html). +## Linker Driver +When used as a linker, Flang's frontend driver assembles the command line for an +external linker command (e.g., LLVM's `lld`) and invokes it to create the final +executable by linking static and shared libraries together with all the +translation units supplied as object files. + +By default, the Flang linker driver adds several libraries to the linker +invocation to make sure that all entrypoints for program start +(Fortran's program unit) and runtime routines can be resolved by the linker. + +An abridged example (only showing the Fortran specific linker flags, omission +indicated by `[...]`) for such a linker invocation on a Linux system would look +like this: + +``` +$ flang -v -o example example.o +"/usr/bin/ld" [...] example.o [...] "--whole-archive" "-lFortran_main" +"--no-whole-archive" "-lFortranRuntime" "-lFortranDecimal" [...] +``` + +The automatically added libraries are: + +* `Fortran_main`: Provides the main entry point `main` that then invokes + `_QQmain` with the Fortran program unit. This library has a dependency to + the `FortranRuntime` library. +* `FortranRuntime`: Provides most of the Flang runtime library. +* `FortranDecimal`: Provides operations for decimal numbers. + +The default is that, when using Flang as the linker, one of the Fortran +translation units provides the program unit and therefore it is assumed that +Fortran is the main code part (calling into C/C++ routines via `BIND (C)` +interfaces). When composing the linker commandline, Flang uses +`--whole-archive` and `--no-whole-archive` (Windows: `/WHOLEARCHIVE:`, +Darwin & AIX: *not implemented yet*) to make sure that all for `Fortran_main` +is processed by the linker. This is done to issue a proper error message when +multiple definitions of `main` occur. This happens, for instance, when linking +a code that has a Fortran program unit with a C/C++ code that also defines a +`main` function. A user may be required to explicitly provide the C++ runtime +libraries at link time (e.g., via `-lstdc++` for STL) + +If the code is C/C++ based and invokes Fortran routines, one can either use Clang +or Flang as the linker driver. If Clang is used, it will automatically all +required runtime libraries needed by C++ (e.g., for STL) to the linker invocation. +In this case, one has to explicitly provide the Fortran runtime libraries +`FortranRuntime` and/or `FortranDecimal`. An alternative is to use Flang to link +and use the `-fno-fortran-main` flag. This flag removes +`Fortran_main` from the linker stage and hence requires one of the C/C++ +translation units to provide a definition of the `main` function. In this case, +it may be required to explicitly supply C++ runtime libraries as mentioned above. + +When creating shared or static libraries using Flang with `-shared` or `-static` +flag, Fortran_main is automatically removed from the linker stage (i.e., +`-fno-fortran-main` is on by default). It is assumed that when creating a +static or shared library, the generated library does not need a `main` +function, as a final link stage will occur that will provide the `Fortran_main` +library when creating the final executable. + ## Frontend Driver Flang's frontend driver is the main interface between compiler developers and the Flang frontend. The high-level design is similar to Clang's frontend diff --git a/flang/test/Driver/dynamic-linker.f90 b/flang/test/Driver/dynamic-linker.f90 index 1cbd407d21ce..7c3f1b5a53fe 100644 --- a/flang/test/Driver/dynamic-linker.f90 +++ b/flang/test/Driver/dynamic-linker.f90 @@ -3,10 +3,12 @@ ! RUN: %flang -### --target=x86_64-linux-gnu -rpath /path/to/dir -shared \ ! RUN: -static %s 2>&1 | FileCheck \ -! RUN: --check-prefixes=GNU-LINKER-OPTIONS %s +! RUN: --check-prefixes=GNU-LINKER-OPTIONS \ +! RUN: --implicit-check-not=GNU-LINKER-OPTIONS-NOT %s ! RUN: %flang -### --target=x86_64-windows-msvc -rpath /path/to/dir -shared \ ! RUN: -static %s 2>&1 | FileCheck \ -! RUN: --check-prefixes=MSVC-LINKER-OPTIONS %s +! RUN: --check-prefixes=MSVC-LINKER-OPTIONS \ +! RUN: --implicit-check-not=MSVC-LINKER-OPTIONS-NOT %s ! RUN: %flang -### --target=aarch64-linux-none -rdynamic %s 2>&1 | FileCheck --check-prefixes=RDYNAMIC-LINKER-OPTION %s ! TODO: Could the linker have an extension or a suffix? @@ -14,6 +16,7 @@ ! GNU-LINKER-OPTIONS-SAME: "-shared" ! GNU-LINKER-OPTIONS-SAME: "-static" ! GNU-LINKER-OPTIONS-SAME: "-rpath" "/path/to/dir" +! GNU-LINKER-OPTIONS-NOT: "-lFortran_main.a" ! RDYNAMIC-LINKER-OPTION: "{{.*}}ld" ! RDYNAMIC-LINKER-OPTION-SAME: "-export-dynamic" @@ -22,3 +25,4 @@ ! MSVC-LINKER-OPTIONS: "{{.*}}link{{(.exe)?}}" ! MSVC-LINKER-OPTIONS-SAME: "-dll" ! MSVC-LINKER-OPTIONS-SAME: "-rpath" "/path/to/dir" +! MSVC-LINKER-OPTIONS-NOT: "/WHOLEARCHIVE:Fortran_main" -- 2.11.4.GIT