성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <div style='font-family: 맑은 고딕, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>windbg로 확인하는 .NET CLR 메서드</div> <br /> windbg와 sos 명령어 실습 겸 CLR 메서드에 대한 JIT 컴파일 여부를 확인해 볼 텐데요. 대략 다음의 글에 대한 실습 과정이라고 해야겠지요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects ; <a target='_tab' href='https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/may/net-framework-internals-how-the-clr-creates-runtime-objects'>https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/may/net-framework-internals-how-the-clr-creates-runtime-objects</a> </pre> <br /> 실습을 위해 다음과 같이 간단한 코드를 만들어서 실행시키고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { MyMethod(); Console.ReadLine(); } <b style='COLOR: blue'>static void MyMethod()</b> { Console.WriteLine("MyMethod"); } <b style='COLOR: blue'>static void MyMethod2()</b> { Console.WriteLine("MyMethod2"); } } } </pre> <br /> 멈춘 그 시점에 windbg로 연결하고 다음과 같이 sos.dll을 로딩시켜 줍니다. (여기서는 x64 / .NET 3.5 환경에서 테스트합니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 0:003> .load "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727\\SOS.dll" SOS.dll (SOS Debugging Extension) ; <a target='_tab' href='https://learn.microsoft.com/en-us/dotnet/framework/tools/sos-dll-sos-debugging-extension'>https://learn.microsoft.com/en-us/dotnet/framework/tools/sos-dll-sos-debugging-extension</a> </pre> <br /> 우선, 기계어로 번역되지 않은 MyMethod2 먼저 확인해 보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 0:003> <b style='COLOR: blue'>!name2ee ConsoleApplication1.exe!ConsoleApplication1.Program.MyMethod2</b> Module: 000007ff000433d0 (ConsoleApplication1.exe) Token: 0x0000000006000003 <b style='COLOR: blue'>MethodDesc: 000007ff000439a8</b> Name: ConsoleApplication1.Program.MyMethod2() Not JITTED yet. Use !bpmd -md 000007ff000439a8 to break on run. 0:003> <b style='COLOR: blue'>!dumpmd 000007ff000439a8</b> Method Name: ConsoleApplication1.Program.MyMethod2() Class: 000007ff00182210 MethodTable: 000007ff000439d0 mdToken: 06000003 Module: 000007ff000433d0 <b style='COLOR: blue'>IsJitted: no CodeAddr: ffffffffffffffff </b></pre> <br /> JIT 컴파일링이 된 MyMethod를 확인해 보면 다음과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 0:003> <b style='COLOR: blue'>!name2ee ConsoleApplication1.exe!ConsoleApplication1.Program.MyMethod</b> Module: 000007ff000433d0 (ConsoleApplication1.exe) Token: 0x0000000006000002 MethodDesc: <b style='COLOR: blue'>000007ff00043998</b> Name: ConsoleApplication1.Program.MyMethod() <b style='COLOR: blue'>JITTED Code Address: 000007ff00190180</b> 0:003> <b style='COLOR: blue'>!dumpmd 000007ff00043998</b> Method Name: ConsoleApplication1.Program.MyMethod() Class: 000007ff00182210 MethodTable: 000007ff000439d0 mdToken: 06000002 Module: 000007ff000433d0 <b style='COLOR: blue'>IsJitted: yes CodeAddr: 000007ff00190180 </b></b></pre> <br /> JIT 컴파일된 기계어 주소 "CodeAddr: 000007ff00190180"에 대해서 "u" 명령어를 내려서 확인하는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 0:003> <b style='COLOR: blue'>u 000007ff00190180</b> Lf // 또는 !u [MethodDesc 값] 000007ff`00190180 4883ec28 sub rsp,28h 000007ff`00190184 90 nop 000007ff`00190185 48b8c8360400ff070000 mov rax,7FF000436C8h 000007ff`0019018f 8b00 mov eax,dword ptr [rax] 000007ff`00190191 85c0 test eax,eax 000007ff`00190193 7405 je 000007ff`0019019a 000007ff`00190195 e846b29ef1 call mscorwks!JIT_DbgIsJustMyCode (000007fe`f1b7b3e0) 000007ff`0019019a 90 nop 000007ff`0019019b 48b950306e1200000000 mov rcx,126E3050h 000007ff`001901a5 488b09 mov rcx,qword ptr [rcx] 000007ff`001901a8 e8b3908ef0 <b style='COLOR: blue'>call mscorlib_ni+0x319260 (000007fe`f0a79260) // Console.WriteLine</b> 000007ff`001901ad 90 nop 000007ff`001901ae eb00 jmp 000007ff`001901b0 000007ff`001901b0 4883c428 add rsp,28h 000007ff`001901b4 f3c3 rep ret </pre> <br /> IL 코드도 확인해 볼까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 0:003> <b style='COLOR: blue'>!dumpil 000007ff00043998</b> <b style='COLOR: blue'>ilAddr = 0000000000332065</b> IL_0000: nop IL_0001: ldstr "MyMethod" IL_0006: call System.Console::WriteLine IL_000b: nop IL_000c: ret </pre> <br /> 보시는 것처럼 ilAddr == 0000000000332065 주소에 MyMethod의 Body가 확인됩니다. 풀어볼까요? ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 0:003> <b style='COLOR: blue'>db 0000000000332065</b> Le 00000000`00332065 36 00 72 01 00 00 70 28-11 00 00 0a 00 2a 6.r...p(.....* <b style='COLOR: blue'>0x36 == Tiny header</b> typedef struct IMAGE_COR_ILMETHOD_TINY { BYTE Flags_CodeSize; } IMAGE_COR_ILMETHOD_TINY; 0x36 == 00110110b 하위 2bit 10b == CorILMethod_TinyFormat 상위 6bit 001101b == 0xd IL 코드 크기 (즉, 이후 13byte들은 실행되어야 할 IL코드) <b style='COLOR: blue'>0x00 == nop</b> <b style='COLOR: blue'>0x72 == ldstr</b> 0x01 0x00 0x00 0x70 == [0x70000001 문자열 token] <b style='COLOR: blue'>0x28 == call</b> 0x11 0x00 0x00 0x0a == [0x0a000011 메서드 token] <b style='COLOR: blue'>0x00 == nop 0x2a == ret</b> </pre> <br /> <hr style='width: 50%' /><br /> <br /> JIT 컴파일 되기 전과 후를 windbg의 MethodDesc 테이블을 확인해 보면 알 수 있습니다.<br /> <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 0:003> <b style='COLOR: blue'>!DumpMT -MD 000007ff000439d0</b> EEClass: 000007ff00182210 Module: 000007ff000433d0 Name: ConsoleApplication1.Program mdToken: 02000002 (D:\...[경로생략]...\ConsoleApplication1.exe) BaseSize: 0x18 ComponentSize: 0x0 Number of IFaces in IFaceMap: 0 Slots in VTable: 9 -------------------------------------- MethodDesc Table Entry MethodDesc JIT Name 000007fef0a4abe0 000007fef07ce828 PreJIT System.Object.ToString() 000007fef0a52560 000007fef07ce830 PreJIT System.Object.Equals(System.Object) 000007fef0a4bc70 000007fef07ce870 PreJIT System.Object.GetHashCode() 000007fef0afe5f0 000007fef07ce8a0 PreJIT System.Object.Finalize() 000007ff0004c040 000007ff000439c8 NONE ConsoleApplication1.Program..ctor() 000007ff00190120 000007ff00043988 JIT ConsoleApplication1.Program.Main(System.String[]) 000007ff00190180 000007ff00043998 JIT ConsoleApplication1.Program.MyMethod() <b style='COLOR: blue'>000007ff0004c030 000007ff000439a8 NONE ConsoleApplication1.Program.MyMethod2()</b> 000007ff001901e0 000007ff000439b8 JIT ConsoleApplication1.Program.ShowMethodDesc() </pre> <br /> Entry 값이 "000007ff0004c030"이므로, IL 코드에서 이 함수를 부를 때 "000007ff0004c030" 주소에 있는 코드를 실행하게 됩니다. 어떤 코드인지는 감이 잡히시죠?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 0:003> u 000007ff0004c030 <b style='COLOR: blue'>000007ff`0004c030 e8bbea8bf1 call mscorwks!PrecodeFixupThunk (000007fe`f190aaf0)</b> 000007ff`0004c035 cc int 3 000007ff`0004c036 0402 add al,2 000007ff`0004c038 e8b3ea8bf1 call mscorwks!PrecodeFixupThunk (000007fe`f190aaf0) 000007ff`0004c03d cc int 3 000007ff`0004c03e 06 ??? 000007ff`0004c03f 01e8 add eax,ebp 000007ff`0004c041 ab stos dword ptr [rdi] </pre> <br /> 물론, JIT된 이후에는 Entry 값이 교체되는데, 그것이 바로 실제 기계어로 번역된 코드의 주솟값입니다.<br /> <br /> 재미있게도, 위의 확인 과정은 Reflection으로도 가능합니다.<br /> <br /> 위에서 name2ee를 통해서 "MethodDesc" 값을 확인해 보았는데요. 코드에서는 다음과 같이 MethodInfo.MethodHandle.Value 값이 MethodDesc 값과 동일하다는 것을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > MethodInfo methodInfo = typeof(Program).GetMethod("MyMethod", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); IntPtr methodDesc = <b style='COLOR: blue'>methodInfo.MethodHandle.Value</b>; </pre> <br /> 기계어로 번역되기 전과 후의 주소는 다음과 같이 확인해 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > MethodInfo methodInfo = ...; IntPtr pAddress = methodInfo.<b style='COLOR: blue'>MethodHandle.GetFunctionPointer()</b>; Console.WriteLine("Stub or Jitted Address == " + pAddress.ToString("x")); </pre> <br /> JIT 컴파일되기 전과 후에 각각 MethodHandle.GetFunctionPointer() 값을 구하면 서로 다르다는 것을 확인할 수 있고 그 값을 windbg의 MethodDesc 테이블에 있는 Entry 값과 비교해보면 동일한 것을 알 수 있습니다.<br /> <br /> <a target='_tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=552&boardid=331301885'>첨부한 솔루션은 이를 확인해 본 간단한 소스 코드</a>입니다.<br /> <br /><br /><hr /><span style='color: Maroon'>[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
7922
(왼쪽의 숫자를 입력해야 합니다.)