Diagnosing STATUS_INVALID_IMAGE_FORMAT (C000007B) Errors
There are instances where you may encounter an error message that says something like, “The application was unable to start correctly” with an error code of C000007B. There are other error codes where you may see that message, but I'm only going to focus on the STATUS_INVALID_IMAGE_FORMAT C000007B here. In fact, there are more than 100 places in the Windows code where the load of an executable image (.exe, .dll, .ocx, etc.) is being attempted, but fails with this error code. As you might expect, there are many criteria for an executable image to be considered valid for loading. For this reason, you may search the Internet and find all sorts of different suggestions for how to solve this type of problem, and those solutions are typically limited to just a subset of the reasons for this error.
Really an Invalid Image Format?
The most obvious reason for this error would be that the file itself truly is in a bad state, and is truly not an executable image. This could mean the file was corrupted in some way, or a file that really isn't an executable was renamed to have .exe, .dll, or .ocx extension even though it isn't truly that type of file. If you happen to know which image file is being rejected, the best resolution might be to ensure that the file system is in good shape (check the disk for errors and repair) and replace the file with a good copy, which could mean reinstalling the software that should have put it in place.
Or was it just meant for another architecture?
But quite often, the reason is that the executable file doesn't match the current architecture, even though the file would be a valid image if used in the architecture for which it was built. For instance, an x64 process is told to load a DLL, and the DLL that is found was built for x86. Since a 32-bit DLL doesn't match the expected 64-bit architecture, the system returns the error that it has an invalid format. Or, a 64-bit .exe can't be run on a 32-bit Windows machine. Likewise, binaries built for operating systems with other architectures (ARM, IA64, etc.) won't run on a Windows edition that expects an IA32 or AMD64 instruction set. In my experience, the vast majority of times this error is encountered, it is for a reason like this. This current blog post was inspired by a customer case in which the underlying problem turned out to be that the x86 (32-bit) build of the mfc140ud.dll had been copied into the folder where the x64 (64-bit) build was expected. His 64-bit application tried to load the 32-bit DLL, and a cryptic error was logged in the Application event log with "Faulting module name: ntdll.dll" and "Exception code: 0xc000007b". No more clue about the DLL in question was given.
Many times, it will not be clear to you exactly which file is being rejected. You may be executing a program that loads numerous files, and the system just tells you that the application was unable to start correctly. You may also find the code C000007B in the event log, and not know what's going on. There are a handful of ways that you can try to pin it down.
The Dependency Walker utility was written many years ago to help identify the hierarchy of dependencies required for an executable to load. One of its main purposes was to identify things that may be missing. It also helps to identify which DLLs will be loaded, from which locations, and which exported symbols from each dependency are used by the other binaries that depend on them. It has been an extremely useful tool for a lot of reasons, and should probably be in your "tool box" if you do software development, system administration, or other similar jobs. There are a few different builds of this tool for different architectures, and you'll probably want to install at least a couple of these.
Install and run the build of depends.exe that matches the architecture of the binary you are trying to execute. In my customer's case, we ran the x64 build of depends because his .exe was built for that architecture. It's quite likely that you will see some warnings about some missing DLLs. Some of these can be ignored, and the FAQ for Dependency Walker will discuss some of these. In the top left, there will be a tree view with the dependency hierarchy. In the middle section below that, there will be a list of the dependency modules identified. Right click on one of the file names in that list, and ensure that "Full Paths" is selected. In one of the columns to the right, labeled "CPU", you should see the architecture that matches your main executable. In my customer's case, we saw "x64" for almost all of the modules, but "x86" in red on the row for the mfc140ud.dll that was wrong. Because we could see the full path to the DLL, it became obvious pretty quickly that the 32-bit build of that DLL was in the folder where the 64-bit build belonged, and the resolution was obvious (put the 64-bit build there in its place).
The Process Monitor tool is also a "must have" for anyone in IT for a Windows system. It captures all file system, registry, and other process/thread activity executing on the machine. This can be an enormous amount of data, but fortunately it can be filtered either during capture or during review of the log. It will also reveal all attempts to find and load a DLL, along with the result of these attempts. In our case, the attempt to execute his program led to the Windows Error Reporting (WER) to raise an error message and an event log entry. So, we used the filter option to include only those events where the Process Name was the name of his executable, or where it was werfault.exe (two different filter criteria). This reduced the quantity of entries to examine. We could see the point at which werfault.exe had been launched, telling us that the problem had occurred right before that. Looking above that, we found the last entries triggered by his application, and that it was searching for mfc140ud.dll in several other location where it didn't exist, with "NAME NOT FOUND" results, before raising the fatal error about the invalid image format. Prior to searching all the places where it didn't exist, we saw that it found mfc140ud.dll in the folder where the 64-bit DLLs reside. But it wasn't obvious right away why it kept on looking in the wrong places. In retrospect, we now understand that it was because it found the 32-bit build, determined it was wrong, and kept looking for a 64-bit build. When it didn't find one, it then complained about the invalid image format of the 32-bit DLL. In the future, we'll know how to interpret that pattern.
WinDbg with Loader Snaps
The Windows Debugger and the Global Flags tool are two among the many Debugging Tools for Windows, yet another suite of valuable tools for anyone who needs to diagnose software problems. When diagnosing a failure of a program to start, whether because of the invalid image format error, or any of a hundred other reasons, you need to know how to use gflags.exe and windbg.exe to examine the "loader snaps". These are detailed diagnostic messages emitted by the Windows loader as it attempts to load and initialize all the dependencies of an executable. To enable the display of loader snaps in the debugger, first run the Global Flags tool (gflags.exe) from the folder where your Debugging Tools for Windows were installed. Select the "Image File" tab, type the name of your main executable (like "myprogram.exe") in the text box, then press the Tab key to refresh the display. This should cause all the checkboxes that were grayed out before to be selectable. Near the top left, you will see "Show loader snaps". Check that box. It will be worthwhile to you to read about the other options here for the future. For now, this is the only one we need. Press "OK". This gflags tool creates or modifies subkeys of the "Image File Execution Options" key in the registry. You could learn what changes to make there directly, but using gflags is easier and safer. [Note: "safer" does not mean absolutely safe. You still have the capability to make improper registry changes even using gflags. For instance, using a page file that is too small with system-wide full-page heap could render the system unbootable. Read about any settings you consider using.]
Now that loader snaps are enabled, run the Windows Debugger (windbg.exe) for the appropriate bitness of the application you are debugging. From the File menu, choose "Open Executable". Complete the form for any command line arguments and starting directory options you may need, and press "OK". You should see an enormous amount of information in the output window as the loader emits all the details of what it's doing. When it encounters an invalid image (or any other problem) and fails to load, you should see details of exactly what it tried up to that point, and some information about what it didn't like. Most likely, you'll want to read this information from the bottom up, since the error will be near the end, and you can look backwards from there to get a clearer picture of how things went awry. A lot of this output may be excessive or arcane detail to you, but it's rare that you shouldn't be able to discover the cause of a loading problem by looking at this detail. In my customer's case, we didn't need to use this tactic. But loading problems are not always identifiable by Dependency Walker or Process Monitor, so this final step should nail it down if the other ones didn't.
Still Didn't Figure it Out?
If you got this far and still don't have the answer, it may be time to open a support case. If you are the developer of the application failing to load, or if it's a Microsoft application, create a support case directly with Microsoft. If not, contact the vendor of the application for support, and they may need to create a case with us if they can't figure it out.