BLOG ARTICLE Programming 궁금하니? | 52 ARTICLE FOUND

  1. 2008.01.23 [VC]DLL에대한 정리
  2. 2008.01.18 [펌] Windbg 를 이용한 디버깅 방법

출처: http://psmon.x-y.net/xy_bb/board.php?id=PgSkill&board_sec=0&page=2&mode=view&total=46&no=25



기본적인것이 잘 정리된것 같아서.. 퍼왔습니다.

DLL (Dynamic Link Library)이란,

어플리케이션에서 동적으로 링크하여 사용할 수 있는 라이브러리를 말하며,
확장자로는 .dll, .fon, .drv, .exe 등이 사용된다.

장점으로는,
일단, 여러 어플리케이션이 사용한다면 메모리 낭비를 줄일 수 있겠고,
업그레이드및 모듈별관리가 용이한점을 들 수 있겠다.

구조는,
간단히 코드부분과 데이터로 구성되어있으며, 스택이 없다는 것이 특징이다.
//스택은 어플이케이션의 것을 사용한다
스택이 없으므로 독립적인 프로세스가 될 수 없으며
운영체제로 부터의 메세지를 받을 수 없다
내부에 Exported Function Table을 가지고 있으며,
서수 + 기호이름 + 실제함수를 가리키는 포인터로 구성되어 있다.

(MSDN-TechnicalArticles-VisualTools-VisualC++-Visual C++4.0- DLLs for Beginners 참조)

종류는,
- Regular DLL (Statically linked MFC DLL) : 배포시 자신의 DLL만 제공가능
- Regular DLL (using Shared MFC DLL) : 배포시 자신의 DLL과 MFC공유DLL을 같이 제공
-> 어플리케이션이 MFC이외의 경우에도 사용가능
C언어 기반의 인터페이스제공 필요.
- MFC Extension DLL (using Shared MFC DLL) : MFC로 작성된 어플리케이션에서만 사용가능

링킹방법으로는,
- Implicit Linking (암시적링킹)
- DLL + Lib + header file 필요.
- 어플리케이션 로딩때 같이 로딩.
- Explicit Linking (명시적링킹)
- 로딩타임 마음대로 결정가능 ( 예: 영문리소스/한글리소스 동적으로 결정 )
- 주요함수
- LoadLibrary : DLL모듈 로딩
- GetProcAddress : 함수의 포인터를 얻어옴
- FreeLibrary : DLL 종료 (reference counter를 1감소시킨다)

컴파일러 커맨드
- _WINDLL : 현재 프로젝트가 DLL임을 의미
- _USRDLL : Regular DLL임을 의미
- _AFXDLL : using Shared MFC DLL 임을 의미
- _AFXEXT : MFC Extension DLL임을 의미

일반적인 익스포트함수 예제

extern "C" declspec(dllexport) void WINAPI function(void);
// extern "C" : 컴파일때 decoration name을 생성하지 않도록 하기위해 (C-Style)
// declspec(dllexport/dllimport) : Export/Import된 함수임을 의미
// WINAPI
- "windef.h"의 119 line 에 _stdcall 로 정의되어 있음
- PASCAL calling convension을 의미 : push는 거꾸로(오른쪽에서 왼쪽), pop은 Callee가 담당.
- 참고로, C++은 디폴트로 _cdecl을 사용 : push는 거꾸로(오른쪽에서 왼쪽), pop은 Caller가 담당.

DLL 찾기 순서
1 어플리케이션(EXE)을 포함한 폴더
2 프로세스의 현재 폴더
3 시스템 폴더
4 윈도우즈 폴더
5 Path에 지정되어 있는 폴더들...


TIPs

1. DLL에 공유데이터를 포함시키기 위한 방법

- memory-mapped file을 사용하는 방법. (요건 나중에...)

- named data section을 사용하는 방법.

#pragma data_seg ("Shared")
int iShared = 0;
#pragma data_seg ()


2. DLL내에 있는 Resource 참조 방법

- 가장 일반적인 MFC 어플리케이션 + MFC Extension DLL 의 경우 리소스를 찾는 순서

1. 어플리케이션(EXE)의 리소스 검색
2. DLL의 리소스 검색
3. MFC공유DLL의 리소스 검색
-> 중복되는 리소스이름이 있는 경우는 먼저 찾아진 리소스를 사용하게 된다.
임의로 찾기 순서를 바꾸기 위해서
AfxSetResourceHandle(원하는 모듈의 리소스핸들)을 사용할 수 있다.
예) 다음의 코드는 DLL내에 있는 리소스를 먼저 찾기 위한 코드이다.
// DLL의 코드
Handle hInstOld = AfxGetResourceHandle(); // 어플리케이션의 리소스핸들을 보관.
AfxSetResourceHandle(DLL의 DllMain에서 얻은 DLL의 리소스핸들);
// ...
AfxSetResourceHandle(hInstOld); // 원래대로 복귀

