Export all characters when creating a DLL

With VS2005, I want to create a DLL and automatically export all characters without adding __declspec (dllexport) all over the world and without manually creating .def files. Is this the best way to do this?

+62
c ++ windows visual-studio visual-studio-2005
Oct 22 '08 at 11:48
source share
6 answers

It can be done...

To do this, we use the / DEF linker parameter to pass a “module definition file” containing a list of our exports. On your question, I see what you know about these files. However, we do not do this manually. The export list itself is created by the dumpbin / LINKERMEMBER command and controls the output using a simple script in the module definition file format.

The setup is very large, but it allows us to compile code generated without dllexport ads for Unix on Windows.

+37
Oct 22 '08 at 14:36
source share

Short answer

You can do this with the new version of CMake (any version of cmake-3.3.20150721-g9cd2f-win32-x86.exe or higher).

This is currently in the developer's branch. This feature will later be added to the release version of cmake-3.4.

Link to cmake dev:

cmake_dev

Link to an article that describes the technique:

Create dlls on Windows without declspec () using the new export function of all CMake

Link to an example project:

cmake_windows_export_all_symbols




Long answer

Caution! All information below refers to the MSVC compiler or Visual Studio.

If you use other compilers, such as gcc on Linux or the MinGW gcc compiler on Windows, you will not have layout errors due to unexported characters, since the gcc compiler by default exports all characters to a dynamic library (dll) instead of MSVC or Intel compilers Windows.,

In windows, you must explicitly export the symbol from the DLL.

More information about this is provided at the links:

Export from DLL

How to: export C ++ classes from a DLL

So if you want to export all characters from a dll using MSVC (Visual Studio compiler), you have two options:

  • Use the __declspec (dllexport) keyword in the class / function definition.
  • Create a module definition file (.def) and use the .def file when building the DLL.



1. Use the __declspec (dllexport) keyword in the class / function definition




1.1. Add the macros "__declspec (dllexport) / __ declspec (dllimport)" to the class or method that you want to use. So if you want to export all classes, you must add these macros to all

More information on this is provided here:

Export from DLL using __declspec (dllexport)

Example of use (replace "Project" with the real name of the project):

// ProjectExport.h #ifndef __PROJECT_EXPORT_H #define __PROJECT_EXPORT_H #ifdef USEPROJECTLIBRARY #ifdef PROJECTLIBRARY_EXPORTS #define PROJECTAPI __declspec(dllexport) #else #define PROJECTAPI __declspec(dllimport) #endif #else #define PROJECTAPI #endif #endif 

Then add “PROJECT” to all classes. Define "USEPROJECTLIBRARY" only if you want to export / import characters from the dll. Define "PROJECTLIBRARY_EXPORTS" for the DLL.

Class export example:

 #include "ProjectExport.h" namespace hello { class PROJECTAPI Hello {} } 

Function export example:

 #include "ProjectExport.h" PROJECTAPI void HelloWorld(); 

Note: do not forget to include the file "ProjectExport.h".




1.2. Export to C function. If you use the C ++ compiler for compilation code written in C, you can add extern "C" in front of the function to eliminate name distortion

More information on C ++ name distortion is provided here:

Name Decoration

Usage example:

 extern "C" __declspec(dllexport) void HelloWorld(); 

More information on this is provided here:

Export C ++ functions for use in C language executables




2. Create a module definition file (.def) and use the .def file when building the DLL.

More information on this is provided here:

Export from DLL using DEF files

Next, I will describe three approaches to creating a .def file.




2.1. Export Functions C

In this case, you can simply add function declarations to the .def file manually.

Usage example:

 extern "C" void HelloWorld(); 

Example .def file (__cdecl naming convention):

 EXPORTS _HelloWorld 



2.2. Export symbols from a static library

I tried the approach suggested by "user72260".

He said:

  • First, you can create a static library.
  • Then use "dumpbin / LINKERMEMBER" to export all the characters from the static library.
  • Parse the output.
  • Put all the results in a .def file.
  • Create a dll using the .def file.

I used this approach, but it is not always convenient to create two assemblies (one as a static and the other as a dynamic library). However, I must admit that this approach really works.




2.3. Export characters from .obj files or using CMake




2.3.1. Using CMake

