Build a MinGW-W64 Import Library with dlltool
By the end of this guide you will have a Windows module definition file, an export object and a static import library for a 64-bit DLL. The import library is the file a program links against; the DLL is 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 do not need root privileges. Work in a disposable build directory because the output names are overwritten if you rerun the commands.
Checkpoint: 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 select a different target or a different Binutils installation. The installed help accepts -D for the DLL name; although the local manual also documents the long spelling --dll-name, the short spelling is the portable choice for this installation.
Checkpoint: 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 named add. The name must match the symbol exported by the DLL. A definition file does not build the DLL and does not prove that the function exists.
Before generating files, inspect what you are about to use:
sed -n '1,20p' example.def
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.
Generate the import library and export object
- Pass the definition file to
dlltooland 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; keeping it aligned with the LIBRARY line avoids confusing a linker or loader.
Verify that both files exist and that the library is 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 can vary with the export list and tool version. The file command should identify libexample.a as a current ar archive.
Use the outputs in a build
- Link the export object into the DLL and the import library into the consuming program.
The general link 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 that dll.o actually defines the DLL and that program.o calls an exported function. They are deliberately separate from the dlltool step: dlltool makes the linker inputs, while the compiler driver performs the final links. If your cross-compiler uses a different driver name, substitute that driver without changing the library format.
For a DLL whose exports are marked in object-file .drectve sections, you can ask the tool to make 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 are exported. --export-all-symbols changes that policy and can expose more of an object file's global interface than intended. If you use it, review the generated definition file and consider --exclude-symbols name1,name2. Do not add --no-default-excludes casually, because it also permits special symbols that the normal exclusion list protects.
Checkpoint: identify an existing library
- Ask
dlltoolwhich 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 be treated as an error.
Choose the less surprising defaults
Libraries are deterministic by default in this installed build, so repeated builds can have stable archive metadata. You can state that policy explicitly with --deterministic-libraries. The opposite, --non-deterministic-libraries, records actual timestamps and user or group IDs and 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. The resulting executable also needs the static delay-import support library described by the Binutils documentation. For ordinary linking, use -l.
Do not use -n as a routine cleanup strategy. It preserves temporary assembler files, and repeating it also preserves temporary object files. That is useful for diagnosing an assembler failure, but it can leave confusing build debris. Rerunning the command with the same output names replaces generated files, so copy any files you need before doing that.
Done means
- The version check selected
x86_64-w64-mingw32-dlltoolfrom the expected MinGW-W64 package. - The
.deffile names the intended DLL and exports only reviewed symbols. libexample.aexists and is an ar archive.example.expexists when the DLL link needs an export object.-I libexample.areports the DLL name you expect.- The final linker command uses the import library for consumers and does not confuse it with the DLL itself.