Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Posix: Move magic DSO registration code to rt.dso druntime module #3850

Merged
merged 6 commits into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .azure-pipelines/1-posix-setup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ steps:
tar -xf llvm.tar.xz --strip 1 -C llvm
# Non-Mac: make lld the default linker (possibly with enabled assertions unfortunately)
if [ "$CI_OS" != "osx" ]; then
#sudo ln -sf "$PWD/llvm/bin/ld.lld" /usr/bin/ld
# FIXME: https://github.com/ldc-developers/ldc/issues/3786
sudo ln -sf /usr/bin/ld.gold /usr/bin/ld
sudo ln -sf "$PWD/llvm/bin/ld.lld" /usr/bin/ld
fi
# Set PARALLEL_JOBS env variable and persist it for future steps
if [ "$CI_OS" = "osx" ]; then
Expand Down
6 changes: 3 additions & 3 deletions .azure-pipelines/4a-mac_x64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ steps:
CMAKE_OSX_DEPLOYMENT_TARGET=$IOS_DEPLOYMENT_TARGET \
BUILD_LTO_LIBS=ON
mkdir installed/lib-{arm64,ios-arm64,ios-x86_64}
cp -a build-libs-arm64/lib/*.{a,dylib} installed/lib-arm64
cp -a build-libs-ios-arm64/lib/*.{a,dylib} installed/lib-ios-arm64
cp -a build-libs-ios-x86_64/lib/*.{a,dylib} installed/lib-ios-x86_64
cp -a build-libs-arm64/lib/*.{a,dylib,o} installed/lib-arm64
cp -a build-libs-ios-arm64/lib/*.{a,dylib,o} installed/lib-ios-arm64
cp -a build-libs-ios-x86_64/lib/*.{a,dylib,o} installed/lib-ios-x86_64
sections="
\"arm64-apple-macos\":
{
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/supported_llvm_versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ jobs:
os: ubuntu-18.04
host_dc: ldc-beta
llvm_version: "11.1.0"
cmake_opts: "-DRT_SUPPORT_SANITIZERS=ON"
cmake_opts: "-DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON"
- job_name: Ubuntu 18.04, LLVM 9, latest LDC beta
os: ubuntu-18.04
host_dc: ldc-beta
llvm_version: "9.0.1"
cmake_opts: "-DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON"
cmake_opts: "-DBUILD_SHARED_LIBS=OFF -DRT_SUPPORT_SANITIZERS=ON"
- job_name: Ubuntu 18.04, LLVM 8, latest LDC beta
os: ubuntu-18.04
host_dc: ldc-beta
llvm_version: "8.0.0"
cmake_opts: "-DBUILD_SHARED_LIBS=OFF"
cmake_opts: "-DLIB_SUFFIX=64"
- job_name: Ubuntu 18.04, LLVM 6, latest DMD beta
os: ubuntu-18.04
host_dc: dmd-beta
llvm_version: "6.0.1"
cmake_opts: "-DLIB_SUFFIX=64 -DLDC_LINK_MANUALLY=ON"
cmake_opts: "-DLDC_LINK_MANUALLY=ON"
- job_name: macOS 10.15, LLVM 10, latest DMD beta
os: macos-10.15
host_dc: dmd-beta
Expand Down
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ install:
tar -xf llvm.tar.xz --strip 1 -C llvm || travis_terminate 1
rm llvm.tar.xz || travis_terminate 1
# Make lld the default linker (possibly with enabled assertions unfortunately)
#- sudo ln -sf "$PWD/llvm/bin/ld.lld" /usr/bin/ld
# FIXME: https://github.com/ldc-developers/ldc/issues/3786
- sudo ln -sf /usr/bin/ld.gold /usr/bin/ld
- sudo ln -sf "$PWD/llvm/bin/ld.lld" /usr/bin/ld

script:
- unset LD_LIBRARY_PATH
Expand Down
10 changes: 9 additions & 1 deletion driver/linker-gcc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,14 @@ void ArgsBuilder::build(llvm::StringRef outputPath,
args.push_back(objfile);
}

// add precompiled rt.dso object file (in lib directory) when linking
// against shared druntime
const auto &libDirs = ConfigFile::instance.libDirs();
if (!defaultLibNames.empty() && linkAgainstSharedDefaultLibs() &&
!libDirs.empty()) {
args.push_back((llvm::Twine(libDirs[0]) + "/ldc_rt.dso.o").str());
}

// Link with profile-rt library when generating an instrumented binary.
if (opts::isInstrumentingForPGO()) {
addProfileRuntimeLinkFlags(*global.params.targetTriple);
Expand Down Expand Up @@ -533,7 +541,7 @@ void ArgsBuilder::build(llvm::StringRef outputPath,
addUserSwitches();

// lib dirs
for (const char *dir_c : ConfigFile::instance.libDirs()) {
for (const char *dir_c : libDirs) {
const llvm::StringRef dir(dir_c);
if (!dir.empty())
args.push_back(("-L" + dir).str());
Expand Down
12 changes: 6 additions & 6 deletions driver/linker-msvc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,12 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath,
args.push_back(objfile);
}

// add precompiled rt.dso_windows object file (in lib directory) when linking
// add precompiled rt.dso object file (in lib directory) when linking
// against shared druntime
if (!defaultLibNames.empty() && linkAgainstSharedDefaultLibs()) {
args.push_back("dso_windows.obj");
const auto &libDirs = ConfigFile::instance.libDirs();
if (!defaultLibNames.empty() && linkAgainstSharedDefaultLibs() &&
!libDirs.empty()) {
args.push_back((llvm::Twine(libDirs[0]) + "/ldc_rt.dso.obj").str());
}

// .res/.def files
Expand Down Expand Up @@ -197,16 +199,14 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath,
}

// lib dirs
const auto &libDirs = ConfigFile::instance.libDirs();
for (const char *dir_c : libDirs) {
const llvm::StringRef dir(dir_c);
if (!dir.empty())
args.push_back(("/LIBPATH:" + dir).str());
}

if (useInternalToolchain && !libDirs.empty()) {
args.push_back(
(llvm::Twine("/LIBPATH:") + *libDirs.begin() + "/mingw").str());
args.push_back((llvm::Twine("/LIBPATH:") + libDirs[0] + "/mingw").str());
}

// default libs
Expand Down
226 changes: 25 additions & 201 deletions gen/modules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,73 +83,31 @@ void Module::checkAndAddOutputFile(const FileName &file) {
}

namespace {
/// Ways the druntime module registry system can be implemented.
/// Ways the druntime ModuleInfo registry system can be implemented.
enum class RegistryStyle {
/// Modules are inserted into a linked list starting at the _Dmodule_ref
/// global.
/// ModuleInfo refs are inserted into a linked list starting at the
/// _Dmodule_ref global.
legacyLinkedList,

/// Module references are emitted into the .minfo/__minfo section.
sectionSimple,

/// Module references are emitted into the __minfo section. Global
/// constructors/destructors make sure _d_dso_registry is invoked once per ELF
/// object.
sectionELF,

/// Module references are emitted into the .minfo section. Global
/// constructors/destructors make sure _d_dso_registry is invoked once per
/// shared object. A "TLS anchor" function to identify the TLS range
/// corresponding to this image is also passed to druntime.
sectionDarwin
/// Pointers to defined ModuleInfos are emitted into the special .minfo /
/// __minfo section. A linked binary then contains pointers to all ModuleInfos
/// of linked object files in that section.
section,
};

/// Returns the module registry style to use for the current target triple.
/// Returns the ModuleInfo registry style to use for the current target triple.
RegistryStyle getModuleRegistryStyle() {
const auto &t = *global.params.targetTriple;

if (t.isOSWindows() || t.getEnvironment() == llvm::Triple::Android ||
t.isOSBinFormatWasm()) {
return RegistryStyle::sectionSimple;
}

if (t.isOSDarwin()) {
return RegistryStyle::sectionDarwin;
}

if (t.isOSLinux() || t.isOSFreeBSD() || t.isOSNetBSD() || t.isOSOpenBSD() ||
t.isOSBinFormatWasm() || t.isOSDarwin() || t.isOSLinux() ||
t.isOSFreeBSD() || t.isOSNetBSD() || t.isOSOpenBSD() ||
t.isOSDragonFly()) {
return RegistryStyle::sectionELF;
return RegistryStyle::section;
}

return RegistryStyle::legacyLinkedList;
}

LLGlobalVariable *declareDSOGlobal(llvm::StringRef mangledName, LLType *type,
bool isThreadLocal = false) {
auto global = declareGlobal(Loc(), gIR->module, type, mangledName, false,
isThreadLocal, false);
global->setVisibility(LLGlobalValue::HiddenVisibility);
return global;
}

LLGlobalVariable *defineDSOGlobal(llvm::StringRef mangledName, LLConstant *init,
bool isThreadLocal = false) {
auto global =
defineGlobal(Loc(), gIR->module, mangledName, init,
LLGlobalValue::LinkOnceODRLinkage, false, isThreadLocal);
global->setVisibility(LLGlobalValue::HiddenVisibility);
return global;
}

LLFunction *createDSOFunction(llvm::StringRef mangledName,
LLFunctionType *type) {
auto fn = LLFunction::Create(type, LLGlobalValue::LinkOnceODRLinkage,
mangledName, &gIR->module);
fn->setVisibility(LLGlobalValue::HiddenVisibility);
return fn;
}

/// Build ModuleReference and register function, to register the module info in
/// the global linked list.
///
Expand Down Expand Up @@ -222,160 +180,26 @@ LLFunction *build_module_reference_and_ctor(const char *moduleMangle,
return ctor;
}

/// Builds a void*() function with hidden visibility that returns the address of
/// a dummy TLS global (also with hidden visibility).
///
/// The global is non-zero-initialised and aligned to 16 bytes.
llvm::Function *buildGetTLSAnchor() {
// Create a dummmy TLS global private to this module.
const auto one = llvm::ConstantInt::get(LLType::getInt8Ty(gIR->context()), 1);
const auto anchor =
defineDSOGlobal("ldc.tls_anchor", one, /*isThreadLocal=*/true);
anchor->setAlignment(LLMaybeAlign(16));

