Microsoft MVP성태의 닷넷 이야기
디버깅 기술: 11. (Managed) Main Method에 Break Point 걸기 [링크 복사], [링크+제목 복사],
조회: 29706
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 6개 있습니다.)
(시리즈 글이 9개 있습니다.)
디버깅 기술: 11. (Managed) Main Method에 Break Point 걸기
; https://www.sysnet.pe.kr/2/0/469

디버깅 기술: 11.1. (Managed) Main Method에 Break Point 걸기 - 내용 보강
; https://www.sysnet.pe.kr/2/0/470

디버깅 기술: 35. windbg - 분석 예: 시작하자마자 비정상 종료하는 프로세스 - NullReferenceException
; https://www.sysnet.pe.kr/2/0/996

디버깅 기술: 37. .NET 4.0 응용 프로그램의 Main 함수에 BreakPoint 걸기
; https://www.sysnet.pe.kr/2/0/1021

디버깅 기술: 59. NT 서비스가 시작하자마자 디버거를 연결시키는 방법 (1)
; https://www.sysnet.pe.kr/2/0/1586

디버깅 기술: 60. NT 서비스가 시작하자마자 디버거를 연결시키는 방법 (2)
; https://www.sysnet.pe.kr/2/0/1587

디버깅 기술: 61. NT 서비스 시작 단계에서 닷넷 메서드에 BP를 걸어 디버깅하는 방법
; https://www.sysnet.pe.kr/2/0/1598

디버깅 기술: 100. windbg - .NET 4.0 응용 프로그램의 Main 메서드에 Breakpoint 걸기
; https://www.sysnet.pe.kr/2/0/11322

디버깅 기술: 125. WinDbg로 EXE의 EntryPoint에서 BP 거는 방법
; https://www.sysnet.pe.kr/2/0/11859






(Managed) Main Method에 Break Point 걸기


[실습 준비]
1. "Debugging Tools for Windows" 설치.
2. 심벌 환경 설정: 커널 구조체 살펴보기 토픽을 참조. 
그냥 간단하게 다음과 같은 환경변수를 추가해 주셔도 됩니다.

환경변수 이름: _NT_SYMBOL_PATH
환경변수 값  : SRV*c:\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols;C:\Windows\system32;.




.NET의 JIT 컴파일러는 IL-code를 Natvie-code로 변경하는 작업을, 해당 메서드가 실행되기 바로 직전에 수행합니다. 즉, "클래스" 단위가 아닌 "메서드" 단위로 JIT 컴파일링이 된다는 것인데요. 이에 관해서는 다음의 토픽에서 잘 설명해 주고 있습니다.

Method Calls: Part 1 (Normal Call) 
; http://codebetter.com/blogs/gregyoung/archive/2006/07/20/147512.aspx

위의 토픽에서는 VS.NET 2005 환경에서 SOS.dll만을 가지고 메서드가 JIT 컴파일 되기 전/후의 상황을 Method Description 테이블을 통해서 직접 확인하는 방법을 알려주고 있습니다. 물론, 이것이 현실적으로 직접적인 도움이 되는 지식은 아니지만, 이렇게 눈으로 확인해 봄으로써 .NET에 대한 이해의 깊이를 더해줄 수 있기 때문에, ^^ 시간되시는 분은 따라해 보시기 바랍니다.

결국, 기계가 실행할 수 있는 것은 "Managed"가 아닌 "Unmanaged" 코드임을 알 수 있는데요. 사실, ".NET 어셈블리" 자체도 "Unmanaged 스타트업" 코드가 초기에 실행되어, mscoree.dll에서 제공해 주는 "CLR 호스팅 API"를 사용해서 내부에서 JIT 컴파일러를 통해 적절하게 "Unmanaged" 코드로 변경시켜 주면서 실행해 주는 구조일뿐입니다.

개인적으로, 바로 이 부분에서 의문이 생기더군요. (사실 이것도 그다지 밥벌이하고는 상관없겠지만!) 그렇다면 "Main" 함수에는 어떻게 접근할 수 있을까? 라는 것이었습니다. 이미 .NET 환경이 초기화된 이후에는 sos.dll을 사용해서 적절하게 처리를 할 수 있는 반면 Main 함수 자체는 최초의 실행 코드이기 때문에 그 부분에 BP를 거는 것이 그다지 쉽지는 않을 거란 생각이 들었습니다.

그래도 ^^ 한번 해봐야 겠지요. 그래서 자료를 먼저 찾아보았습니다. 대략 다음과 같은 주옥같은 자료들이 눈에 띄였습니다.

