mandrake
mandrake

Reputation: 1253

Not all symbols of an DLL-exported class is exported (VS9)

I'm building a DLL from a group of static libraries and I'm having a problem where only parts of classes are exported.

What I'm doing is declaring all symbols I want to export with a preprocessor definition like:

#if defined(MYPROJ_BUILD_DLL)
//Build as a DLL
#   define MY_API __declspec(dllexport)
#elif defined(MYPROJ_USE_DLL)
//Use as a DLL
#   define MY_API __declspec(dllimport)
#else
//Build or use as a static lib
#   define MY_API
#endif

For example:

class MY_API Foo{ 
   ...
}

I then build static library with MYPROJ_BUILD_DLL & MYPROJ_USE_DLL undefined causing a static library to be built.

In another build I create a DLL from these static libraries. So I define MYPROJ_BUILD_DLL causing all symbols I want to export to be attributed with __declspec(dllexport) (this is done by including all static library headers in the DLL-project source file).

Edit: Note on unrefenced symbols: Linker options Keep Unreferenced Data (/OPT:NOREF) and Do Not Remove Redundant COMDATs (/OPT:NOICF) is set so that no unreferenced symbols will be removed.

Ok, so now to the problem. When I use this new DLL I get unresolved externals because not all symbols of a class is exported. For example in a class like this:

class MY_API Foo{
public:
   Foo(char const* );
   int bar();
private:
   Foo( char const*, char const* );
};

Only Foo::Foo( char const*, char const*); and int Foo::bar(); is exported. How can that be? I can understand if the entire class was missing, due to e.g. I forgot to include the header in the DLL-build. But it's only partial missing.

Also, say if Foo::Foo( char const*) was not implemented; then the DLL build would have unresolved external errors. But the build is fine (I also double checked for declarations without implementation).

Note: The combined size of the static libraries I'm combining is in the region of 30MB, and the resulting DLL is 1.2MB.

I'm using Visual Studio 9.0 (2008) to build everything. And Depends to check for exported symbols.

Edit: For the ones who wonder why I don't just build a DLL from each of the static libraries: I can't because they cross-reference each other (that's why I need to group them together in one a single DLL). I know, it's horrible I can't really understand the logic behind it.

Upvotes: 0

Views: 2667

Answers (3)

Mike C
Mike C

Reputation: 1238

This is probably years too late, but OP's complaint was that a private method (the destructor) was not exported from a dllexport'd class. Isn't that normal? No external user is allowed to call a private method.

Upvotes: 0

Swingline Rage
Swingline Rage

Reputation: 1130

Remember that when you link against a static LIB, by default the linker is only pulling in the functions, classes, and data that the client (which in this case is your DLL) actually references.

So what happens is this:

  1. You build your static LIB, and that's fine. This LIB is 100% valid.
  2. You now build your static DLL around the original binary LIB. It pulls in only the stuff that it actually references. It doesn't pull in the entire class definition which is what it needs to do. This DLL, though it builds, is invalid.
  3. A client then uses the DLL, and expects to see a complete binary definition for the exported class, because that's what the client sees in the accompanying header file.
  4. However the class has only been partially imported, and this is why you're getting those link errors.

To fix:

  • If you're able to, don't build your DLL off your static LIB. Build your DLL off the original source code. And build your static LIB off the original source code.
  • Otherwise you can possibly fiddle with linker settings but I strongly recommend option 1 above. Note that OPT:NOREF won't work here as OPT:NOREF has no effect on static LIBs.

What wont't fix it:

  • High-level linker tweaks like OPT:NOREF, anything involving COMDATs, etc. If you want those functions present, you have to make sure they're referenced, either by referencing them, or by telling the linker explicitly, "hey, this Symbol X is referenced".

As a side note:

  • Anytime I build a DLL or LIB, I build both a DLL and a LIB, using exactly the technique you're using. Having a single code base that can generate either a DLL or a LIB by toggling a setting is ideal. But building a DLL off of a (binary) static LIB when you own the source code for both... I'm having a hard time imagining when such a scenario would be necessary.

Upvotes: 3

Hans Passant
Hans Passant

Reputation: 942237

The problem is surely that you use the already-built static .lib in your DLL project. That cannot work, you have to rebuild the .lib so that the functions get the __declspec(dllexport) declarator and will be exported by the linker.

At that point, it just isn't all that useful anymore to create the DLL compatible version of the .lib in the first place. Just create two projects, one that creates the static .lib, another that creates the DLL. Technically it is possible to still use the static .lib in your DLL project but you'll have to export the functions with a .def file. That can be high maintenance if the library has a lot of exports.

Upvotes: 2

Related Questions