Build a MinGW-W64 Import Library with dlltool
x86_64-w64-mingw32-dlltool turns a Windows module definition file into an import library and an export object. The import library and the DLL are two different files, and only the DLL gets loaded at run time.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 15 minutes if you already have a .def file, or 30 minutes if you are checking exports from source as well. The commands below need no root privileges. Work in a disposable build directory, because the output names get overwritten if you rerun the commands.
1. Confirm the toolchain
Check the executable and its version:
x86_64-w64-mingw32-dlltool --version
dpkg-query -W -f='${Package} ${Version}\n' binutils-mingw-w64-x86-64
On the machine used for this guide, the command prints GNU x86_64-w64-mingw32-dlltool (GNU Binutils) 2.41.90.20240122, from package binutils-mingw-w64-x86-64. The x86_64-w64-mingw32ucrt-dlltool alias has the same installed manual and purpose.
Use the target-prefixed command throughout. Calling an unqualified dlltool can pick a different target, or a different Binutils installation entirely. The installed help accepts -D for the DLL name; the local manual also documents the long spelling --dll-name, but the short spelling is the more portable choice on this installation.
2. Prepare a small definition file
Create a working directory and describe the exported function:
mkdir -p dlltool-demo
cd dlltool-demo
cat > example.def <<'EOF'
LIBRARY example.dll
EXPORTS
add
EOF
A .def file is a text description of the DLL interface. This one names example.dll and exports a function called add, and that name must match the symbol the DLL actually exports. A definition file does not build the DLL, and it does not prove the function exists.
Inspect what you are about to use before generating anything:
sed -n '1,20p' example.def
Checkpoint
Expected output is the three lines shown above. Stop here if the DLL name or export list is not the interface you intend to publish. Incorrect exports usually surface later as link errors or missing-entry-point errors.
3. Generate the import library and export object
Pass the definition file to dlltool and request both outputs:
x86_64-w64-mingw32-dlltool \
-d example.def \
-D example.dll \
-l libexample.a \
-e example.exp
-d reads the definition file. -l creates the import library, conventionally named with a .a suffix for the GNU linker. -e creates the binary export object. The -D value records the DLL name in the generated interface data, and keeping it aligned with the LIBRARY line avoids confusing a linker or loader later.
Verify both files exist and that the library is actually an archive:
ls -l libexample.a example.exp
file libexample.a
For this example the installed tool creates a 2,492-byte libexample.a and a 600-byte example.exp. Sizes shift with the export list and tool version. file should identify libexample.a as a current ar archive.
4. Use the outputs in a build
Link the export object into the DLL and the import library into the consuming program. The general shape is:
x86_64-w64-mingw32-gcc dll.o example.exp -o example.dll
x86_64-w64-mingw32-gcc program.o libexample.a -o program.exe
Those commands assume dll.o actually defines the DLL and program.o calls an exported function. They are deliberately separate from the dlltool step: dlltool makes the linker inputs, and the compiler driver performs the final links. Different cross-compiler driver name? Swap it in without changing the library format.
For a DLL whose exports are marked in object-file .drectve sections, you can ask the tool to build a definition file instead:
x86_64-w64-mingw32-dlltool -z discovered.def dll.o
The default is conservative: only symbols explicitly listed in a .def file or marked in .drectve sections get exported. --export-all-symbols changes that policy and can expose more of an object file's global interface than you intended. Use it and review the generated definition file, and consider --exclude-symbols name1,name2 too. Do not add --no-default-excludes casually, it also permits special symbols the normal exclusion list otherwise protects.
5. Identify an existing library
Ask dlltool which DLL an import library refers to:
x86_64-w64-mingw32-dlltool -I libexample.a
Expected output is:
example.dll
-I writes the associated DLL name to standard output. It fails if the file is missing or is not an import library. Add --identify-strict when more than one associated DLL should count as an error.
6. Choose the less surprising defaults
Libraries are deterministic by default in this installed build, so repeated builds keep stable archive metadata. State that policy explicitly with --deterministic-libraries if you like. The opposite, --non-deterministic-libraries, records real timestamps and user or group IDs, which makes byte-for-byte comparisons less useful.
Use -y delay.lib only when you have deliberately chosen delay loading. It creates a delay-import library, not an ordinary replacement, and the resulting executable also needs the static delay-import support library described in the Binutils documentation. For ordinary linking, stick with -l.
Do not use -n as a routine cleanup strategy. It preserves temporary assembler files, and repeating it preserves temporary object files too. Useful for diagnosing an assembler failure, but it can leave confusing build debris behind. Rerunning the command with the same output names replaces generated files, so copy anything you need first.
Done means
- Confirmed the toolchain. The version check selected
x86_64-w64-mingw32-dlltoolfrom the expected MinGW-W64 package. - Reviewed the definition file. The
.deffile names the intended DLL and exports only reviewed symbols. - Built the import library.
libexample.aexists and is an ar archive. - Built the export object.
example.expexists when the DLL link needs one. - Checked the identity.
-I libexample.areports the DLL name you expect. - Linked correctly. The final linker command uses the import library for consumers and does not confuse it with the DLL itself.