Create dlls on Windows without declspec() using new CMake export all feature

CMake 3.4 will have a new feature to simplify porting C and C++ software using shared libraries from Linux/UNIX to Windows. Linux/UNIX developers are often surprised to learn that creating a shared library on Windows known as a DLL (dynamic linked library) requires changes to the source code or an explicit listing of all the symbols that the dll will export.  The compilers on Linux/UNIX have the ability to export all symbols in a shared library automatically. On Windows, you must either use compiler directives __declspec(import) and __declspec(export) to declare which symbols are exported/imported from a shared library, or you must create a module definition text file (.def) with a list of all the symbols you want to export and pass that file to the linker.

With C libraries, creating a .def file by hand or automatically is not very difficult since you just have to list the names of all the functions in the library. However, with C++ code, name mangling and the sheer number of functions make crafting a .def file by hand nearly impossible. The standard workaround uses the preprocessor to conditionally insert __declspec(import) and __declspec(export) into the code.  However, with a large existing C++ code base, it can be difficult and time consuming to edit all of the source code. CMake now has a feature which allows it to query .obj files that will make up a DLL and create a .def file automatically, in most cases without needing to modify the original source code.

 

The feature is implemented in CMake via a new target property, WINDOWS_EXPORT_ALL_SYMBOLS. When enabled, this property causes CMake to automatically create a .def  file with all symbols found in the input .obj files for a SHARED library on Windows.  The .def file will be passed to the linker causing all symbols to be exported from the DLL. For global data symbols, __declspec(dllimport) must still be used when compiling against the code in the DLL. The symbol is exported from the DLL correctly and automatically, but the compiler needs to know that it is being imported from a DLL at compile time.  All other function symbols will be automatically exported and imported by callers.  This simplifies porting projects to Windows by reducing the need for explicit dllexport markup, even in C++ classes. This property is initialized by the value of the CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS variable when a target is created.

 

To try this out on an existing project, run

on a CMake project.  This should turn all add_library calls that do not explicitly specify build type into shared builds. If there are no global data variables in the project, all libraries will be built as DLLs with no errors.  If you run into undefined symbols, check for global data like static data members of classes. The easiest way to handle the global data is to use the CMake GenerateExportHeader module like this:

// edit the source like this:

Note, if you use GenerateExportHeader and still want static builds to work, you will need to add a -Dmylibrary_STATIC during static builds. See the generated mylibary_export.h for more details.

Real World Use Cases

After adding this feature to CMake, I tried it on two relatively large software projects VXL ( (http://vxl.sourceforge.net/) and ITK (www.itk.org). Although, it did take some time, it was much easier than adding dll markup to every source file. In the end the changes required fell into the following repeated issues and fixes:

  • Handling class static data members

    • Add dll import/export macros to the static data.

    • Eliminate the static data –

      • This can be done by making a static getter function with a local static variable.

  • Static const members

    • These are not exported by the new feature.  In some cases to support older compilers, these are put in .cxx files and compiled into the library and are not just inlined in the .h files.  If you have this case, you will need to use dllimport and dllexport markup and not just dllimport mark up as the auto export will not work.

  • Inherit from std::string or other templated classes with dllimport/dllexport mark up.  

    • This can cause duplicate symbol errors in the parent templated class to show up in some cases. The fix for this is to remove the markup and let the auto-export do its thing.

  • Not having fully linked libraries. On many Unix systems the linker will allow for shared libraries to have unresolved symbols at create time, that are expected to be resolved at the link time of the executable.  Windows dlls must resolve all symbols, so your library must explicitly link to everything it depends on.  One way to find out where symbols exist in a big system if you get an undefined symbol at link to time is to look for the symbol in the .def files that are created. From a bash (cygwin or git) shell you could run something like this:

  • Static data members of templated classes.  It is best to just avoid these, or only use explicit instantiation or specialization for these.  I was not able to get these to work at all.

Thanks

I would like to give a special thanks to Valeri Fine (code author) and Bertrand Bellenot and the rest of the ROOT team at CERN for contributing the .obj file symbols parsing code, and for testing the feature on their project. Enjoy!

Links

Nightly CMake with new feature: http://www.cmake.org/files/dev/cmake-3.3.20150721-g9cd2f-win32-x86.exe

Nightly Docs for feature:

http://www.cmake.org/cmake/help/git-master/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.html

ITK changes for this feature: http://review.source.kitware.com/#/c/20020

VXL changes for this feature: https://github.com/vxl/vxl/pull/4

Valeri Fine presented this approach and introduced the original code at Computing in High Energy Physics Berlin, Lichtenberger Congress Center April 7-11, 2977:

http://www.ifh.de/CHEP97/abstract/g340.htm

http://www.ifh.de/CHEP97/paper/340.ps.

 

18 Responses to Create dlls on Windows without declspec() using new CMake export all feature

  1. Pingback: Tomas P

  2. Pingback: Bill Hoffman

  3. Pingback: Valeri Fine

  4. Pingback: pelletee r

  5. Pingback: Guillaume Dumont

  6. Pingback: jasjuang

  7. Pingback: Bill Hoffman

    • Pingback: jasjuang

  8. Pingback: Bill Hoffman

    • Pingback: jasjuang

      • Pingback: Bill Hoffman

  9. Pingback: NameRakes

    • Pingback: Bill Hoffman

      • Pingback: NameRakes

        • Pingback: NameRakes

          • Pingback: runaboutbill

  10. Pingback: S@b!!!

  11. Pingback: Compiling Giac in Windows (VS2008) | Jose's Blog

Questions or comments are always welcome!

X