수업 내용 정리
gdb란?
Gnu DeBugger의 약자로, 프로그램을 런타임에서 디버깅하는 도구다.
다양한 유닉스 기반 시스템에서 동작하며 C, C++, Java, Go, Rust 등 다양한 언어를 지원한다.
gdb 명령어
시작과 종료
- gdb [프로그램명] : 프로그램 시작
- quit, q : GDB 종료
소스 보기
- list : main 함수를 기점으로 소스 출력
- list [라인 번호] : 특정 라인을 기준으로 출력
- list [함수명] : 함수의 소스를 출력
- list - : 출력된 행의 이전 행을 출력
- list [파일명]:[함수명] : 파일의 특정 함수 소스를 출력
- layout asm : 어셈블리 코드를 보여주는 창 추가
브레이크 포인트 설정:
- break [함수명] : 특정 함수 시작 부분에 브레이크 포인트 설정
- break [라인 번호] : 특정 라인에 브레이크 포인트 설정
- info break : 설정된 브레이크 포인트 확인
- delete [브레이크 포인트 번호] : 브레이크 포인트 삭제
- disable/enable [브레이크 포인트 번호] : 브레이크 포인트 활성화/비활성화
프로그램 실행:
- run, r : 프로그램 실행
- continue, c : 브레이크 포인트에서 실행을 다시 시작
- step : 한 라인씩 실행
- next : 한 라인씩 실행 (함수 호출 시에는 함수 안으로 들어가지 않고, 다음 라인으로 넘어감)
- finish : 현재 함수를 완료할 때까지 실행
- return [값] : 함수를 강제로 종료하고 값을 반환
- advance [라인 번호] : 특정 라인까지 프로그램 실행
- advance [파일:라인 번호] : 특정 파일의 특정 라인까지 프로그램 실행
변수 확인
- print [변수명] : 변수 값 출력
- info variables : 모든 변수 출력
- watch [변수명] : 변수가 변경될 때 브레이크 포인트 설정
메모리 확인
- x/n [형식] [주소] : 특정 메모리 주소의 내용을 특정 형식으로 출력
- info address [변수명] : 변수의 메모리 주소 확인
- info stack : 스택 상태 확인
- info frame : 현재 함수의 프레임 정보를 출력
ELF 파일 구조
ELF 파일은 유닉스 계열 운영체제에서 사용되는 표준 바이너리 파일 포맷이다.