- MFC 어플리케이션(or 델파이/비베...) + Regular DLL 의 경우.

- Regular DLL을 사용하는 경우에는 위와같이 DLL내의 리소스를 참조할 수 없다.
따라서 DLL내의 리소스(다이얼로그와같은)를 참조하기 위해서는,
다음과 같이 임의로 Module State를 전환시켜주는 함수가 필요하다.
- AFX_MANAGE_STATE(AfxGetStaticModuleState());
일반적으로 프로세스(EXE)의 인스턴스핸들은 거의 항상 0x00400000이고,
DLL은 0x10000000 이다.
DLL내에서 위의 함수를 사용한 후에는 인스턴스핸들이 DLL의 인스턴스핸들(0x10000000)로
바뀐 것을 확인해 볼 수 있을것이다.

- (어플리케이션 + ) Regular DLL + MFC Extension DLL

- Regular DLL에서 MFC Extension DLL내에 있는 리소스를 참조하려 할때 역시
그냥 되지는 않는다. 이때는 두가지 방법이 있는데,

첫번째는,

잠시 리소스를 참조하기 위해 사용되는 인스턴스핸들을 MFC Extension DLL의 것으로
바꿨다가 복구해주는 방법이다.

HINSTANCE hInstOld = AfxGetResourceHandle();
AfxSetResourceHandle(g_hModule);
m_menu.LoadMenu(IDR_LISTOUTPUT)
AfxSetResourceHandle(hInstOld); // restore the old resource chain

이 방법은 위의 리소스찾기 순서를 바꾸기 위해 사용했던 방법과 같다.
g_hModule은 DLLMain에 있던 hInstance를 전역변수로 보관한 것이다.

두번째는,

아래 소스는 MFC Extension DLL을 만들었을 때 만들어지는 소스의 일부이다.
잘 보고 가장 긴 주석문에서 시키는 대로 하면된다.

new CDynLinkLibrary(Implicit_MFCExt_dllDLL); 를 주석처리하고,
따로 함수를 만들어 익스포트 시킨다.
그리고, 그 함수를 Regular DLL의 InitInstance와 같은 함수에서
첨에 한번 초기화를 시키도록 해 주면 된다.
그러면 Regular DLL의 리소스체인에 attach된다.

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// Remove this if you use lpReserved
UNREFERENCED_PARAMETER(lpReserved);

if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("IMPLICIT_MFCEXT_DLL.DLL Initializing!\n");

// Extension DLL one-time initialization
if (!AfxInitExtensionModule(Implicit_MFCExt_dllDLL, hInstance))
return 0;

// Insert this DLL into the resource chain
// NOTE: If this Extension DLL is being implicitly linked to by
// an MFC Regular DLL (such as an ActiveX Control)
// instead of an MFC application, then you will want to
// remove this line from DllMain and put it in a separate
// function exported from this Extension DLL. The Regular DLL
// that uses this Extension DLL should then explicitly call that
// function to initialize this Extension DLL. Otherwise,
// the CDynLinkLibrary object will not be attached to the
// Regular DLL's resource chain, and serious problems will
// result.

//new CDynLinkLibrary(Implicit_MFCExt_dllDLL);

g_hModuleInstance = hInstance;
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("IMPLICIT_MFCEXT_DLL.DLL Terminating!\n");
// Terminate the library before destructors are called
AfxTermExtensionModule(Implicit_MFCExt_dllDLL);
}
return 1; // ok
}

extern "C" AFX_EXT_API void WINAPI InitMFCExtDLL()
{
// create a new CDynLinkLibrary for this app
new CDynLinkLibrary(Implicit_MFCExt_dllDLL);
}

AND

출처: http://cafe.naver.com/architect1.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=33

 

I. 디버깅

1. 기본 디버깅 환경

Windows Driver 디버깅을 위해선 다음 그림과 같이 디버깅용 컴퓨터가 필요합니다. 디버깅 컴퓨터에는 타겟 컴퓨터와 같은 운영체제가 로딩되어 있어야 하고 디버깅을 위한 프로그램이 필요합니다. 디버깅 프로그램은 DDK에서 제공하는데 자세한 사용법은 도움말을 참조하시기 바랍니다.

Debugger                         Debuggee

 

그림에서 있듯이 컴퓨터간의 데이터 교환은 RSC-232 직렬 통신을 통해 이루어 지는데 통신에 필요한 케이블은 다음 그림과 같이 만들어 사용하면 됩니다.

2. Windows 2000/XP에서 디버깅 환경

