I'm writing a native CLR Profiler which does some heavy IL rewriting. When developing a new feature, we sometimes encounter a CLR verification error. For small methods, it's pretty easy to compare the bytes before and after, looking at the various elements (method header, signature, locals, code and exception table, mostly) and finding the error. Sometimes, this can be on huge methods and the process might take a while. I'm trying to dump the current module to a file, in order to easily run peverify.exe (and https://github.com/dotnet/corert/tree/master/src/ILVerify). I found IMetaDataEmit::Save, which looks perfect on paper (we're using IMetaDataEmit constantly to perform the IL rewriting). I can dump my module, open it in a hex viewer and see the changes I made. However, it only dumps the module (the .Net directory inside the PE). How can I create a full PE (dll/exe) from this module, preferably programmatically?
Asked
Active
Viewed 199 times
2
-
I'm not aware of any pre-built system components that allow you to assemble a PE image (unless there's something hidden in the [DbgHelp](https://learn.microsoft.com/en-us/windows/desktop/debug/dbghelp-functions) API). The fact that the [Roslyn](https://github.com/dotnet/roslyn) project implements its own [PEWriter](https://github.com/dotnet/roslyn/tree/master/src/Compilers/Core/Portable/PEWriter) seems to support that. Maybe that can serve as a starting point to implement your own, either in unmanaged or managed code. – IInspectable Apr 22 '19 at 13:35
-
The [System.Reflection.PortableExecutable](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.portableexecutable?view=netcore-2.2) namespace seems to provide a fair amount of support for PE images. I'm not sure how much you can do with that, though. And it's .NET Core only, in case that matters. – IInspectable Apr 22 '19 at 14:13
-
@IInspectable, the profiler callbacks are not permitted to re-enter managed code. (the profiler callbacks are run in-process, so bad things happen if - for example - the jit callbacks need to be jitted or the GC callbacks need to allocate managed memory.) – Brian Reichle Apr 24 '19 at 09:44
-
`However, it only dumps the module (the .Net directory inside the PE).` the module *is* the PE file. If you have a multi-module assembly, then that assembly will span multiple PE files. What exactly is missing when using `IMetaDataEmit::Save*`. – Brian Reichle Apr 24 '19 at 10:03
-
@bri: As I understand, `IMetaDataEmit::Save*` merely serializes the *contents* of what is called a section in a PE image. It will not write a full PE image. You are still required to piece that together, including the DOS stub and PE header as well as the section header(s). Unless I misunderstood. – IInspectable Apr 24 '19 at 10:06
-
@IInspectable: Emitting a single section doesn't really sound right to me, it might be emitting it as a COFF file (in which case it might be possible to use a linker to convert it into a PE file). – Brian Reichle Apr 24 '19 at 10:51
-
Hey Brian, indeed - it only dumps the .Net section. I can clearly see the hex content of the Save method as an inner part of the actual PE. What @IInspectable wrote is correct, it's missing all of the "PE" parts of the PE, the ones which are not .net specific. I would love to find a way of using a linker to generate it, but I failed my attempts so far. – Egozy Apr 28 '19 at 10:41
-
@Egozy, the COFF file doesn't have the "PE" signature, if that's what you were looking for. I believe the [COFF file format](https://learn.microsoft.com/en-us/windows/desktop/debug/pe-format#coff-file-header-object-and-image) starts with the "machine" field, so if the generated file starts with `4C 01` or `64 86` then it's probably a COFF file. The [linker documentation](https://learn.microsoft.com/en-us/cpp/build/reference/dot-obj-files-as-linker-input?view=vs-2019) suggests it can accept COFF files with the `.obj` extension as input. – Brian Reichle Apr 30 '19 at 10:04
-
If it really is just dumping the ".Net stuff", then it's probably not going to be usable (would need the fix-up information). You might be able to write something custom to convert the file to a PE file ... but that sounds like an awful lot of bother. The approach I used in the past was to just write a simple application to disassemble the method body in isolation. – Brian Reichle Apr 30 '19 at 10:05