Understanding DLL Files and DLL Hell


A dynamic-link library (DLL) file is an executable file that allows programs to share code and other resources necessary to perform particular tasks. Microsoft Windows provides DLL files that contain functions and resources that allow Windows-based programs to operate in the Windows environment.

DLLs most often appear as files with a .DLL extension; however, they may also have an .exe or other extension. For example, the Shell.dll file provides the Object Linking and Embedding (OLE) drag-and-drop routines that Windows and other programs use. The Kernel.exe, User.exe, and Gdi.exe files are examples of DLLs with .exe extensions. They provide code, data, or routines to programs running in the Windows operating system. For example, one of these files provides the "Create Window" function that programs use when a new window is created on the screen. In Windows, an installable driver is also a DLL. A program can open, enable, query, disable, and close the driver based on instructions written in the DLL file.

DLLs can be found in the Windows directory, the Windows\System directory, or a program's directory.

If you start a program and one of its DLL files is missing or damaged, you may receive an error message such as "Cannot find xyz.dll." If you start a program that has an outdated DLL file or mismatched DLL files, the error message "Call to undefined dynalink" might appear. If this happens, you must get a copy of the DLL file and put it in the proper directory before the program will run correctly.

To determine the origin of a DLL file

More about DLL's

The initial purpose for DLLs was saving both disk space and memory required for applications. Any code which many applications share could be separated into a DLL which only exists as a single disk file and a single instance in memory. Extensive use of DLLs allowed early versions of Windows to work under tight memory conditions.

DLLs provide the standard benefits of shared libraries, such as modularity. Modularity allows changes to be made to code and data in a single self-contained DLL shared by several applications without any change to the applications themselves. This basic form of modularity allows for relatively compact patches and service packs for large applications, such as Microsoft Office, Microsoft Visual Studio, and even Microsoft Windows itself.

Another benefit of the modularity is the use of generic interfaces for plug-ins. A single interface may be developed which allows old as well as new modules to be integrated seamlessly at run-time into pre-existing applications, without any modification to the application itself. This concept of dynamic extensibility is taken to the extreme with ActiveX.

With this many benefits, using DLLs also has a drawback: the DLL hell, when several applications conflict on which version of a shared DLL library is to be used. Such conflicts can usually be resolved by placing the different versions of the problem DLL into the applications' folders, rather than a system-wide folder; however, this also nullifies the savings provided by using shared DLLs. Currently, Microsoft .NET is targeted as a solution to the problem of DLL hell by allowing side-by-side coexistence of different versions of a same shared library. With modern computers which have plenty of disk space and memory, it can be a reasonable approach.

Features of DLL files

Memory management
In Win32, the DLL files are organized into sections. Each section has its own set of attributes, such as being writable or read-only, executable (for code) or non-executable (for data), and so on.

The code sections of a DLL are usually shared among all the processes that use the DLL; that is, they occupy a single place in physical memory, and do not take up space in the page file. If the physical memory occupied by a code section is to be reclaimed, its contents are discarded, and later reloaded directly from the DLL file as necessary.

In contrast to code sections, the data sections of a DLL are usually private; that is, each process using the DLL has its own copy of all the DLL's data. Optionally, data sections can be made shared, allowing inter-process communication via this shared memory area. However, because user restrictions do not apply to the use of shared DLL memory, this creates a security hole; namely, one process can corrupt the shared data, which will likely cause all other sharing processes to behave undesirably. For example, a process running under a guest account can in this way corrupt another process running under a privileged account. This is an important reason to avoid the use of shared sections in DLLs.

When a DLL is compressed by an executable packer, such as UPX, all of its code sections are marked as read-and-write, and therewith unshared. Read-and-write code sections, much like private data sections, are private to each process and backed up by the page file. Thus, compressing DLLs increases both their memory and disk space consumption, and should be generally avoided for shared DLLs.

Symbol resolution and binding
Each function exported by a DLL is identified by a numeric ordinal and optionally a name. Likewise, functions can be imported from a DLL either by ordinal or by name. It is common for internal functions to be exported by ordinal only. For most Windows API functions only the names are preserved across different Windows releases; the ordinals are subject to change. So, one cannot reliably import Windows API functions by their ordinals.

Importing functions by ordinal does not necessarily provide better performance than importing them by namC: export tables of DLLs are ordered by name, so binary search can be used to find a function in this table by its name. On the other hand, only linear search can be used to find a function by its ordinal.

It is also possible to bind an executable to a specific version of DLL, that is, to resolve the addresses of imported functions at compile-time. For bound imports, the linker saves the timestamp and checksum of the DLL to which the import is bound. At run-time Windows checks to see if the same version of library is being used, and if so, Windows bypasses processing the imports. Otherwise, if the library is different from the one which was bound to, Windows processes the imports in a normal way.

Bound executables load somewhat faster if they are run in the same environment that they were compiled for, and exactly the same time if they are run in a different environment, so there's no drawback for binding the imports. For example, all the standard Windows applications are bound to the system DLLs of their respective Windows release. A good opportunity to bind an application's imports to its target environment is during the application's installation.

Explicit run-time linking
DLL files may be explicitly loaded at run-time, a process referred to simply as run-time dynamic linking by Microsoft, by using the LoadLibrary (or LoadLibraryEx) API function. The GetProcAddress API function is used to lookup exported symbols by name, and FreeLibrary � to unload the DLL. These functions are analogous to dlopen, dlsym, and dlclose in the POSIX API.

Note that with implicit run-time linking, referred to simply as load-time dynamic linking by Microsoft, if the linked DLL file cannot be found, Windows will display an error message and fail to load the application. The application developer cannot handle the absence of DLL files linked implicitly by the compile-time linker. On the other hand, with explicit run-time linking, developers have the opportunity to provide a graceful fall-back facility.

The procedure for explicit run-time linking is the same in any language, since it depends on the Windows API rather than language constructs.