Important Note: You do not need export macros to classes or functions!

Important Note: You cannot use / GL ( Optimizing the entire program ) when using this approach!

  • Create a CMake project based on the file "CMakeLists.txt".
  • Add the following line to the CMakeLists.txt file: set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
  • Then create a Visual Studio project using "CMake (cmake-gui)".
  • Compile the project.

Usage example:

The root folder

CMakeLists.txt (root folder)

 cmake_minimum_required(VERSION 2.6) project(cmake_export_all) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(dir ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}/bin") set(SOURCE_EXE main.cpp) include_directories(foo) add_executable(main ${SOURCE_EXE}) add_subdirectory(foo) target_link_libraries(main foo) 

main.cpp (root folder)

 #include "foo.h" int main() { HelloWorld(); return 0; } 

Foo folder (root folder / Foo folder)

CMakeLists.txt (Foo folder)

 project(foo) set(SOURCE_LIB foo.cpp) add_library(foo SHARED ${SOURCE_LIB}) 

foo.h (folder Foo)

 void HelloWorld(); 

foo.cpp (Foo folder)

 #include <iostream> void HelloWorld() { std::cout << "Hello World!" << std::endl; } 

Link to an example project again:

cmake_windows_export_all_symbols

CMake uses a different approach than "2.2. Exporting Symbols from a Static Library" approach.

This does the following:

1) Create a file "objects.txt" in the assembly directory with information about the .obj files used in the dll.

2) Compile the dll, i.e. create .obj files.

3) Based on the information in the "objects.txt" file, extract all the characters from the .obj file.

Usage example:

 DUMPBIN /SYMBOLS example.obj > log.txt 

More information on this is provided here:

/ SYMBOLS

4) Parsing extracted from .obj file data.

In my opinion, I would use calling convection, for example, "__cdecl / __ fastcall", the character field "SECTx / UNDEF" (third column), the character field "External / Static" (fifth column), "??", "? "information for analyzing .obj files.

I don’t know how exactly CMake parses the .obj file. However, CMake is open source, so you can find out if it is of interest to you.

Link to the CMake project:

CMake_github

5) Put all exported characters in a .def file.

6) Link the DLL using the .def file.

Steps 4) -5), that is, analyzing the .obj files and creating the .def file before linking and using the .def file that CMake does using the "Pre-Link event". While the "Pre-Link event" is triggered, you can call any program you want. Therefore, in the case of "using CMake" "Pre-Link Event", call CMake with the following information about where to put the .def file and where the "objects.txt" file is located and with the argument "-E __create_def". You can verify this information by creating a CMake Visusal Studio project using "set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)", and then check the project file .vcxproj for dll.

If you try to compile the project without "set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" or with "set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)", you will get layout errors due to characters not being exported from dll.

More information on this is provided here:

Understanding custom build steps and build events




2.3.2. Without using CMake

You can create a small program to parse the .obj file yourself without using CMake. However, I have to admit that CMake is a very useful program, especially for cross-platform development.

+29
Aug 29 '15 at 10:03
source share

I wrote a small program to analyze the output of "dumpbin / linkmember" in a .lib file. I have 8000 function links to export from a single DLL.

The problem with doing this in a DLL is that you need to link the DLL without exporting the definitions once to create the .lib file and then generate the .def, which means that now you need to re-link the DLL using the .def file so that actually export links.

Working with static libraries is easier. Compile all your sources into static libraries, run dumbin, generate .def with your small program, and then link the libraries together in a DLL when export names are available.

Unfortunately, my company will not allow me to show you the source. This work recognizes which "public characters" in the dump output are not needed in your def file. You should drop a lot of these links, NULL_IMPORT_DESCRIPTOR, NULL_THUNK_DATA, __imp *, etc.

+8
Apr 08 '09 at 20:50
source share

Thanks @Maks for the detailed answer .

Below is an example of what I used in the Pre-Link event to create a def file from obj. I hope this will be helpful to someone.

 dumpbin /SYMBOLS $(Platform)\$(Configuration)\mdb.obj | findstr /R "().*External.*mdb_.*" > $(Platform)\$(Configuration)\mdb_symbols (echo EXPORTS & for /F "usebackq tokens=2 delims==|" %%E in ('type $(Platform)\$(Configuration)\mdb_symbols') do @echo %%E) > $(Platform)\$(Configuration)\lmdb.def 

