Bug 23974 – A ModuleInfo in a separate Windows DLL should not be referred to by MIimportedModules

Status
NEW
Severity
enhancement
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
Windows
Creation time
2023-06-05T22:01:45Z
Last change time
2024-12-13T19:29:34Z
Keywords
dll, pull
Assigned to
No Owner
Creator
Walter Bright
See also
https://issues.dlang.org/show_bug.cgi?id=23177
Moved to GitHub: dmd#20296 →

Comments

Comment #0 by bugzilla — 2023-06-05T22:01:45Z
An instance of ModuleInfo contains an array of the ModuleInfo's for imported modules. The purpose is to ensure the static constructors of imported modules are run before the static constructor of the module with the ModuleInfo. However, when a DLL is loaded, its DllMain is called, which then calls the DLL's static initialization functions. So, already, all those constructors are already called. There can be no further initialization order dependency of the EXE's static constructors on the DLL's static constructors. Hence, the ModuleInfo's of a module imported from a DLL should not be listed in the ImportedModules of the EXE module. But suppose the DLL depends on the EXE's static constructors being run first? Or if two DLLs have mutually dependent static constructors? I don't see any good way to resolve this, and in fact, if DLLs and EXEs are set up that way, it sounds like a failed attempt at encapsulation. See also: https://issues.dlang.org/show_bug.cgi?id=23177
Comment #1 by alphaglosined — 2023-06-05T22:09:03Z
This could be solved by implementing: https://issues.dlang.org/show_bug.cgi?id=23850 By knowing when a module is outside of a binary the reference can be elided.
Comment #2 by bugzilla — 2023-06-05T23:54:33Z
A module is imported (and outside the binary) when it contains exported declarations: export extern int x = 3; export int foo();
Comment #3 by bugzilla — 2023-06-05T23:57:05Z
(In reply to Walter Bright from comment #2) > A module is imported (and outside the binary) when it contains exported > declarations: > > export extern int x = 3; > > export int foo(); Argh, I mean: module mydll; export extern __gshared int x; export int foo(); and then mydll is imported.
Comment #4 by alphaglosined — 2023-06-06T00:23:00Z
(In reply to Walter Bright from comment #2) > A module is imported (and outside the binary) when it contains exported This may not be true. Separate compilation is quite common when creating shared libraries. To clarify: outside of binary means outside of dll/exe, rather than outside of object file (just so we are all on the same page). You can have a dependency that was built as a static library, that was later linked in, that must be accessed via internal symbols and not dllimport. We do this today with dub. It also depends upon the use of a .di generator that is functioning correctly (which the avoidance of was a motivating factor for Martin in his work with ldc). This is why we must parameterize export with a version identifier to be able to tell the compiler if a symbol is dllimport or internal based on what stage it is in the compilation process. We have got to account for modules being bindings, while also containing D code that needs to be compiled and accessed internally.
Comment #5 by bugzilla — 2023-06-06T03:54:46Z
I don't think that is necessary. I fixed the only outstanding .di gen bug I could find in bugzilla. Taking a pile of modules, and randomly assigning some to a DLL and others to the EXE will never work with Windows DLLs. It'll always need to be designed to be in a DLL. For example, templates in a DLL meant to be expanded by the EXE will never work properly. They are not like Linux shared libraries.
Comment #6 by r.sagitario — 2023-06-06T17:42:15Z
I don't think this issue is a real problem: - it's not easy to create DLLs with cyclic import dependencies, and even if you manage to do it, the system won't load them AFAICT. - so DLLs already have a "higher level" ordering that respects the required module initializer order - static initializers already contain a check to avoid multiple executions, so it is no problem that it might get called across DLL boundaries, it just does nothing. The actual difficulty is that the dependency between modules is part of the ModuleInfo and needs a data relocation through the import table. This is not supported by the OS. https://github.com/dlang/dmd/pull/14849 solves this similar to LDC by running some initializers before most of the C runtime is started. This is needed for other data entries as well (e.g. TypeInfo), so it does not help a lot to avoid it for ModuleInfo.
Comment #7 by alphaglosined — 2023-06-06T17:53:17Z
(In reply to Rainer Schuetze from comment #6) > This is needed for other data entries as well (e.g. TypeInfo), so > it does not help a lot to avoid it for ModuleInfo. It does help to get binaries sizes down (even if it would be a tiny amount), so the fundamental idea isn't a bad one. I think it's a worthy change, but only if the user tells the compiler that a module is out of binary. If it's derived, there is a good chance it will get the wrong idea when it comes to mixed builds (which are very common) and could lead to some very unhappy people. All the stuff like ModuleInfo, TypeInfo ext. (but not RTInfo) is currently dependent on Rainer's PR.
Comment #8 by bugzilla — 2023-06-07T06:43:33Z
(In reply to Rainer Schuetze from comment #6) > so it does not help a lot to avoid it for ModuleInfo. It's an easy enough change, and it should be solid. I know this doesn't solve the TypeInfo problem, but one by one we knock the problems down.
Comment #9 by dlang-bot — 2023-06-07T07:14:51Z
@WalterBright created dlang/dmd pull request #15298 "fix Issue 23974 - A ModuleInfo in a separate Windows DLL should not b…" fixing this issue: - fix Issue 23974 - A ModuleInfo in a separate Windows DLL should not be referred to by MIimportedModules https://github.com/dlang/dmd/pull/15298
Comment #10 by robert.schadek — 2024-12-13T19:29:34Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/20296 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB