How to debug DLL issues in MinGW?
Asked Answered
A

1

3

When using MinGW, it's easy to run into a DLL hell, i.e. your program not running because it either doesn't find the required DLLs or finds incompatible versions of them.

This can manifest itself as:

  • "The code execution cannot proceed because __.dll was not found."
  • "procedure entry point __ could not be located in the dynamic link library __"
  • Application not starting without any error message.

How do I fix this?


(This is intended as a canonical duplicate on this topic.)

Alabaster answered 9/6 at 10:45 Comment(0)
A
5

What do the errors mean?

Your program requires several .dll ("shared libraries") to run. Those are either parts of the standard library (distributed with your compiler), or third-party libraries you're using.

  • "execution cannot proceed because __.dll was not found" — the required .dll wasn't found in one of the several predefined search paths (see below).

  • "procedure entry point … could not be located …" — the required .dll was found, but not the correct version of it.

    The filename in the error message can be a red herring, it's not necessarily the offending library.

  • Application not starting without any errors — the error might not show when running from the console or from an IDE. Double-click your .exe in the Explorer to see the full error message, which will be one of the above. (By "explorer" I mean the Windows "Files" app, not e.g. the VSC tab with the same name.)

Where does my program look for the DLLs?

You can refer to the manual, but a short summary is: (the search happens in this order)

  • The directory where your .exe is located.

  • C:\Windows and some of its subdirectories. ( ⚠️ Never modify those or copy anything in there!)

  • Directories in PATH, in the order they're listed. (PATH is a an "environment variable" (a system setting) containing a list directories that are, among other things, searched for DLLs).

    There are two PATH settings, system-wide and per-user, the former is searched first.

How to fix?

There are several options, from simplest to harderst, pick one.

  1. Static linking

    Add -static to your linker flags. This makes your program not depend on the DLLs in the first place, embedding their contents into the executable.

    Why is it not good?

    • Your users can't easily update the libraries to newer versions if they need to.

    • Some libraries are licensed in a way that hinders this. (E.g. with LGPL license, linking statically requires to open-source your software.)

  2. Run from MSYS2 terminal (if you're using MSYS2)

    The MSYS2 terminal automatically adjusts its own PATH to have the compiler installation as the first thing in there (which includes the standard library DLLs you likely have problems with).

    There are several MSYS2 shortcuts in the start menu, and you MUST pick the correct one (usually UCRT64 or MINGW64, but read What are MSYS2 environments? How do I pick one? to know for sure).

    While this fixes the problem for you, you can't expect your users to install MSYS2, so you should look into other solutions in the long term.

    If this didn't help, you likely didn't use the correct MSYS2 shortcut. You can also consult "What not to do" below, the part about junk in C:\Windows.

  3. Fix your PATH

    Modify your PATH to include the directory with the DLLs. Usually it will be the compiler installation directory (since that's where its standard library is), e.g. C:\path\to\MinGW\bin (substitute your path). If you're using MSYS2, this will be C:\msys64\ucrt64\bin or C:\msys64\mingw64\bin (read What are MSYS2 environments? How do I pick one? to know which directory to choose).

    (This only makes sense for the standard library DLLs. For third-party libraries, always copy them next to the executable as explained in (4) below.)

    This directory must be the first thing in the PATH. Otherwise you risk picking up incompatible versions of the same DLLs from preceding directories.

    There are two PATH settings, system-wide and user-specific. The former has priority, so use the system-wide PATH.

    While this fixes the problem for you, you can't expect your users to install MinGW, so you should look into other solutions in the long term.

    How do I set PATH?

    • In the settings, type "env" into the search box, click Edit the system environment variables, click Environment Variables... at the bottom, then in the second list titled System variables double-click Path, type your path (e.g. C:\path\to\MinGW\bin) into an empty cell at the bottom, click Move Up repeatedly to make it the first entry, then click OK in all the open dialogues.

    • Don't forget to restart the console/IDE after changing PATH!

    If this doesn't work and you're sure you did everything correctly, consult "What not to do" below, the part about junk in C:\Windows.

  4. Copy the required DLLs next to the executable

    Copying DLLs is the best long-term solution. You need to do this anyway to distribute your app to others (this or link statically, see above). The next section explains how to do this.

Which DLLs should I copy next to the executable?

The required DLLs could be mentioned in the error message, but:

  • You can't be sure the list is complete.
  • This approach can't be automated.

There are tools that will tell you the list of DLLs your app depends on, e.g. ntldd. It is dated, but gets the job done.

Note: To do this, you need to have PATH set up correctly (see above; or be running in the MSYS2 terminal as explained above), so you should be already able to run your app. And you must not use -static.

MSYS2 users can install ntldd using pacman -S ...-ntldd (e.g. mingw-w64-ucrt-x86_64-ntldd or mingw-w64-x86_64-ntldd, consult What are MSYS2 environments? How do I pick one?). If you don't use MSYS2, download from the GitHub link above.

Run ntldd -R my_program.exe. You will see a long list. Ignore anything that's not in your compiler directory (and isn't a third-party library you installed separately). In MSYS2 terminal you can run ntldd -R my_program.exe | grep 'C:\\msys64' to filter automatically.

I ended up with libstdc++-6.dll, libgcc_s_seh-1.dll, libwinpthread-1.dll, but your list can be different depending on the type of MinGW you use.

Copy those DLLs next to your executable, and you should be done. Writing a script to automate this is left as an exercise to the reader.

What not to do?

  • Don't copy third-party libraries into your compiler installation.

    • You might lose track of what you copied.

    • If you're using MSYS2, your custom libraries can conflict with what the package manager installs.

    Instead keep them in a separate directory (use -I and -L to tell your compiler where it is), and copy the DLLs next to the executable.

  • Don't copy anything into C:\Windows.

    Some users (or even installers) in their infinite wisdom might copy a DLL into C:\Windows. This will cause problems, because this directory has priority over the contents of PATH.

    In general, if you see the same DLL both in your compiler installation and in C:\Windows, it might mean that the latter doesn't belong in there, and should be deleted. But use your best judgement, it's your responsibility to not break your system.

Alabaster answered 9/6 at 10:45 Comment(2)
I wonder why has nobody ever done a script that is called "package" that just recursively uses ntldd and puts everything in your directory of choice. I think chatgpt could do it in 1 hourPapa
@Papa ntldd is already recursive with the -R flag. But yeah, it shouldn't be hard, you just need to reject libs in C:\Windows and perhaps some others.Alabaster

© 2022 - 2024 — McMap. All rights reserved.