NTSD and SOS basics
; https://docs.microsoft.com/en-us/archive/blogs/thottams/ntsd-and-sos-basics

COM Interop 소개
; https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/january/clr-inside-out-introduction-to-com-interop

Bp at the Main(entry point) function of managed application 
; http://byung.egloos.com/2434057

그럼, 위의 글을 모두 종합해서 ... 저랑 같이 실습을 해볼까요? ^^




0. 테스트 코드

namespace ConsoleApplication1
{
	using System;
	public class Program
	{
		string str;
		public void MyMethod(string arg)
		{
			str = "Member Variable";
		}
		static void Main()
		{
			Program s = new Program();
			s.MyMethod("Hello");
		}
	}
}

1. 디버거 실행
	C:\temp>windbg.exe ConsoleApplication1.exe
	또는
	C:\temp>ntsd.exe ConsoleApplication1.exe	

비스타를 사용하시는 분이라면, "NTSD.exe"의 경우에는 "Run as administrator"를 이용하여 명령어 창을 띄운 후, 그 안에서 실행해 주시면 됩니다. (참고로 여기서는 WinDBG.exe를 기준으로 설명하겠지만, 사실 NTSD.exe와 거의 차이가 없습니다.)

위와 같이 실행했으면 화면에는 다음과 같은 텍스트가 보이게 됩니다.

Microsoft (R) Windows Debugger  Version 6.6.0007.5
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: D:\temp\DumpTest\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
Symbol search path is: SRV*\\localhost\d$\Symbol\OSSymbols*http://msdl.microsoft.com/download/symbols;SRV*\\localhost\d$\Symbol\ProductSymbols;C:\Windows\system32;.
Executable search path is: 
ModLoad: 00920000 00928000   ConsoleApplication1.exe
ModLoad: 77c40000 77d5e000   ntdll.dll
eax=009226fe ebx=7ffd5000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=77ca0f18 esp=001efdb8 ebp=00000000 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000200
ntdll!RtlUserThreadStart:
77ca0f18 89442404        mov     dword ptr [esp+4],eax ss:0023:001efdbc=00000000
 

로드되자마자 BP가 걸려서 응용 프로그램 실행이 멈춰 있습니다. VS.NET 2005와 비교해 본다면 "F11" 키를 눌러서 디버그 실행을 한 것과 같다고 볼 수 있겠습니다.

2. Symbol 폴더 설정
환경변수에 "_NT_SYMBOL_PATH" 값을 설정하신 분은 건너뛰셔도 됩니다. 만약 하지 않은 경우라면 별도의 ".symfix", ".sympath" 등의 명령어를 사용해서 임의로 구성이 가능합니다.

3. sos.dll 로드 확인
".chain" 명령어를 사용해서 sos 확장 DLL이 로드되었는 지 확인합니다. 아마 "WinDbg 6.6.0007.5" 버전을 사용하고 계신 분이라면, 기본적으로 sos.dll이 로드되었을 것이므로 별도로 명령을 주지 않아도 됩니다. 아래는 실제 실행 예입니다.

0:000> .chain
Extension DLL search Path:
    C:\Program Files\De....[중간 생략]...les\Debugging Tools for Windows\
Extension DLL chain:
    C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos: image 2.0.50727.312, API 1.0.0, built Thu Oct 19 12:37:43 2006
        [path: C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll]
    dbghelp: image 6.6.0007.5, API 6.0.6, built Sun Jul 09 05:11:32 2006
        [path: C:\Program Files\Debugging Tools for Windows\dbghelp.dll]
    ext: image 6.6.0007.5, API 1.0.0, built Sun Jul 09 05:10:52 2006
        [path: C:\Program Files\Debugging Tools for Windows\winext\ext.dll]
    exts: image 6.6.0007.5, API 1.0.0, built Sun Jul 09 05:10:48 2006
        [path: C:\Program Files\Debugging Tools for Windows\WINXP\exts.dll]
    uext: image 6.6.0007.5, API 1.0.0, built Sun Jul 09 05:11:02 2006
        [path: C:\Program Files\Debugging Tools for Windows\winext\uext.dll]
    ntsdexts: image 6.0.5457.0, API 1.0.0, built Sun Jul 09 05:29:38 2006
        [path: C:\Program Files\Debugging Tools for Windows\WINXP\ntsdexts.dll]