const auto getAnchor = createDSOFunction(
"ldc.get_tls_anchor", LLFunctionType::get(getVoidPtrType(), false));

IRBuilder<> builder(llvm::BasicBlock::Create(gIR->context(), "", getAnchor));
builder.CreateRet(anchor);

return getAnchor;
}

/// Builds the ldc.register_dso function, which is called by the global
/// {c, d}tors to invoke _d_dso_registry.
///
/// Pseudocode:
/// void ldc.register_dso() {
/// auto record = {1, dsoSlot, minfoBeg, minfoEnd[, getTlsAnchor]};
/// _d_dso_registry(cast(CompilerDSOData*)&record);
/// }
///
/// On Darwin platforms, the record contains an extra pointer to a function
/// which returns the address of a TLS global.
llvm::Function *buildRegisterDSO(RegistryStyle style, llvm::Value *dsoSlot,
llvm::Value *minfoBeg, llvm::Value *minfoEnd) {
const auto fn = createDSOFunction(
"ldc.register_dso",
LLFunctionType::get(LLType::getVoidTy(gIR->context()), false));

const auto dsoRegistry =
getRuntimeFunction(Loc(), gIR->module, "_d_dso_registry");
const auto recordPtrTy = dsoRegistry->getFunctionType()->getContainedType(1);

llvm::Function *getTlsAnchorPtr = nullptr;
if (style == RegistryStyle::sectionDarwin) {
getTlsAnchorPtr = buildGetTLSAnchor();
}

{
const auto bb = llvm::BasicBlock::Create(gIR->context(), "", fn);
IRBuilder<> b(bb);

llvm::Constant *version = DtoConstSize_t(1);
llvm::SmallVector<llvm::Type *, 6> memberTypes;
memberTypes.push_back(version->getType());
memberTypes.push_back(dsoSlot->getType());
memberTypes.push_back(minfoBeg->getType());
memberTypes.push_back(minfoEnd->getType());
if (style == RegistryStyle::sectionDarwin) {
memberTypes.push_back(getTlsAnchorPtr->getType());
}
llvm::StructType *stype =
llvm::StructType::get(gIR->context(), memberTypes, false);
llvm::Value *record = b.CreateAlloca(stype);

unsigned i = 0;
b.CreateStore(version, b.CreateStructGEP(stype, record, i++));
b.CreateStore(dsoSlot, b.CreateStructGEP(stype, record, i++));
b.CreateStore(minfoBeg, b.CreateStructGEP(stype, record, i++));
b.CreateStore(minfoEnd, b.CreateStructGEP(stype, record, i++));
if (style == RegistryStyle::sectionDarwin) {
b.CreateStore(getTlsAnchorPtr, b.CreateStructGEP(stype, record, i++));
}

b.CreateCall(dsoRegistry, b.CreateBitCast(record, recordPtrTy));
b.CreateRetVoid();
}

return fn;
}

