www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - export keyword messes up static linking

reply torhu <fake address.dude> writes:
I'm porting headers for a C lib do D.  There are a bunch of globals in 
the C lib.  To get those correctly linked with the D code, I have to 
declare them 'export extern extern (C)'.

That works fine for dynamically linking with the C lib, at least with 
dmd on windows.  The problem is that it won't work if you try to link 
statically.  The linker accepts it, but at least some of the globals are 
not linked with the C definitions, they don't contain the correct values.

And for linking statically using dmd on linux, it just doesn't work. 
The export symbols get mangled like '_imp__foo', so it won't link.

Is there any way around this?  I've tried using a .def file, but it 
doesn't do anything.  This is what I tried:

EXETYPE NT
SUBSYSTEM CONSOLE

IMPORTS
     cglobals.var
     cglobals.array


If that had worked, I wouldn't have had to declare global C variables 
'export' in my import modules, so I would have been able to link the 
same code both statically and dynamically.

Littering the code with version statements for this seems a bit over the 
top.  And using the C preprocessor on D code wasn't supposed to be 
necessary.
Jan 24 2007
parent reply Carlos Santander <csantander619 gmail.com> writes:
torhu escribió:
 I'm porting headers for a C lib do D.  There are a bunch of globals in 
 the C lib.  To get those correctly linked with the D code, I have to 
 declare them 'export extern extern (C)'.
 
 That works fine for dynamically linking with the C lib, at least with 
 dmd on windows.  The problem is that it won't work if you try to link 
 statically.  The linker accepts it, but at least some of the globals are 
 not linked with the C definitions, they don't contain the correct values.
 
 And for linking statically using dmd on linux, it just doesn't work. The 
 export symbols get mangled like '_imp__foo', so it won't link.
 
 Is there any way around this?  I've tried using a .def file, but it 
 doesn't do anything.  This is what I tried:
 
 EXETYPE NT
 SUBSYSTEM CONSOLE
 
 IMPORTS
     cglobals.var
     cglobals.array
 
 
 If that had worked, I wouldn't have had to declare global C variables 
 'export' in my import modules, so I would have been able to link the 
 same code both statically and dynamically.
 
 Littering the code with version statements for this seems a bit over the 
 top.  And using the C preprocessor on D code wasn't supposed to be 
 necessary.

Start with something simple. Maybe this will give you some ideas: $ cat cmod.c #include <stdio.h> void foo() { printf("hi from c\n"); } $ cat dmod.d extern (C) void foo(); void main() { foo(); } $ gcc -c cmod.c $ gdmd dmod cmod.o $ ./dmod hi from c That was using GDC. On Windows (using DMD) you would replace "gcc" with "dmc" and "gdmd dmod cmod.o" with "dmd dmod cmod.obj". If you have a library, put all your declarations in a single .d (or .di) file, and use the .lib instead of the .obj. You can check the Bindings project at DSource to see how it's done. -- Carlos Santander Bernal
Jan 24 2007
parent reply torhu <fake address.dude> writes:
I think I'll try to restate the problem in a simpler way.

I have a C lib, that I want to link with D apps.  So I write a D 
'header', like so:

module clib;

export extern extern (C) {
     int foo;
     char* bar;
}


This works just fine, if I link dynamically with the C lib.  But if I 
want to link statically, it doesn't work.  This is because 'export' 
can't be there when you link statically.  foo and bar ptr get random 
values in the D app.

In this example 'export' works like msvc's __declspec(dllimport).  So in 
C code, you could use a macro to turn it off for static linking.


Does Optlink have a switch for this, or can someone tell me how to write 
a .def file so I don't have to declare the variables 'export'?

This C lib is cross-platform, so I'd also like to know if gcc (for dmd 
use), or at least gdc has a solution for this.  Currently we use a 
script that just does 's/export//g'.
Jan 25 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
torhu wrote:
 I think I'll try to restate the problem in a simpler way.
 
 I have a C lib, that I want to link with D apps.  So I write a D 
 'header', like so:
 
 module clib;
 
 export extern extern (C) {
     int foo;
     char* bar;
 }
 
 
 This works just fine, if I link dynamically with the C lib.  But if I 
 want to link statically, it doesn't work.  This is because 'export' 
 can't be there when you link statically.  foo and bar ptr get random 
 values in the D app.
 
 In this example 'export' works like msvc's __declspec(dllimport).  So in 
 C code, you could use a macro to turn it off for static linking.
 
 
 Does Optlink have a switch for this, or can someone tell me how to write 
 a .def file so I don't have to declare the variables 'export'?
 
 This C lib is cross-platform, so I'd also like to know if gcc (for dmd 
 use), or at least gdc has a solution for this.  Currently we use a 
 script that just does 's/export//g'.

