.NET Core 3: 'Bad IL format'

We have recently been migrating our compliance platform from .NET 4.7 to .NET Core 3.

The existing code uses Autofac to manage inversion of control and we wanted to stick with it for now, instead of using the built-in DI middleware that comes packaged with .NET Core 3.

Part of the Autofac container building process involves scanning assemblies within the bin folder for plugins. The existing code contained a section that looked something like:

var assemblyNames = Directory.EnumerateFiles
(
    binPath,
    "*.dll",
    SearchOption.AllDirectories
);

assemblies.AddRange(assemblyNames.Select(Assembly.LoadFrom));

This code had been working fine for the past 2 years, so when I ran the .NET Core 3 version and received the following exception, it stumped me for a while:

System.BadImageFormatException: 'Bad IL format.'

At first I thought I might have some old DLLs left over from the .NET 4.7 build, so I deleted all bin and obj folders then tried again - to no avail. A quick Google search lead me to this Stack Overflow question. It suggests three possible fixes:

  1. If your application uses 32-bit components, make sure that it always runs as a 32-bit application.
  2. Make sure that you are not using a component that was created with a different version of the .NET Framework.
  3. Make sure that the file image is a valid managed assembly or module.

I tried changing the platform target on the .NET Core 3 project from AnyCPU to 32-bit and then 64-bit, but both made no difference. I was confident that points 2 and 3 were covered because I had updated all NuGet packages and the code as only scanning for .dll files.

The next thing I tried was creating a new Git branch and then converting the project to .NET Core 2.2 (the latest version before 3.0). I cleaned the bin folders and ran the project - it worked!

So I was able to determine that it was a problem specific to .NET Core 3. I took a closer look at the generated bin folder for .NET Core 3 and noticed one difference from the other framework versions - a folder named runtimes.

If you notice in the code snippet above, I set the searchOption parameter to:

SearchOption.AllDirectories

Within the runtimes folder there are a cocktail of DLLs built for x86 and x64 architectures and debugging the above code confirmed that it was these DLLs (which I didn’t need to scan anyway) that were causing the problem.

I looked into this and discovered that applications created in .NET Core can be published in two different modes; self-contained and runtime-dependent. By default, it seems they are published as self-contained (which creates the runtimes folder). I didn’t want to change this behaviour, so I fixed it by simply changing the searchOptions parameter to:

SearchOption.TopDirectoryOnly

This ensures that only the DLLs contained in the bin folder (not sub folders) are scanned.