디버깅을 시작하기 이전에 컴퓨터간의 통신 속도를 맞춰줘야 하고 타겟 컴퓨터를 디버그 모드 설정 해야 합니다. Windows NT 계열에서는 다음과 같이 boot.ini 파일에 다음과 같이 추가해 주면 부팅 디버그 모드로 부팅할 일반 모드로 부팅할 사용자에게 묻는데 이때 디버그 모드를 선택해 주면 디버그 모드로 부팅하게 됩니다.

[boot loader]

timeout=30 default=multi(0)disk(0)rdisk(0)partition(1)\WINNT

[operating systems]

multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect

multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /debugport=com1 /baudrate=57600

통신 속도 설정은 위의 내용을 보면 알겠지만 /debugport 직렬 케이블이 연결된 포트이고 /baudrate 통신 속도를 나타냅니다. 여기에 설정된 속도와 Windows 2000 DDK에서 제공하는 디버깅 프로그램에서의 속도를 맞춰주면 됩니다.

 

다음은 Windbg 실행하여 환경설정을 하고 디버깅 하는 방법입니다.

1 방법은 ddk 설치 같이 설치 되는 windbg 실행 방법이고, 2번의 방법은 최신 Windbg6.2.7.4 실행 방법입니다.  2번의 Windbg6.2.7.4 사용하는 것이 사용하기에도 쉽고 자세한 디버깅을 있습니다.

 

1) 기본 windbg실행

View->Options 선택했을 뜨는 창으로 포트, 속도 Flags 다음과 같이 설정해 주면 됩니다.

참고로 디버그 모드로 부팅하였는데 Windbg 디버깅에 관련된 아무런 내용도 출력되지 않을 시에는 Windbg에서 디버깅 명령인 ‘Ctrl+C’ ‘g+ Enter’ 번갈아 눌러 주시면 디버깅 내용이 출력됩니다.

 

 

 

2) Windbg 6.2.7.4

다운 방법: http://www.microsoft.com/whdc/ddk/debugging/installx86beta.mspx 에서

Install 32-bit Beta Version 6.2.7.4 (April 29, 2003) 8.2 MB 클릭하여 다운 받으시기 바랍니다.

 

실행 화면

 

kernel debugger 실행

 

 

속도와 포트 설정

 

위와 같이 하면 기본적인 디버깅은 설정이 됐습니다.

 

추가적으로,

File->Symbol File Path

: 드라이버를 컴파일 하면, xxx.pdb파일이 xxx.sys파일과 같이 생성되는데, 파일을 등록 해주면, break 경우( ) bluescreen) 해당 소스에 line 함수 명들과 같은 자세한 정보가 출력이 됩니다.

xxx.pdb파일을 symbol(심볼) 파일이라고 합니다. 파일은 컴파일 드라이버 파일에 대해 함수위치와 소스정보에 대해 가지고 있습니다. 그래서 관련 파일이 로딩되면, 컴파일 소스에 대한 정보(함수들, 소스의 line 정보(소스가 있다면)…) 얻을 있습니다.

등록방법은 File->Symbol File Path 클릭할 경우, 심볼 파일의 경로를 설정하여주면, 해당 경로의 심볼을 로딩하게 됩니다. 심볼이 바뀐 경우에는 Reload check 줍니다.

 

그림을 보면, 심볼이 세미콜론으로 2개가 등록이 되어있습니다.

SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols

: web으로 관련 심볼을 다운받는 것입니다. 예전에 심볼을 미리 다운받아서 위치를 지정해 주었지만, 지금은 필요할 경우 웹에서 직접 다운 받는다. 심볼파일 중에는 ndis.sys 같은 커널 함수들에 대한 심볼들을 포함하고 있어서 커널 소스는 없어도 함수 이름 등은 수가 있습니다.

c:\symbols

: 경로는 사용자가 추가해 주는 심볼 파일 경로입니다. 사용자가 컴파일해서 만들어진 심볼파일을 폴더에 복사해놓으면 심볼이 로딩됩니다.

 

아래의 그림은 심볼이 등록되었음을 보여주고 있습니다.

 

File->Source File Path

소스의 경로를 지정해 줍니다. 소스를 로딩 경우, break 지점이 소스 내에 있으면, 해당 소스의 라인에서 p명령어를 치며, 디버깅을 나갈 있습니다.

 

View

MFC 에서 디버기을 메모리, 디스어셈블, 스택 등을 있듯이 여기서도 있습니다.

 

3. 디버깅 명령어

Windows 드라이버의 디버깅은 Windows Visual Studio와는 달리 사용자가 디버깅 명령을 직접 입력하는 방식으로 진행됩니다. 디버깅을 위해선 여러 가지 디버깅 명령어들을 다룰 있어야 되는데 디버깅 명령어에 대한 자세한 내용은 DDK 도움말을 참고하시기 바랍니다.

