From 836a4a8f8fc9a06e2f911bcdaa78664b40a2ab5f Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Fri, 29 Sep 2023 14:34:47 +0100 Subject: [PATCH] Use the system linker for linking symbols libraries outside Windows. Builtin lld is giving some weird linking errors when linking with the new LLVM version. We probably need to set some custom options. Using the system linker should be a better idea anyway, more robust and future proof. --- src/CppParser/Parser.cpp | 3 +- src/Generator/Driver.cs | 1 - src/Generator/Passes/GenerateSymbolsPass.cs | 75 ++++++++-- src/Parser/LinkerOptions.cs | 158 +++++++++++++++++++- 4 files changed, 219 insertions(+), 18 deletions(-) diff --git a/src/CppParser/Parser.cpp b/src/CppParser/Parser.cpp index 048dbbf416..f784383cb7 100644 --- a/src/CppParser/Parser.cpp +++ b/src/CppParser/Parser.cpp @@ -4851,8 +4851,7 @@ ParserResult* Parser::Compile(const std::string& File) const llvm::Triple Triple = c->getTarget().getTriple(); llvm::StringRef Dir(llvm::sys::path::parent_path(File)); llvm::SmallString<1024> Object(Dir); - llvm::sys::path::append(Object, - (Triple.isOSWindows() ? "" : "lib") + Stem + ".o"); + llvm::sys::path::append(Object, Stem + ".o"); c->getFrontendOpts().OutputFile = std::string(Object); llvm::LLVMContext context; diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 82780bfa2c..2da4fad808 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -87,7 +87,6 @@ public void Setup() ValidateOptions(); ParserOptions.Setup(Platform.Host); Context = new BindingContext(Options, ParserOptions); - Context.LinkerOptions.Setup(ParserOptions.TargetTriple, ParserOptions.LanguageVersion); Generator = CreateGeneratorFromKind(Options.GeneratorKind); } diff --git a/src/Generator/Passes/GenerateSymbolsPass.cs b/src/Generator/Passes/GenerateSymbolsPass.cs index 385771ae36..41d7b5473d 100644 --- a/src/Generator/Passes/GenerateSymbolsPass.cs +++ b/src/Generator/Passes/GenerateSymbolsPass.cs @@ -63,19 +63,7 @@ private void GenerateSymbols() new[] { module }).SelectMany(d => d.Libraries)) linkerOptions.AddLibraries(library); - using (var result = Parser.ClangParser.Build( - Context.ParserOptions, linkerOptions, path, - Last: remainingCompilationTasks == 1)) - { - if (PrintDiagnostics(result)) - { - compiledLibraries[module] = new CompiledLibrary - { - OutputDir = Options.OutputDir, - Library = module.SymbolsLibraryName - }; - } - } + compiledLibraries[module] = Build(linkerOptions, path, module); } } @@ -83,6 +71,67 @@ private void GenerateSymbols() } } + private CompiledLibrary Build(LinkerOptions linkerOptions, string path, Module module) + { + var useBuiltinToolchain = Platform.IsWindows; + if (useBuiltinToolchain) + { + using var result = Parser.ClangParser.Build( + Context.ParserOptions, linkerOptions, path, + Last: remainingCompilationTasks == 1); + + if (!PrintDiagnostics(result)) + return null; + } + else + { + using var result = Parser.ClangParser.Compile(Context.ParserOptions, path); + if (result != null) + { + if (!PrintDiagnostics(result)) + return null; + } + + LinkerOptions.UseCompilerDriverAsLinker = true; + + linkerOptions.Setup(Context.ParserOptions.TargetTriple, Context.ParserOptions.LanguageVersion); + linkerOptions.AddArguments("-L" + Path.GetDirectoryName(path)); + + var objectFile = Path.ChangeExtension(path, "o"); + linkerOptions.AddArguments(objectFile); + + var targetPlatform = Options.Compilation.Platform.GetValueOrDefault(Platform.Host); + var sharedObjectFile = LinkerOptions.GetSharedObjectName(path, targetPlatform); + linkerOptions.AddArguments("-o " + sharedObjectFile); + linkerOptions.SetupLibraryArguments(); + + var linker = LinkerOptions.GetLinkerExecutableName(targetPlatform); + var invocation = linkerOptions.GetLinkerInvocation(); + + Diagnostics.Message($"Linking library {Path.GetFileName(sharedObjectFile)}..."); + if (Options.Verbose) + Diagnostics.Message($"Invoking the linker with: {linker} {invocation}"); + + var outMessage = ProcessHelper.Run( + linker, invocation, out var errorCode, out var errorMessage); + + if (errorCode != 0) + { + Diagnostics.Error($"Linking failed with: {outMessage} {errorMessage}"); + } + else + { + Diagnostics.Message($"Linking success."); + } + } + + return new CompiledLibrary + { + OutputDir = Options.OutputDir, + Library = module.SymbolsLibraryName + }; + } + public override bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecialization specialization) { if (!specialization.IsGenerated || diff --git a/src/Parser/LinkerOptions.cs b/src/Parser/LinkerOptions.cs index 6fab1a24a2..c460487aa8 100644 --- a/src/Parser/LinkerOptions.cs +++ b/src/Parser/LinkerOptions.cs @@ -1,4 +1,8 @@ -using System.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; namespace CppSharp.Parser { @@ -8,6 +12,8 @@ public LinkerOptions() { } + public static bool UseCompilerDriverAsLinker = true; + public LinkerOptions(LinkerOptions other) { for (uint i = 0; i < other.ArgumentsCount; i++) @@ -45,7 +51,7 @@ public void Setup(string triple, LanguageVersion? languageVersion) AddArguments("-L" + (SystemLibraryPath ?? "/usr/lib/x86_64-linux-gnu")); AddArguments("-lc"); AddArguments("--shared"); - AddArguments("-rpath"); + AddArguments(UseCompilerDriverAsLinker ? "-Wl,-rpath" : "-rpath"); AddArguments("."); break; case TargetPlatform.MacOS: @@ -70,5 +76,153 @@ public void Setup(string triple, LanguageVersion? languageVersion) break; } } + + public void SetupLibraryArguments() + { + for (uint i = 0; i < LibraryDirsCount; i++) + { + var dir = GetLibraryDirs(i); + AddArguments("-L" + dir); + } + + for (uint i = 0; i < LibrariesCount; i++) + { + var lib = GetLibraries(i); + AddArguments("-l" + lib); + } + } + + public string GetLinkerInvocation() + { + var args = new List(); + for (uint i = 0; i < ArgumentsCount; i++) + { + var arg = GetArguments(i); + args.Add(arg); + } + + return string.Join(" ", args); + } + + public static string GetSharedObjectName(string path, TargetPlatform targetPlatform) + { + var prefix = GetPlatformSharedObjectPrefix(targetPlatform); + var extension = GetPlatformSharedObjectExtension(targetPlatform); + var name = $"{prefix}{Path.GetFileNameWithoutExtension(path)}.{extension}"; + return Path.Join(Path.GetDirectoryName(path), name); + } + + public static string GetLinkerExecutableName(TargetPlatform targetPlatform) + { + // If LLD exists on the PATH, then prefer it. If not, use the host linker. + var lldLinkerExe = GetLLDLinkerExecutableName(targetPlatform); + return (ExistsOnPath(lldLinkerExe) && !UseCompilerDriverAsLinker) ? + lldLinkerExe : GetPlatformLinkerExecutableName(targetPlatform); + } + + public static string GetPlatformSharedObjectPrefix(TargetPlatform targetPlatform) + { + switch (targetPlatform) + { + case TargetPlatform.Windows: + return ""; + case TargetPlatform.Linux: + case TargetPlatform.Android: + case TargetPlatform.MacOS: + case TargetPlatform.iOS: + case TargetPlatform.WatchOS: + case TargetPlatform.TVOS: + case TargetPlatform.Emscripten: + return "lib"; + default: + throw new ArgumentOutOfRangeException(nameof(targetPlatform), targetPlatform, null); + } + } + + public static string GetPlatformSharedObjectExtension(TargetPlatform targetPlatform) + { + switch (targetPlatform) + { + case TargetPlatform.Windows: + return "dll"; + case TargetPlatform.Linux: + case TargetPlatform.Android: + case TargetPlatform.Emscripten: + return "so"; + case TargetPlatform.MacOS: + case TargetPlatform.iOS: + case TargetPlatform.WatchOS: + case TargetPlatform.TVOS: + return "dylib"; + default: + throw new ArgumentOutOfRangeException(nameof(targetPlatform), targetPlatform, null); + } + } + + public static string GetPlatformLinkerExecutableName(TargetPlatform targetPlatform) + { + switch (targetPlatform) + { + case TargetPlatform.Windows: + return "link.exe"; + case TargetPlatform.Linux: + return UseCompilerDriverAsLinker ? "gcc" : "ld"; + case TargetPlatform.Android: + case TargetPlatform.MacOS: + case TargetPlatform.iOS: + case TargetPlatform.WatchOS: + case TargetPlatform.TVOS: + return "ld"; + case TargetPlatform.Emscripten: + return GetLLDLinkerExecutableName(targetPlatform); + default: + throw new ArgumentOutOfRangeException(nameof(targetPlatform), targetPlatform, null); + } + } + + public static string GetLLDLinkerExecutableName(TargetPlatform targetPlatform) + { + switch (targetPlatform) + { + case TargetPlatform.Windows: + return "lld-link"; + case TargetPlatform.Linux: + case TargetPlatform.Android: + return "ld.lld"; + case TargetPlatform.MacOS: + case TargetPlatform.iOS: + case TargetPlatform.WatchOS: + case TargetPlatform.TVOS: + return "ld64.lld"; + case TargetPlatform.Emscripten: + return "wasm-ld"; + default: + throw new ArgumentOutOfRangeException(nameof(targetPlatform), targetPlatform, null); + } + } + + private static bool ExistsOnPath(string fileName) + { + return GetFullPath(fileName) != null; + } + + private static string GetFullPath(string fileName) + { + if (fileName == null) throw new ArgumentNullException(nameof(fileName)); + if (File.Exists(fileName)) + return Path.GetFullPath(fileName); + + var environmentVariablePath = Environment.GetEnvironmentVariable("PATH"); + if (environmentVariablePath == null) + throw new NullReferenceException(nameof(environmentVariablePath)); + + foreach (var path in environmentVariablePath.Split(Path.PathSeparator)) + { + var fullPath = Path.Combine(path, fileName); + if (File.Exists(fullPath)) + return fullPath; + } + return null; + } } }