Search code examples
c#.netreflectionreflection.emitdynamic-assemblies

Dynamic assembly created using Reflection.Emit crashes with exit code -532462766


I have been following this article to generate a dynamic assembly as follows:

var directory = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
var file = new FileInfo(Path.Combine(directory.FullName, @"MyDynamicAssembly.exe"));

var domain = AppDomain.CurrentDomain;
var name = new AssemblyName("Namespace.With.Dots");
var builderAssembly = domain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Save, directory.FullName);
var builderModule = builderAssembly.DefineDynamicModule("Namespace.With.Dots.Temp.exe");
var builderType = builderModule.DefineType("Program", TypeAttributes.Class | TypeAttributes.Public, typeof(object));
var builderMethod = builderType.DefineMethod("Main", MethodAttributes.Private | MethodAttributes.Static, typeof(int), new Type [] { typeof(string []) });

var generator = builderMethod.GetILGenerator();
generator.Emit(OpCodes.Ldstr, "Hello, World!");
generator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type [] { typeof(string) }));
generator.EmitWriteLine("Hello again.");
generator.Emit(OpCodes.Ldc_I4, 0);
generator.Emit(OpCodes.Ret);

builderAssembly.SetEntryPoint(builderMethod, PEFileKinds.ConsoleApplication);
builderAssembly.Save(file.Name, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);

var process = Process.Start(file.FullName); // Crashes with image below.
var result = process.WaitForExit();
var exitCode = process.ExitCode; // -532462766.

Here is what I know about the code above:

  • It is creating a dynamic assembly as Save only.
  • The assembly name, module name and output PE name are all different (I'm assuming this is not a problem).
  • It creates a public static class called Program.
  • It creates a single method in this class with signature private static int Main (string []).
  • It sets this method as an entry point and configures the assembly to be a console app.
  • It writes the assembly as ILOnly which is processor architecture agnostic.
  • It configures the assembly image as i386 which is what I'm running on (Win7 32 bit with an Intel processor).

METHOD:

  • Pushes a string literal reference to the stack.
  • Calls Console.WriteLine with the arguments taken from the stack.
  • Calls Console.WriteLine again using EmitWriteLine.
  • Pushes 0 as an Int32 to the stack as a return value.
  • Returns.

CRASH:

Ignore the filename on the image. It would be MyDynamicAssembly.exe per the code above. App crash info

Any pointers on what's going wrong here would be appreciated.


Solution

  • You'll have a lot more tough debugging jobs ahead of you beyond this one. You basically only ever get two exit codes. -532462766 or 0xe0434352 is the infamous "CCR" exception. The CLR died trying to load the assembly and can't perform the normal exception handling logic. You of course want to make sure that your generated IL is correct by testing it in-process before you try to run it stand-alone in a separate process. You'll at least have a debugger available that way.

    The other one is -532459699 or 0xe0434f4d, the normal "COM" exception. Produced when the code threw a plain .NET exception and it wasn't handled because of a lack of try/catch and no AppDomain.UnhandledException event handler. You'll have to make-do without a stack trace and can only reverse-engineer the location where the exception was thrown with the hints in this answer.

    Very punishing trouble-shooting of course, you basically do not ever want to do this. At least consider loading the code in another AppDomain so you stand a chance to produce a diagnostic and recover. It can still be in another process by writing a small "host" program that creates the appdomain and loads the assembly and generates a diagnostic. Also provides you with a way to use the debugger.