In this tutorial, we’ll the DLL hell problem and its possible solutions.
2. DLL Hell Problem
The DLL (dynamically linked library) hell problem is one of the prime weaknesses of dynamic linking.
This problem occurs when the DLL that is loaded by the operating system differs from the version our application expects. As a result, we get unresolved symbols. For example, that can happen if some functions have different signatures in the DLL’s newer version.
Let’s say our application uses version 1.o 0f a DLL library :
Now, let’s suppose we update this DLL to version 1.1. So, in the new version, a few functions are changed, but our application still expects the old API. As a result, will crash at runtime:
The reason is that we have no built-in mechanisms to check for backward compatibility when working with DLLs. Thus, even minor changes to a DLL can cause problems.
The most obvious way of solving this problem is to turn to static linking. This way, we eliminate the DLLs but lose all resource optimization we get from runtime library sharing between different applications.
2.1. DLL Hell Problem in Windows
The DLL Hell problem is much more specific to Windows-based software systems because they often have incompatible support libraries.
In Windows systems, applications often install popular DLLs globally. However, each may require a different version of the same DLL.
Nowadays, this problem is localized to .NET systems and occurs very infrequently. In a .NET system, the loader is smart enough to pick the right assembly version of the library by decoding the version number and doing a simple table lookup from the Windows registry.
2.2. DLL Hell Problem in Linux
We don’t find the DLL Hell problem in most Linux or Unix-based systems.
Most Linux distributions have a package manager (such as apt for Debian). The package manager installs all dependencies needed for the new software. Hence, we don’t have to manually install each dependency for the applications we want to use.
Further, most Unix applications use shared libraries. Each shared library is a separate package with a well-defined version. Since a Linux application usually declares its dependency with an explicit shared library name and version, the system knows which package to use. For instance, software X can have dependency statements such as “X depends on shared library Y (version >=6.0)”. That automates the choice of the right version of Y.
3. The Solution
We’ll show two ways of solving this problem.
3.1. Side-by-Side Versioning in .NET
Let’s say two applications, and , share whose version is 220.127.116.11. Now, we update to a new version 18.104.22.168, and corresponding changes are made in but not in . So, will crash.
We solve the problem in a .NET system using side-by-side versioning. Here, each library follows the standard versioning system :
- denotes the major number
- denotes a minor number
- denotes the revised number
- denotes the new version number
The operating system places each shared library’s assembly at the central location we call the Global Assembly Cache (GAC). Further, each assembly entry in the GAC has four fields:
- The name of the assembly code
- Version number
- Culture (e.g., language code such as “en” or “de”)
- Public Key Token (to decrypt certain functions of the library)
Now, for the example above, we’ll have 2 versions of in GAC. The application will use with version 22.214.171.124 and will use with version 126.96.36.199. Thus, we load different versions of the shared library assemblies from GAC at run-time.
However, side-by-side versioning can be used only if DLLs have no shared dependencies.
3.2. Make Application Portable
We can also make our application portable. This way, our program will have its private copy of any DLLs it requires. These private DLLs share no components so we can load them as standalone pieces of software. Further, this approach works smoothly as the operating system searches the application’s executable directory before any system memory location.
However, it makes our applications vulnerable to virus injection. A hacker can easily corrupt our library copy and cause our program to crash or exploit sensitive data. Thus, this increased flexibility comes at the expense of security if we don’t keep our private DLLs up to date with the security patches.
In this article, we explored the DLL hell problem and its causes. We also enumerated a few solutions. We can solve the DLL Hell problem most efficiently by adopting side-by-side versioning. There, we store different versions of the library at a central location so that each application can fetch exactly the version it needs.