다음은 디버깅에서 많이 사용하는 명령어를 정리한 것으로 정도의 명령어로도 간단한 디버깅은 충분히 수행할 있을 것이라 생각됩니다.

명령어

Ctrl + C, Ctrl + Break

일시 중지

g

계속 진행

bp

Break Point 설정 ) bp SnSReceive

bl

Break Point List 보기

p

Asm 명령어 줄씩 디버깅

bc

Break Point 삭제 ) bp SnSReceive

db, dw, dd

메모리 덤프

Ln cs:eip

현제 타겟 컴퓨터의 실행 위치

?

명령어 도움말

.reboot

타겟 컴퓨터 재부팅

 

. !analyze -v

디버기(debugee) BlueScreen 발생하거나, 시스템에 문제가 있을 경우, Windbg 프로그램은 break 걸리게 된다. 이때 break 원인을 자세히 알기 위해서 !analyze -v명령을 명령줄에 치면, 자세한 정보를 얻을 있다.

 

 

 

 

 

4. Windows 98에서 디버깅 환경

디버깅을 시작하기 이전에 컴퓨터간의 통신 속도와 사용할 포트를 맞춰줘야 하고 디버깅이 시작될 타겟 컴퓨터를 디버그 모드 시작해야 합니다. Windows 98 에서는 컴퓨터간의 통신 속도를 맞추기 위해서 다음과 같은 절차가 필요합니다.

 

1) 타겟 컴퓨터에서 해야할 .

a)       타겟 컴퓨터에 98DDK 설치되어 있어야 한다.

b)       설치된 경로를 C:\98DDK라고 하면 C:\98DDK\bin\Runwdb98.bat 파일의 내용을 다음과 같이 수정해야한다.

수정전: @wdeb98 %1  /n  /x  /c:2  /r:19200  /f:runwdeb.wrf  ..\win.com

수정후: @wdeb98 %1  /n  /x  /c:1  /r:19200  /f:runwdeb.wrf  c:\windows\win.com

 

여기서 통신속도(/r) 사용자가 정해주는 값으로 19200 사용해도 되고 다른 값으로 사용해도 된다. 하지만 값은 반드시 디버거의 Rterm98.exe에서 사용하는 값과 같아야 한다. 디버깅을 하다가 종종 출력되는 내용이 깨지는 경우가 있는데 이럴때는 통신속도를 변경하여 알맞은 속도로 설정해주면 된다. 포트에 해당되는 것은 ‘/c’ 이다. 값도 기본적으로 2 되어있고 그대로 사용하면 된다. 중요한 것은 어떤 포트를 사용하느냐가 아니라 시리얼케이블이 연결된 포트와 /c 포트와 일치해야한다는 것이다. “/f:runwdeb.wrf” 심볼파일을 등록할 사용하는 파일로 부분에 대해서는 ‘c)’ 자세하게 기술하겠다.

c)       C:\98DDK\bin\ runwdeb.wrf 내용은 아래와 같다.

사용자가 드라이버 소스를 컴파일해서 만들어진 심볼파일을 SnSDrv.sym이라고 하면 위의

림과 같이 기입해주고 SnSDrv.sym 파일을 C:\98DDK\bin 복사해주면 디버깅시 심볼이 등록

된다.

 

98DDK에서는 이러한 심볼파일들을 다음과 같이 제공하고 있다.

98SE: 98ddk CD \DEBUG_WINDOWS98SE\Retail\

98: 98ddk CD \Dbg_sym\Retail\

 

아래의 그림은 디버깅이 시작될 심볼이 등록되었음을 보여주는 화변을 캡쳐한 것이다.

 

2) 디버깅 컴퓨터에서 해야할 .

a)       디버깅 컴퓨터에 98DDK 설치되어 있어야 한다.

b)       C:\98DDK\bin\Rterm98.exe 실행한다. 다음은 실행한 화면이다.

c)       메뉴에서 “Settings->Options ” 선택하면 아래와 같은 창이 뜬다.

d)       위의 그림과 같이 설정해주면 타겟 컴퓨터에서 설정한 통신속도와 포트가 일치한다.

e)       확인 누른다.

 

3) 디버깅하기

a)       이제 모든 환경이 설정되었다.

b)       디버깅 컴퓨터에서는 C:\98DDK\bin\Rterm98.exe 실행한다.

c)       타겟 컴퓨터에는 디버깅하고자하는 드라이버가 설치되어 있어야하고 심볼을 등록해주면 자세한 정보를 얻을 수가 있다.

d)       타겟 컴퓨터를 도스모드로 재부팅한다.

e)       C:\98DDK\bin\Runwdb98.bat 파일을 실행하면 디버깅 컴퓨터에 실행된 Rterm98 화면에서 디버깅되는 내용을 확인 있다.

 

AND