Recovery of local names in .Net

So question is: why decompilers doesn't recover names of local variables? I thought that decompiler erase all info about local names and simply uses ldarg_0 etc. This is why this code:

    private static int Foo()
    {
        int locA = Console.ReadKey().KeyChar;
        int b = Console.ReadKey().KeyChar;
        int c = Console.ReadKey().KeyChar;
        return locA*b * c;
    }

is decompiled into this one:

private static int Foo() // from .Net reflector 8.2
{
    int keyChar = Console.ReadKey().KeyChar;
    int num2 = Console.ReadKey().KeyChar;
    int num3 = Console.ReadKey().KeyChar;
    return ((keyChar * num2) * num3);
}

it was clearly until today, when i found a decompiler ILSpy, and it decompiles it like that:

// ConsoleApplication101.Program

private static int Foo()
{
    int locA = (int)Console.ReadKey().KeyChar;
    int b = (int)Console.ReadKey().KeyChar;
    int c = (int)Console.ReadKey().KeyChar;
    return locA * b * c;
}

so original code - here it is! (i know, that i forbade compiler to optimize my code, but i don't care)

question is: why all decompilers used (reflector, dotPeek etc) doesn't show this vastly important info when compiler provides it in exe!

.method private hidebysig static int32  Foo() cil managed
{
  // Размер кода:       56 (0x38)
  .maxstack  2
  .locals init ([0] int32 locA,
           [1] int32 b,
           [2] int32 c,
           [3] int32 CS$1$0000,
           [4] valuetype [mscorlib]System.ConsoleKeyInfo CS$0$0001)
  IL_0000:  nop
  IL_0001:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_0006:  stloc.s    CS$0$0001
  IL_0008:  ldloca.s   CS$0$0001
  IL_000a:  call       instance char [mscorlib]System.ConsoleKeyInfo::get_KeyChar()
  IL_000f:  stloc.0
  IL_0010:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_0015:  stloc.s    CS$0$0001
  IL_0017:  ldloca.s   CS$0$0001
  IL_0019:  call       instance char [mscorlib]System.ConsoleKeyInfo::get_KeyChar()
  IL_001e:  stloc.1
  IL_001f:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_0024:  stloc.s    CS$0$0001
  IL_0026:  ldloca.s   CS$0$0001
  IL_0028:  call       instance char [mscorlib]System.ConsoleKeyInfo::get_KeyChar()
  IL_002d:  stloc.2
  IL_002e:  ldloc.0
  IL_002f:  ldloc.1
  IL_0030:  mul
  IL_0031:  ldloc.2
  IL_0032:  mul
  IL_0033:  stloc.3
  IL_0034:  br.s       IL_0036
  IL_0036:  ldloc.3
  IL_0037:  ret
} // end of method Program::Foo

Answers


Your observaton/assumption is not correct. That metadata is not part of the exe. ILSpy and ildasm are only able to show the original variable names because those programs read the available pdb file.

After compilation you get two files: an exe file and an pdb file. The pdb file holds metadata (like variablenames, linenumbers). If you open an exe with a tool that is capable of also reading the accompanied pdb file you get a decompiled result that more closely matches your original source file.

To verify your self you can rename the pdb file to a different extension and then open the exe in either ildasm or ilpsy. The ildasm result is:

// Code size       56 (0x38)
  .maxstack  2
  .locals init (int32 V_0,
           int32 V_1,
           int32 V_2,
           int32 V_3,
           valuetype [mscorlib]System.ConsoleKeyInfo V_4)

With an pdb file present:

 // Code size       56 (0x38)
  .maxstack  2
  .locals init ([0] int32 locA,
           [1] int32 b,
           [2] int32 c,
           [3] int32 CS$1$0000,
           [4] valuetype [mscorlib]System.ConsoleKeyInfo CS$0$0001)

You can see the difference. The localnames are read from the pdb file.

To verify if your EXE can find a pdb you can use the DUMPBIN command:

dumpbin /pdbPATH:VERBOSE ConsoleApplication1.exe

which will render output like this:

Dump of file ConsoleApplication1.exe

File Type: EXECUTABLE IMAGE PDB file 'C:...\ConsoleApplication1.pdb' checked. (File not found) PDB file found at 'c:...\obj\x86\Debug\ConsoleApplication1.pdb'

John Robbin (Wintellect) PDB Files: What Every Developer Must Know


Need Your Help

Library function objdump output

c++ c arm objdump

I have the following objdump output when debugging a crash. I could narrow down the issue to the following function LanManager::Interface() library function, but with my little experience with object

Android emulator is based on QEMU. Can I use KVM with it?

android android-emulator qemu

My laptop supports hardware virtualization (AMD SVM) and I know that: