Issue 6019 - Phobos imports in autogenerated .di header files break implicit linking with DLLs
Summary: Phobos imports in autogenerated .di header files break implicit linking with ...
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: Other Windows
: P3 normal
Assignee: No Owner
URL:
Keywords: dll
: 9220 (view as issue list)
Depends on:
Blocks:
 
Reported: 2011-05-16 16:02 UTC by Andrej Mitrovic
Modified: 2023-06-04 07:10 UTC (History)
5 users (show)

See Also:


Attachments
DLL Bug (1.38 KB, application/zip)
2011-12-18 10:43 UTC, Andrej Mitrovic
Details

Note You need to log in before you can comment on or make changes to this issue.
Description Andrej Mitrovic 2011-05-16 16:02:19 UTC
Test case: http://dl.dropbox.com/u/9218759/dll_bug.zip

Use the build.bat file or issue these commands:
cd dll
dmd -H -of..\mydll.dll -L/IMPLIB mydll.d
cd..
dmd driver.d -I. dll\mydll.lib

The exported D function is found by the linker, and you can find the name in the mydll.dll, mydll.lib, and driver.obj files, it has this name:
_D3dll5mydll3fooFiZi

However DMD also generates a mydll.obj file which references this symbol:
_D3dll5mydll12__ModuleInfoZ

This symbol isn't exported in the DLL and you won't find it in the import lib, and hence the linker error.

Daniel Green made a comment from this thread http://www.digitalmars.com/d/archives/digitalmars/D/learn/.di_header_imports_with_DLL_symbols_fails_to_link_25571.html:

"Probably unrelated, but this same issue showed up in the GDC backend. 
Apparently, the compiler tried to be smart about exporting ModuleInfo 
only for those modules that needed it.  The fix was to always export it 
regardless."

Note that you cannot just pass a .di header file directly to DMD because DMD will statically compile the mydll.foo function found in the header file, and the application won't need the DLL anymore.

You also cannot prototype the foo() function directly in driver.d, because this function will be mangled together with its module name (mangled mydll.driver vs mangled driver.foo).

The alternative is manually figuring out the mangling of the function name and using explicit linking via GetProcAddress & friends.

---------------

One more thing: Apparently in v2.053 linking with core.dll_helper doesn't work anymore:
import core.dll_helper;
void main() {}

 Error 42: Symbol Undefined _D4core10dll_helper12__ModuleInfoZ
--- errorlevel 1

Is that a known issue? Linking with core.sys.windows.dll does work though.
Comment 1 Andrej Mitrovic 2011-05-16 18:41:33 UTC
Also the guide which shows how to write and use D DLLs using a similar technique and .def files exhibits the same problem (and it also imports the non-existent std.gc module):

http://d-programming-language.org/dll.html
Comment 2 Andrej Mitrovic 2011-05-16 18:47:19 UTC
(In reply to comment #0)
> 
> One more thing: Apparently in v2.053 linking with core.dll_helper doesn't work
> anymore:
> import core.dll_helper;
> void main() {}
> 
>  Error 42: Symbol Undefined _D4core10dll_helper12__ModuleInfoZ
> --- errorlevel 1
> 
> Is that a known issue? Linking with core.sys.windows.dll does work though.

Disregard that. It seems I've had this module leftover from 2.052. That should teach me not to copy/paste new releases over old ones.
Comment 3 Andrej Mitrovic 2011-05-17 15:29:33 UTC
Ok, I think I have found a very simple test case: http://dl.dropbox.com/u/9218759/dll_bugs.zip

The only difference between the two projects is that the one which does an import to a Phobos function will not compile. So I'm changing the title of this bug report.
Comment 4 Andrej Mitrovic 2011-05-17 15:49:02 UTC
Freakin' hell.

Ok so I've ran it down to blaming the header generation.

Here's an example with a hand-generated .di header file versus automatically generated .di file. The hand-edited one is almost the same as the compiler generates it, except it has imports to Phobos *commented out*.

http://dl.dropbox.com/u/9218759/DLL_bug_test_case.zip
Comment 5 Rainer Schuetze 2011-05-17 23:53:29 UTC
The import in the di file tells the compiler that there are dependencies to other modules that need to be respected during module initialization. That's why they are placed into the module info.

I agree it is very annoying not to be able to use the generated di files (let alone the d files). Two possible solutions:

- add a switch to the compiler to only put exported symbols into the di file and leave out imports. This might make it difficult to compile this file if there are still references to symbols defined by the imports.

- add a modifier to the "import" declaration that avoids adding dependencies to the module info. (IIRC this has also been discussed to break cyclic module initialization.) If used in driver.d, it might even restrict imported symbols to the symbols marked "export" in the di file. If used for the imports of the generated di-file, it might break the correct init order when used for modules built to static libraries (as druntime).

BTW: I checked the generated object files: the buggy version also adds symbols
_D47TypeInfo_S3std6traits15__T8DemangleTkZ8Demangle6__initZ and _D3std6traits15__T8DemangleTkZ8Demangle6__initZ. I don't know why this happens, but as these are comdats, they problably won't break anything.
Comment 6 Andrej Mitrovic 2011-06-24 10:37:27 UTC
It seems another workaround for this is to create a dummy symbol in the module that imports a header file, e.g.:

extern(C) int D3dll5mydll12__ModuleInfoZ;  // implicitly adds _ before the name

This fixes linker errors. However it has to be put in the module that imports "dll.mydll", and not inside dll.mydll module itself as that creates some kind of symbol clashes.
Comment 7 Andrej Mitrovic 2011-12-18 10:43:50 UTC
Created attachment 1054 [details]
DLL Bug
Comment 8 Andrej Mitrovic 2011-12-18 10:44:25 UTC
(In reply to comment #7)
> Created an attachment (id=1054) [details]
> DLL Bug

The other samples bit-rotted, newest one is in the attachment.
Comment 9 Martin Nowak 2013-10-02 04:42:10 UTC
*** Issue 9220 has been marked as a duplicate of this issue. ***
Comment 10 Martin Nowak 2013-10-02 05:08:42 UTC
Most declarations don't require linkage. The ones that do are module ctors/dtors (__ModuleInfoZ), classes (vtbl and classinfo) and anything that's non-zero initialized (__initZ), i.e. structs with non-zero initialized fields, enums with non-zero first member, char and maybe floats.
BTW the same issue is present in deimos where you also want headers that don't require linkage.
Maybe we can come up with a good idea how to solve this.
How about this? Using weak undefined symbols to link against the imported ModuleInfo would result in null pointers during runtime.
Comment 11 Martin Nowak 2013-10-02 08:51:30 UTC
(In reply to comment #10)
> How about this? Using weak undefined symbols to link against the imported
> ModuleInfo would result in null pointers during runtime.

That could cause some issues with the weird multilibs. Not sure currently if every archive member references it's module's ModuleInfo.
Comment 12 Richard (Rikki) Andrew Cattermole 2022-11-25 16:31:27 UTC
I'm removing the bootcamp keyword from this bug.

Setting a symbol as export is not enough to get this working.

DllImport while in theory implemented in dmd as of 2.102.0 does not do any patching at runtime required to dereference the pointer.

I went into attempting to solve this in my post here:
https://forum.dlang.org/post/ohflhdaggtapeqpynkjd@forum.dlang.org
Comment 13 Walter Bright 2023-06-04 07:10:24 UTC
(In reply to Andrej Mitrovic from comment #1)
> http://d-programming-language.org/dll.html

Now https://wiki.dlang.org/Win32_DLLs_in_D