Windows: Make implicit dllimport more selective

* Newly require `-link-defaultlib-shared` for implicit dllimport.
  E.g., this enables to compile druntime DLL with `-fvisibility=public`
  for pure exports and no (local) imports (such as builtin TypeInfos).
* `-link-defaultlib-shared` alone now only implicitly imports symbols
  from druntime/Phobos.
  This simplifies building complex DLLs linked against a bunch of
  static libs (dub only supports static lib dependencies!); the static
  libs don't need to be compiled with `-fvisibility=public` anymore
  (if the DLL itself isn't either), `-link-defaultlib-shared` is
  sufficient.
  This is mainly useful for existing DLLs with explicit exports, to make
  them link against *shared* druntime/Phobos and so end up with a single
  druntime/Phobos for the whole process.
This commit is contained in:
Martin Kinkelin 2021-06-14 15:27:34 +02:00
parent 2f0ece3274
commit 9865e459d1
15 changed files with 73 additions and 23 deletions

View file

@ -109,6 +109,14 @@ enum FeatureState : byte
enabled = 1 /// Specified as `-preview=`
}
version (IN_LLVM)
enum DLLImport : byte
{
none,
defaultLibsOnly, // only symbols from druntime/Phobos
all
}
// Put command line switches in here
extern (C++) struct Param
{
@ -305,7 +313,7 @@ version (IN_LLVM)
// Windows-specific:
bool dllexport; // dllexport ~all defined symbols?
bool dllimport; // dllimport data symbols not defined in any root module?
DLLImport dllimport; // dllimport data symbols not defined in any root module?
} // IN_LLVM
}

View file

@ -98,6 +98,15 @@ enum class FeatureState : signed char
enabled = 1 /// Specified as `-preview=`
};
#if IN_LLVM
enum class DLLImport : char
{
none,
defaultLibsOnly, // only symbols from druntime/Phobos
all
};
#endif
// Put command line switches in here
struct Param
{
@ -280,7 +289,7 @@ struct Param
// Windows-specific:
bool dllexport; // dllexport ~all defined symbols?
bool dllimport; // dllimport data symbols not defined in any root module?
DLLImport dllimport; // dllimport data symbols not defined in any root module?
#endif
};

View file

@ -44,6 +44,9 @@ struct Id
static Identifier *dcompute;
static Identifier *dcPointer;
static Identifier *object;
static Identifier *core;
static Identifier *etc;
static Identifier *std;
static Identifier *ensure;
static Identifier *require;
static Identifier *xopEquals;

View file

@ -1090,10 +1090,10 @@ int cppmain() {
v == opts::SymbolVisibility::public_ ||
// default with -shared
(v == opts::SymbolVisibility::default_ && global.params.dll);
global.params.dllimport =
v == opts::SymbolVisibility::public_ ||
// enforced when linking against shared default libs
linkAgainstSharedDefaultLibs();
global.params.dllimport = !linkAgainstSharedDefaultLibs() ? DLLImport::none
: v == opts::SymbolVisibility::public_
? DLLImport::all
: DLLImport::defaultLibsOnly;
}
// allocate the target abi

View file

