DLL File Documentation
Summary
A Dynamic Link Library (DLL) is a Windows file holding compiled code and resources that programs load at run time instead of building the code into every executable. It uses the same Portable Executable (PE) format as an .exe, with the MIME type application/vnd.microsoft.portable-executable. A .dll is not meant to be double-clicked: it has no startup entry point and only runs when a host process loads it. If an app reports a DLL is missing, reinstall the app or the matching runtime rather than downloading the file.
Technical details
| Feature | Value |
|---|---|
| Full name | Dynamic Link Library |
| File extension | .dll |
| MIME type | application/vnd.microsoft.portable-executable |
| Format type | Windows PE (Portable Executable) shared library — compiled binary |
| Developer | Microsoft |
| Introduced | 1985 (Windows 1.0, NE format); current PE format since Windows NT 3.1, 1993 |
| Specification | Microsoft PE/COFF Specification |
| Open standard | Partial — the PE format is documented publicly by Microsoft |
| Byte order | Little-endian |
| Container / base format | PE/COFF (MZ DOS stub + PE headers) |
| Magic number (hex) | 4D 5A (“MZ”) at offset 0; 50 45 00 00 (“PE\0\0”) at e_lfanew |
| DLL flag | Bit 0x2000 (IMAGE_FILE_DLL) in the COFF Characteristics field |
| Entry point | Optional DllMain; no standalone startup — loaded, not launched |
| Loaded by | Windows loader at process start or via LoadLibrary |
| Code type | Native machine code, or CIL bytecode for .NET managed assemblies |
| Registration | COM DLLs registered with regsvr32 (needs DllRegisterServer) |
| Digital signature | Optional Authenticode certificate |
| Equivalents | .so (Linux), .dylib (macOS) |
| Related extensions | .exe, .sys, .ocx, .drv, .so, .dylib |
| Specification URL | learn.microsoft.com/windows/win32/debug/pe-format |
What is a DLL file?
DLL stands for Dynamic Link Library. It is a Windows file that holds compiled code, data and resources meant to be shared by more than one program and loaded while a program is already running, rather than being linked permanently into each executable. Microsoft introduced the idea in Windows 1.0 in 1985; the modern file layout has used the Portable Executable (PE) format since Windows NT 3.1 in 1993. Windows itself is assembled from thousands of DLLs, among them kernel32.dll, user32.dll and ntdll.dll, and almost every installed application ships its own.
A DLL is the same kind of file as an EXE. Both are PE images: an MS-DOS stub followed by PE and COFF headers, section tables and sections. The two differ in only two important ways. A DLL is marked with a flag in its header, and instead of a single program entry point it publishes an export table of functions that other modules call. That is why double-clicking a .dll does nothing useful: there is no main to run. The file becomes active only when the Windows loader maps it into a process, either at startup or on demand through the LoadLibrary API.
The MZ stub and the PE header chain
Every DLL begins with the two ASCII bytes MZ (4D 5A), the signature of the old DOS executable header named after its designer, Mark Zbikowski. Modern Windows never executes the DOS stub, but it is kept for backward compatibility and normally prints “This program cannot be run in DOS mode” if the file is launched under DOS. The stub’s job today is to point forward: a 4-byte field named e_lfanew at offset 0x3C stores the file offset of the real PE header.
Offset Field Meaning
0x00 e_magic 'MZ' (4D 5A) — DOS header signature
0x3C e_lfanew uint32: file offset of the PE signature
<e_lfanew>
+0x00 Signature 'PE\0\0' (50 45 00 00)
+0x04 COFF File Header machine, NumberOfSections, Characteristics ...
+0x18 Optional Header magic (0x10B = PE32, 0x20B = PE32+), entry point,
ImageBase, section alignment, data directories
At the offset named by e_lfanew the reader finds the 4-byte signature 50 45 00 00 (PE\0\0). Immediately after it is the 20-byte COFF file header, then the optional header, whose magic value distinguishes a 32-bit image (0x10B, PE32) from a 64-bit one (0x20B, PE32+). All multi-byte integers in the PE format are little-endian, which is the opposite of container formats like MP4 that use big-endian fields.
The Characteristics flag that makes a DLL a DLL
What separates a library from an application is a single bit. The COFF file header contains a 16-bit Characteristics field, and bit 0x2000 (IMAGE_FILE_DLL) marks the image as a dynamic-link library. A normal DLL usually carries a Characteristics value of 0x210E, which combines IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LINE_NUMS_STRIPPED, IMAGE_FILE_LOCAL_SYMS_STRIPPED, IMAGE_FILE_32BIT_MACHINE and IMAGE_FILE_DLL.
| Characteristics bit | Value | Meaning |
|---|---|---|
IMAGE_FILE_EXECUTABLE_IMAGE | 0x0002 | The image is valid and can be run/loaded |
IMAGE_FILE_32BIT_MACHINE | 0x0100 | Built for a 32-bit word machine |
IMAGE_FILE_DLL | 0x2000 | The image is a DLL, not a standalone program |
Because the flag lives in the header and not in the file name, renaming a DLL to .exe does not turn it into a runnable program, and renaming an EXE to .dll does not make it loadable as a library. The loader reads the flag, not the extension.
The export table: how functions leave a DLL
The purpose of a DLL is to offer code to other modules, and it does that through the export directory, one of the entries in the optional header’s data-directory array. The export directory lists the functions the library makes callable, each identified either by a name or by a numeric ordinal. It is built from three parallel arrays.
IMAGE_EXPORT_DIRECTORY
├─ AddressOfFunctions RVAs of each exported function's code
├─ AddressOfNames RVAs of the exported names (ASCII strings)
└─ AddressOfNameOrdinals index into AddressOfFunctions for each name
To resolve GetProcAddress(hModule, "SomeFunction"), the loader binary-searches AddressOfNames for the string, reads the matching entry in AddressOfNameOrdinals to get an index, and uses that index into AddressOfFunctions to obtain the function’s address (an RVA, a relative virtual address measured from the image base). Exporting purely by ordinal, with the names stripped, is how some system and third-party libraries hide their API surface. The complementary import table records the reverse: the DLLs and functions this module itself needs, which the loader must satisfy before the module can run.
Sections: .text, .data and the .rsrc resource tree
After the headers, a DLL is divided into named sections, each with its own file offset, virtual address and access flags. The common ones are .text for executable code, .data and .rdata for initialised and read-only data, .reloc for base relocations, and .rsrc for resources. In a native DLL .text holds machine code; in a .NET assembly it holds CIL (Common Intermediate Language) bytecode plus a small native stub.
The .rsrc section is a tree of icons, bitmaps, dialog templates, string tables, and a version-info block. This is why tools such as Resource Hacker can open a DLL and extract or replace its icons without touching the code: they walk the resource directory, not the instructions. Extracting an icon from a system DLL (for example shell32.dll) to an ICO file is the one genuinely common “conversion” people perform on a DLL, and it is really a resource extraction.
Load-time linking, run-time linking and DllMain
There are two ways a DLL enters a process. Load-time (implicit) linking happens when a program was compiled against an import library: the DLL is named in the executable’s import table, and the Windows loader maps it in automatically at process start, failing with a “missing DLL” error if it cannot be found. Run-time (explicit) linking happens when the program calls LoadLibrary("name.dll") itself and then GetProcAddress to fetch each function pointer, which is how plug-ins and codecs are loaded on demand.
A DLL may export an optional entry point named DllMain. The loader calls it with a reason code on four events: process attach, process detach, thread attach and thread detach. Unlike an EXE’s main, DllMain is not a program that runs to completion; it is a short notification hook, and doing heavy work inside it (especially calling other DLLs) is a well-known source of loader-lock deadlocks.
Managed .NET assemblies versus native DLLs
Not every .dll contains machine code. A .NET assembly is a PE file whose .text section carries CIL bytecode and a metadata table describing every type, method and field. Because the metadata is so complete, decompilers such as ILSpy or dotPeek reconstruct near-original C# from a managed DLL. A native DLL compiled from C or C++ keeps no such metadata, so a disassembler like Ghidra can only recover approximate C, and the original variable names and structure are gone. The presence of a CLR header (the COM descriptor data directory, index 14) is what tells a reader whether a DLL is managed or native.
Is a DLL file safe? DLL hijacking and search-order abuse
A DLL is executable code, so it is exactly as dangerous as an EXE, and attackers exploit that in a specific way. In DLL hijacking (also called DLL search-order hijacking or sideloading, catalogued as MITRE ATT&CK technique T1574.001), an attacker places a malicious DLL with the expected name in a directory that Windows searches before the legitimate one. A trusted, often signed, application then loads the rogue library and runs the attacker’s code inside its own process, inheriting its privileges and slipping past defences that trust the parent program. The dangerous property is that no exploit of the application is needed: it loads the wrong file by name.
Two practical rules follow. First, never download a single DLL from a “DLL download” or “DLL fixer” website; those are a long-standing malware channel, and a mismatched version usually will not fix the error anyway. Fix a genuine “missing DLL” message by reinstalling the application, installing the official Microsoft Visual C++ Redistributable (for vcruntime140.dll, msvcp140.dll and similar) or the DirectX End-User Runtime (for d3dx9_*.dll, xinput1_3.dll), or repairing system files with sfc /scannow. Second, check an unknown DLL’s Authenticode signature (Properties › Digital Signatures) and scan it before trusting it. Deleting files from C:\Windows\System32 to free space breaks Windows.
Frequently asked questions
What single thing distinguishes a DLL from an EXE?
Bit 0x2000 (IMAGE_FILE_DLL) in the COFF Characteristics field. Both are PE images with an MZ/PE\0\0 header chain; the flag, plus the presence of an export table instead of a real entry point, is what makes the loader treat one as a library and the other as a program.
Why does regsvr32 fail on some DLLs?
Because regsvr32 only registers COM DLLs, which must export a DllRegisterServer function. An ordinary DLL has no such export, so regsvr32 returns error 0x80070057 or module error 126. Non-COM libraries are loaded directly by the applications that need them and require no registration.
References
- Microsoft Learn — PE Format specification
- Microsoft Learn — What is a DLL
- MITRE ATT&CK — Hijack Execution Flow: DLL Search Order Hijacking
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.