Basically I just took one of the objects (mdb.obj) and grepped the mdb_ * functions. Then he analyzed the output in order to save only the names, taking into account the number of spaces for indentation (one after splitting into tokens, and the other in the echo. Although I do not know if this matters).

The real scenario is likely to be a little more complicated.

+2
Apr 18 '18 at 5:28
source share

I want to create a DLL and automatically export all characters without adding __declspec (dllexport) everywhere and without manually creating .def files. Is there any way to do this?

This is a late answer, but it provides details of Max's answer in Section (2). It also avoids scripts and uses a C ++ program called dump2def . The source code for dump2def below.

Finally, the steps below assume that you are working from the Visual Studio Developer prompt , which is the Windows terminal where vcvarsall.bat was running. You must make sure that the build tools such as cl.exe , lib.exe , link.exe and nmake.exe are in the way.

More information on this is provided here:

Export from DLL using DEF files
...

Instructions below to use:

  • static.lib - static library archive (* .a file on Linux)
  • dynamic.dll - dynamic library (* .so file in Linux)
  • import.lib - dynamic library (import library on Windows)

Also note that although you export everything from a DLL, clients should still use declspec(dllimport) for all the characters (classes, functions, and data) that they use. Also look at MSDN.

First take your objects and create a static archive:

 AR = lib.exe ARFLAGS = /nologo CXX_SRCS = a.cpp b.cpp c.cpp ... LIB_OBJS = a.obj b.obj c.obj ... static.lib: $(LIB_OBJS) $(AR) $(ARFLAGS) $(LIB_OBJS) /out:$@ 

Secondly, run dumpbin.exe/LINKERMEMEBER in the archive to create the *.dump file:

 dynamic.dump: dumpbin /LINKERMEMBER static.lib > dynamic.dump 

Third, run dump2def.exe for the *.dump file to get the *.dump file. The source code for dump2def.exe lower.

 dynamic.def: static.lib dynamic.dump dump2def.exe dynamic.dump dynamic.def 

Fourth, compile the DLL:

 LD = link.exe LDFLAGS = /OPT:REF /MACHINE:X64 LDLIBS = kernel32.lib dynamic.dll: $(LIB_OBJS) dynamic.def $(LD) $(LDFLAGS) /DLL /DEF:dynamic.def /IGNORE:4102 $(LIB_OBJS) $(LDLIBS) /out:$@ 

/IGNORE:4102 used to avoid this warning. It is expected in this case:

 dynamic.def : warning LNK4102: export of deleting destructor 'public: virtual v oid * __ptr64 __cdecl std::exception::'scalar deleting destructor'(unsigned int) __ptr64'; image may not run correctly 

When the dynamic.dll recipe is called, it creates a dynamic.lib import file and a dynamic.exp file, too:

 > cls && nmake /f test.nmake dynamic.dll ... Creating library dynamic.lib and object dynamic.exp 

As well as:

  C:\Users\Test\testdll>dir *.lib *.dll *.def *.exp Volume in drive C is Windows Volume Serial Number is CC36-23BE Directory of C:\Users\Test\testdll 01/06/2019 08:33 PM 71,501,578 static.lib 01/06/2019 08:33 PM 11,532,052 dynamic.lib Directory of C:\Users\Test\testdll 01/06/2019 08:35 PM 5,143,552 dynamic.dll Directory of C:\Users\Test\testdll 01/06/2019 08:33 PM 1,923,070 dynamic.def Directory of C:\Users\Test\testdll 01/06/2019 08:35 PM 6,937,789 dynamic.exp 5 File(s) 97,038,041 bytes 0 Dir(s) 139,871,186,944 bytes free 

