Visual C++에서 디버그 정보 생성
GENERATING DEBUG INFORMATION WITH VISUAL C++
1. 디버그 정보 종류
2. 디버그 정보 포맷
3. 디버그 정보 생성 (general)
4. 디버그 정보 생성 (Visual C++ 6.0)
5. 디버그 정보 생성 (Visual C++ 2002, 2003, 2005)
6. 정적 라이브러리를 위한 디버그 정보
7. 실행파일의 디버그 정보 및 사이즈
8. .DBG 파일들
9. 디버거와 디버그 정보 포맷
10. Operation System 심볼
소개
개발자는 응용프로그램을 디버그하기 위해 디버거를 사용할 때, 다음과 같은 것들을 원한다.
한 단계씩 소스 코드를 실행하고, 소스 파일에 브레이크 포인트를 설정하며, 다양한 변수의 값(복잡한 사용자 정의 타입은 물론)을 조사하는 것.
그러나 실행파일은 대부분 기계어 명령어, 운영체제를 위한 헤더, 테이블과 같은 raw byte의 연속일 뿐이다. 또한 실행파일이 운영체제에 의해 로드되고 실행될 때, 다양한 목적(스택, 힙, …)을 위해 추가적으로 메모리를 사용한다. 그러나 이것들 역시 전부 raw byte이다.
그렇다면 디버거는 현재 실행하는 있는 CPU 명령어에 대응하는 소스파일과 라인의 위치를 어떻게 알 수 있을까? 또는, 함수 Y에 있는 로컬 변수 X에 대응하는 스택 메모리의 주소를 어떻게 알 수 있을까? 해답은 디버그 정보에 있다.실행하는 프로그램의 raw byte에 고수준 프로그래밍 언어를 링크시킨다.
1. 디버그 정보의 종류
단순하게, 여기서는 현재 마이크로소프트의 디버거와 인텔 x86 플랫폼에 대한 디버그 정보로 제한한다. 다음의 리스트는 현재 필자가 알고 있는 모든 디버그 정보이다.
디버그 정보 | 설명 |
Public 함수/변수 | 몇몇 컴파일 유닛(소스 파일)에 보이는 함수와 변수. 모든 함수와 변수는 위치와 이름을 디버그 정보에 저장한다. |
Private 함수/변수 | 컴파일 유닛에 보이지 않는 것들을 포함한 모든 함수와 변수 (정적 함수, 정적 변수, 함수 파라미터 포함). 모든 함수와 변수는 위치와 사이즈, 이름을 디버그 정보에 저장한다. |
소스파일/라인 정보 | 실행 파일의 위치에 대응하는 모든 소스파일의 라인에 매핑한다. (물론 주석처럼 모든 소스 라인이 매핑되는 것은 아니며, 디버그 정보에도 포함되지 않는다.) |
타입 정보 | 모든 함수와 변수에 대해, 디버그 정보는 타입에 대한 추가 정보를 저장할 수 있다. 디버거는 변수나 함수 파라미터가 정수형, 문자열, 사용자 정의 타입 인지 등의 정보를 알려준다. 그리고 함수에 대해서는 파라미터의 수, calling convention, 함수 리턴 타입의 정보를 알려준다. |
FPO 정보 | FOP(frame pointer omission, (스택) 프레임 포인터 생략) 최적화로 컴파일된 함수에 대해, 디버거가 함수의 stack frame의 사이즈를 결정하기 위한 어떤 데이터를 디버그 정보에 저장한다. 비록 frame pointer를 사용하지 않 을 때조차. 이 정보 없이 디버그는 최적화된 응용프로그램을 디버깅할 때 정확한 call stack을 보여줄 수 없다. |
편집/계속 정보 | 이 정보는 studio 시스템이 Edit(편집)과 Continue(계속) 기능을 구현하는데 도움을 준다. |
* ‘location(위치)’는 여러 의미가 있다. 함수에서는 항상 함수의 첫 byte 주소를 의미하며, 전역/정적 변수에서는 메모리에서 변수의 첫 byte 주소를 의미한다. 그리고 로컬 변수와 함수 파라미터는 보통 함수의 stack frame에서 미리 정의된 위치로부터 offset된 변수의 첫 byte 주소이다.
location의 다른 타입도 물론 가능하다(예, register, TLS slot, metadata token).
2. 디버그 정보 포맷 (Debug information formats)
지금까지 디버거 정보의 종류를 알아보았으며, 이제 디버거 정보가 어떻게 저장되는지 살펴보자.
예전에는 마이크로소프트 개발 툴은 패키징된 다른 디버그 정보 포맷을 사용해왔다. 여기서는 널리 사용되는 COFF (Common Object File Format, 유닉스에서 사용되는 파일 포맷), CodeView, Program Database format에 대해 살펴본다.
앞으로 살펴볼 모든 포맷에 대해, 다음의 특징들을 주로 알아볼 것이다
- 어떤 종류의 디버그 정보가 이 포맷을 사용하여 저장될까?
- 과연 디버그 정보는 어디에 저장될까? (실행 파일 자체에? 아니면 따로?)
- 포맷이 문서화되어 있나? 없나?
COFF
모든 디버그 정보 포맷 중에 가장 오래된 것으로, 디버그 정보 중 딱 3가지 종류만 포함한다.
- public 함수와 변수, 소스 파일과 라인 정보, FPO 정보
COFF 디버그 정보는 항상 실행 파일 자체에 저장되며, 별도의 파일에 저장될 수 없다.
이 포맷은 문서화되었으며, 문서는 아래 링크에서 볼 수 있다.
- Microsoft Portable Executable and Common Object File Format Specification
CodeView
이건 새롭고 좀 더 복잡한 포맷이다. Edit와 Continue 데이터를 제외한 모든 이용 가능한 디버그 정보를 저장할 수 있다.
코드뷰 정보는 보통 실행 파일 자체에 저장되나, 실행파일에서 정보를 빼내서 파일(보통 .DBG 확장자를 갖는다)에 저장할 수도 있다.
코드뷰 포맷은 일부 문서화되어 있으며, 문서는 MSDN의 Visual C++ 5.0 Symbolic Debug Information Specification문서에 있다.
Program Database
세가지 디버그 정보 포맷 중 가장 새로운 것이다. Edit와 Continue 데이터를 포함한 가능한 모든 디버그 정보를 저장할 수 있다. 또한 incremental linking(증분 링크)을 지원한다. (다른 포맷에서는 불가능)
프로그램 데이터베이스 정보는 항상 실행파일과 별도로 파일(보통 .PDB 확장자)에 저장된다.
프로그램 데이터베이스 포맷은 문서화되어 있지 않는 대신, 특별한 프로그래밍 인터페이스(DbgHelp와 DIA)가 이용 가능하다.
현재 데이터베이스 포맷에는 2 종류가 있다. 첫 번째 버전은 종종 PDB 2.0이라 불리며, Visual Studio 6.0에서 사용된다. 두 번째 버전과 새로운 버전 (PDB7.0이라 불림)은 Visual Studio.NET에서 사용된다. PDB7.0 포맷은 하위 호환이 불가능하다 (Visual Studio 6.0 디버거는 읽을 수 없다)
다음의 테이블은 세가지 포맷에 대한 비교 데이터이다.
Format | Documented | Storage | Public functions and variables | Private functions and variables | Source files and lines | Type information | FPO information | EnC information |
COFF | Yes | Executable | + | - | + | - | + | - |
CodeView | Yes | Executable or separate file (.DBG) | + | + | + | + | + | - |
Program Database | No | Separate file (.PDB) | + | + | + | + | + | + |
3. 디버그 정보 생성 (Generating debug information)
Build process
전형적인 실행파일의 빌드 프로세스는 두 단계로 구성한다 – 컴파일과 링크.
먼저 컴파일러는 소스 파일을 파싱하고, 기계어 명령어를 만들어내며, 이는 오브젝트 파일에 저장된다. 다음으로 링커는 모든 이용 가능한 오브젝트 파일을 최종 실행 파일에 결합한다. 오브젝프 파일은 물론, 실제 오브젝트 파일의 모음이라 할 수 있는 라이브러리를 사용하도록 요구될 수도 있다. 전체 프로세스가 다음의 그림에 나와있다.
여기서, 실행 파일을 위한 디버그 정보는 다음 두 단계를 거쳐 생성된다.
먼저, 컴파일러가 모든 소스 파일에 대한 디버그 정보를 만들어 내도록 요청한다. 다음으로 링커가 모든 파일에 대한 디버그 정보를 실행 파일에 대한 디버그 정보에 병합시키도록 요구한다.
이 프로세스는 다음 그림과 같다.
디폴트 설정에선, 컴파일러와 링커는 디버그 정보를 만들어내진 않는다. 그래서 컴파일러와 링커가 모든 단계마다 원하는 디버그 정보를 만들어낼 수 있도록 명시해줘야 한다. 이 때 디버그 정보 종류, 디버그 정보 포맷, 디버그 정보 저장 파일들을 명시할 수 있다.
아래는 Microsoft Visual C++ 6.0과 Microsoft Visual C++.NET(2002 and 2003)의 컴파일러와 링커 옵션에 대해 살펴본다.
4. Visual C++ 6.0
Compiler
아래 열거한 옵션 중 하나를 사용하면, 컴파일러는 소스 파일에 대한 디버그 정보를 만들게 된다.
: /Zd, /Z7, /Zi, /ZI (모든 옵션은 IDE에서 설정 가능)
- /Zd 옵션 : COFF 포맷 사용, 오브젝트 파일에 결과 저장
- /Z7 옵션 : CodeView 포맷 사용, 오브젝트 파일에 결과 저장
- /Zi 옵션 ; Program Database 포맷 사용, 별도의 .PDB 파일에 저장
- /ZI 옵션 : /Zi 옵션과 거의 동일, Edit와 Continue 데이터를 포함
/Zi와 /ZI 옵션에 의해 생성된 .PDB 파일 이름은 디폴트가 VC60.PDB 이지만, /Fd 컴파일러 옵션을 사용해서 변경할 수 있다.
이를 정리하면 다음과 같다.
Option | Format | Storage | Contents |
/Zd | COFF | .OBJ file |
|
/Z7 | CodeView | .OBJ file |
|
/Zi | Program Database | .PDB file |
|
/ZI | Program Database | .PDB file |
|
Linker
실행 파일에 대한 디버그 정보를 위해선 다음의 옵션을 링커에 설정한다.
: /debug, /debugtype, /pdb, /pdbtype (IDE에서 옵션 설정 가능)
- /debug 옵션 : 링커에게 실행파일에 대한 디버그 정보를 생성할지 여부를 묻는다. 이 옵션이 설정되어 있지 않으면, 디버그 정보는 만들어지지 않으며, 다른 옵션은 무시된다.
- /debugtype 옵션 : 생성된 디버그 정보에 대한 포맷을 명시한다.
- debugtype : coff => COFF 포맷 사용
- /debugtype : cv => CodeView 또는 Program Database 포맷 사용 (/pdb 옵션)
- /debugtype : both => COFF와 CodeView/Program Databse 포맷 둘다 사용
/debugtype:coff 옵션을 사용할 때 중요한 점은 파일과 라인 정보가 디버그 정보에 포함되지 않는다는 것이다. 비록 오브젝트 파일을 위해 생성한 디버그 정보를 포함되었을지라도. 그래서 소스 파일과 라인 정보가 필요하면 /debugtype:cv 또는 /debugtype:both 옵션을 사용해야 한다.
/pdb 옵션은 CodeView 또는 Program Database 포맷을 사용할지 여부이다. /pdb:none 이면 CodeView 포맷을 사용하고, /pdb:filename 이면 Program Databse 포맷을 사용한다. 이때 .PDB 파일을 명시한다. /debugtype:coff 옵션이 설정되어 있으면, /pdb 옵션은 무시된다.
/pdbtype 옵션은 하나 이상의 오브젝트 파일과 라이브러리에 대한 디버그 정보가 .PDB 파일에 저장될 때만 유효하다. /pdbtype:setp 옵션은 링커에게 디버그 정보를 원본 .PDB 파일에 남겨놓도록 하며, 실행 파일을 위한 .PDB 파일로 복사하지 않는다. 그 결과 링크 프로세스는 좀 더 빠르게 진행되지만, 모든 .PDB 파일은 실행파일을 성공적으로 디버그하길 원할 것이다. 디버그 정보 파일을 다수의 .PDB 파일들에 저장하지 않으려면, /pdbtype:con 옵션을 사용한다. 이 옵션은 링커가 모든 .PDB 파일들의 내용을 실행파일의 최종 .PDB파일로 복사할 것이다.
좀더 이해를 돕기 위해, 다음에 정리해 놓았다.
/debugtype | /pdb | Format | Storage |
coff | /pdb:none (has no effect) | COFF | In the executable |
coff | /pdb:filename (has no effect) | COFF | In the executable |
cv | /pdb:none | CodeView | In the executable |
cv | /pdb:filename | Program Database | In .PDB file |
both | /pdb:none | COFF and CodeView | In the executable |
both | /pdb:filename | COFF and Program Database | COFF information in the executable, Program Database information in .PDB file |
5. Visual C++ 2002, 2003, 2005
Compiler
컴파일러는 아래의 옵션을 사용하여 소스파일의 디버그 정보를 만들게 된다.
: /Zd, /Z7, /Zi, /ZI
(모든 옵션은 IDE에서 설정가능하며, /Zd는 Visual C++ 2005에서 지원하지 않는다.)
- /Z7 옵션 : CodeView 포맷 사용, object 파일에 결과 저장
- /Zd, /Zi, /ZI 옵션 : Program Database 포맷 사용. .PDB 파일에 결과 저장
(3가지 옵션에 대한 차이점은 디버그 정보의 내용이 다르며, 아래 표 참조할 것)
/Zd, /Zi, /ZI 옵션으로 생성된 .PDB 파일은 디폴트가 각각 VC70.PDB, VC71.PDB, VC80.PDB 이다. (Visual Studio 버전에 따라 다름). 그러나 /Fd 컴파일러 옵션을 사용하여 변경할 수 있다.
주목할 점은 이러한 컴파일러의 새로운 버전들은 COFF 포맷을 사용하는 디버그 정보를 만들지 않는다.
요약 정보
Option | Format | Storage | Contents |
/Z7 | CodeView | .OBJ file |
|
/Zd | Program Database | .PDB file |
|
/Zi | Program Database | .PDB file |
|
/ZI | Program Database | .PDB file |
|
Linker
다음의 옵션들은 링커에서 실행파일에 대한 디버그 정보를 생성할 때 사용한다.
: /debug, /pdb, /pdbstripped (IDE에서 설정 가능)
- /debug 옵션 : 실행파일에 대한 디버그 정보를 생성
이 옵션이 명시되지 않으면, 디버그 정보는 생성되지 않으며, 다른 옵션은 무시된다.
디버그 정보 포맷은 항상 Program Database이며, 항상 .PDB 파일에 저장된다.
링커는 디폴트로서 .PDB 파일의 이름으로 실행 파일의 이름을 사용한다.
.PDB 파일은 모든 디버그 정보의 내용을 포함할 수 있다.
- /pdb 옵션 : .PDB 파일이름으로 디폴트 이름을 명시하지 않아도 된다.
- /pdbstripped 옵션 : 추가적인 .PDB 파일을 생성하도록 한다. 그 내용은 다음의 내용으로 제한된다.
n public function 과 variables
n FPO 정보
주목할 점은 COFF와 CodeView 포맷은 더 이상 링커에 의해 지원되지 않는다.
6. Debug information for static libraries (정적 라이브러리를 위한 디버그 정보)
정적 라이브러리를 위한 디버그 정보를 만드는 프로세스는 실행파일보다 좀 더 단순하다. 그 이유는 링크 단계가 없기 때문이다. 컴파일러 버전과는 상관없이, 컴파일러가 정적 라이브러리를 위한 디버그 정보를 생성할지는 /Z* 옵션들(/Zd, /Z7, /Zi, /ZI) 중 하나를 사용할 수 있다.
여기서 한가지 고려해야 할 중요한 점은 디버그 정보가 어디에 저장되느냐 하는 것이다. /Z7이나 /Zd 옵션이 사용될 때, 디버그 정보는 .LIB 파일에 저장되며, /Zi 나 /ZI 옵션이 사용되면, 별도의 .PDB 파일에 저장된다. (파일 이름은 /Fd 옵션에서 명시)
7. Debug information and size of the executable (실행 파일의 디버그 정보와 사이즈)
실행파일을 위한 디버그 정보를 생성한다면, 실행 파일의 사이즈는 어떤 영향을 받을까?
그 대답은 디버그 정보를 저장하는 장소에 따라 다르며, 그 다음은 디버그 정보 포맷에 따라 다르다.
보통 COFF 나 CodeView 포맷을 사용하면, 디버그 정보는 실행 파일에 저장된다. 이 경우엔 실행 파일의 사이즈는 상당히 증가한다. (디버그 정보를 갖는 실행파일은 디버그 정보가 없는 실행파일보다 2배 이상 커진다.)
Program Database 포맷이 사용되면, 디버그 정보는 별도의 파일에 저장된다. 이 때 실행 파일의 사이즈는 거의 영향을 받지 않는다. 단지 몇 백 bytes 증가할 뿐이다. 그 이유는 디버그 정보를 갖는 파일의 위치를 디버거에게 알려주기 위한 작은 헤더만을 실행파일에 추가로 포함시킨다.
여기서 실행파일의 불필요한 팽창을 피할 필요가 있다.
/debug 링커 옵션을 사용하면 부작용이 생기는데, 디폴트 /opt:ref 옵션을 /opt:noref 옵션으로 변경한다는 것이다. 그 결과 디버그 정보 생성을 활성화하면 링커에 의해 수행되는 사이즈 최적화가 비활성화 된다. 그래서 사이즈 최적화를 다시 활성화시키려면, /opt:ref 옵션을 명시적으로 활성화시켜야 한다.
8. .DBG files
CodeView 포맷을 사용하면, 링커는 항상 실행파일에 디버그 정보를 저장한다. 그러나 Rebase라 불리는 팁을 사용하면, 디버그 정보를 별도의 .DBG 파일에 저장할 수 있다.
Rebase는 Visual Studio에 포함되어 있으며, 다양한 목적으로 사용된다. 그러나 디버그 정보를 추출하는데 사용되는 명령어는 아주 간단하다.
rebase –b BaseAddr –x SymbolDir [-p] ExeName
Option | Description |
-b BaseAddr | Specifies the new base address of the executable. If you do not want to change the base address, specify the same address as the one currently used by the executable. |
-x SymbolDir | Specifies the directory where the .DBG file will be stored. It is also possible to specify . (dot), which means the current directory. |
-p | If this option is used, the .DBG file will contain only the following kinds of debug information:
Other kinds of debug information are discarded. |
예를 들어, 다음 명령어는 DLL로부터 디버그 정보를 추출해서 현재 디렉토리의 .DBG 파일에 저장한다.
rebase –b 0x60000000 –x . MyDll.dll
9. Debuggers and debug information formats (디버거 및 디버그 정보 포맷)
응용프로그램에서 특정한 디버그 정보 포맷을 사용해야 할 때, 현재 사용하는 디버거가 그것을 이해할 수 있어야 한다. 다음 테이블은 가장 인기 있는 디버거와 지원하는 디버거 정보 포맷을 보여준다.
Debugger | COFF | CodeView | Program Database (2.0) | Program Database (7.0) |
Visual Studio 2002, 2003, 2005 | - | + | + | + |
Visual C++ 6.0 | + | + | + | - |
WinDbg 6.3 | + | + | + |
WinDbg 6.3 디버거는 CodeView 포맷을 부분적으로만 지원한다. 단지 다음의 디버그 정보만을 읽을 수 있을 뿐이다.
- public function and variables
- FOP information
- source files and lines
그래서 소스 코드를 단계화시키고 콜 스택을 볼 수 있지만, 타입 정보가 없기 때문에 변수의 값을 볼 수는 없다.
10. Operation system symbols (운영체제 심볼)
다음 표는 Windows 운영체제 컴포넌트의 심볼 파일에서 사용가능한 디버그 정보 포맷 리스트이다.
Operating system | Format |
Windows NT 4.0 | CodeView (.DBG files) |
Windows 2000 | CodeView (.DBG files) and Program Database (2.0) |
Windows XP (including SP1 and SP1a) | Program Database (2.0) |
Windows XP SP2 | Program Database (7.0) |
Windows 2003 Server | Program Database (2.0) |
<출처>
http://www.debuginfo.com/articles/gendebuginfo.html#kindsofdebuginfo