1. Introduction

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 App_{1} uses version 1.o 0f a DLL library Lib_{DLL}:

DLL HELL WORKING

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 APP_{1} still expects the old API. As a result, APP_{1} will crash at runtime:

DLL HELL CRASH

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, APP_{1} and APP_{2}, share dll_{shared} whose version is 1.0.0.0. Now, we update dll_{shared} to a new version 2.0.0.0, and corresponding changes are made in APP_{1} but not in APP_{2}. So, APP_{2} will crash.

We solve the problem in a .NET system using side-by-side versioning. Here, each library follows the standard versioning system W.X.Y.Z:

  • W denotes the major number
  • X denotes a minor number
  • Y denotes the revised number
  • Z 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:

  1. The name of the assembly code
  2. Version number
  3. Culture (e.g., language code such as “en” or “de”)
  4. Public Key Token (to decrypt certain functions of the library)

Now, for the example above, we’ll have 2 versions of dll_{shared} in GAC. The application APP_{1} will use dll_{shared} with version 1.0.0.0 and APP_{2} will use dll_{shared} with version 2.0.0.0. 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.

4. Conclusion

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.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.