void emitModuleRefToSection(RegistryStyle style, std::string moduleMangle,
// Emits a pointer to the specified ModuleInfo into the special
// .minfo (COFF & MachO) / __minfo section.
void emitModuleRefToSection(std::string moduleMangle,
llvm::Constant *thisModuleInfo) {
assert(style == RegistryStyle::sectionSimple ||
style == RegistryStyle::sectionELF ||
style == RegistryStyle::sectionDarwin);
// Only for the first D module to be emitted into this llvm::Module we need to
// create the global ctors/dtors. The magic linker symbols used to get the
// start and end of the .minfo section also only need to be emitted for the
// first D module.
// For all subsequent ones, we just need to emit an additional reference into
// the .minfo section.
const bool isFirst = !gIR->module.getGlobalVariable("ldc.dso_slot");

const auto moduleInfoPtrTy = DtoPtrToType(getModuleInfoType());
const auto moduleInfoRefsSectionName =
global.params.targetTriple->isWindowsMSVCEnvironment()

const auto &triple = *global.params.targetTriple;
const auto sectionName =
triple.isOSBinFormatCOFF()
? ".minfo"
: style == RegistryStyle::sectionDarwin ? "__DATA,.minfo" : "__minfo";
: triple.isOSBinFormatMachO() ? "__DATA,.minfo" : "__minfo";

const auto thismrefIRMangle =
getIRMangledModuleRefSymbolName(moduleMangle.c_str());
auto thismref = defineDSOGlobal(thismrefIRMangle,
DtoBitCast(thisModuleInfo, moduleInfoPtrTy));
thismref->setSection(moduleInfoRefsSectionName);
auto thismref = defineGlobal(Loc(), gIR->module, thismrefIRMangle,
DtoBitCast(thisModuleInfo, moduleInfoPtrTy),
LLGlobalValue::LinkOnceODRLinkage, false, false);
thismref->setVisibility(LLGlobalValue::HiddenVisibility);
thismref->setSection(sectionName);
gIR->usedArray.push_back(thismref);

if (!isFirst || style == RegistryStyle::sectionSimple) {
// Nothing left to do.
return;
}

// Use magic linker symbol names to obtain the begin and end of the .minfo
// section.
const auto magicBeginSymbolName = (style == RegistryStyle::sectionDarwin)
? "\1section$start$__DATA$.minfo"
: "__start___minfo";
const auto magicEndSymbolName = (style == RegistryStyle::sectionDarwin)
? "\1section$end$__DATA$.minfo"
: "__stop___minfo";
auto minfoBeg = declareDSOGlobal(magicBeginSymbolName, moduleInfoPtrTy);
auto minfoEnd = declareDSOGlobal(magicEndSymbolName, moduleInfoPtrTy);

// We want to have one global constructor and destructor per object (i.e.
// executable/shared library) that calls _d_dso_registry with the respective
// DSO record.
// To enable safe direct linking of D objects (e.g., "g++ dcode.o cppcode.o"),
// we emit a pair of global {c,d}tors into each object file, both pointing to
// a common ldc.register_dso() function.
// These per-object-file pairs will be folded to a single one when linking the
// DSO, together with the ldc.dso_slot globals and associated
// ldc.register_dso() functions.

// This is the DSO slot for use by the druntime implementation.
const auto dsoSlot =
defineDSOGlobal("ldc.dso_slot", getNullPtr(getVoidPtrType()));

const auto registerDSO = buildRegisterDSO(style, dsoSlot, minfoBeg, minfoEnd);

// We need to discard the {c,d}tor refs if this IR module's ldc.register_dso()
// function is discarded to prevent duplicate refs.
// Unfortunately, this doesn't work for macOS (v10.12, Xcode v9.2, LLVM
// v7.0.0).
if (style == RegistryStyle::sectionELF) {
llvm::appendToGlobalCtors(gIR->module, registerDSO, 65535, registerDSO);
llvm::appendToGlobalDtors(gIR->module, registerDSO, 65535, registerDSO);
return;
}

// macOS: emit the {c,d}tor refs manually
const auto dsoCtor = defineDSOGlobal("ldc.dso_ctor", registerDSO);
const auto dsoDtor = defineDSOGlobal("ldc.dso_dtor", registerDSO);
gIR->usedArray.push_back(dsoCtor);
gIR->usedArray.push_back(dsoDtor);
dsoCtor->setSection("__DATA,__mod_init_func,mod_init_funcs");
dsoDtor->setSection("__DATA,__mod_term_func,mod_term_funcs");
}

// Add module-private variables and functions for coverage analysis.
Expand Down Expand Up @@ -569,7 +393,7 @@ void registerModuleInfo(Module *m) {
const auto miCtor = build_module_reference_and_ctor(mangle, moduleInfoSym);
AppendFunctionToLLVMGlobalCtorsDtors(miCtor, 65535, true);
} else {
emitModuleRefToSection(style, mangle, moduleInfoSym);
emitModuleRefToSection(mangle, moduleInfoSym);
}
}
}
Expand Down
Loading