mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-10 21:06:33 +03:00
Embed linker directives in ELF and Mach-O object files (#3259)
Resolves #3245 by adding `pragma(lib, <name>)` library names to `llvm.dependent-libraries` for ELF object files. For Mach-O, embed appropriate linker options for `pragma(lib)` and support generic `pragma(linkerDirective, <flag>, ...)` as well.
This commit is contained in:
parent
656a216c22
commit
82f6d4fb85
9 changed files with 128 additions and 50 deletions
|
@ -1851,25 +1851,27 @@ version (IN_LLVM)
|
||||||
const(char)* arg1str = null;
|
const(char)* arg1str = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.params.mscoff)
|
// IN_LLVM: extended pragma(linkerDirective) support - not just for COFF
|
||||||
{
|
// object files, and not restricted to a single string arg
|
||||||
if (pd.ident == Id.linkerDirective)
|
if (pd.ident == Id.linkerDirective)
|
||||||
{
|
{
|
||||||
if (!pd.args || pd.args.dim != 1)
|
if (!pd.args || pd.args.dim == 0)
|
||||||
pd.error("one string argument expected for pragma(linkerDirective)");
|
pd.error("one or more string arguments expected for pragma(linkerDirective)");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto se = semanticString(sc, (*pd.args)[0], "linker directive");
|
for (size_t i = 0; i < pd.args.dim; ++i)
|
||||||
|
{
|
||||||
|
auto se = semanticString(sc, (*pd.args)[i], "linker directive");
|
||||||
if (!se)
|
if (!se)
|
||||||
goto Lnodecl;
|
break;
|
||||||
(*pd.args)[0] = se;
|
(*pd.args)[i] = se;
|
||||||
if (global.params.verbose)
|
if (global.params.verbose)
|
||||||
message("linkopt %.*s", cast(int)se.len, se.peekString().ptr);
|
message("linkopt %.*s", cast(int)se.len, se.peekString().ptr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
goto Lnodecl;
|
goto Lnodecl;
|
||||||
}
|
}
|
||||||
}
|
else if (pd.ident == Id.msg)
|
||||||
if (pd.ident == Id.msg)
|
|
||||||
{
|
{
|
||||||
if (pd.args)
|
if (pd.args)
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,10 +101,12 @@ namespace {
|
||||||
#if LDC_LLVM_VER < 500
|
#if LDC_LLVM_VER < 500
|
||||||
/// Add the Linker Options module flag.
|
/// Add the Linker Options module flag.
|
||||||
/// If the flag is already present, merge it with the new data.
|
/// If the flag is already present, merge it with the new data.
|
||||||
void emitLinkerOptions(IRState &irs, llvm::Module &M, llvm::LLVMContext &ctx) {
|
void emitLinkerOptions(IRState &irs) {
|
||||||
|
llvm::Module &M = irs.module;
|
||||||
|
llvm::LLVMContext &ctx = irs.context();
|
||||||
if (!M.getModuleFlag("Linker Options")) {
|
if (!M.getModuleFlag("Linker Options")) {
|
||||||
M.addModuleFlag(llvm::Module::AppendUnique, "Linker Options",
|
M.addModuleFlag(llvm::Module::AppendUnique, "Linker Options",
|
||||||
llvm::MDNode::get(ctx, irs.LinkerMetadataArgs));
|
llvm::MDNode::get(ctx, irs.linkerOptions));
|
||||||
} else {
|
} else {
|
||||||
// Merge the Linker Options with the pre-existing one
|
// Merge the Linker Options with the pre-existing one
|
||||||
// (this can happen when passing a .bc file on the commandline)
|
// (this can happen when passing a .bc file on the commandline)
|
||||||
|
@ -121,10 +123,10 @@ void emitLinkerOptions(IRState &irs, llvm::Module &M, llvm::LLVMContext &ctx) {
|
||||||
|
|
||||||
// If we reach here, we found the Linker Options flag.
|
// If we reach here, we found the Linker Options flag.
|
||||||
|
|
||||||
// Add the old Linker Options to our LinkerMetadataArgs list.
|
// Add the old Linker Options to our linkerOptions list.
|
||||||
auto *oldLinkerOptions = llvm::cast<llvm::MDNode>(flag->getOperand(2));
|
auto *oldLinkerOptions = llvm::cast<llvm::MDNode>(flag->getOperand(2));
|
||||||
for (const auto &Option : oldLinkerOptions->operands()) {
|
for (const auto &Option : oldLinkerOptions->operands()) {
|
||||||
irs.LinkerMetadataArgs.push_back(Option);
|
irs.linkerOptions.push_back(Option);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace Linker Options with a newly created list.
|
// Replace Linker Options with a newly created list.
|
||||||
|
@ -132,7 +134,7 @@ void emitLinkerOptions(IRState &irs, llvm::Module &M, llvm::LLVMContext &ctx) {
|
||||||
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
|
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
|
||||||
llvm::Type::getInt32Ty(ctx), llvm::Module::AppendUnique)),
|
llvm::Type::getInt32Ty(ctx), llvm::Module::AppendUnique)),
|
||||||
llvm::MDString::get(ctx, "Linker Options"),
|
llvm::MDString::get(ctx, "Linker Options"),
|
||||||
llvm::MDNode::get(ctx, irs.LinkerMetadataArgs)};
|
llvm::MDNode::get(ctx, irs.linkerOptions)};
|
||||||
moduleFlags->setOperand(i, llvm::MDNode::get(ctx, Ops));
|
moduleFlags->setOperand(i, llvm::MDNode::get(ctx, Ops));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -140,10 +142,12 @@ void emitLinkerOptions(IRState &irs, llvm::Module &M, llvm::LLVMContext &ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/// Add the "llvm.linker.options" metadata.
|
void addLinkerMetadata(llvm::Module &M, const char *name,
|
||||||
/// If the metadata is already present, merge it with the new data.
|
llvm::ArrayRef<llvm::MDNode *> newOperands) {
|
||||||
void emitLinkerOptions(IRState &irs, llvm::Module &M, llvm::LLVMContext &ctx) {
|
if (newOperands.empty())
|
||||||
auto *linkerOptionsMD = M.getOrInsertNamedMetadata("llvm.linker.options");
|
return;
|
||||||
|
|
||||||
|
llvm::NamedMDNode *node = M.getOrInsertNamedMetadata(name);
|
||||||
|
|
||||||
// Add the new operands in front of the existing ones, such that linker
|
// Add the new operands in front of the existing ones, such that linker
|
||||||
// options of .bc files passed on the cmdline are put _after_ the compiled .d
|
// options of .bc files passed on the cmdline are put _after_ the compiled .d
|
||||||
|
@ -151,17 +155,25 @@ void emitLinkerOptions(IRState &irs, llvm::Module &M, llvm::LLVMContext &ctx) {
|
||||||
|
|
||||||
// Temporarily store metadata nodes that are already present
|
// Temporarily store metadata nodes that are already present
|
||||||
llvm::SmallVector<llvm::MDNode *, 5> oldMDNodes;
|
llvm::SmallVector<llvm::MDNode *, 5> oldMDNodes;
|
||||||
for (auto *MD : linkerOptionsMD->operands())
|
for (auto *MD : node->operands())
|
||||||
oldMDNodes.push_back(MD);
|
oldMDNodes.push_back(MD);
|
||||||
|
|
||||||
// Clear the list and add the new metadata nodes.
|
// Clear the list and add the new metadata nodes.
|
||||||
linkerOptionsMD->clearOperands();
|
node->clearOperands();
|
||||||
for (auto *MD : irs.LinkerMetadataArgs)
|
for (auto *MD : newOperands)
|
||||||
linkerOptionsMD->addOperand(MD);
|
node->addOperand(MD);
|
||||||
|
|
||||||
// Re-add metadata nodes that were already present
|
// Re-add metadata nodes that were already present
|
||||||
for (auto *MD : oldMDNodes)
|
for (auto *MD : oldMDNodes)
|
||||||
linkerOptionsMD->addOperand(MD);
|
node->addOperand(MD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the "llvm.{linker.options,dependent-libraries}" metadata.
|
||||||
|
/// If the metadata is already present, merge it with the new data.
|
||||||
|
void emitLinkerOptions(IRState &irs) {
|
||||||
|
llvm::Module &M = irs.module;
|
||||||
|
addLinkerMetadata(M, "llvm.linker.options", irs.linkerOptions);
|
||||||
|
addLinkerMetadata(M, "llvm.dependent-libraries", irs.linkerDependentLibs);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -253,7 +265,7 @@ void CodeGenerator::writeAndFreeLLModule(const char *filename) {
|
||||||
generateBitcodeForDynamicCompile(ir_);
|
generateBitcodeForDynamicCompile(ir_);
|
||||||
|
|
||||||
emitLLVMUsedArray(*ir_);
|
emitLLVMUsedArray(*ir_);
|
||||||
emitLinkerOptions(*ir_, ir_->module, ir_->context());
|
emitLinkerOptions(*ir_);
|
||||||
|
|
||||||
// Issue #1829: make sure all replaced global variables are replaced
|
// Issue #1829: make sure all replaced global variables are replaced
|
||||||
// everywhere.
|
// everywhere.
|
||||||
|
|
|
@ -433,21 +433,23 @@ public:
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static llvm::StringRef getPragmaStringArg(PragmaDeclaration *decl) {
|
static llvm::StringRef getPragmaStringArg(PragmaDeclaration *decl,
|
||||||
assert(decl->args && decl->args->length == 1);
|
d_size_t i = 0) {
|
||||||
Expression *e = (*decl->args)[0];
|
assert(decl->args && decl->args->length > i);
|
||||||
assert(e->op == TOKstring);
|
auto se = (*decl->args)[i]->isStringExp();
|
||||||
StringExp *se = static_cast<StringExp *>(e);
|
assert(se);
|
||||||
DString str = se->peekString();
|
DString str = se->peekString();
|
||||||
return {str.ptr, str.length};
|
return {str.ptr, str.length};
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(PragmaDeclaration *decl) override {
|
void visit(PragmaDeclaration *decl) override {
|
||||||
|
const auto &triple = *global.params.targetTriple;
|
||||||
|
|
||||||
if (decl->ident == Id::lib) {
|
if (decl->ident == Id::lib) {
|
||||||
assert(!irs->dcomputetarget);
|
assert(!irs->dcomputetarget);
|
||||||
llvm::StringRef name = getPragmaStringArg(decl);
|
llvm::StringRef name = getPragmaStringArg(decl);
|
||||||
|
|
||||||
if (global.params.targetTriple->isWindowsGNUEnvironment()) {
|
if (triple.isWindowsGNUEnvironment()) {
|
||||||
if (name.endswith(".lib")) {
|
if (name.endswith(".lib")) {
|
||||||
// On MinGW, strip the .lib suffix, if any, to improve compatibility
|
// On MinGW, strip the .lib suffix, if any, to improve compatibility
|
||||||
// with code written for DMD (we pass the name to GCC via -l, just as
|
// with code written for DMD (we pass the name to GCC via -l, just as
|
||||||
|
@ -463,10 +465,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// With LLVM 3.3 or later we can place the library name in the object
|
if (triple.isWindowsMSVCEnvironment()) {
|
||||||
// file. This seems to be supported only on Windows.
|
|
||||||
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
|
|
||||||
// Win32: /DEFAULTLIB:"curl"
|
|
||||||
if (name.endswith(".a")) {
|
if (name.endswith(".a")) {
|
||||||
name = name.drop_back(2);
|
name = name.drop_back(2);
|
||||||
}
|
}
|
||||||
|
@ -474,12 +473,10 @@ public:
|
||||||
name = name.drop_back(4);
|
name = name.drop_back(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// embed linker directive in COFF object file; don't push to
|
||||||
|
// global.params.linkswitches
|
||||||
std::string arg = ("/DEFAULTLIB:\"" + name + "\"").str();
|
std::string arg = ("/DEFAULTLIB:\"" + name + "\"").str();
|
||||||
|
gIR->addLinkerOption(llvm::StringRef(arg));
|
||||||
// Embed library name as linker option in object file
|
|
||||||
auto Value = llvm::MDString::get(gIR->context(), arg);
|
|
||||||
gIR->LinkerMetadataArgs.push_back(
|
|
||||||
llvm::MDNode::get(gIR->context(), Value));
|
|
||||||
} else {
|
} else {
|
||||||
size_t const n = name.size() + 3;
|
size_t const n = name.size() + 3;
|
||||||
char *arg = static_cast<char *>(mem.xmalloc(n));
|
char *arg = static_cast<char *>(mem.xmalloc(n));
|
||||||
|
@ -488,14 +485,25 @@ public:
|
||||||
memcpy(arg + 2, name.data(), name.size());
|
memcpy(arg + 2, name.data(), name.size());
|
||||||
arg[n - 1] = 0;
|
arg[n - 1] = 0;
|
||||||
global.params.linkswitches.push(arg);
|
global.params.linkswitches.push(arg);
|
||||||
|
|
||||||
|
if (triple.isOSBinFormatMachO()) {
|
||||||
|
// embed linker directive in Mach-O object file too
|
||||||
|
gIR->addLinkerOption(llvm::StringRef(arg));
|
||||||
|
} else if (triple.isOSBinFormatELF()) {
|
||||||
|
// embed library name as dependent library in ELF object file too
|
||||||
|
// (supported by LLD v9+)
|
||||||
|
gIR->addLinkerDependentLib(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (decl->ident == Id::linkerDirective) {
|
} else if (decl->ident == Id::linkerDirective) {
|
||||||
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
|
// embed in object file (if supported)
|
||||||
// Embed directly as linker option in object file
|
if (triple.isWindowsMSVCEnvironment() || triple.isOSBinFormatMachO()) {
|
||||||
llvm::StringRef directive = getPragmaStringArg(decl);
|
assert(decl->args);
|
||||||
auto Value = llvm::MDString::get(gIR->context(), directive);
|
llvm::SmallVector<llvm::StringRef, 2> args;
|
||||||
gIR->LinkerMetadataArgs.push_back(
|
args.reserve(decl->args->length);
|
||||||
llvm::MDNode::get(gIR->context(), Value));
|
for (d_size_t i = 0; i < decl->args->length; ++i)
|
||||||
|
args.push_back(getPragmaStringArg(decl, i));
|
||||||
|
gIR->addLinkerOption(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
visit(static_cast<AttribDeclaration *>(decl));
|
visit(static_cast<AttribDeclaration *>(decl));
|
||||||
|
|
|
@ -192,6 +192,21 @@ void IRState::setStructLiteralConstant(StructLiteralExp *sle,
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void IRState::addLinkerOption(llvm::ArrayRef<llvm::StringRef> options) {
|
||||||
|
llvm::SmallVector<llvm::Metadata *, 2> mdStrings;
|
||||||
|
mdStrings.reserve(options.size());
|
||||||
|
for (const auto &s : options)
|
||||||
|
mdStrings.push_back(llvm::MDString::get(context(), s));
|
||||||
|
linkerOptions.push_back(llvm::MDNode::get(context(), mdStrings));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRState::addLinkerDependentLib(llvm::StringRef libraryName) {
|
||||||
|
auto n = llvm::MDString::get(context(), libraryName);
|
||||||
|
linkerDependentLibs.push_back(llvm::MDNode::get(context(), n));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
IRBuilder<> *IRBuilderHelper::operator->() {
|
IRBuilder<> *IRBuilderHelper::operator->() {
|
||||||
IRBuilder<> &b = state->scope().builder;
|
IRBuilder<> &b = state->scope().builder;
|
||||||
assert(b.GetInsertBlock() != NULL);
|
assert(b.GetInsertBlock() != NULL);
|
||||||
|
|
|
@ -252,11 +252,16 @@ public:
|
||||||
|
|
||||||
/// Vector of options passed to the linker as metadata in object file.
|
/// Vector of options passed to the linker as metadata in object file.
|
||||||
#if LDC_LLVM_VER >= 500
|
#if LDC_LLVM_VER >= 500
|
||||||
llvm::SmallVector<llvm::MDNode *, 5> LinkerMetadataArgs;
|
llvm::SmallVector<llvm::MDNode *, 5> linkerOptions;
|
||||||
|
llvm::SmallVector<llvm::MDNode *, 5> linkerDependentLibs;
|
||||||
#else
|
#else
|
||||||
llvm::SmallVector<llvm::Metadata *, 5> LinkerMetadataArgs;
|
llvm::SmallVector<llvm::Metadata *, 5> linkerOptions;
|
||||||
|
llvm::SmallVector<llvm::Metadata *, 5> linkerDependentLibs;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void addLinkerOption(llvm::ArrayRef<llvm::StringRef> options);
|
||||||
|
void addLinkerDependentLib(llvm::StringRef libraryName);
|
||||||
|
|
||||||
// MS C++ compatible type descriptors
|
// MS C++ compatible type descriptors
|
||||||
llvm::DenseMap<size_t, llvm::StructType *> TypeDescriptorTypeMap;
|
llvm::DenseMap<size_t, llvm::StructType *> TypeDescriptorTypeMap;
|
||||||
llvm::DenseMap<llvm::Constant *, llvm::GlobalVariable *> TypeDescriptorMap;
|
llvm::DenseMap<llvm::Constant *, llvm::GlobalVariable *> TypeDescriptorMap;
|
||||||
|
|
|
@ -245,8 +245,7 @@ void DtoDefineNakedFunction(FuncDeclaration *fd) {
|
||||||
// Embed a linker switch telling the MS linker to export the naked function.
|
// Embed a linker switch telling the MS linker to export the naked function.
|
||||||
// This mimics the effect of the dllexport attribute for regular functions.
|
// This mimics the effect of the dllexport attribute for regular functions.
|
||||||
const auto linkerSwitch = std::string("/EXPORT:") + mangle;
|
const auto linkerSwitch = std::string("/EXPORT:") + mangle;
|
||||||
auto Value = llvm::MDString::get(gIR->context(), linkerSwitch);
|
gIR->addLinkerOption(llvm::StringRef(linkerSwitch));
|
||||||
gIR->LinkerMetadataArgs.push_back(llvm::MDNode::get(gIR->context(), Value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gIR->funcGenStates.pop_back();
|
gIR->funcGenStates.pop_back();
|
||||||
|
|
11
tests/codegen/linker_directives_linux.d
Normal file
11
tests/codegen/linker_directives_linux.d
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
|
||||||
|
|
||||||
|
// REQUIRES: atleast_llvm500, target_X86
|
||||||
|
|
||||||
|
// CHECK: !llvm.dependent-libraries = !{!0}
|
||||||
|
// CHECK: !0 = !{!"mylib"}
|
||||||
|
pragma(lib, "mylib");
|
||||||
|
|
||||||
|
// silently ignored because not (yet?) embeddable in ELF object file:
|
||||||
|
pragma(linkerDirective, "-myflag");
|
||||||
|
pragma(linkerDirective, "-framework", "CoreFoundation");
|
13
tests/codegen/linker_directives_mac.d
Normal file
13
tests/codegen/linker_directives_mac.d
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// RUN: %ldc -mtriple=x86_64-apple-darwin -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
|
||||||
|
|
||||||
|
// REQUIRES: atleast_llvm500, target_X86
|
||||||
|
|
||||||
|
// CHECK: !llvm.linker.options = !{!0, !1, !2}
|
||||||
|
|
||||||
|
// CHECK: !0 = !{!"-lmylib"}
|
||||||
|
pragma(lib, "mylib");
|
||||||
|
|
||||||
|
// CHECK: !1 = !{!"-myflag"}
|
||||||
|
pragma(linkerDirective, "-myflag");
|
||||||
|
// CHECK: !2 = !{!"-framework", !"CoreFoundation"}
|
||||||
|
pragma(linkerDirective, "-framework", "CoreFoundation");
|
13
tests/codegen/linker_directives_win.d
Normal file
13
tests/codegen/linker_directives_win.d
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// RUN: %ldc -mtriple=x86_64-pc-windows-msvc -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
|
||||||
|
|
||||||
|
// REQUIRES: atleast_llvm500, target_X86
|
||||||
|
|
||||||
|
// CHECK: !llvm.linker.options = !{!0, !1, !2}
|
||||||
|
|
||||||
|
// CHECK: !0 = !{!"/DEFAULTLIB:\22mylib\22"}
|
||||||
|
pragma(lib, "mylib");
|
||||||
|
|
||||||
|
// CHECK: !1 = !{!"-myflag"}
|
||||||
|
pragma(linkerDirective, "-myflag");
|
||||||
|
// CHECK: !2 = !{!"-framework", !"CoreFoundation"}
|
||||||
|
pragma(linkerDirective, "-framework", "CoreFoundation");
|
Loading…
Add table
Add a link
Reference in a new issue