참고로, windbg.exe와는 달리 ntsd.exe에서는 기본적으로 sos.dll이 로드되어 있지 않았습니다.

4. sos 확장 API 사용을 위한 프로그램 부분 실행
sos.dll에서 제공되는 확장 명령어를 사용하려면 mscorwks.dll이 로드되어야 합니다. 실제로, mscorwks.dll이 로드되기 전에 sos 명령어를 사용하게 되면 다음과 같은 오류를 보게 됩니다.

0:000> !clrstack
Failed to find runtime DLL (mscorwks.dll), 0x80004005
Extension commands need mscorwks.dll in order to have something to do.

mscorwks.dll을 로드하는 시점까지 프로그램을 실행시키는 방법은 "sxe ld mscorwks" 명령어를 사용하는 방법이 있습니다.
출력 결과는 아래와 같습니다.

0:000> sxe ld mscorwks
0:000> g
ModLoad: 79000000 79045000   C:\Windows\system32\mscoree.dll
ModLoad: 76720000 767f8000   C:\Windows\system32\KERNEL32.dll
(154c.b70): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=0026f53c edx=77ca0f34 esi=fffffffe edi=77d05d14
eip=77c82ea8 esp=0026f554 ebp=0026f584 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77c82ea8 cc              int     3
0:000> g
ModLoad: 77b80000 77c3f000   C:\Windows\system32\ADVAPI32.dll
ModLoad: 77db0000 77e73000   C:\Windows\system32\RPCRT4.dll
... [중간 생략]...
ModLoad: 76690000 7670d000   C:\Windows\system32\USP10.dll
ModLoad: 752a0000 75434000   C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.6000.16386_none_5d07289e07e1d100\comctl32.dll
ModLoad: 79e70000 7a3d6000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
eax=0026eb90 ebx=00000000 ecx=0026eb88 edx=00343bc2 esi=7ffdf000 edi=20000000
eip=77ca0f34 esp=0026ebcc ebp=0026ec10 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
77ca0f34 c3              ret

하지만, 아직 CLR에 "ConsoleApplication1"의 IL들이 로드조차 되지 않아서 그런지 sos.dll에서 제공되는 "!bpmd" 메서드를 사용해도 정상적으로 동작되지 않았습니다. 그래서 저 같은 경우에는 일단 "unmanaged" 방식으로 돌아와서 디버깅 방법을 찾기 시작했습니다. 우선, 테스트 소스에서 Main 함수에 예외를 발생하도록 일부러 다음과 같이 수정했습니다.

static void Main()
{
  throw new ApplicationException();
  Program s = new Program();
  s.MyMethod("Hello");
}

빌드하고, windbg.exe로 불러들여서 예외발생까지 코드를 실행시킨 후 "k" 명령어를 통해서 다음과 같이 호출 스택을 얻었습니다.

0:000> g
Tue Feb 27 23:25:40.120 2007 (GMT+9): ModLoad: 77b80000 77c3f000   C:\Windows\system32\ADVAPI32.dll
...[중간 생략]...
Tue Feb 27 23:25:40.300 2007 (GMT+9): ModLoad: 79e70000 7a3d6000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
ChildEBP RetAddr  Args to Child              
0026ebc8 77c9fb70 77c6e334 00000054 ffffffff ntdll!KiFastSystemCallRet
...[중간 생략]...
0026fa28 00000000 0034271e 7ffd3000 00000000 ntdll!_RtlUserThreadStart+0x23
Tue Feb 27 23:25:40.498 2007 (GMT+9): ModLoad: 78130000 781cb000   C:\Windows\WinSxS\x86_microsoft.vc80
....[중간 생략]...
Tue Feb 27 23:25:40.618 2007 (GMT+9): ModLoad: 79060000 790b3000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorjit.dll
Tue Feb 27 23:25:40.668 2007 (GMT+9): (12a8.f40): CLR exception - code e0434f4d (first chance)
Tue Feb 27 23:25:40.683 2007 (GMT+9): (12a8.f40): CLR exception - code e0434f4d (!!! second chance !!!)
eax=0026ed20 ebx=004b5108 ecx=00000001 edx=00000000 esi=0026eda8 edi=e0434f4d
eip=7673b09e esp=0026ed20 ebp=0026ed70 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
KERNEL32!RaiseException+0x58:
7673b09e c9              leave