@ -1670,6 +1670,22 @@ std::string llvmTypeToString(llvm::Type *type) {
return result;
}
bool isDefaultLibSymbol(Dsymbol *sym) {
auto mod = sym->getModule();
if (!mod)
return false;
auto md = mod->md;
if (!md)
return false;
if (md->packages.length == 0)
return md->id == Id::object;
auto p = md->packages.ptr[0];
return p == Id::core || p == Id::std || p == Id::etc || p == Id::ldc;
}
llvm::GlobalVariable *declareGlobal(const Loc &loc, llvm::Module &module,
llvm::Type *type,
llvm::StringRef mangledName,

View file

@ -245,6 +245,9 @@ LLConstant *toConstantArray(LLType *ct, LLArrayType *at, T *str, size_t len,
llvm::Constant *buildStringLiteralConstant(StringExp *se, bool zeroTerm);
/// Is the specified symbol part of druntime/Phobos?
bool isDefaultLibSymbol(Dsymbol *sym);
/// Tries to declare an LLVM global. If a variable with the same mangled name
/// already exists, checks if the types match and returns it instead.
///

View file

@ -190,8 +190,9 @@ LLFunction *build_module_reference_and_ctor(const char *moduleMangle,
LLConstant *mref = gIR->module.getNamedGlobal(mrefIRMangle);
LLType *modulerefPtrTy = getPtrToType(modulerefTy);
if (!mref) {
mref = declareGlobal(Loc(), gIR->module, modulerefPtrTy, mrefIRMangle,
false, false, global.params.dllimport);
mref =
declareGlobal(Loc(), gIR->module, modulerefPtrTy, mrefIRMangle, false,
false, global.params.dllimport != DLLImport::none);
}
mref = DtoBitCast(mref, getPtrToType(modulerefPtrTy));

View file

@ -423,7 +423,7 @@ bool ldc_optimize_module(llvm::Module *M) {
addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel());
if (global.params.dllimport) {
if (global.params.dllimport != DLLImport::none) {
mpm.add(createDLLImportRelocationPass());
}

View file

@ -439,8 +439,7 @@ void buildTypeInfo(TypeInfoDeclaration *decl) {
// immutable on the D side, and e.g. synchronized() can be used on the
// implicit monitor.
const bool isConstant = false;
// TODO: no dllimport when compiling druntime itself
const bool useDLLImport = isBuiltin && global.params.dllimport;
bool useDLLImport = isBuiltin && global.params.dllimport != DLLImport::none;
gvar = declareGlobal(decl->loc, gIR->module, type, irMangle, isConstant,
false, useDLLImport);
}

View file

@ -56,7 +56,9 @@ bool IrAggr::useDLLImport() const {
if (!global.params.targetTriple->isOSWindows())
return false;
if (global.params.dllimport || aggrdecl->isExport()) {
if (aggrdecl->isExport() || global.params.dllimport == DLLImport::all ||
(global.params.dllimport == DLLImport::defaultLibsOnly &&
isDefaultLibSymbol(aggrdecl))) {
// dllimport, unless defined in a root module (=> no extra indirection for
// other root modules, assuming *all* root modules will be linked together
// to one or more binaries).

View file

@ -27,7 +27,11 @@ llvm::GlobalVariable *IrModule::moduleInfoSymbol() {
const auto irMangle = getIRMangledModuleInfoSymbolName(M);
const bool useDLLImport = global.params.dllimport && !M->isRoot();
bool useDLLImport = false;
if (global.params.dllimport == DLLImport::all)
useDLLImport = !M->isRoot();
else if (global.params.dllimport == DLLImport::defaultLibsOnly)
useDLLImport = !M->isRoot() && isDefaultLibSymbol(M);
moduleInfoVar = declareGlobal(Loc(), gIR->module,
llvm::StructType::create(gIR->context()),

View file

@ -85,9 +85,13 @@ void IrGlobal::declare() {
if (global.params.targetTriple->isOSWindows()) {
// dllimport isn't supported for thread-local globals (MSVC++ neither)
if (!V->isThreadlocal()) {
// with -fvisibility=public / -link-defaultlib-shared, also include all
// with -fvisibility=public / -link-defaultlib-shared, also include
// extern(D) globals
if (V->isExport() || (global.params.dllimport && V->linkage == LINK::d)) {
if (V->isExport() ||
(V->linkage == LINK::d &&
(global.params.dllimport == DLLImport::all ||
(global.params.dllimport == DLLImport::defaultLibsOnly &&
isDefaultLibSymbol(V))))) {
const bool isDefinedInRootModule =
!(V->storage_class & STCextern) && !V->inNonRoot();
if (!isDefinedInRootModule)

View file

@ -708,7 +708,7 @@ macro(build_runtime_variant d_flags c_flags ld_flags lib_suffix path_suffix emit
AND NOT ${all_d_files_at_once})
set(phobos2_o "")
set(phobos2_bc "")
compile_phobos2("${phobos2_d_flags};-relocation-model=pic;-fvisibility=public"
compile_phobos2("${phobos2_d_flags};-relocation-model=pic;-fvisibility=public;-link-defaultlib-shared"
"${lib_suffix}${SHARED_LIB_SUFFIX}" "${path_suffix}"
"${emit_bc}" "${all_d_files_at_once}" "OFF" phobos2_o phobos2_bc)
@ -761,7 +761,7 @@ macro(build_runtime_variant d_flags c_flags ld_flags lib_suffix path_suffix emit
if(phobos2_common STREQUAL "")
set(phobos2_o "")
set(phobos2_bc "")
compile_phobos2("${phobos2_d_flags};-relocation-model=pic;-fvisibility=public"
compile_phobos2("${phobos2_d_flags};-relocation-model=pic;-fvisibility=public;-link-defaultlib-shared"
"${lib_suffix}${SHARED_LIB_SUFFIX}" "${path_suffix}"
"OFF" "${all_d_files_at_once}" "${all_d_files_at_once}" phobos2_o phobos2_bc)
endif()

@ -1 +1 @@
Subproject commit 65a26b5756fd1b4f693b013e1838ac11ac2e19d4
Subproject commit c424142bbab8def67d362ac18d534c2be165d39b

View file

@ -4,11 +4,12 @@
// REQUIRES: Windows
// generate DLL and import lib
// generate DLL and import lib (public visibility by default)
// RUN: %ldc %S/inputs/fvisibility_dll_lib.d -betterC -shared -of=%t_lib.dll
// compile, link and run the app; -link-defaultlib-shared for importing data symbols as dllimport
// RUN: %ldc %s -I%S/inputs -betterC -link-defaultlib-shared %t_lib.lib -of=%t.exe
// compile, link and run the app;
// `-link-defaultlib-shared -fvisibility=public` for dllimporting data symbols
// RUN: %ldc %s -I%S/inputs -betterC -link-defaultlib-shared -fvisibility=public %t_lib.lib -of=%t.exe
// RUN: %t.exe
import fvisibility_dll_lib;