1.1 ELF 바이너리 구조
2.1 ELF File Header
ELF 파일 헤더에는
1.
ELF 파일임을 나타내는 매직 코드
2.
ELF 파일 종류
3.
파일의 다른 요소들에 대한 오프셋
을 가지고 있습니다.
typedef struct {
unsigned char e_ident[16]; /* 매직코드 및 기타 정보 */
uintl6_t e_type; /* 목적 파일 형식 */
uintl6_t e_machine; /* 아키텍처 */
uint32_t e_version; /* 목적 파일 버전 */
uint64_t e_entry; /* 엔트리 포인트 가상 주소 */
uint64_t e_phoff; /* 프로그램 헤더 테이블 파일 오프셋 */
uint64_t e_shoff; /* 섹션 헤더 테이블 파일 오프셋 */
uint32_t e_flags; /* 프로세서에 따른 전용 플래그 */
uintl6_t e_ehsize; /* ELF 헤더 크기(byte 단위) */
uintl6_t e_phentsize; /* 프로그램 헤더 테이블 엔트리 크기 */
uintl6_t e_phnum; /* 프로그램 헤더 테이블 엔트리 수 */
uintl6_t e_shentsize; /* 섹션 헤더 테이블 엔트리 크기 */
uintl6_t e_shnum; /* 섹션 헤더 테이블 엔트리 수 */
uintl6_t e_shstrndx; /* 섹션 헤더 문자열 테이블 인덱스 */
} Elf64_Ehdr;
C
복사
readelf -h 명령어를 사용하여 a.out의 File Header를 확인합니다.
# readelf -h a.out
ELF Header:
Magic: 7f 45 4c 46 //(0x00~03 고정값 e_ident[00~03]
02 //Class 값, e_idnet[04]
01 //Data 값, e_ident[05]
01 //EI_Version, e_ident[06]
00 // OS/ABI 값 e_ident[07]
00 // ABI Version 값, e_ident[08]
00 00 00 00 00 00 00 // Reserved, e_ident[09~15]
Class: ELF64 // 2:ELF64, 1:ELF32, e_ident[04]
Data: 2's complement, little endian // 1: little endian, 2: big endian, e_ident[05]
Version: 1 (current) // 1: current, e_ident[06]
OS/ABI: UNIX - System V // e_ident[07]
ABI Version: 0 // e_ident[08]
Type: DYN (Shared object file) //e_type
Machine: Advanced Micro Devices X86-64 //e_machine
Version: 0x1 // 위의 EI_VERSION과 동일, e_version
Entry point address: 0x530 // Entry Point Offset, e_entry
Start of program headers: 64 (bytes into file) // Program Header File Offset, e_phoff
Start of section headers: 6440 (bytes into file) // Section Header File Offset, e_shoff
Flags: 0x0 // default 0, e_flags
Size of this header: 64 (bytes) // x64일 경우 64bytes 고정, x86일 경우 52bytes 고정, e_ehsize
Size of program headers: 56 (bytes) // Program Header Size, e_phentsize
Number of program headers: 9 // Program Header Number, e_phnum
Size of section headers: 64 (bytes) // Section Header Size, e_shnum
Number of section headers: 29 // Section Header Number, e_shnum
Section header string table index: 28 // e_shstrndx
C
복사
Section Header String Table에는 말 그대로 Section Header의 String이 포함되어 있습니다.
root@DESKTOP-2HBCL3H:~/chp1/2# readelf -x .shstrtab a.out
Hex dump of section '.shstrtab':
0x00000000 002e7379 6d746162 002e7374 72746162 ..symtab..strtab
0x00000010 002e7368 73747274 6162002e 696e7465 ..shstrtab..inte
0x00000020 7270002e 6e6f7465 2e414249 2d746167 rp..note.ABI-tag
0x00000030 002e6e6f 74652e67 6e752e62 75696c64 ..note.gnu.build
0x00000040 2d696400 2e676e75 2e686173 68002e64 -id..gnu.hash..d
0x00000050 796e7379 6d002e64 796e7374 72002e67 ynsym..dynstr..g
0x00000060 6e752e76 65727369 6f6e002e 676e752e nu.version..gnu.
0x00000070 76657273 696f6e5f 72002e72 656c612e version_r..rela.
0x00000080 64796e00 2e72656c 612e706c 74002e69 dyn..rela.plt..i
0x00000090 6e697400 2e706c74 2e676f74 002e7465 nit..plt.got..te
0x000000a0 7874002e 66696e69 002e726f 64617461 xt..fini..rodata
0x000000b0 002e6568 5f667261 6d655f68 6472002e ..eh_frame_hdr..
0x000000c0 65685f66 72616d65 002e696e 69745f61 eh_frame..init_a
0x000000d0 72726179 002e6669 6e695f61 72726179 rray..fini_array
0x000000e0 002e6479 6e616d69 63002e64 61746100 ..dynamic..data.
0x000000f0 2e627373 002e636f 6d6d656e 7400 .bss..comment.
C
복사
2.2 Section Header
Section이란
ELF 바이너리의 코드와 데이터가 연속적이지만 겹치지 않는 조각의 형태로
논리적으로 분리되어 있는것을 Section 이라고 한다
또한 모든 Section은
1.
Section Header 에서 Section과 관련된 모든 정보를 찾을수 있으며
2.
Section Header Table 에서 모든 Section에 대한 해더 정보를 찾을 수 있다.
그리고 Section을 구분하는 목적은
1.
링커가 바이너리를 해석할 때 편리한 단위로 나눈 것이다.
2.
그래서 링킹 작업이 필요 없는 경우 Section Header가 불필요하다.
a.
외부 링킹을 하지 않는, 모든 함수가 하나의 C 파일에 짜져 있는 경우는 불필요 할 듯..?
Segment란
바이너리를 실행해 메모리상에 프로세스 형태로 불러올 때
실행 가능한 ELF 바이너리 내부의 코드와 데이터를 논리적인 영역으로 구분하는 것을
Segment라고 한다.
typedef struct {
uint32_t sh_name; /* 섹션 이름(문자열 테이블 인덱스 번호) */
uint32_t sh_type; /* 섹션 타입 */
uint64_t sh_flags; /* 섹션 플래그 */
uint64_t sh_addr; /* 실행시점의 섹션 가상 주소 */
uint64_t sh_offset; /* 섹션 파일 오프셋 */
uint64_t sh_size; /* 바이트 단위의 섹션 크기 */
uint32_t sh_link; /* 다른 참조 섹션 */
uint32_t sh_info; /* 추가적인 섹션 정보 */
uint64_t sh_addralign; /* 섹션 배치 규칙 */
uint64_t sh_entsize; /* 섹션 테이블이 존재하는 경우 엔트리 크기 */
} Elf64_Shdr;
C
복사
2.2.1 sh_name
2.2.2 sh_type
해당 필드에 표기된 숫자로 Section의 타입을 구분합니다.
1.
SHT_PROGBITS
•
기계어 명령어, 상수값 등의 프로그램 데이터를 포함하고 있는 Section
•
링커가 따로 분석해야 할 별도의 특별한 구조를 갖지 않음
2.
Symbol Table 관련 Section
•
정적 심벌 테이블 타입인 SHT_SYMTAB
•
동적 링킹 심벌 테이블 타입인 SHT_DYNSYM
•
문자열 테이블 SHT_STRTAB
3.
Relocation 관련 Section
•
정적 링킹에 관련된 SHT_REL, SHT_RELA
•
동적 링킹에 관련된 SHT_DYNAMIC
4.
2.2.3 sh_flags
해당 플래그는 Section과 관련한 추가 정보를 담고 있으며
중요 플래그로는 세가지가 있습니다
1.
SHF_WRITE
•
해당 플래그는 실행 시점에 Section이 쓰기 가능한 상태임을 의미합니다.
2.
SHF_ALLOC
•
주어진 바이너리가 실행될 때 해당 Section의 정보가 가상 메모리에 적재됨을 의미합니다.
여기서 설명하는 가상 메모리는 Swap File System.
즉 하드디스크의 일부를 메모리처럼 이용하는 방법을 말합니다.
가상 메모리 주소(Virtual Address) 와 혼동하지 않도록 주의!
3.
SHF_EXECINSTR
•
해당 Section이 실행 가능한 명령어들을 담고 있는 Section임을 의미합니다.
2.2.4 sh_addr, sh_offset, sh_size
1.
sh_addr
•
Section이 적재 될 가상 메모리 주소
2.
sh_offset
•
Section의 파일 오프셋
3.
sh_size
•
Section의 사이즈
2.2.5 sh_link
Section 사이의 연관 정보를 가지고 있는 필드이며, 관련된 Section의 인덱스 정보(Section Header Table의 Index)를 명시적으로 해당 필드에 표기합니다.
2.2.6 sh_info
섹션과 관련된 추가 정보를 제공하는 필드입니다.
2.2.7 sh_addralign
해당 Section의 align 유무를 설정하는 필드입니다.
2.2.8 sh_entsize
자료 구조 형태로 테이블을 가지는 Section(Symbol Table, Reloc Table)들의 table entry size를 가지는 필드입니다.
2.3 Section
readelf 명령어를 이용하여 본 샘플 파일입니다.
# readelf --sections --wide a.out
There are 29 section headers, starting at offset 0x1928:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000000238 000238 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000000254 000254 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 0000000000000274 000274 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000298 000298 00001c 00 A 5 0 8
[ 5] .dynsym DYNSYM 00000000000002b8 0002b8 0000a8 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000000360 000360 000082 00 A 0 0 1
[ 7] .gnu.version VERSYM 00000000000003e2 0003e2 00000e 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000000003f0 0003f0 000020 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000000410 000410 0000c0 18 A 5 0 8
[10] .rela.plt RELA 00000000000004d0 0004d0 000018 18 AI 5 22 8
[11] .init PROGBITS 00000000000004e8 0004e8 000017 00 AX 0 0 4
[12] .plt PROGBITS 0000000000000500 000500 000020 10 AX 0 0 16
[13] .plt.got PROGBITS 0000000000000520 000520 000008 08 AX 0 0 8
[14] .text PROGBITS 0000000000000530 000530 0001a2 00 AX 0 0 16
[15] .fini PROGBITS 00000000000006d4 0006d4 000009 00 AX 0 0 4
[16] .rodata PROGBITS 00000000000006e0 0006e0 000012 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 00000000000006f4 0006f4 00003c 00 A 0 0 4
[18] .eh_frame PROGBITS 0000000000000730 000730 000108 00 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000200db8 000db8 000008 08 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000200dc0 000dc0 000008 08 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000200dc8 000dc8 0001f0 10 WA 6 0 8
[22] .got PROGBITS 0000000000200fb8 000fb8 000048 08 WA 0 0 8
[23] .data PROGBITS 0000000000201000 001000 000010 00 WA 0 0 8
[24] .bss NOBITS 0000000000201010 001010 000008 00 WA 0 0 1
[25] .comment PROGBITS 0000000000000000 001010 000029 01 MS 0 0 1
[26] .symtab SYMTAB 0000000000000000 001040 0005e8 18 27 43 8
[27] .strtab STRTAB 0000000000000000 001628 000202 00 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00182a 0000fe 00 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),
l (large), p (processor specific)
C
복사
결과에서 샘플 파일에 대한
Section Name, Type, Address, Offset, Size, Type, Flag, Link, Info, Align 정보를 확인할 수 있습니다.
2.3.1 .init, .fini Section
1장에서 잠깐 설명한 것처럼 바이너리 실행, 종료 시 실행되는 루틴이 저장되어 있는 섹션입니다.
2.3.2 .text Section
.text Section은 일반적인 바이너리에서 실행 코드들이 존재하는 섹션입니다.
당연히 Type은 PROGBITS로 설정되어 있으며, Flags 또한 AX로 설정되어 있습니다.
일반적으로 gcc 컴파일러를 이용하여 컴파일 하는 경우
_start, _registertm_clones, frame_dummy와 같은 표준 함수가 자동으로 해당 섹션안에 포함됩니다.
_start 함수의 경우 메인 함수를 호출하는데 Disassemble한 결과를 보면 아래와 같습니다.
0000000000000530 <_start>:
530: 31 ed xor ebp,ebp
532: 49 89 d1 mov r9,rdx
535: 5e pop rsi
536: 48 89 e2 mov rdx,rsp
539: 48 83 e4 f0 and rsp,0xfffffffffffffff0
53d: 50 push rax
53e: 54 push rsp
53f: 4c 8d 05 8a 01 00 00 lea r8,[rip+0x18a] # 6d0 <__libc_csu_fini>
546: 48 8d 0d 13 01 00 00 lea rcx,[rip+0x113] # 660 <__libc_csu_init>
54d: 48 8d 3d e6 00 00 00 lea rdi,[rip+0xe6] # 63a <main>
554: ff 15 86 0a 20 00 call QWORD PTR [rip+0x200a86] # 200fe0 <__libc_start_main@GLIBC_2.2.5>
55a: f4 hlt
55b: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0]
000000000000063a <main>:
63a: 55 push rbp
63b: 48 89 e5 mov rbp,rsp
63e: 48 83 ec 10 sub rsp,0x10
642: 89 7d fc mov DWORD PTR [rbp-0x4],edi
645: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
649: 48 8d 3d 94 00 00 00 lea rdi,[rip+0x94] # 6e4 <_IO_stdin_used+0x4>
650: e8 bb fe ff ff call 510 <puts@plt>
655: b8 00 00 00 00 mov eax,0x0
65a: c9 leave
65b: c3 ret
65c: 0f 1f 40 00 nop DWORD PTR [rax+0x0]
C
복사
__libc_start_main@plt 함수를 호출하며, rdi 레지스터에 main 함수의 address를 전달합니다.
전달받은 함수는 해당 바이너리의 메인 함수를 호출하게 됩니다.
2.3.3 .rodata, .data, .bss
2.3.4 지연바인딩과 .plt, .got, .got.plt
지연바인딩(Lazy Binding)이란
컴파일시 공유 라이브러리 함수의 링킹을 실행하지 않고,
런타임에서 해당 함수를 처음 참조하여 실행했을 때 링킹하는 것을
지연바인딩이라고 한다.
해당 기능은 동적 바인딩 시 시간 낭비를 줄일려고 하는 기법이며, LD_BIND_NOW를 이용하여 해당 기능을 Disable 할 수 있습니다. (bash shell에서 LD_BIND_NOW=1 입력)
•
호출 순서
1.
메인함수에서 공유 라이브러리 함수(.plt section) 호출
2.
함수@plt 함수로 jmp되어 .got section 의 함수 주소 참조
3.
.got section에서 참조한 함수 포인터로 jmp
4.
해당 함수 포인터에는 extern으로 함수가 선언되어 있음.
•
지연 바인딩을 쓰는 이유
1.
데이터 영역에 존재하기 때문에 쓰기가 불가능하여 보안성 강화
2.
공유 라이브러리의 코드 공유를 위해 사용
2.3.5 .rel, .rela section
두 Section 모두 재배치 과정에서 링커가 활용할 정보를 담고 있습니다.
root@DESKTOP-2HBCL3H:~/chp1/2# readelf --relocs a.out
Relocation section '.rela.dyn' at offset 0x410 contains 8 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000200db8 000000000008 R_X86_64_RELATIVE 630
000000200dc0 000000000008 R_X86_64_RELATIVE 5f0
000000201008 000000000008 R_X86_64_RELATIVE 201008
000000200fd8 000100000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000200fe0 000300000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000200fe8 000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200ff0 000500000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000200ff8 000600000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0
Relocation section '.rela.plt' at offset 0x4d0 contains 1 entry:
Offset Info Type Sym. Value Sym. Name + Addend
000000200fd0 000200000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0
C
복사
해당 결과에는 Reloc Type이 2가지가 존재합니다.
1.
R_X86_64_GLOB_DAT
2.
R_X86_64_JUMP_SLO
1번의 경우 Data Symbol의 올바른 주소를 계산하고 올바른 오프셋에 연결하는데 사용됩니다.
2번의 경우 Jump Slot으로 오프셋에는 해당 라이브러리의 함수 주소를 담는데 사용됩니다.
2.3.6 .dynamic Section
.dynamic Section의 경우 바이너리를 로드 할 때 운영체제와 동적 링커에게 로드맵을 제시하는 역할을 합니다.
.dynamic Section은 Elf64_Dyn 구조의 테이블을 포함하며, 태그라고 표현되기도 합니다.
root@DESKTOP-2HBCL3H:~/chp1/2# readelf --dynamic a.out
Dynamic section at offset 0xdc8 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x4e8
0x000000000000000d (FINI) 0x6d4
0x0000000000000019 (INIT_ARRAY) 0x200db8
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x200dc0
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x298
0x0000000000000005 (STRTAB) 0x360
0x0000000000000006 (SYMTAB) 0x2b8
0x000000000000000a (STRSZ) 130 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x200fb8
0x0000000000000002 (PLTRELSZ) 24 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x4d0
0x0000000000000007 (RELA) 0x410
0x0000000000000008 (RELASZ) 192 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
0x000000006ffffffe (VERNEED) 0x3f0
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x3e2
0x000000006ffffff9 (RELACOUNT) 3
0x0000000000000000 (NULL) 0x0
C
복사
결과를 보면 각 태그와, 태그의 타입, Name과 Value를 볼 수 있습니다.
DT_NEEDED의 경우 동적 링커에게 해당 바이너리와 의존성 관계를 알려주는 역할을 합니다.
DT_VERNEED, DT_VERNEEDNUM 태그는 버전 의존성 테이블의 시작 주소와 엔트리 수를 가르킵니다.
2.3.7 .init_array, .fini_array Section
.init_array Section의 경우 main 함수가 시작되기 전 필요한 생성자 함수들이 연결되어 있는 포인터들의 배열입니다 (vtable)
.fini_array의 경우 .init_array와 동일하지만 소멸자 함수들이 연결되어 있는 VTable입니다.
2.3.8 .shstrtab, .symtab, .strtab, .dynsym, .dynstr Section
1.
.shstrtab
•
바이너리에서 사용되는 모든 Section의 이름을 문자열로 담고있는 Section.
•
숫자로 된 Index 를 가지고 있습니다.
2.
.symtab
•
심볼 테이블 정보를 가지고 있는 Section.
•
링킹시 해당 이름을 가지고 있는 함수와 변수들과 연결된다
•
strip 적용 시 사라진다
3.
.strtab
•
심볼들의 실제 이름을 가지고 있는 Section
•
strip 적용 시 사라진다
4.
.dynsym
•
동적 링킹 시 필요한 심볼 테이블 정보를 가지고 있는 Section
•
동적 링킹에 필요한 Section이기 때문에 Strip을 적용해도 사라지지 않는다
5.
.dyntab
•
동적 링킹 시 필요한 심볼들의 실제 이름을 가지고 있다.
•
동적 링킹에 필요한 Section이기 때문에 Strip을 적용해도 사라지지 않는다
4. 프로그램 헤더
프로그램 해더는 바이너리를 Segment 관점에서 해석할 수 있도록 하는 구조체이다.
Segment의 경우 0개, 그 이상의 Section을 포함하며, 본질적으로 Section을 하나의 단일 조각으로 묶어서 처리한다.
# readelf --wide --segments a.out
Elf file type is DYN (Shared object file)
Entry point 0x530
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0001f8 0x0001f8 R 0x8
INTERP 0x000238 0x0000000000000238 0x0000000000000238 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000838 0x000838 R E 0x200000
LOAD 0x000db8 0x0000000000200db8 0x0000000000200db8 0x000258 0x000260 RW 0x200000
DYNAMIC 0x000dc8 0x0000000000200dc8 0x0000000000200dc8 0x0001f0 0x0001f0 RW 0x8
NOTE 0x000254 0x0000000000000254 0x0000000000000254 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x0006f4 0x00000000000006f4 0x00000000000006f4 0x00003c 0x00003c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x000db8 0x0000000000200db8 0x0000000000200db8 0x000248 0x000248 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .dynamic .got .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .dynamic .got
C
복사
2.4.1 p_type 필드
p_type 필드는 Segment의 유형을 식별하는 역할을 하며, 여기서 중요한 값은 PT_LOAD, PT_DYNAMIC, PT_INTERP이다
1.
PT_LOAD
•
프로세스가 초기에 생성되고 메모리에서 로드되는 과정에서 사용됨
•
첫번째 LOAD Segment의 경우 코드 영역, 두번째 LOAD Segment의 경우 데이터 영역
2.
PT_INTERP
•
바이너리 로드 할 때 사용할 인터프리터의 이름을 지정 (동적 라이브러리 로드)
3.
PT_DYNAMIC
•
.dynamic Section이 포함되어 있으며, 바이너리 로드 시 인터프리터가 수행해야 할 구문 분석 등의 준비작업을 명시
2.4.2 p_flags
해당 Segment에 대한 실행 권한을 명시합니다.
PF_X = 실행 권한
PF_W = 쓰기 권한
PF_R = 읽기 권한
2.4.3 p_offset, p_vaddr, p_paddr, p_filesz, p_memsz
각각 Segment의 파일 오프셋, 가상 메모리 주소, 물리 메모리 주소, 파일 사이즈, 메모리 사이즈를 뜻합니다.
2.4.4 p_align
정렬에 필요한 메모리 양(바이트 단위)를 뜻합니다.