성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Windbg 환경에서 확인해 본 .NET 메서드 JIT 컴파일 전과 후 - 두 번째 이야기</h1> <p> 이전 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windbg 환경에서 확인해 본 .NET 메서드 JIT 컴파일 전과 후 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1023'>https://www.sysnet.pe.kr/2/0/1023</a> </pre> <br /> x86 환경에서의 JIT 컴파일 전/후에 대한 코드를 살펴봤는데요. 간단하게 다시 요약해 보면, .NET 메서드를 호출하는 call 호출인 경우,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 01020877 ff15584de400 call dword ptr ds:<span style='color: blue; font-weight: bold'>[0E44D58h]</span> ds:002b:00e44d58=01020440 </pre> <br /> 최초 시점에 [0E44D58h] 주소에는 JIT 컴파일을 수행하는 코드의 주소가 담겨 있습니다. 하지만, 일단 한 번이라도 수행이 되면 [0E44D58h] 주소에는 IL 코드가 기계어로 번역된 주소로 치환되므로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>dd 0e44d58 L1</span> 00e44d58 <span style='color: blue; font-weight: bold'>010208d0</span> 0:000> <span style='color: blue; font-weight: bold'>!ip2md 010208d0</span> MethodDesc: 00e44d50 Method Name: ConsoleApp1.Program.Start() Class: 00e412a0 MethodTable: 00e44d64 mdToken: 06000002 Module: 00e44044 IsJitted: yes <span style='color: blue; font-weight: bold'>CodeAddr: 010208d0</span> Transparency: Critical Source file: C:\..\ConsoleApp1\Program.cs @ 34 </pre> <br /> 이후 다시 "call [0E44d58h]" 코드가 수행되면 "call 010208d0"와 같이 동작하므로 곧바로 기계어로 번역된 함수의 본체를 실행하게 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 반면, 최신 버전의 .NET 4.8/x64에서는 이런 과정이 완전히 달라졌는데요, 아래는 이 과정을 잘 표현하고 있습니다. (세부적인 사항은 환경에 따라 달라질 수 있다는 점을 염두에 두어야 합니다.)<br /> <br /> [그림 출처: <a target='tab' href='https://www.cnblogs.com/zkweb/p/7746222.html'>https://www.cnblogs.com/zkweb/p/7746222.html</a>]<br /> <img onclick='toggle_img(this)' class='imgView' alt='jit_before_after.jpg' src='/SysWebRes/bbs/jit_before_after.jpg' /><br /> <br /> 그럼, .NET 4.8/x64 환경으로 <a target='tab' href='https://www.sysnet.pe.kr/2/0/1023'>지난 글</a>의 코드를 테스트해볼까요? ^^ 우선 !clrstack으로 Program.Main의 IP를 확인하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!clrstack</span> OS Thread Id: 0x6240 (0) Child SP IP Call Site 000000e6b43fe698 00007ffd8b55c184 [InlinedCallFrame: 000000e6b43fe698] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr) 000000e6b43fe698 00007ffd6ffeca28 [InlinedCallFrame: 000000e6b43fe698] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr) 000000e6b43fe660 00007ffd6ffeca28 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr) 000000e6b43fe740 00007ffd707f89fc System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef) 000000e6b43fe7d0 00007ffd707f8905 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) 000000e6b43fe830 00007ffd6ff27b19 System.IO.StreamReader.ReadBuffer() 000000e6b43fe880 00007ffd6ff27753 System.IO.StreamReader.ReadLine() 000000e6b43fe8e0 00007ffd709aa44d System.IO.TextReader+SyncTextReader.ReadLine() 000000e6b43fe940 00007ffd70774688 System.Console.ReadLine() 000000e6b43fe970 <span style='color: blue; font-weight: bold'>00007ffd148b08f3 ConsoleApp1.Program.Main</span>(System.String[]) [C:\...\ConsoleApp1\Program.cs @ 19] 000000e6b43feb98 00007ffd73df6bd3 [GCFrame: 000000e6b43feb98] </pre> <br /> 덤프해 보면 대충 Program.Start 메서드의 호출 코드를 다음과 같이 특정할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!U /d 00007ffd148b08f3</span> Normal JIT generated code ConsoleApp1.Program.Main(System.String[]) Begin 00007ffd148b0890, size 90 ...[생략]... C:\..\ConsoleApp1\Program.cs @ 19: 00007ffd`148b08ee e87d3dec5b call mscorlib_ni+0xdc4670 (00007ffd`70774670) (System.Console.ReadLine(), mdToken: 0000000006000b6b) >>> 00007ffd`148b08f3 488bce mov rcx,rsi 00007ffd`148b08f6 e88dfbffff <span style='color: blue; font-weight: bold'>call 00007ffd`148b0488</span> (ConsoleApp1.Program.Start(), mdToken: 0000000006000002) ...[생략]... </pre> <br /> <a name='fixup_precode'></a> 오호~~~, call [...] 형식이 아니라 곧바로 offset 범위의 호출을 하고 있습니다. 해당 위치(00007ffd`148b0488)의 코드를 덤프하면 다음과 같은 형식을 띠고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 다이어그램 상으로 보면 이 단계가 "Fixup Precode"입니다. 00007ffd`148b0488 e8a340545f <span style='color: blue; font-weight: bold'>call clr!PrecodeFixupThunk</span> (00007ffd`73df4530) 00007ffd`148b048d 5e pop rsi 00007ffd`148b048e 0201 add al,byte ptr [rcx] 00007ffd`148b0490 e89b40545f call clr!PrecodeFixupThunk (00007ffd`73df4530) 00007ffd`148b0495 5e pop rsi 00007ffd`148b0496 0400 add al,0 00007ffd`148b0498 e8597a14fd call 00007ffd`119f7ef6 00007ffd`148b049d 7f00 jg 00007ffd`148b049f 00007ffd`148b049f 00e8 add al,ch 00007ffd`148b04a1 8b4054 mov eax,dword ptr [rax+54h] </pre> <br /> 처음부터 clr!PrecodeFixupThunk 함수를 call하고 이후로는 일반적인 함수의 prologue 형식에 맞지 않는 코드가 나오고 있습니다. 사실 "5e 0201e89b40545f은 코드가 아니라 PrecodeFixupThunk 함수 내에서 다음과 같이 데이터로써 다뤄지게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 다이어그램 상으로 보면 이 단계가 "Fixup Precode Chunk"입니다. clr!PrecodeFixupThunk: 00007ffd`73df4530 58 pop rax // call clr!PrecodeFixupThunk의 바로 다음 주소: 00007ffd`148b048d // 여기서 pop을 한번 했으므로, // 다음번 ret에서 돌아갈 주소는 Main에서 Program.Method를 호출한 다음 지점 00007ffd`73df4531 4c0fb65002 movzx r10,byte ptr [rax+2] // 00007ffd`148b048d+2의 위치 == 1 00007ffd`73df4536 4c0fb65801 movzx r11,byte ptr [rax+1] // 00007ffd`148b048d+1의 위치 == 2 00007ffd`73df453b 4a8b44d003 mov rax,qword ptr [rax+r10*8+3] // [rax + 1*8 + 3] == [00007ffd`148b0498] == 00007ffd`147a59e8 00007ffd`73df4540 4e8d14d8 lea r10,[rax+r11*8] // 00007ffd`147a59e8 + r11 * 8 == 00007ffd`147a59f8 00007ffd`73df4544 e9b7020000 <span style='color: blue; font-weight: bold'>jmp clr!ThePreStub</span> (00007ffd`73df4800) 00007ffd`73df4549 0f1f8000000000 nop dword ptr [rax] /* 위의 코드에서 rax, r10과 r11을 사용하는데 이는 <a target='tab' href='https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019'>volatile 레지스터</a>이기 때문입니다. */ </pre> <br /> 결국 그 데이터로 구해지는 것은, 즉 r10 레지스터에 들어 있는 값은 해당 메서드의 MethodDesc값(위의 예에서는 00007ffd`147a59f8)입니다. 따라서 이 메서드에 대해 clr!ThePreStub으로 jump해 기계어 코드로 번역을 하는 것입니다. 이로 인해 당연히 00007ffd`147a59f8 값으로 덤프해 보는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!DumpMD /d 00007ffd147a59f8</span> Method Name: ConsoleApp1.Program.Start() Class: 00007ffd147a2510 MethodTable: 00007ffd147a5a10 mdToken: 0000000006000002 Module: 00007ffd147a4148 <span style='color: blue; font-weight: bold'>IsJitted: no CodeAddr: ffffffffffffffff</span> Transparency: Critical </pre> <br /> 이후의 코드는 "jmp clr!ThePreStub"으로 넘어가 "compileMethod"까지 불려 비로소 닷넷 메서드의 "Native Code"가 실행됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데 재미있는 것은, 일단 저렇게 ConsoleApp1.Program.Start가 실행되었는데도 불구하고 다시 해당 메서드를 호출하는 코드에 진입해 보면 여전히 Fixup Precode -> PrecodeFixupThunk -> ThePreStub -> PreStubWorker 단계로 실행이 연결된다는 점입니다. <br /> <br /> 이상하군요... 닷넷 메서드의 호출에 저렇게 많은 코드가 실행된다는 것은 결코 효율적인 실행 방식이라고 볼 수 없습니다. 한마디로... 혼란스러웠는데요, 그러다 생각난 것이 바로 ^^ 아래의 글이었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET Core 2.1 - Tiered Compilation 도입 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11539'>https://www.sysnet.pe.kr/2/0/11539</a> </pre> <br /> 즉, .NET 4.8/x64 환경에서는 처음 몇 번 실행되는 닷넷 메서드의 경우 시간이 많이 걸리는 최적화 코드를 생성하기보다는 대충 돌아가는 코드를 만들어 놓고, 이후 최적화가 필요할 정도로 실행되었다고 판단될 때 "Fixup Precode"가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 00007ffd`148b0488 <span style='color: blue; font-weight: bold'>e8a340545f call clr!PrecodeFixupThunk</span> (00007ffd`73df4530) 00007ffd`148b048d 5e pop rsi 00007ffd`148b048e 0201 add al,byte ptr [rcx] 00007ffd`148b0490 e89b40545f call clr!PrecodeFixupThunk (00007ffd`73df4530) 00007ffd`148b0495 5e pop rsi 00007ffd`148b0496 0400 add al,0 00007ffd`148b0498 e8597a14fd call 00007ffd`119f7ef6 00007ffd`148b049d 7f00 jg 00007ffd`148b049f </pre> <br /> 이렇게 단순 점프 문으로 바뀝니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 00007ffd`148b0488 <span style='color: blue; font-weight: bold'>e9e3040000 jmp 00007ffd`148b0970</span> 00007ffd`148b048d 5f pop rdi 00007ffd`148b048e 0203 add al,byte ptr [rbx] 00007ffd`148b0490 e90b050000 jmp 00007ffd`148b09a0 </pre> <br /> 그리고 점프 대상이 되는 코드는 새롭게 JIT 컴파일된 주소를 가리킵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!ip2md 00007ffd`148b0970</span> MethodDesc: 00007ffd147a59f8 Method Name: ConsoleApp1.Program.Start1() Class: 00007ffd147a24e8 MethodTable: 00007ffd147a5a30 mdToken: 0000000006000002 Module: 00007ffd147a4148 IsJitted: yes CodeAddr: <span style='color: blue; font-weight: bold'>00007ffd148b0970</span> // 00007ffd`148b0970 Transparency: Critical Source file: C:\..\ConsoleApp1\Program.cs @ 49 </pre> <br /> 이로써 다이어그램에서 본 두 번째 "After JIT"와 같이 "Fixup Precode" -> "Native Code"대로 실행하는 것을 확인할 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 위에서 언급한 Fixup Precode의 데이터에 대한 해석은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 00007ffd`148b0488 e8a340545f call clr!PrecodeFixupThunk (00007ffd`73df4530) 00007ffd`148b048d 5e pop rsi 00007ffd`148b048e 0201 add al,byte ptr [rcx] 00007ffd`148b0490 e89b40545f call clr!PrecodeFixupThunk (00007ffd`73df4530) 00007ffd`148b0495 5e pop rsi 00007ffd`148b0496 0400 add al,0 00007ffd`148b0498 e8597a14fd call 00007ffd`119f7ef6 00007ffd`148b049d 7f00 jg 00007ffd`148b049f 00007ffd`148b049f 00e8 add al,ch 00007ffd`148b04a1 8b4054 mov eax,dword ptr [rax+54h] </pre> <br /> 아래의 문서에서 자세하게 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Method Descriptor: Precode ; <a target='tab' href='https://github.com/dotnet/runtime/blob/master/docs/design/coreclr/botr/method-descriptor.md#precode'>https://github.com/dotnet/runtime/blob/master/docs/design/coreclr/botr/method-descriptor.md#precode</a> </pre> <br /> 또한 다이어그램에서 본 "ThePreStub" 다음 단계로 나오는 "clr!PreStubWorker"의 코드는 다음과 같고,<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > clr!PreStubWorker: 00007ffd`73e00dc0 4053 push rbx 00007ffd`73e00dc2 56 push rsi 00007ffd`73e00dc3 57 push rdi 00007ffd`73e00dc4 4154 push r12 00007ffd`73e00dc6 4155 push r13 00007ffd`73e00dc8 4156 push r14 00007ffd`73e00dca 4157 push r15 00007ffd`73e00dcc 4881ec00030000 sub rsp,300h 00007ffd`73e00dd3 48c7842450020000feffffff mov qword ptr [rsp+250h],0FFFFFFFFFFFFFFFEh 00007ffd`73e00ddf 488bf2 mov rsi,rdx 00007ffd`73e00de2 488bf9 mov rdi,rcx 00007ffd`73e00de5 4533ff xor r15d,r15d 00007ffd`73e00de8 4c897c2458 mov qword ptr [rsp+58h],r15 00007ffd`73e00ded ff1515367b00 call qword ptr [clr!_imp_GetLastError (00007ffd`745b4408)] 00007ffd`73e00df3 448be8 mov r13d,eax 00007ffd`73e00df6 e86532ffff call clr!GetThread (00007ffd`73df4060) 00007ffd`73e00dfb 488bd8 mov rbx,rax 00007ffd`73e00dfe 4c8b058bec7b00 mov r8,qword ptr [clr!s_gsCookie (00007ffd`745bfa90)] 00007ffd`73e00e05 4c89842428010000 mov qword ptr [rsp+128h],r8 00007ffd`73e00e0d 4889bc2440010000 mov qword ptr [rsp+140h],rdi 00007ffd`73e00e15 4889b42448010000 mov qword ptr [rsp+148h],rsi 00007ffd`73e00e1d 488d057c527c00 lea rax,[clr!PrestubMethodFrame::`vftable' (00007ffd`745c60a0)] 00007ffd`73e00e24 4889842430010000 mov qword ptr [rsp+130h],rax 00007ffd`73e00e2c 4c8da42430010000 lea r12,[rsp+130h] 00007ffd`73e00e34 488b4b10 mov rcx,qword ptr [rbx+10h] 00007ffd`73e00e38 48898c2438010000 mov qword ptr [rsp+138h],rcx 00007ffd`73e00e40 488d842430010000 lea rax,[rsp+130h] 00007ffd`73e00e48 48894310 mov qword ptr [rbx+10h],rax 00007ffd`73e00e4c 488d842430010000 lea rax,[rsp+130h] 00007ffd`73e00e54 4889842410010000 mov qword ptr [rsp+110h],rax 00007ffd`73e00e5c 44383ddd71a000 cmp byte ptr [clr!g_StackProbingEnabled (00007ffd`74808040)],r15b 00007ffd`73e00e63 0f85bf673a00 jne clr!PreStubWorker+0x3a6868 (00007ffd`741a7628) [br=0] 00007ffd`73e00e69 488b051880a000 mov rax,qword ptr [clr!Microsoft_Windows_DotNETRuntimePrivateHandle (00007ffd`74808e88)] ds:00007ffd`74808e88=002301b3ef242c20 00007ffd`73e00e70 48898424c0020000 mov qword ptr [rsp+2C0h],rax 00007ffd`73e00e78 4889842480010000 mov qword ptr [rsp+180h],rax 00007ffd`73e00e80 4c8d15c9c07d00 lea r10,[clr!PrestubWorker_V1 (00007ffd`745dcf50)] 00007ffd`73e00e87 4c89942488010000 mov qword ptr [rsp+188h],r10 00007ffd`73e00e8f 488d0dcac07d00 lea rcx,[clr!PrestubWorkerEnd_V1 (00007ffd`745dcf60)] 00007ffd`73e00e96 48898c2498010000 mov qword ptr [rsp+198h],rcx 00007ffd`73e00e9e 488d0d9bc07d00 lea rcx,[clr!StartupId (00007ffd`745dcf40)] 00007ffd`73e00ea5 48898c2490010000 mov qword ptr [rsp+190h],rcx 00007ffd`73e00ead 48898c24a0010000 mov qword ptr [rsp+1A0h],rcx 00007ffd`73e00eb5 48898c24c8020000 mov qword ptr [rsp+2C8h],rcx 00007ffd`73e00ebd 4c899424d0020000 mov qword ptr [rsp+2D0h],r10 00007ffd`73e00ec5 48898424d8020000 mov qword ptr [rsp+2D8h],rax 00007ffd`73e00ecd 0f10057cc07d00 movups xmm0,xmmword ptr [clr!PrestubWorker_V1 (00007ffd`745dcf50)] 00007ffd`73e00ed4 0f11842450010000 movups xmmword ptr [rsp+150h],xmm0 00007ffd`73e00edc 833d417fa00000 cmp dword ptr [clr!MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context+0x24 (00007ffd`74808e24)],0 00007ffd`73e00ee3 0f854e673a00 jne clr!PreStubWorker+0x3a6877 (00007ffd`741a7637) 00007ffd`73e00ee9 833d5072a00000 cmp dword ptr [clr!g_IBCLogger (00007ffd`74808140)],0 00007ffd`73e00ef0 0f8555673a00 jne clr!PreStubWorker+0x3a688b (00007ffd`741a764b) 00007ffd`73e00ef6 0fb64602 movzx eax,byte ptr [rsi+2] 00007ffd`73e00efa c1e003 shl eax,3 00007ffd`73e00efd 4863c8 movsxd rcx,eax 00007ffd`73e00f00 488bc6 mov rax,rsi 00007ffd`73e00f03 482bc1 sub rax,rcx 00007ffd`73e00f06 4883e818 sub rax,18h 00007ffd`73e00f0a 4889442460 mov qword ptr [rsp+60h],rax 00007ffd`73e00f0f 48898424a8010000 mov qword ptr [rsp+1A8h],rax 00007ffd`73e00f17 48898424b0010000 mov qword ptr [rsp+1B0h],rax 00007ffd`73e00f1f 488b00 mov rax,qword ptr [rax] ds:00007ffd`147959d0=0000000000000060 00007ffd`73e00f22 4889842488000000 mov qword ptr [rsp+88h],rax 00007ffd`73e00f2a 488b4c2460 mov rcx,qword ptr [rsp+60h] 00007ffd`73e00f2f 488bd0 mov rdx,rax 00007ffd`73e00f32 4803d1 add rdx,rcx 00007ffd`73e00f35 48899424b8010000 mov qword ptr [rsp+1B8h],rdx 00007ffd`73e00f3d f6c201 test dl,1 00007ffd`73e00f40 0f8514673a00 jne clr!PreStubWorker+0x3a689a (00007ffd`741a765a) 00007ffd`73e00f46 44897c2424 mov dword ptr [rsp+24h],r15d 00007ffd`73e00f4b 48898c24c0010000 mov qword ptr [rsp+1C0h],rcx 00007ffd`73e00f53 488b01 mov rax,qword ptr [rcx] 00007ffd`73e00f56 4889842490000000 mov qword ptr [rsp+90h],rax 00007ffd`73e00f5e 488bf8 mov rdi,rax 00007ffd`73e00f61 48037c2460 add rdi,qword ptr [rsp+60h] 00007ffd`73e00f66 4889bc2498000000 mov qword ptr [rsp+98h],rdi 00007ffd`73e00f6e 40f6c701 test dil,1 00007ffd`73e00f72 0f85cb121d00 jne clr!PreStubWorker+0x1d1483 (00007ffd`73fd2243) 00007ffd`73e00f78 833dc171a00000 cmp dword ptr [clr!g_IBCLogger (00007ffd`74808140)],0 00007ffd`73e00f7f 0f85e8663a00 jne clr!PreStubWorker+0x3a68ad (00007ffd`741a766d) 00007ffd`73e00f85 f6470840 test byte ptr [rdi+8],40h 00007ffd`73e00f89 7522 jne clr!PreStubWorker+0x1ed (00007ffd`73e00fad) 00007ffd`73e00f8b 833dae71a00000 cmp dword ptr [clr!g_IBCLogger (00007ffd`74808140)],0 00007ffd`73e00f92 0f85e4663a00 jne clr!PreStubWorker+0x3a68bc (00007ffd`741a767c) 00007ffd`73e00f98 488b4720 mov rax,qword ptr [rdi+20h] 00007ffd`73e00f9c 48898424c8010000 mov qword ptr [rsp+1C8h],rax 00007ffd`73e00fa4 f60004 test byte ptr [rax],4 00007ffd`73e00fa7 0f856d790f00 jne clr!PreStubWorker+0x531 (00007ffd`73ef891a) 00007ffd`73e00fad c744242801000000 mov dword ptr [rsp+28h],1 00007ffd`73e00fb5 0fb64606 movzx eax,byte ptr [rsi+6] 00007ffd`73e00fb9 2407 and al,7 00007ffd`73e00fbb 3c05 cmp al,5 00007ffd`73e00fbd 0f84fa0d1600 je clr!PreStubWorker+0x50a (00007ffd`73f61dbd) [br=0] 00007ffd`73e00fc3 833d7671a00000 cmp dword ptr [clr!g_IBCLogger (00007ffd`74808140)],0 00007ffd`73e00fca 0f85bb663a00 jne clr!PreStubWorker+0x3a68cb (00007ffd`741a768b) 00007ffd`73e00fd0 0fb64606 movzx eax,byte ptr [rsi+6] 00007ffd`73e00fd4 2407 and al,7 00007ffd`73e00fd6 3c07 cmp al,7 00007ffd`73e00fd8 0f84bc663a00 je clr!PreStubWorker+0x3a68da (00007ffd`741a769a) 00007ffd`73e00fde c744242001000000 mov dword ptr [rsp+20h],1 00007ffd`73e00fe6 833d5371a00000 cmp dword ptr [clr!g_IBCLogger (00007ffd`74808140)],0 00007ffd`73e00fed 0f85ba663a00 jne clr!PreStubWorker+0x3a68ed (00007ffd`741a76ad) 00007ffd`73e00ff3 0fb64602 movzx eax,byte ptr [rsi+2] 00007ffd`73e00ff7 c1e003 shl eax,3 00007ffd`73e00ffa 4863c8 movsxd rcx,eax 00007ffd`73e00ffd 488bc6 mov rax,rsi 00007ffd`73e01000 482bc1 sub rax,rcx 00007ffd`73e01003 4883e818 sub rax,18h 00007ffd`73e01007 48898424a0000000 mov qword ptr [rsp+0A0h],rax ss:00000052`625fe980=0000000000000001 00007ffd`73e0100f 48898424f0010000 mov qword ptr [rsp+1F0h],rax 00007ffd`73e01017 488b00 mov rax,qword ptr [rax] 00007ffd`73e0101a 48898424a8000000 mov qword ptr [rsp+0A8h],rax 00007ffd`73e01022 488bf8 mov rdi,rax 00007ffd`73e01025 4803bc24a0000000 add rdi,qword ptr [rsp+0A0h] 00007ffd`73e0102d 4889bc24b0000000 mov qword ptr [rsp+0B0h],rdi 00007ffd`73e01035 40f6c701 test dil,1 00007ffd`73e01039 0f8519121d00 jne clr!PreStubWorker+0x1d1498 (00007ffd`73fd2258) 00007ffd`73e0103f f6470840 test byte ptr [rdi+8],40h 00007ffd`73e01043 7522 jne clr!PreStubWorker+0x2a7 (00007ffd`73e01067) 00007ffd`73e01045 833df470a00000 cmp dword ptr [clr!g_IBCLogger (00007ffd`74808140)],0 00007ffd`73e0104c 0f856a663a00 jne clr!PreStubWorker+0x3a68fc (00007ffd`741a76bc) 00007ffd`73e01052 488b4720 mov rax,qword ptr [rdi+20h] 00007ffd`73e01056 48898424f8010000 mov qword ptr [rsp+1F8h],rax 00007ffd`73e0105e f60040 test byte ptr [rax],40h 00007ffd`73e01061 0f85700d1600 jne clr!PreStubWorker+0x53b (00007ffd`73f61dd7) 00007ffd`73e01067 4c897c2450 mov qword ptr [rsp+50h],r15 00007ffd`73e0106c 833dcd70a00000 cmp dword ptr [clr!g_IBCLogger (00007ffd`74808140)],0 00007ffd`73e01073 0f85c0663a00 jne clr!PreStubWorker+0x3a6979 (00007ffd`741a7739) 00007ffd`73e01079 0fb64602 movzx eax,byte ptr [rsi+2] 00007ffd`73e0107d c1e003 shl eax,3 00007ffd`73e01080 4863c8 movsxd rcx,eax 00007ffd`73e01083 488bc6 mov rax,rsi 00007ffd`73e01086 482bc1 sub rax,rcx 00007ffd`73e01089 4883e818 sub rax,18h 00007ffd`73e0108d 48898424f0000000 mov qword ptr [rsp+0F0h],rax 00007ffd`73e01095 4889842478020000 mov qword ptr [rsp+278h],rax 00007ffd`73e0109d 488b00 mov rax,qword ptr [rax] 00007ffd`73e010a0 48898424f8000000 mov qword ptr [rsp+0F8h],rax 00007ffd`73e010a8 4c8bf0 mov r14,rax 00007ffd`73e010ab 4c03b424f0000000 add r14,qword ptr [rsp+0F0h] 00007ffd`73e010b3 4c89b42400010000 mov qword ptr [rsp+100h],r14 00007ffd`73e010bb 41f6c601 test r14b,1 00007ffd`73e010bf 0f85a8111d00 jne clr!PreStubWorker+0x1d14ad (00007ffd`73fd226d) 00007ffd`73e010c5 4c89b42480020000 mov qword ptr [rsp+280h],r14 00007ffd`73e010cd 8b0d6d70a000 mov ecx,dword ptr [clr!g_IBCLogger (00007ffd`74808140)] 00007ffd`73e010d3 85c9 test ecx,ecx 00007ffd`73e010d5 0f856d663a00 jne clr!PreStubWorker+0x3a6988 (00007ffd`741a7748) 00007ffd`73e010db 0fb64606 movzx eax,byte ptr [rsi+6] 00007ffd`73e010df 2407 and al,7 00007ffd`73e010e1 3c05 cmp al,5 00007ffd`73e010e3 0f843c0d1600 je clr!PreStubWorker+0x51e (00007ffd`73f61e25) 00007ffd`73e010e9 85c9 test ecx,ecx 00007ffd`73e010eb 0f8566663a00 jne clr!PreStubWorker+0x3a6997 (00007ffd`741a7757) 00007ffd`73e010f1 418b06 mov eax,dword ptr [r14] 00007ffd`73e010f4 2500000c00 and eax,0C0000h 00007ffd`73e010f9 3d00000400 cmp eax,40000h 00007ffd`73e010fe 0f8452661500 je clr!PreStubWorker+0x4e4 (00007ffd`73f57756) 00007ffd`73e01104 b800800000 mov eax,8000h 00007ffd`73e01109 0fb77e04 movzx edi,word ptr [rsi+4] 00007ffd`73e0110d 66854606 test word ptr [rsi+6],ax 00007ffd`73e01111 7508 jne clr!PreStubWorker+0x35b (00007ffd`73e0111b) 00007ffd`73e01113 b8ff030000 mov eax,3FFh 00007ffd`73e01118 6623f8 and di,ax 00007ffd`73e0111b 6689bc2458030000 mov word ptr [rsp+358h],di 00007ffd`73e01123 85c9 test ecx,ecx 00007ffd`73e01125 0f853b663a00 jne clr!PreStubWorker+0x3a69a6 (00007ffd`741a7766) 00007ffd`73e0112b 410fb7460c movzx eax,word ptr [r14+0Ch] 00007ffd`73e01130 6689442438 mov word ptr [rsp+38h],ax 00007ffd`73e01135 663bf8 cmp di,ax 00007ffd`73e01138 0f82ad070000 jb clr!PreStubWorker+0x477 (00007ffd`73e018eb) 00007ffd`73e0113e 48899c2418010000 mov qword ptr [rsp+118h],rbx 00007ffd`73e01146 8b430c mov eax,dword ptr [rbx+0Ch] 00007ffd`73e01149 89842420010000 mov dword ptr [rsp+120h],eax 00007ffd`73e01150 85c0 test eax,eax 00007ffd`73e01152 742b je clr!PreStubWorker+0x3bf (00007ffd`73e0117f) 00007ffd`73e01154 48899c2498020000 mov qword ptr [rsp+298h],rbx 00007ffd`73e0115c 44897c2478 mov dword ptr [rsp+78h],r15d 00007ffd`73e01161 44897b0c mov dword ptr [rbx+0Ch],r15d 00007ffd`73e01165 488d4308 lea rax,[rbx+8] 00007ffd`73e01169 48898424f8020000 mov qword ptr [rsp+2F8h],rax 00007ffd`73e01171 8b00 mov eax,dword ptr [rax] 00007ffd`73e01173 89442444 mov dword ptr [rsp+44h],eax 00007ffd`73e01177 a85f test al,5Fh 00007ffd`73e01179 0f85f6653a00 jne clr!PreStubWorker+0x3a69b5 (00007ffd`741a7775) 00007ffd`73e01179 0f85f6653a00 jne clr!PreStubWorker+0x3a69b5 (00007ffd`741a7775) [br=0] 00007ffd`73e0117f 488b542450 mov rdx,qword ptr [rsp+50h] 00007ffd`73e01184 488bce mov rcx,rsi 00007ffd`73e01187 e8b4000000 call clr!MethodDesc::DoPrestub (00007ffd`73e01240) 00007ffd`73e0118c 4889442458 mov qword ptr [rsp+58h],rax 00007ffd`73e01191 83bc242001000000 cmp dword ptr [rsp+120h],0 00007ffd`73e01199 8b430c mov eax,dword ptr [rbx+0Ch] 00007ffd`73e0119c 0f84e2653a00 je clr!PreStubWorker+0x3a69c4 (00007ffd`741a7784) 00007ffd`73e011a2 85c0 test eax,eax 00007ffd`73e011a4 7525 jne clr!PreStubWorker+0x40b (00007ffd`73e011cb) 00007ffd`73e011a6 48899c24a8020000 mov qword ptr [rsp+2A8h],rbx 00007ffd`73e011ae c744247c01000000 mov dword ptr [rsp+7Ch],1 00007ffd`73e011b6 c7430c01000000 mov dword ptr [rbx+0Ch],1 00007ffd`73e011bd 8b058d6ea000 mov eax,dword ptr [clr!g_TrapReturningThreads (00007ffd`74808050)] 00007ffd`73e011c3 85c0 test eax,eax 00007ffd`73e011c5 0f85c0c00f00 jne clr!PreStubWorker+0x4fd (00007ffd`73efd28b) 00007ffd`73e011cb 488b942498010000 mov rdx,qword ptr [rsp+198h] 00007ffd`73e011d3 0f1002 movups xmm0,xmmword ptr [rdx] 00007ffd`73e011d6 0f11842460010000 movups xmmword ptr [rsp+160h],xmm0 00007ffd`73e011de 833d3f7ca00000 cmp dword ptr [clr!MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context+0x24 (00007ffd`74808e24)],0 00007ffd`73e011e5 0f85a7653a00 jne clr!PreStubWorker+0x3a69d2 (00007ffd`741a7792) 00007ffd`73e011eb c684247001000000 mov byte ptr [rsp+170h],0 00007ffd`73e011f3 803d466ea00000 cmp byte ptr [clr!g_StackProbingEnabled (00007ffd`74808040)],0 00007ffd`73e011fa 0f85a6653a00 jne clr!PreStubWorker+0x3a69e6 (00007ffd`741a77a6) 00007ffd`73e01200 32c0 xor al,al 00007ffd`73e01202 0f8525f91c00 jne clr!PreStubWorker+0x1cfd6d (00007ffd`73fd0b2d) 00007ffd`73e01208 e89336ffff call clr!ThePreStubPatch (00007ffd`73df48a0) 00007ffd`73e0120d 498b442408 mov rax,qword ptr [r12+8] 00007ffd`73e01212 48894310 mov qword ptr [rbx+10h],rax 00007ffd`73e01216 418bcd mov ecx,r13d 00007ffd`73e01219 ff1539327b00 call qword ptr [clr!_imp_SetLastError (00007ffd`745b4458)] 00007ffd`73e0121f 488b442458 mov rax,qword ptr [rsp+58h] 00007ffd`73e01224 4881c400030000 add rsp,300h 00007ffd`73e0122b 415f pop r15 00007ffd`73e0122d 415e pop r14 00007ffd`73e0122f 415d pop r13 00007ffd`73e01231 415c pop r12 00007ffd`73e01233 5f pop rdi 00007ffd`73e01234 5e pop rsi 00007ffd`73e01235 5b pop rbx 00007ffd`73e01236 c3 ret </pre> <br /> 마지막 즈음의 00007ffd`73e0121f 주소에서 실행하는 "mov rax,qword ptr [rsp+58h]" 코드의 rax 반환값은 Native Code의 주소에 해당합니다. 따라서 ThePreStub의 코드에서는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > clr!ThePreStub: 00007ffd`73df4800 4157 push r15 00007ffd`73df4802 4156 push r14 00007ffd`73df4804 4155 push r13 00007ffd`73df4806 4154 push r12 00007ffd`73df4808 55 push rbp 00007ffd`73df4809 53 push rbx 00007ffd`73df480a 56 push rsi 00007ffd`73df480b 57 push rdi 00007ffd`73df480c 4883ec68 sub rsp,68h 00007ffd`73df4810 48898c24b0000000 mov qword ptr [rsp+0B0h],rcx 00007ffd`73df4818 48899424b8000000 mov qword ptr [rsp+0B8h],rdx 00007ffd`73df4820 4c898424c0000000 mov qword ptr [rsp+0C0h],r8 00007ffd`73df4828 4c898c24c8000000 mov qword ptr [rsp+0C8h],r9 00007ffd`73df4830 660f7f442420 movdqa xmmword ptr [rsp+20h],xmm0 00007ffd`73df4836 660f7f4c2430 movdqa xmmword ptr [rsp+30h],xmm1 00007ffd`73df483c 660f7f542440 movdqa xmmword ptr [rsp+40h],xmm2 00007ffd`73df4842 660f7f5c2450 movdqa xmmword ptr [rsp+50h],xmm3 00007ffd`73df4848 488d4c2468 lea rcx,[rsp+68h] // sub rsp, 68h 이전의 rsp 위치 = push rdi 시점의 rsp 00007ffd`73df484d 498bd2 mov rdx,r10 // PrecodeFixupThunk에서 계산한 값 00007ffd`73df4850 e86bc50000 <span style='color: blue; font-weight: bold'>call clr!PreStubWorker</span> (00007ffd`73e00dc0) // 반환값으로 Method의 기계어 위치를 반환 00007ffd`73df4855 660f6f442420 movdqa xmm0,xmmword ptr [rsp+20h] 00007ffd`73df485b 660f6f4c2430 movdqa xmm1,xmmword ptr [rsp+30h] 00007ffd`73df4861 660f6f542440 movdqa xmm2,xmmword ptr [rsp+40h] 00007ffd`73df4867 660f6f5c2450 movdqa xmm3,xmmword ptr [rsp+50h] 00007ffd`73df486d 488b8c24b0000000 mov rcx,qword ptr [rsp+0B0h] 00007ffd`73df4875 488b9424b8000000 mov rdx,qword ptr [rsp+0B8h] 00007ffd`73df487d 4c8b8424c0000000 mov r8,qword ptr [rsp+0C0h] 00007ffd`73df4885 4c8b8c24c8000000 mov r9,qword ptr [rsp+0C8h] 00007ffd`73df488d 4883c468 add rsp,68h 00007ffd`73df4891 5f pop rdi 00007ffd`73df4892 5e pop rsi 00007ffd`73df4893 5b pop rbx 00007ffd`73df4894 5d pop rbp 00007ffd`73df4895 415c pop r12 00007ffd`73df4897 415d pop r13 00007ffd`73df4899 415e pop r14 00007ffd`73df489b 415f pop r15 00007ffd`73df489d 48ffe0 <span style='color: blue; font-weight: bold'>jmp rax</span> </pre> <br /> 이렇게 PreStubWorker의 반환 주소로 jmp하는 것으로 마무리를 하게 됩니다.<br /> <br /> 참고로, 메서드가 처음 실행되는 경우는 JIT 컴파일을 하겠지만 이후에는 해당 Native Code의 주소를 가리키도록 내부적으로 MethodDesc 구조체의 "CodeAddr" 주솟값에 보관하게 됩니다. 이후, 다시 최적화된 Native Code가 생성되기까지는 00007ffd`73e01187 위치의 "call clr!MethodDesc::DoPrestub" 함수 내의 "MethodDesc::GetMethodEntryPoint" 함수를 통해 Native Code 주소를 바로 사용하는 식입니다.<br /> <br /> 실제로 windbg에서 ba 명령으로 MethodDesc의 CodeAddr 위치 값이 "read"되는 시점을 잡아 보면 다음과 같은 callstack을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>ba r8 7ffd147a5a18</span> // 7ffd147a5a18 == MethodDesc의 CodeAddr 위치 0:000> <span style='color: blue; font-weight: bold'>g</span> Breakpoint 1 hit clr!MethodDesc::GetMethodEntryPoint+0x119: 00007ffd`73df700b eb01 jmp clr!MethodDesc::GetMethodEntryPoint+0xed (00007ffd`73df700e) 0:000&gt; <span style='color: blue; font-weight: bold'>k</span> # Child-SP RetAddr Call Site <span style='color: blue; font-weight: bold'>00 00000000`0079e850 00007ffd`73ff0acb clr!MethodDesc::GetMethodEntryPoint+0x119 01 00000000`0079e880 00007ffd`73e0118c clr!MethodDesc::DoPrestub+0xfee 02 00000000`0079eaa0 00007ffd`73df4855 clr!PreStubWorker+0x3cc 03 00000000`0079ede0 00007ffd`148b0cfc clr!ThePreStub+0x55</span> 04 00000000`0079ee90 00007ffd`73df6bd3 0x00007ffd`148b0cfc 05 00000000`0079f020 00007ffd`73df6aa0 clr!CallDescrWorkerInternal+0x83 06 00000000`0079f060 00007ffd`73df7130 clr!CallDescrWorkerWithHandler+0x4e 07 00000000`0079f0a0 00007ffd`73ecf622 clr!MethodDescCallSite::CallTargetWorker+0x102 08 00000000`0079f1a0 00007ffd`73ecffe7 clr!RunMain+0x25f 09 00000000`0079f380 00007ffd`73ecfe9a clr!Assembly::ExecuteMainMethod+0xb7 0a 00000000`0079f670 00007ffd`73ecf7e3 clr!SystemDomain::ExecuteMainMethod+0x643 0b 00000000`0079fc70 00007ffd`73ecf761 clr!ExecuteEXE+0x3f 0c 00000000`0079fce0 00007ffd`73ed0ca4 clr!_CorExeMainInternal+0xb2 0d 00000000`0079fd70 00007ffd`75918c01 clr!CorExeMain+0x14 0e 00000000`0079fdb0 00007ffd`75a3a56c mscoreei!CorExeMain+0x112 0f 00000000`0079fe10 00007ffd`8b277bd4 MSCOREE!CorExeMain_Exported+0x6c 10 00000000`0079fe40 00007ffd`8b52ced1 KERNEL32!BaseThreadInitThunk+0x14 11 00000000`0079fe70 00000000`00000000 ntdll!RtlUserThreadStart+0x21 </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2498
(왼쪽의 숫자를 입력해야 합니다.)