ELF 파일은 위와 같이 크게 ELF Header, Program Header Table, Section Header Table로 구성된다.
ELF 파일의 시작 부분에 위치하며, 파일의 구조 및 속성을 정의한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x10c0
Start of program headers: 64 (bytes into file)
Start of section headers: 14616 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30
|
파일의 종류를 나타내는 매직넘버와 데이터 저장 방식, OS, 아키텍처, 헤더의 크기 등 파일에 관한 정보들이 담겨있다.
실행에 필요한 정보를 담고 있으며, 커널이 ELF 파일을 실행할 때 이 정보를 참고해서 메모리에 어떻게 적재할지를 결정한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000888 0x0000000000000888 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x00000000000002e9 0x00000000000002e9 R E 0x1000
LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
0x00000000000000ec 0x00000000000000ec R 0x1000
LOAD 0x0000000000002d88 0x0000000000003d88 0x0000000000003d88
0x0000000000000288 0x00000000000004f8 RW 0x1000
DYNAMIC 0x0000000000002d98 0x0000000000003d98 0x0000000000003d98
0x0000000000000200 0x0000000000000200 RW 0x8
NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000030 0x0000000000000030 R 0x8
NOTE 0x0000000000000368 0x0000000000000368 0x0000000000000368
0x0000000000000044 0x0000000000000044 R 0x4
GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000030 0x0000000000000030 R 0x8
GNU_EH_FRAME 0x0000000000002008 0x0000000000002008 0x0000000000002008
0x0000000000000034 0x0000000000000034 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000002d88 0x0000000000003d88 0x0000000000003d88
0x0000000000000278 0x0000000000000278 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .plt.sec .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .data .bss
06 .dynamic
07 .note.gnu.property
08 .note.gnu.build-id .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .dynamic .got
|
구조체에는 아래와 같은 필드들이 저장되어 있다.
1
2
3
4
5
6
7
8
9
10
| struct Elf64_Phdr {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
Elf64_Xword p_filesz;
Elf64_Xword p_memsz;
Elf64_Xword p_align;
};
|
- p_type : 세그먼트의 타입 (ex. PT_LOAD, PT_DYNAMIC 등)
- p_flags : 세그먼트 접근 권한 (읽기, 쓰기, 실행)
- p_offset : 파일 내 세그먼트 시작 오프셋
- p_vaddr : 메모리에 로드될 가상 주소
- p_paddr : 물리 주소 (대부분 시스템에선 무시됨)
- p_filesz : 파일에 존재하는 세그먼트 크기
- p_memsz : 메모리에 할당될 세그먼트 크기
- p_align : 정렬 조건 (페이지 단위 정렬 등)
.text, .data와 같은 섹션들에 관한 정보들이 저장된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
| Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000318 00000318
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.pr[...] NOTE 0000000000000338 00000338
0000000000000030 0000000000000000 A 0 0 8
[ 3] .note.gnu.bu[...] NOTE 0000000000000368 00000368
0000000000000024 0000000000000000 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000038c 0000038c
0000000000000020 0000000000000000 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003b0 000003b0
0000000000000030 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003e0 000003e0
0000000000000150 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 0000000000000530 00000530
000000000000015e 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 000000000000068e 0000068e
000000000000001c 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 00000000000006b0 000006b0
0000000000000070 0000000000000000 A 7 2 8
[10] .rela.dyn RELA 0000000000000720 00000720
0000000000000108 0000000000000018 A 6 0 8
[11] .rela.plt RELA 0000000000000828 00000828
0000000000000060 0000000000000018 AI 6 24 8
[12] .init PROGBITS 0000000000001000 00001000
000000000000001b 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 0000000000001020 00001020
0000000000000050 0000000000000010 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001070 00001070
0000000000000010 0000000000000010 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001080 00001080
0000000000000040 0000000000000010 AX 0 0 16
[16] .text PROGBITS 00000000000010c0 000010c0
000000000000021c 0000000000000000 AX 0 0 16
[17] .fini PROGBITS 00000000000012dc 000012dc
000000000000000d 0000000000000000 AX 0 0 4
[18] .rodata PROGBITS 0000000000002000 00002000
0000000000000007 0000000000000000 A 0 0 4
[19] .eh_frame_hdr PROGBITS 0000000000002008 00002008
0000000000000034 0000000000000000 A 0 0 4
[20] .eh_frame PROGBITS 0000000000002040 00002040
00000000000000ac 0000000000000000 A 0 0 8
[21] .init_array INIT_ARRAY 0000000000003d88 00002d88
0000000000000008 0000000000000008 WA 0 0 8
[22] .fini_array FINI_ARRAY 0000000000003d90 00002d90
0000000000000008 0000000000000008 WA 0 0 8
[23] .dynamic DYNAMIC 0000000000003d98 00002d98
0000000000000200 0000000000000010 WA 7 0 8
[24] .got PROGBITS 0000000000003f98 00002f98
0000000000000068 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000004000 00003000
0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000004040 00003010
0000000000000240 0000000000000000 WA 0 0 64
[27] .comment PROGBITS 0000000000000000 00003010
000000000000002b 0000000000000001 MS 0 0 1
[28] .symtab SYMTAB 0000000000000000 00003040
0000000000000450 0000000000000018 29 21 8
[29] .strtab STRTAB 0000000000000000 00003490
0000000000000367 0000000000000000 0 0 1
[30] .shstrtab STRTAB 0000000000000000 000037f7
000000000000011a 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), l (large), p (processor specific)
|
각 구조체에는 아래와 같은 필드들이 저장되어 있다.
1
2
3
4
5
6
7
8
9
10
11
12
| typedef struct {
Elf64_Word sh_name;
Elf64_Word sh_type;
Elf64_Xword sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Word sh_link;
Elf64_Word sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
} Elf64_Shdr;
|
- sh_name : 섹션 이름 (문자열 테이블의 인덱스)
- sh_type : 섹션 타입 (ex. SHT_PROGBITS, SHT_SYMTAB 등)
- sh_flags : 섹션 속성 (읽기/쓰기/실행 등)
- sh_addr : 실행 시 메모리에서의 가상 주소
- sh_offset : 파일 내 섹션 시작 오프셋
- sh_size : 섹션 크기 (바이트 단위)
- sh_link : 관련된 다른 섹션의 인덱스
- sh_info : 섹션에 대한 부가 정보
- sh_addralign : 섹션 정렬 조건
- sh_entsize : 항목 크기 (테이블 형식일 경우)
실습용 코드 작성 후 명령어 실습 후 정리
실습용 코드
두 정수 값을 입력받고, 두 정수를 더하는 함수를 통해 더한 값을 출력하는 코드를 작성하였다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
int x, y;
scanf("%d %d", &x, &y);
printf("%d", add(x, y));
return 0;
}
|
명령어 실습

gcc test.c -o test -g 명령어로 컴파일한 뒤 gdb ./test 명령어로 gdb를 실행하였다.

list 명령어를 통해 심볼이 남아있는 것을 확인할 수 있다.
b main 명령어로 main 함수에 브레이크 포인트를 설정하였다.

disas 명령어로 어셈블리어 코드를 확인할 수 있다.

layout asm 명령어로 어셈블리어 코드 창을 띄워 코드를 실행하면서 확인할 수도 있다.

n 명령어로 한 줄씩 실행해볼 수 있다. x, y에 각각 3과 7을 입력한 뒤, add 함수에 브레이크 포인트를 설정하여 함수 안으로 진입해보았다.

info stack과 info frame 명령어로 함수가 호출된 것을 확인할 수 있고, 매개변수들과 지역변수의 정보들을 확인할 수 있다.

print 명령어로 변수의 주소를 확인하고 이를 x/w [주소] 명령어를 통해 메모리 상에 위치한 값을 직접 확인할 수 있다.

스택 프레임의 포인터인 rbp를 통해서도 매개변수가 위치한 메모리를 확인할 수 있다.

return 0 명령어를 통해 add 함수를 강제로 0으로 반환해보았다. 원래라면 10(3 + 7)이 출력되어야 하지만 0이 출력되는 것을 확인할 수 있다.