blob: aa71989094a89cd06982bd8459ff0fc997928f3d [file] [log] [blame]
// Copyright 2020 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "compiler/plugins/target/LLVMCPU/LinkerTool.h"
#include "iree/compiler/Utils/ToolUtils.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
#define DEBUG_TYPE "llvm-linker"
namespace mlir::iree_compiler::IREE::HAL {
// Unix linker (ld-like); for ELF files.
class UnixLinkerTool : public LinkerTool {
public:
using LinkerTool::LinkerTool;
std::string getSystemToolPath() const override {
// First check for setting the linker explicitly.
auto toolPath = LinkerTool::getSystemToolPath();
if (!toolPath.empty())
return toolPath;
// No explicit linker specified, search the environment for common tools.
// We want LLD:
// * On Apple, we want the system linker, which is named `ld`
if (targetIsApple()) {
// On macOS, the standard system linker is `ld`, and it's
// unconditionally what we want to use.
toolPath = findToolInEnvironment({"ld"});
} else {
// On Linux, the only linker basename that's standard is `ld` but it could
// be any of ld.bfd, ld.gold, ld.lld, which are inequivalent in the way
// explained in the comment below on the -shared flag. We specifically
// want ld.lld here, however we still search for `ld` as a fallback name,
// in case the linker would be ld.lld but would be installed only under
// the name `ld`.
//
// Having `ld` as a fallback name also makes sense (at least
// theoretically) on "generic Unix": `ld` is the standard name of the
// system linker, and `-static -shared` should in theory be supported by
// the system linker (as suggested by both the FreeBSD and GNU man pages
// for ld).
//
// On the other hand, on Linux where the possible fallbacks are ld.bfd or
// ld.gold, we are specifically not interested in falling back on any
// of these, at least given current behavior.
toolPath = findToolInEnvironment({"ld.lld", "ld"});
}
if (!toolPath.empty())
return toolPath;
llvm::errs() << "No Unix linker tool found in environment.\n";
return "";
}
std::optional<Artifacts>
linkDynamicLibrary(StringRef libraryName,
ArrayRef<Artifact> objectFiles) override {
Artifacts artifacts;
// Create the shared object name; if we only have a single input object we
// can just reuse that.
if (objectFiles.size() == 1) {
artifacts.libraryFile =
Artifact::createVariant(objectFiles.front().path, "so");
} else {
artifacts.libraryFile = Artifact::createTemporary(libraryName, "so");
}
artifacts.libraryFile.close();
SmallVector<std::string, 8> flags = {
getSystemToolPath(),
"-o " + artifacts.libraryFile.path,
};
if (targetIsApple()) {
// Statically link all dependencies so we don't have any runtime deps.
// We cannot have any imports in the module we produce.
flags.push_back("-static");
// Produce a Mach-O dylib file.
flags.push_back("-dylib");
flags.push_back("-flat_namespace");
flags.push_back(
"-L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib "
"-lSystem");
} else {
// Avoids including any libc/startup files that initialize the CRT as
// we don't use any of that. Our shared libraries must be freestanding.
flags.push_back("-nostdlib"); // -nodefaultlibs + -nostartfiles
// Statically link all dependencies so we don't have any runtime deps.
// We cannot have any imports in the module we produce.
flags.push_back("-static");
// Generate a dynamic library (ELF type: ET_DYN), otherwise dlopen()
// won't succeed on it. This is not incompatible with -static. The GNU
// man page for ld, `man ld`, says the following:
//
// -static
// Do not link against shared libraries. [...] This option can be
// used with -shared. Doing so means that a shared library is
// being created but that all of the library's external references
// must be resolved by pulling in entries from static libraries.
//
// While that much is said in the GNU ld man page, the reality is that
// out of ld.bfd, ld.gold and ld.lld, only ld.lld actually implements
// that. Meanwhile, ld.bfd interprets -static -shared as just -static,
// and ld.gold rejects -static -shared outright as "incompatible".
flags.push_back("-shared");
}
// Strip debug information (only, no relocations) when not requested.
if (!targetOptions.target.debugSymbols) {
flags.push_back("--strip-debug");
}
// Link all input objects. Note that we are not linking whole-archive as
// we want to allow dropping of unused codegen outputs.
for (auto &objectFile : objectFiles) {
flags.push_back(objectFile.path);
}
auto commandLine = llvm::join(flags, " ");
if (failed(runLinkCommand(commandLine)))
return std::nullopt;
return artifacts;
}
private:
bool targetIsApple() const {
return targetTriple.isOSDarwin() || targetTriple.isiOS();
}
};
std::unique_ptr<LinkerTool>
createUnixLinkerTool(const llvm::Triple &targetTriple,
LLVMTargetOptions &targetOptions) {
return std::make_unique<UnixLinkerTool>(targetTriple, targetOptions);
}
} // namespace mlir::iree_compiler::IREE::HAL