0:000> k
ChildEBP RetAddr  
0026ed70 79f9691a KERNEL32!RaiseException+0x58
0026edd0 7a098e88 mscorwks!RaiseTheExceptionInternalOnly+0x226
0026ee94 003e00a7 mscorwks!JIT_Throw+0xfc
WARNING: Frame IP not in any known module. Following frames may be wrong.
0026eeb0 79e826bd 0x3e00a7
0026ef30 79e8451b mscorwks!CallDescrWorkerWithHandler+0xa3
0026f06c 79e84403 mscorwks!MethodDesc::CallDescr+0x19c
0026f084 79e843e0 mscorwks!MethodDesc::CallTargetWorker+0x20
0026f098 79ef3e20 mscorwks!MethodDescCallSite::CallWithValueTypes+0x18
0026f1fc 79ef3c19 mscorwks!ClassLoader::RunMain+0x220
0026f464 79ef3aee mscorwks!Assembly::ExecuteMainMethod+0xa6
0026f934 79ef3737 mscorwks!SystemDomain::ExecuteMainMethod+0x398
0026f984 79ef18d1 mscorwks!ExecuteEXE+0x59
0026f9cc 79003aa0 mscorwks!_CorExeMain+0x11b
0026f9dc 76763833 mscoree!_CorExeMain+0x2c
0026f9e8 77c7a9bd KERNEL32!BaseThreadInitThunk+0xe
0026fa28 00000000 ntdll!_RtlUserThreadStart+0x23

대강 봐도, "mscorwks!ClassLoader::RunMain"이 Main 메서드를 실행해 주는 함수라는 것을 쉽게 짐작할 수 있습니다.

이제, mscorwks.dll 로드를 굳이 명시적으로 확인할 필요없이 다음과 같이 직접 unmanaged API 함수에 BP를 걸어 놓는 것으로 문제를 해결할 수 있습니다.

0:000> bp mscorwks!ClassLoader::RunMain
Bp expression 'mscorwks!ClassLoader::RunMain' could not be resolved, adding deferred bp

현재는 mscorwks모듈이 로드되어 있지 않기 때문에 RunMain기호를 찾을 수 없지만, 이후에 혹시나 발견이 되면 설정해 주겠다고 친절하게(?) 메시지를 보여주고 있습니다. 이제 거의 다 되었습니다. "g" 명령어로 실행시키면 다음과 같이 RunMain 함수에서 실행이 멈추는 것을 확인할 수 있습니다.

0:000> g
Tue Feb 27 23:36:53.110 2007 (GMT+9): ModLoad: 77b80000 77c3f000   C:\Windows\system32\ADVAPI32.dll
....[중간 생략]...
Tue Feb 27 23:36:55.763 2007 (GMT+9): ModLoad: 79e70000 7a3d6000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
ChildEBP RetAddr  Args to Child              
0030ef14 77c9fb70 77c6e334 00000054 ffffffff ntdll!KiFastSystemCallRet
....[중간 생략]...
mscorlib\7fe79782947b85d961fd55cb5e02a129\mscorlib.ni.dll
Tue Feb 27 23:37:01.632 2007 (GMT+9): Breakpoint 0 hit
eax=0030f584 ebx=00000000 ecx=1e3ea55d edx=80000001 esi=007c3000 edi=00000000
eip=79ef3c78 esp=0030f54c ebp=0030f59c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
mscorwks!ClassLoader::RunMain:
79ef3c78 55              push    ebp

5. Main 함수에 BP 설정
자, 다 되었습니다. 이제 sos.dll에서 제공되는 확장 명령어를 이용해서 Managed 함수에 쉽게 BP를 설정할 수 있습니다. 여기서는 Main 함수가 목표였기 때문에 다음과 같이 설정하시면 됩니다.

0:000> !bpmd ConsoleApplication1 ConsoleApplication1.Program.Main
Found 1 methods...
MethodDesc = 007c3000
Adding pending breakpoints...

0:000> g
Tue Feb 27 23:42:31.292 2007 (GMT+9): ModLoad: 79060000 790b3000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorjit.dll
Tue Feb 27 23:42:31.313 2007 (GMT+9): (f68.16c0): CLR notification exception - code e0444143 (first chance)
JITTED ConsoleApplication1!ConsoleApplication1.Program.Main()
Setting breakpoint: bp 00D30070 [ConsoleApplication1.Program.Main()]
Tue Feb 27 23:42:31.413 2007 (GMT+9): Breakpoint 1 hit
eax=007c3000 ebx=0030f21c ecx=ffffffff edx=ffffffff esi=00135108 edi=00000000
eip=00d30070 esp=0030f1f4 ebp=0030f200 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
00d30070 83ec08          sub     esp,8

