Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mysterious call is added when reference is assigned in c#

I know other people wrote similar questions, but I think mine is a different case, since I couldn't find any solution.

I have an object assignment, something very simple like this:

_buffer3 = buffer; //they are just simple reference types

the assembly code generated is the following

 mov         edx,dword ptr [ebp-3Ch] 
 mov         eax,dword ptr [ebp-40h] 
 lea         edx,[edx+4] 
 call        69C322F0 

now, just for to understand what's going on, I wanted to step inside the call (why a call should be used in an assignment?). However the code at that address doesn't exist and I cannot step in. if I type the address in the address code field, that's what is shown:

69C322F0  ???  

Any help trying to solve this mistery? :)

Edit..apparently the mysterious call is added when a reference is assigned inside a method of a class.

If I have this class:

        private class Test
        {
            int _X;
            int _Y;
            Test _t;

            public void SetValues(int x, int y, Test t)
            {
                _X = x;
                _Y = y;
            }
        }

the assembly generated for the method SetValues is :

                _X = x;
00000028  mov         eax,dword ptr [ebp-3Ch] 
0000002b  mov         edx,dword ptr [ebp-40h] 
0000002e  mov         dword ptr [eax+8],edx 
                _Y = y;
00000031  mov         eax,dword ptr [ebp-3Ch] 
00000034  mov         edx,dword ptr [ebp+0Ch] 
00000037  mov         dword ptr [eax+0Ch],edx 

which makes sense

however if I write this

        private class Test
        {
            int _X;
            int _Y;
            Test _t;

            public void SetValues(int x, int y, Test t)
            {
                _X = x;
                _Y = y;
                _t = t;
            }
        }

the mysterious call appears

                _X = x;
00000028  mov         eax,dword ptr [ebp-3Ch] 
0000002b  mov         edx,dword ptr [ebp-40h] 
0000002e  mov         dword ptr [eax+8],edx 
                _Y = y;
00000031  mov         eax,dword ptr [ebp-3Ch] 
00000034  mov         edx,dword ptr [ebp+0Ch] 
00000037  mov         dword ptr [eax+0Ch],edx 
                _t = t;
0000003a  mov         edx,dword ptr [ebp-3Ch] 
0000003d  mov         eax,dword ptr [ebp+8] 
00000040  lea         edx,[edx+4] 
00000043  call        515E2E48 

IMHO it's something related to the Garbage Collection, but I can't understand what it is and I really would like to figure it out. I know someone of you must know :)

Addendum to the answer, This is an extract I took from Google Books of the book CLR via C#:

This is an extract I took from Google Books of the book: CLR via C#

like image 1000
sebas Avatar asked Sep 25 '22 20:09

sebas


1 Answers

I'll noodle about this a little bit, a complete answer fills a book that puts everybody to sleep. You are getting a very inaccurate view of the machine code, the 32-bit disassembler does a very poor job translating the CALL addresses. That has improved quite a bit in recent VS versions if you look at 64-bit code, it for one no longer fakes the addresses of the machine code instructions. Get there with:

  • Project > Properties > Build tab: Platform target = AnyCPU, untick "Prefer 32-bit"
  • Project > Properties > Debug tab: tick "Enable native code debugging"
  • Tools > Options > Debugging > Symbols: enable the Microsoft Symbol Server
  • Tools > Options > Debugging > General: untick "Suppress JIT optimization".

The last setting change is only necessary if you want to look at the real machine code, the kind that runs on your user's machine. Do beware that you are looking at unoptimized debug code right now. Not otherwise pertinent to this question.

That lights up the disassembler pretty well, although it is still far from ideal. The tail end of your Test.SetValues() method now looks like this:

                _t = t;
00007FFA1ECB0C58  mov         rdx,qword ptr [rbp+50h]  
00007FFA1ECB0C5C  lea         rcx,[rdx+8]  
00007FFA1ECB0C60  mov         rdx,qword ptr [rbp+68h]  
00007FFA1ECB0C64  call        JIT_WriteBarrier (07FFA7E3312B0h)  

The displayed address is now accurate, 0x07FFA7E3312B0. Looking at that code takes one extra step, you have to force the debugger into native mode. Debug > Windows > Call Stack and double-click a native function at the bottom of the trace, like RtlUserThreadStart. Now you can copy/paste "07FFA7E3312B0" into the Address box of the disassembler, type "0x" first. I won't show it here, this is hand-written assembly code that does a rather mysterious thing you can never reverse-engineer from the code.

The better place to look for these jitter helper functions is the source code, although it is not an exact match, the github CoreCLR project is your best bet. Takes you here.

In general, the jitter emits these kind of direct CLR function calls as needed and their name usually starts with "JIT". This one happens to be written in assembly, that is however not very common; most are written in C++.

like image 155
Hans Passant Avatar answered Oct 11 '22 13:10

Hans Passant