Skip to content

Commit

Permalink
Use the system linker for linking symbols libraries outside Windows.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tritao committed Sep 29, 2023
1 parent 78ad409 commit a886484
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 18 deletions.
3 changes: 1 addition & 2 deletions src/CppParser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion src/Generator/Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
74 changes: 61 additions & 13 deletions src/Generator/Passes/GenerateSymbolsPass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,74 @@ 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);
}
}

RemainingCompilationTasks--;
}
}

private CompiledLibrary Build(LinkerOptions linkerOptions, string path, Module module)
{
var useBuiltinToolchain = Platform.IsWindows;
if (useBuiltinToolchain)
{
linkerOptions.Setup(Context.ParserOptions.TargetTriple, Context.ParserOptions.LanguageVersion);
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.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 ||
Expand Down
158 changes: 156 additions & 2 deletions src/Parser/LinkerOptions.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -8,6 +12,8 @@ public LinkerOptions()
{
}

public static bool UseCompilerDriverAsLinker = true;

public LinkerOptions(LinkerOptions other)
{
for (uint i = 0; i < other.ArgumentsCount; i++)
Expand Down Expand Up @@ -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:
Expand All @@ -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<string>();
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;
}
}
}

0 comments on commit a886484

Please sign in to comment.