Glue it together. This is what the Nmake build file looks like. This is part of the real Nmake file :

 all: test.exe test.exe: pch.pch static.lib $(TEST_OBJS) $(LD) $(LDFLAGS) $(TEST_OBJS) static.lib $(LDLIBS) /out:$@ static.lib: $(LIB_OBJS) $(AR) $(ARFLAGS) $(LIB_OBJS) /out:$@ dynamic.map: $(LD) $(LDFLAGS) /DLL /MAP /MAPINFO:EXPORTS $(LIB_OBJS) $(LDLIBS) /out:dynamic.dll dynamic.dump: dumpbin.exe /LINKERMEMBER static.lib /OUT:dynamic.dump dynamic.def: static.lib dynamic.dump dump2def.exe dynamic.dump dynamic.dll: $(LIB_OBJS) dynamic.def $(LD) $(LDFLAGS) /DLL /DEF:dynamic.def /IGNORE:4102 $(LIB_OBJS) $(LDLIBS) /out:$@ clean: $(RM) /F /Q pch.pch $(LIB_OBJS) pch.obj static.lib $(TEST_OBJS) test.exe *.pdb 



And here is the source code for dump2def.exe :

 #include <iostream> #include <fstream> #include <sstream> #include <string> #include <vector> #include <set> typedef std::set<std::string> SymbolMap; void PrintHelpAndExit(int code) { std::cout << "dump2def - create a module definitions file from a dumpbin file" << std::endl; std::cout << " Written and placed in public domain by Jeffrey Walton" << std::endl; std::cout << std::endl; std::cout << "Usage: " << std::endl; std::cout << " dump2def <infile>" << std::endl; std::cout << " - Create a def file from <infile> and write it to a file with" << std::endl; std::cout << " the same name as <infile> but using the .def extension" << std::endl; std::cout << " dump2def <infile> <outfile>" << std::endl; std::cout << " - Create a def file from <infile> and write it to <outfile>" << std::endl; std::exit(code); } int main(int argc, char* argv[]) { // ******************** Handle Options ******************** // // Convenience item std::vector<std::string> opts; for (size_t i=0; i<argc; ++i) opts.push_back(argv[i]); // Look for help std::string opt = opts.size() < 3 ? "" : opts[1].substr(0,2); if (opt == "/h" || opt == "-h" || opt == "/?" || opt == "-?") PrintHelpAndExit(0); // Add <outfile> as needed if (opts.size() == 2) { std::string outfile = opts[1]; std::string::size_type pos = outfile.length() < 5 ? std::string::npos : outfile.length() - 5; if (pos == std::string::npos || outfile.substr(pos) != ".dump") PrintHelpAndExit(1); outfile.replace(pos, 5, ".def"); opts.push_back(outfile); } // Check or exit if (opts.size() != 3) PrintHelpAndExit(1); // ******************** Read MAP file ******************** // SymbolMap symbols; try { std::ifstream infile(opts[1].c_str()); std::string::size_type pos; std::string line; // Find start of the symbol table while (std::getline(infile, line)) { pos = line.find("public symbols"); if (pos == std::string::npos) { continue; } // Eat the whitespace after the table heading infile >> std::ws; break; } while (std::getline(infile, line)) { // End of table if (line.empty()) { break; } std::istringstream iss(line); std::string address, symbol; iss >> address >> symbol; symbols.insert(symbol); } } catch (const std::exception& ex) { std::cerr << "Unexpected exception:" << std::endl; std::cerr << ex.what() << std::endl; std::cerr << std::endl; PrintHelpAndExit(1); } // ******************** Write DEF file ******************** // try { std::ofstream outfile(opts[2].c_str()); // Library name, cryptopp.dll std::string name = opts[2]; std::string::size_type pos = name.find_last_of("."); if (pos != std::string::npos) name.erase(pos); outfile << "LIBRARY " << name << std::endl; outfile << "DESCRIPTION \"Crypto++ Library\"" << std::endl; outfile << "EXPORTS" << std::endl; outfile << std::endl; outfile << "\t;; " << symbols.size() << " symbols" << std::endl; // Symbols from our object files SymbolMap::const_iterator it = symbols.begin(); for ( ; it != symbols.end(); ++it) outfile << "\t" << *it << std::endl; } catch (const std::exception& ex) { std::cerr << "Unexpected exception:" << std::endl; std::cerr << ex.what() << std::endl; std::cerr << std::endl; PrintHelpAndExit(1); } return 0; } 
0
Jan 07 '19 at 1:54
source share

No, you need a macro that resolves __declspec(dllexport) when it is included in a .cpp file that implements exported functions, and solves it in __declspec(dllimport) otherwise.

-3
Oct 22 '08 at 11:57
source share



All Articles