Put "version(C_Lib_Dll) export:" before your declarations. If you then pass "-version=C_Lib_Dll" (no quotes) to DMD (or something like "-fversion=C_Lib_Dll" to GDC) when compiling, or put "version = C_Lib_Dll" in the source above that line, 'export' will be applied to all declarations after that. If you don't do any of those things, it won't. This means you have to put all non-exported symbols (if any) above this line, though. Of course, the "C_Lib_Dll" should be changed to something unique to your C library (and preferably indicate it is to be loaded as a DLL). Normally, the scope of such a statement can be limited by putting it in a {} block, but those aren't allowed at module scope.
Jan 26 2007
parent reply torhu <fake address.dude> writes:
Frits van Bommel wrote:
 torhu wrote:
 I think I'll try to restate the problem in a simpler way.
 
 I have a C lib, that I want to link with D apps.  So I write a D 
 'header', like so:
 
 module clib;
 
 export extern extern (C) {
     int foo;
     char* bar;
 }
 
 
 This works just fine, if I link dynamically with the C lib.  But if I 
 want to link statically, it doesn't work.  This is because 'export' 
 can't be there when you link statically.  foo and bar ptr get random 
 values in the D app.
 
 In this example 'export' works like msvc's __declspec(dllimport).  So in 
 C code, you could use a macro to turn it off for static linking.
 
 
 Does Optlink have a switch for this, or can someone tell me how to write 
 a .def file so I don't have to declare the variables 'export'?
 
 This C lib is cross-platform, so I'd also like to know if gcc (for dmd 
 use), or at least gdc has a solution for this.  Currently we use a 
 script that just does 's/export//g'.

Put "version(C_Lib_Dll) export:" before your declarations. If you then pass "-version=C_Lib_Dll" (no quotes) to DMD (or something like "-fversion=C_Lib_Dll" to GDC) when compiling, or put "version = C_Lib_Dll" in the source above that line, 'export' will be applied to all declarations after that. If you don't do any of those things, it won't. This means you have to put all non-exported symbols (if any) above this line, though. Of course, the "C_Lib_Dll" should be changed to something unique to your C library (and preferably indicate it is to be loaded as a DLL). Normally, the scope of such a statement can be limited by putting it in a {} block, but those aren't allowed at module scope.

Part of the problem is that I want to keep the .d files as close the .h files as possible, so it's easier to find where things are when updating them for new versions etc. from http://www.digitalmars.com/d/attribute.html : "Export means that any code outside the executable can access the member. Export is analogous to exporting definitions from a DLL." This is all that's written about export, apart from some examples on the pages about DLL's and '.h to .d'. They mostly show that export is also used for importing definitions from a DLL, not just exporting. Just putting this on top of the file seems to work with dmd on windows, at least after some preliminary testing: version (STATICLINK) {} else export: I didn't think of using export with a colon, but it seems the best alternative to search and replace. Can I safely assume that if I have a definition in the D code that is declared export (but not extern), it will still be a definition? That seems to be the idea, but I don't feel entirely safe. The other way, leaving out 'export' for symbols that are imported from the DLL when doing dynamic linking actually works for some symbols but not others, which is a bit scary. It's not needed for functions, just for variables.
Jan 26 2007
parent torhu <fake address.dude> writes:
torhu wrote:

 from http://www.digitalmars.com/d/attribute.html :
 "Export means that any code outside the executable can access the 
 member. Export is analogous to exporting definitions from a DLL."
 
 This is all that's written about export, apart from some examples on the 
 pages about DLL's and '.h to .d'.  They mostly show  that export is also 
 used for importing definitions from a DLL, not just exporting.
 
 Just putting this on top of the file seems to work with dmd on windows, 
 at least after some preliminary testing:
 
 version (STATICLINK) {}
 else export:
 
 I didn't think of using export with a colon, but it seems the best 
 alternative to search and replace.  Can I safely assume that if I have a 
 definition in the D code that is declared export (but not extern), it 
 will still be a definition?  That seems to be the idea, but I don't feel 
 entirely safe.
 
 The other way, leaving out 'export' for symbols that are imported from 
 the DLL when doing dynamic linking actually works for some symbols but 
 not others, which is a bit scary.  It's not needed for functions, just 
 for variables.

'export:' at the top of the files seems to work. Except for one thing. Imported functions that are declared like this only partially work: export extern (C) int f(float x); The export makes them it impossible to use their address in static initialization. This doesn't compile: extern (C) int (*proc)(float) = &f; So I still think a suitable .def file might be a cleaner solution.
Jan 26 2007