어떠세요? 재미있지요? ^^

[첨부된 파일은 실제로 위의 실습을 한 WinDBG.exe 실행 화면의 텍스트를 캡쳐한 것입니다.]



[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/23/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2008-06-09 08시21분
kevin25
2011-04-18 11시17분
.NET 4.0 응용 프로그램의 Main 함수에 BreakPoint 걸기
; http://www.sysnet.pe.kr/2/0/1021
정성태
2019-04-06 12시10분
0:000> sxe ld mscorwks.dll

0:000> g

0:000> bp mscorwks!ClassLoader::RunMain

0:000> g

0:000> !bpmd ConsoleApp1 ConsoleApp1.Program.Main

0:000> g

0:000> !clrstack
정성태

1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13667정성태7/7/20246621닷넷: 2273. C# - 리눅스 환경에서의 Hyper-V Socket 연동 (AF_VSOCK)파일 다운로드1
13666정성태7/7/20247700Linux: 74. C++ - Vsock 예제 (Hyper-V Socket 연동)파일 다운로드1
13665정성태7/6/20247880Linux: 73. Linux 측의 socat을 이용한 Hyper-V 호스트와의 vsock 테스트파일 다운로드1
13663정성태7/5/20247474닷넷: 2272. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)의 VMID Wildcards 유형파일 다운로드1
13662정성태7/4/20247491닷넷: 2271. C# - WSL 2 VM의 VM ID를 알아내는 방법 - Host Compute System API파일 다운로드1
13661정성태7/3/20247413Linux: 72. g++ - 다른 버전의 GLIBC로 소스코드 빌드
13660정성태7/3/20247522오류 유형: 912. Visual C++ - Linux 프로젝트 빌드 오류
13659정성태7/1/20247858개발 환경 구성: 715. Windows - WSL 2 환경의 Docker Desktop 네트워크
13658정성태6/28/20248235개발 환경 구성: 714. WSL 2 인스턴스와 호스트 측의 Hyper-V에 운영 중인 VM과 네트워크 연결을 하는 방법 - 두 번째 이야기
13657정성태6/27/20247911닷넷: 2270. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)을 위한 EndPoint 사용자 정의
13656정성태6/27/20248082Windows: 264. WSL 2 VM의 swap 파일 위치
13655정성태6/24/20247850닷넷: 2269. C# - Win32 Resource 포맷 해석파일 다운로드1
13654정성태6/24/20247787오류 유형: 911. shutdown - The entered computer name is not valid or remote shutdown is not supported on the target computer.
13653정성태6/22/20247937닷넷: 2268. C# 코드에서 MAKEINTREOURCE 매크로 처리
13652정성태6/21/20249245닷넷: 2267. C# - Linux 환경에서 (Reflection 없이) DLL AssemblyFileVersion 구하는 방법파일 다운로드2
13651정성태6/19/20248487닷넷: 2266. C# - (Reflection 없이) DLL AssemblyFileVersion 구하는 방법파일 다운로드1
13650정성태6/18/20248410개발 환경 구성: 713. "WSL --debug-shell"로 살펴보는 WSL 2 VM의 리눅스 환경
13649정성태6/18/20247957오류 유형: 910. windbg - !py 확장 명령어 실행 시 "failed to find python interpreter" (2)
13648정성태6/17/20248278오류 유형: 909. C# - DynamicMethod 사용 시 System.TypeAccessException
13647정성태6/16/20249344개발 환경 구성: 712. Windows - WSL 2의 네트워크 통신 방법 - 세 번째 이야기 (같은 IP를 공유하는 WSL 2 인스턴스) [1]
13646정성태6/14/20247758오류 유형: 908. Process Explorer - "Error configuring dump resources: The system cannot find the file specified."
13645정성태6/13/20248197개발 환경 구성: 711. Visual Studio로 개발 시 기본 등록하는 dev tag 이미지로 Docker Desktop k8s에서 실행하는 방법
13644정성태6/12/20248868닷넷: 2265. C# - System.Text.Json의 기본적인 (한글 등에서의) escape 처리 [1]
13643정성태6/12/20248310오류 유형: 907. MySqlConnector 사용 시 System.IO.FileLoadException 오류
13642정성태6/11/20248198스크립트: 65. 파이썬 - asgi 버전(2, 3)에 따라 달라지는 uvicorn 호스팅
13641정성태6/11/20248673Linux: 71. Ubuntu 20.04를 22.04로 업데이트
1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...