# efi-header.S — 말로 풀어 읽기
Linux v6.18.37 · arch/arm64/kernel/efi-header.S

여기서는 CPU에게 일을 시키는 명령보다, EFI 로더가 읽을 파일 설명서를 만드는 줄이 많습니다. .long이나 .short는 실행 동작이 아니라 숫자를 이미지에 써 넣는 지시문입니다. “이 이미지의 어느 위치부터 실행할까?”, “어느 범위가 코드인가?”라는 질문에 로더가 답할 수 있도록 헤더를 만듭니다.

## 처음 읽을 때
코드처럼 생긴 파일이라도 모든 줄을 CPU가 실행하는 것은 아닙니다. .long을 볼 때는 파일 안에 4바이트 칸 하나를 만든다고 생각해 보세요.

## 더 깊이 살펴볼 때
PE의 파일 오프셋, RVA, ELF 섹션, 런타임 VA는 서로 다른 좌표계입니다. 디버그 디렉터리를 왜 .init.rodata로 옮기는지 이 차이로 설명할 수 있습니다.

## MZ 서명인 첫 명령
원본 1–26행

### 7행
```asm
#include <linux/pe.h>
```
UEFI가 읽을 PE/COFF 헤더의 서명, ARM64 기계 종류, 섹션 속성 같은 상수를 가져옵니다. 이 파일의 .long·.short는 대부분 CPU 명령이 아니라 로더에게 제공할 헤더 필드를 만드는 데 사용됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

### 8행
```asm
#include <linux/sizes.h>
```
SZ_64K처럼 바이트 크기를 나타내는 상수를 가져옵니다. 이미지 섹션과 헤더의 정렬 크기를 읽을 때 단위를 분명하게 하기 위한 정의입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

### 10행
```asm
	.macro	efi_signature_nop
```
커널 이미지 첫 명령을 만들 때 쓰는 매크로입니다. EFI 지원 이미지이면 명령 바이트의 시작이 MZ 서명이 되게 하고, 미지원 이미지이면 보통 NOP를 둡니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

### 11행
```asm
#ifdef CONFIG_EFI
```
CONFIG_EFI는 ARM64 커널의 UEFI 부팅·인터페이스 지원 설정이며 EFI stub도 선택합니다. 이 설정이 켜진 이미지에는 UEFI 로더가 알아볼 MZ 서명을 첫 명령 바이트에 넣습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

### 12행
```asm
.L_head:
```
이미지 헤더 시작 주소를 표시합니다. 뒤의 PE 필드들이 사용하는 RVA는 이 시작에서 얼마 떨어졌는지로 계산합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

### 17행
```asm
	ccmp	x18, #0, #0xd, pl
```
이 CCMP의 기계어 바이트가 MZ로 시작하도록 명령을 골랐습니다. 부팅 흐름에서는 의미 있는 계산 결과로 사용하지 않으며, 핵심 목적은 같은 바이트를 UEFI가 실행 파일 서명으로 인식하게 하는 것입니다. 실행하면 PL 조건(N=0)일 때 x18과 0을 비교해 NZCV를 갱신하고, 조건이 거짓이면 NZCV에 즉시값 0xd를 넣습니다. x18 자체는 바꾸지 않습니다.

### 18행
```asm
#else
```
EFI를 지원하지 않는 빌드에서 사용할 첫 명령을 선택하는 분기입니다. UEFI 부팅 가능 이미지라는 MZ 표시를 넣지 않습니다.

### 24행
```asm
	nop
```
EFI 미지원 이미지의 첫 명령에 일반 NOP를 넣습니다. 이미지 시작 바이트로 EFI 부팅 가능 여부를 검사하는 부트로더가 잘못 판단하지 않도록 합니다.

### 25행
```asm
#endif
```
EFI 지원 여부에 따른 첫 명령 선택을 마칩니다.

### 26행
```asm
	.endm
```
공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다. 

![단계별 개념 그림](../media/efi-header-overview.png)
화살표는 빌드 시 필드 출력 또는 로더의 해석 관계입니다. 세 칸을 CPU가 순서대로 실행하지 않습니다.

## PE 파일 헤더와 선택 헤더
원본 27–83행

### 28행
```asm
	.macro	__EFI_PE_HEADER
```
UEFI가 ARM64 커널을 실행 파일로 적재하는 데 필요한 PE/COFF 헤더와 섹션표를 내보내는 매크로입니다. 헤더의 숫자들은 실행 중 계산되는 값이 아니라 조립·링크 단계에서 이미지에 기록됩니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

### 29행
```asm
#ifdef CONFIG_EFI
```
CONFIG_EFI가 켜진 경우에만 실제 PE/COFF 헤더를 만듭니다. 꺼진 빌드는 아래 else에서 PE 헤더 오프셋을 0으로 설정합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

### 30행
```asm
	.set	.Lpe_header_offset, . - .L_head
```
이미지 시작 .L_head에서 현재 PE 헤더까지의 바이트 거리를 계산합니다. Image 헤더가 이 값을 통해 PE 헤더 위치를 알려줄 수 있게 합니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.

### 31행
```asm
	.long	IMAGE_NT_SIGNATURE
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. PE 파일 서명인 PE와 두 NUL 바이트(0x00004550)를 기록합니다. 로더가 뒤의 바이트를 PE/COFF 헤더로 해석하는 기준입니다.

### 32행
```asm
	.short	IMAGE_FILE_MACHINE_ARM64		// Machine
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 대상 CPU 종류를 ARM64(0xaa64)로 기록합니다. UEFI 로더가 이 실행 이미지의 명령어 아키텍처를 구분합니다.

### 33행
```asm
	.short	.Lsection_count				// NumberOfSections
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 뒤에서 계산할 PE 섹션 개수를 기록합니다. 이 파일에서는 .text와 .data 두 섹션의 설명이 이어집니다.

### 34행
```asm
	.long	0 					// TimeDateStamp
```
이 .long은 CPU 명령이 아니라 어셈블러에게 4바이트 정수 데이터를 내보내도록 하는 지시문입니다. 상수 0은 어셈블 단계에서 바로 00 00 00 00으로 기록됩니다. 이 자리는 COFF TimeDateStamp 필드이며, 이 이미지에서는 빌드 시각을 넣지 않습니다.

### 35행
```asm
	.long	0					// PointerToSymbolTable
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. COFF 심볼표의 파일 위치를 0으로 기록합니다. 이 부팅 이미지에 별도의 COFF 심볼표를 제공하지 않는다는 뜻입니다.

### 36행
```asm
	.long	0					// NumberOfSymbols
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. COFF 심볼표의 항목 수도 0으로 기록하여 앞의 심볼표 위치 0과 맞춥니다.

### 37행
```asm
	.short	.Lsection_table - .Loptional_header	// SizeOfOptionalHeader
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. optional header 시작부터 섹션표 직전까지의 크기를 기록합니다. 로더는 이 길이를 사용해 섹션표가 시작하는 위치를 찾습니다.

### 38행
```asm
	.short	IMAGE_FILE_DEBUG_STRIPPED | \
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. COFF Characteristics 비트 조합의 첫 부분입니다. IMAGE_FILE_DEBUG_STRIPPED로 COFF 디버그 정보가 제거된 이미지임을 표시합니다. 아래의 선택적 PE 디버그 디렉터리와는 구분되는 필드입니다.

### 39행
```asm
		IMAGE_FILE_EXECUTABLE_IMAGE | \
```
같은 Characteristics 필드에 EXECUTABLE_IMAGE 비트를 더하여 실행 가능한 이미지임을 표시합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.

### 40행
```asm
		IMAGE_FILE_LINE_NUMS_STRIPPED		// Characteristics
```
LINE_NUMS_STRIPPED 비트까지 합쳐 COFF 소스 줄 번호 정보가 없음을 표시합니다. 세 줄은 각각 별도 필드가 아니라 OR로 합쳐진 하나의 16비트 필드를 만듭니다.

### 42행
```asm
.Loptional_header:
```
PE optional header의 시작 위치입니다. 이름과 달리 실행 이미지의 적재 주소·크기·진입점 등 핵심 정보가 이 영역에 들어갑니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

### 43행
```asm
	.short	IMAGE_NT_OPTIONAL_HDR64_MAGIC		// PE32+ format
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 64비트 PE32+ 형식을 나타내는 0x020b를 기록합니다. 로더가 이후 필드의 크기와 배치를 이 형식으로 해석하게 합니다.

### 44행
```asm
	.byte	0x02					// MajorLinkerVersion
```
이 .byte은 실행 명령이 아니라 어셈블 단계에서 상수를 1바이트 정수로 기록하는 지시문입니다. PE MajorLinkerVersion 필드에 이 헤더가 정한 0x02를 기록합니다. Linux 커널의 주 버전 6을 의미하는 값은 아닙니다.

### 45행
```asm
	.byte	0x14					// MinorLinkerVersion
```
이 .byte은 실행 명령이 아니라 어셈블 단계에서 상수를 1바이트 정수로 기록하는 지시문입니다. PE MinorLinkerVersion 필드에 0x14를 기록합니다. 바로 앞 값과 함께 헤더 형식상 링커 버전 필드를 채우며 Linux의 6.18.37 표시는 아닙니다.

### 46행
```asm
	.long	__initdata_begin - .Lefi_header_end	// SizeOfCode
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. PE의 코드 크기를 헤더 끝부터 __initdata_begin 전까지의 길이로 기록합니다. 뒤의 .text 섹션이 차지하는 범위와 맞춘 값입니다.

### 47행
```asm
	.long	__pecoff_data_size			// SizeOfInitializedData
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. 링커가 계산한 PE 데이터 영역의 메모리 크기를 기록합니다. 원본 파일의 데이터 바이트 수와 메모리에 필요한 크기는 뒤의 .data 섹션에서 별도 필드로 구분합니다.

### 48행
```asm
	.long	0					// SizeOfUninitializedData
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 별도 uninitialized-data 크기 필드는 0으로 둡니다. 뒤의 .data VirtualSize와 SizeOfRawData 차이로 파일에 없는 메모리 영역을 포함하므로 커널 전체에 BSS가 없다는 뜻은 아닙니다.

### 49행
```asm
	.long	__efistub_efi_pe_entry - .L_head	// AddressOfEntryPoint
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. UEFI가 처음 호출할 EFI stub의 efi_pe_entry 위치를 이미지 시작 기준 RVA로 기록합니다. 일반 부트로더가 Image의 첫 명령으로 들어오는 경로와 진입 방식이 다릅니다.

### 50행
```asm
	.long	.Lefi_header_end - .L_head		// BaseOfCode
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 코드 영역이 시작하는 헤더 끝의 위치를 이미지 시작 기준 오프셋으로 기록합니다.

### 52행
```asm
	.quad	0					// ImageBase
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE ImageBase 필드를 0으로 둡니다. 실제 적재 위치를 여기의 고정 가상 주소로 요구하지 않으며 각 위치는 이미지 기준 오프셋으로 표현합니다.

### 53행
```asm
	.long	SEGMENT_ALIGN				// SectionAlignment
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 메모리에 적재할 PE 섹션의 정렬을 SEGMENT_ALIGN, 즉 이 버전 ARM64에서는 64KiB로 지정합니다. 커널의 기본 페이지 크기를 선택하는 필드가 아닙니다.

### 54행
```asm
	.long	PECOFF_FILE_ALIGNMENT			// FileAlignment
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 파일 안의 섹션 데이터 정렬을 PECOFF_FILE_ALIGNMENT인 512바이트로 기록합니다. 앞의 메모리 섹션 정렬과 서로 다른 조건입니다.

### 55행
```asm
	.short	0					// MajorOperatingSystemVersion
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 요구 운영체제 주 버전 필드를 0으로 둡니다. UEFI 부팅용 이미지에서 특정 Windows 버전을 요구하는 값으로 사용하지 않습니다.

### 56행
```asm
	.short	0					// MinorOperatingSystemVersion
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 요구 운영체제 부 버전 필드도 0으로 둡니다.

### 57행
```asm
	.short	LINUX_EFISTUB_MAJOR_VERSION		// MajorImageVersion
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. Linux EFI stub 인터페이스의 주 버전값 3을 기록합니다. 이 버전부터는 비트마스크로도 해석되어 initrd의 EFI 장치 경로 제공과 initrd= 장치 경로 지정 기능 지원을 나타냅니다. Linux 커널 버전 6.18.37과는 별개입니다.

### 58행
```asm
	.short	LINUX_EFISTUB_MINOR_VERSION		// MinorImageVersion
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. EFI stub 인터페이스의 부 버전은 0으로 기록합니다. include/linux/pe.h는 이 필드를 0으로 유지하도록 정의합니다.

### 59행
```asm
	.short	0					// MajorSubsystemVersion
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 요구 subsystem 주 버전 필드를 0으로 둡니다. 뒤의 Subsystem 종류 필드는 별도로 EFI application을 지정합니다.

### 60행
```asm
	.short	0					// MinorSubsystemVersion
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 요구 subsystem 부 버전 필드도 0으로 둡니다.

### 61행
```asm
	.long	0					// Win32VersionValue
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE의 예약된 Win32VersionValue 필드를 0으로 채웁니다.

### 63행
```asm
	.long	_end - .L_head				// SizeOfImage
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. 이미지 시작부터 _end까지의 전체 메모리 크기를 기록합니다. 로더는 코드와 데이터뿐 아니라 파일에 모두 들어 있지 않은 메모리 공간까지 고려해야 합니다.

### 66행
```asm
	.long	.Lefi_header_end - .L_head		// SizeOfHeaders
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 이미지 시작부터 실제 코드 앞까지의 길이를 헤더 전체 크기로 기록합니다. 정렬 때문에 추가된 헤더 뒤 패딩도 이 범위에 포함됩니다.

### 67행
```asm
	.long	0					// CheckSum
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE CheckSum 필드는 0으로 둡니다. 이 필드가 0이라는 사실만으로 Secure Boot 서명 검사 여부를 판단할 수는 없습니다.

### 68행
```asm
	.short	IMAGE_SUBSYSTEM_EFI_APPLICATION		// Subsystem
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. Subsystem을 EFI_APPLICATION으로 지정합니다. UEFI는 이 커널 이미지의 EFI stub을 EFI 응용 프로그램 진입 규약으로 실행합니다.

### 69행
```asm
	.short	IMAGE_DLLCHARACTERISTICS_NX_COMPAT	// DllCharacteristics
```
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. NX_COMPAT를 표시하여 실행 불가 메모리 보호와 양립하는 이미지임을 알립니다. 이 헤더 비트 자체가 ARM의 실행 금지 비트를 하드웨어에 설정하는 명령은 아닙니다.

### 70행
```asm
	.quad	0					// SizeOfStackReserve
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE의 스택 예약 크기를 0으로 기록합니다. 이 필드는 Linux 태스크의 THREAD_SIZE를 지정하는 곳이 아닙니다.

### 71행
```asm
	.quad	0					// SizeOfStackCommit
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE의 초기 스택 commit 크기도 0으로 기록합니다. 실제 커널 스택 구성은 부팅과 태스크 초기화 코드가 담당합니다.

### 72행
```asm
	.quad	0					// SizeOfHeapReserve
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE의 힙 예약 크기를 0으로 기록합니다. Linux의 커널 메모리 할당기 용량을 여기서 정하지 않습니다.

### 73행
```asm
	.quad	0					// SizeOfHeapCommit
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE의 초기 힙 commit 크기도 0으로 기록합니다.

### 74행
```asm
	.long	0					// LoaderFlags
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 예약된 LoaderFlags 필드를 0으로 채웁니다.

### 75행
```asm
	.long	(.Lsection_table - .) / 8		// NumberOfRvaAndSizes
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 이 위치 뒤부터 섹션표 전까지의 길이를 8로 나눠 data directory 항목 수를 기록합니다. 각 항목이 32비트 위치와 32비트 크기로 이루어지므로 8바이트 단위입니다.

### 77행
```asm
	.quad	0					// ExportTable
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. 내보내기 테이블의 위치·크기를 모두 0으로 둡니다. 이 EFI 부팅 이미지에서 PE export table을 제공하지 않습니다.

### 78행
```asm
	.quad	0					// ImportTable
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. 가져오기 테이블도 비워 둡니다. UEFI 서비스는 PE DLL import 방식으로 연결하는 것이 아니라 펌웨어가 전달한 인터페이스를 사용합니다.

### 79행
```asm
	.quad	0					// ResourceTable
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE resource table은 제공하지 않으므로 위치와 크기를 0으로 기록합니다.

### 80행
```asm
	.quad	0					// ExceptionTable
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE exception directory 항목을 비워 둡니다. 이는 ARM 예외 벡터가 없다는 뜻이 아니라 PE 형식의 해당 메타데이터를 제공하지 않는다는 뜻입니다.

### 81행
```asm
	.quad	0					// CertificationTable
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. 원본 이미지의 인증서 테이블 항목을 0으로 둡니다. 이후 별도의 이미지 서명 도구가 추가하는 인증서 정보와 구분해야 합니다.

### 82행
```asm
	.quad	0					// BaseRelocationTable
```
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE base relocation directory는 제공하지 않습니다. Linux 커널 자체의 재배치 처리 유무는 별도 코드와 설정에 따르며 이 항목만으로 결정되지 않습니다.

## 디버그 디렉터리
원본 84–125행

### 84행
```asm
#if defined(CONFIG_DEBUG_EFI) || defined(CONFIG_ARM64_BTI_KERNEL)
```
CONFIG_DEBUG_EFI는 디버그 UEFI에서 사용할 이미지 디버그 정보를 넣는 설정입니다. CONFIG_ARM64_BTI_KERNEL은 커널의 간접 분기 도착점을 검사하는 BTI 지원 설정입니다. 둘 중 하나라도 켜면 PE debug directory를 만들어, 전자는 심볼 파일 위치를, 후자는 BTI 호환 특성을 전달합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

### 85행
```asm
	.long	.Lefi_debug_table - .L_head		// DebugTable
```
이 .long은 CPU가 실행할 뺄셈 명령이 아니라 4바이트 정수 필드를 만드는 어셈블러 지시문입니다. .L_head는 이미지 시작점이고 .Lefi_debug_table은 디버그 디렉터리의 위치입니다. 디렉터리가 별도의 .init.rodata에 있으므로, 링크로 섹션 배치가 정해질 때 두 위치의 차이를 계산해 PE 헤더의 DebugTable RVA로 채웁니다. 부팅할 때 로더는 이미 기록된 이 오프셋을 읽어 디렉터리를 찾습니다.

### 86행
```asm
	.long	.Lefi_debug_table_size
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 조건에 따라 생성한 debug directory 전체 크기를 기록합니다. DEBUG_EFI와 BTI 중 무엇을 켰는지에 따라 들어가는 항목 수가 달라집니다.

### 98행
```asm
	__INITRODATA
```
디버그 디렉터리를 .init.rodata에 배치하도록 섹션을 바꿉니다. RVA로 참조되는 데이터는 PE 섹션이 덮는 범위에 있어야 하는데 헤더 자체는 섹션으로 선언되지 않기 때문입니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.

### 100행
```asm
	.align	2
```
디버그 디렉터리를 4바이트 경계에 맞춥니다. 이곳의 .align 2는 2바이트가 아니라 2의 2승 바이트 정렬입니다. AArch64의 .align 인자는 바이트 수가 아니라 2의 지수입니다. 빌드할 때 필요한 패딩을 넣어 그 경계를 맞춥니다.

### 101행
```asm
.Lefi_debug_table:
```
PE data directory에서 가리킬 디버그 디렉터리 시작입니다. 아래 설정에 따라 CodeView 항목과 확장 이미지 특성 항목이 들어갑니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

### 102행
```asm
#ifdef CONFIG_DEBUG_EFI
```
디버그 정보를 포함한 EFI 빌드에서 CodeView 항목을 추가합니다. CONFIG_DEBUG_EFI는 EFI와 DEBUG_INFO에 의존하며, 디버그 펌웨어에서 vmlinux 심볼을 찾는 데 필요한 메타데이터를 제공합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

### 104행
```asm
	.long	0					// Characteristics
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. CodeView 디버그 디렉터리 항목의 Characteristics는 0으로 둡니다. 선택적 속성을 추가하지 않습니다.

### 105행
```asm
	.long	0					// TimeDateStamp
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 이 디버그 항목의 타임스탬프를 0으로 기록합니다.

### 106행
```asm
	.short	0					// MajorVersion
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. CodeView 디렉터리 항목의 주 버전을 0으로 기록합니다.

### 107행
```asm
	.short	0					// MinorVersion
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. CodeView 디렉터리 항목의 부 버전을 0으로 기록합니다.

### 108행
```asm
	.long	IMAGE_DEBUG_TYPE_CODEVIEW		// Type
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 디버그 정보 종류를 CodeView로 지정합니다. 뒤의 NB10 payload와 vmlinux 경로를 이 형식으로 해석하게 합니다.

### 109행
```asm
	.long	.Lefi_debug_entry_size			// SizeOfData
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 뒤에 배치할 CodeView payload의 전체 바이트 크기를 기록합니다.

### 110행
```asm
	.long	0					// RVA
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. CodeView payload의 RVA는 0으로 둡니다. payload는 PE 섹션 바깥 헤더 뒤에 놓을 수 있으며 다음 FileOffset 필드로 파일에서 찾습니다.

### 111행
```asm
	.long	.Lefi_debug_entry - .L_head		// FileOffset
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 이미지 파일 시작부터 CodeView payload까지의 위치를 기록합니다. 메모리 RVA 대신 파일 오프셋으로 해당 데이터를 찾게 합니다.

### 112행
```asm
#endif
```
DEBUG_EFI가 추가하는 CodeView 디렉터리 항목을 마칩니다. BTI용 항목은 별도 조건으로 이어집니다.

### 113행
```asm
#ifdef CONFIG_ARM64_BTI_KERNEL
```
커널 BTI 설정이 켜졌으면 확장 이미지 특성을 알리는 디렉터리 항목을 추가합니다. BTI는 간접 분기가 허용된 명령 위치에 도착하는지 검사하는 기능입니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

### 114행
```asm
	.long	0					// Characteristics
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 확장 이미지 특성 디렉터리의 Characteristics 필드를 0으로 둡니다.

### 115행
```asm
	.long	0					// TimeDateStamp
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 이 확장 특성 항목의 타임스탬프를 0으로 기록합니다.

### 116행
```asm
	.short	0					// MajorVersion
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 확장 특성 디렉터리의 주 버전을 0으로 기록합니다.

### 117행
```asm
	.short	0					// MinorVersion
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 확장 특성 디렉터리의 부 버전을 0으로 기록합니다.

### 118행
```asm
	.long	IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS	// Type
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 디버그 디렉터리의 Type을 EX_DLLCHARACTERISTICS로 지정합니다. 아래 payload는 디버그 심볼 경로가 아니라 확장 이미지 호환 플래그로 해석됩니다.

### 119행
```asm
	.long	4					// SizeOfData
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 확장 이미지 특성 payload의 길이는 32비트 플래그 하나이므로 4바이트입니다.

### 120행
```asm
	.long	0					// RVA
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 확장 특성 payload 역시 RVA를 0으로 두고 다음 파일 오프셋으로 참조합니다.

### 121행
```asm
	.long	.Lefi_dll_characteristics_ex - .L_head	// FileOffset
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 헤더 뒤에 놓일 확장 특성 플래그의 파일 위치를 기록합니다.

### 122행
```asm
#endif
```
BTI 설정에 따른 확장 특성 디렉터리 항목을 마칩니다.

### 123행
```asm
	.set	.Lefi_debug_table_size, . - .Lefi_debug_table
```
실제로 생성한 디렉터리 시작부터 현재 위치까지의 차이를 크기로 계산합니다. 앞의 data directory 크기 필드는 이 값을 사용합니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.

### 124행
```asm
	.previous
```
디버그 디렉터리를 만들기 전의 헤더 섹션으로 돌아갑니다. 이후 섹션표와 payload는 다시 이미지 헤더 쪽에 이어 붙습니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.

### 125행
```asm
#endif
```
DEBUG_EFI 또는 BTI 때문에 추가한 디버그 디렉터리 생성을 마칩니다.

## 두 섹션의 크기와 권한
원본 126–157행

### 128행
```asm
.Lsection_table:
```
PE 섹션표 시작입니다. 각 섹션 항목은 40바이트이며, 로더에게 파일 범위·메모리 범위와 접근 속성을 알려줍니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

### 129행
```asm
	.ascii	".text\0\0\0"
```
첫 PE 섹션 이름을 .text로 기록하고 8바이트 이름 필드의 나머지를 NUL로 채웁니다. .ascii는 문자열 끝에 NUL을 자동으로 붙이지 않으므로, 소스에 쓴 \0만 실제 0바이트로 들어갑니다.

### 130행
```asm
	.long	__initdata_begin - .Lefi_header_end	// VirtualSize
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. PE .text 섹션이 메모리에서 차지할 크기를 헤더 끝부터 __initdata_begin 직전까지로 지정합니다.

### 131행
```asm
	.long	.Lefi_header_end - .L_head		// VirtualAddress
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. PE .text 섹션의 이미지 기준 시작 위치를 기록합니다.

### 132행
```asm
	.long	__initdata_begin - .Lefi_header_end	// SizeOfRawData
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. 파일에서 가져올 .text 데이터 크기를 기록합니다. 이 섹션에서는 위의 메모리 크기와 같은 식을 사용합니다.

### 133행
```asm
	.long	.Lefi_header_end - .L_head		// PointerToRawData
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 파일에서 .text 바이트가 시작하는 위치를 기록합니다. 이 이미지 배치에서는 RVA와 파일 오프셋이 같은 식으로 계산됩니다.

### 135행
```asm
	.long	0					// PointerToRelocations
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 이 PE .text 섹션에는 별도 COFF relocation 목록을 두지 않으므로 그 파일 위치를 0으로 둡니다.

### 136행
```asm
	.long	0					// PointerToLineNumbers
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE .text의 COFF 줄 번호 테이블 위치를 0으로 둡니다. 설명 페이지의 소스 줄 번호와는 무관한 파일 형식 필드입니다.

### 137행
```asm
	.short	0					// NumberOfRelocations
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. PE .text의 COFF relocation 항목 개수를 0으로 기록합니다.

### 138행
```asm
	.short	0					// NumberOfLineNumbers
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. PE .text의 COFF 줄 번호 항목 개수도 0으로 기록합니다.

### 139행
```asm
	.long	IMAGE_SCN_CNT_CODE | \
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. PE .text의 속성에 코드 포함(CNT_CODE) 비트를 넣습니다. 이어지는 두 줄과 OR로 합쳐 하나의 32비트 필드를 만듭니다.

### 140행
```asm
		IMAGE_SCN_MEM_READ | \
```
같은 .text 속성에 읽기 허용을 추가합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.

### 141행
```asm
		IMAGE_SCN_MEM_EXECUTE			// Characteristics
```
같은 .text 속성에 실행 허용을 추가합니다. 이 조합에는 쓰기 허용 비트가 포함되지 않습니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.

### 143행
```asm
	.ascii	".data\0\0\0"
```
두 번째 PE 섹션 이름을 .data로 기록하고 나머지 이름 필드를 NUL로 채웁니다. .ascii는 문자열 끝에 NUL을 자동으로 붙이지 않으므로, 소스에 쓴 \0만 실제 0바이트로 들어갑니다.

### 144행
```asm
	.long	__pecoff_data_size			// VirtualSize
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. PE .data가 메모리에서 차지하는 전체 크기를 기록합니다. 파일에서 읽어 올 데이터보다 클 수 있으므로 SizeOfRawData와 따로 둡니다.

### 145행
```asm
	.long	__initdata_begin - .L_head		// VirtualAddress
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. __initdata_begin을 .data 섹션의 이미지 기준 시작 위치로 기록합니다.

### 146행
```asm
	.long	__pecoff_data_rawsize			// SizeOfRawData
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. PE .data에 대해 실제 파일에 들어 있는 바이트 수를 기록합니다. 메모리에만 필요한 뒤쪽 공간은 VirtualSize와 구분됩니다.

### 147행
```asm
	.long	__initdata_begin - .L_head		// PointerToRawData
```
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. 파일에서 .data를 읽어 올 시작 오프셋을 기록합니다.

### 149행
```asm
	.long	0					// PointerToRelocations
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE .data의 별도 COFF relocation 목록 위치를 0으로 둡니다.

### 150행
```asm
	.long	0					// PointerToLineNumbers
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE .data의 COFF 줄 번호 테이블 위치도 0으로 둡니다.

### 151행
```asm
	.short	0					// NumberOfRelocations
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. PE .data의 COFF relocation 항목 개수를 0으로 기록합니다.

### 152행
```asm
	.short	0					// NumberOfLineNumbers
```
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. PE .data의 COFF 줄 번호 항목 개수도 0으로 기록합니다.

### 153행
```asm
	.long	IMAGE_SCN_CNT_INITIALIZED_DATA | \
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. PE .data의 속성에 초기화 데이터 포함 비트를 넣습니다. 뒤의 읽기·쓰기 비트와 합쳐 한 필드를 만듭니다.

### 154행
```asm
		IMAGE_SCN_MEM_READ | \
```
PE .data를 읽을 수 있음을 표시합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.

### 155행
```asm
		IMAGE_SCN_MEM_WRITE			// Characteristics
```
PE .data에 쓰기 허용을 추가합니다. 이 조합에는 실행 허용 비트가 없어 .text와 다른 적재 권한을 표현합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.

### 157행
```asm
	.set	.Lsection_count, (. - .Lsection_table) / 40
```
섹션표 전체 바이트 수를 항목 크기 40으로 나눠 섹션 개수를 계산합니다. 위에서 .text와 .data 두 항목을 만들었으므로 결과는 2입니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.

## 부가 데이터와 헤더 끝
원본 158–181행

### 159행
```asm
#ifdef CONFIG_DEBUG_EFI
```
DEBUG_EFI가 켜졌을 때 디렉터리가 가리킬 CodeView payload를 만듭니다. 디렉터리 구조 자체와 그 구조가 가리키는 데이터의 위치는 서로 다릅니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

### 160행
```asm
.Lefi_debug_entry:
```
CodeView 디버그 payload의 시작 주소입니다. 앞의 SizeOfData와 FileOffset은 이 위치와 크기를 사용합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

### 162행
```asm
	.ascii	"NB10"					// Signature
```
CodeView의 NB10 형식을 나타내는 네 문자 서명을 넣습니다. 뒤에 붙는 필드와 파일 경로를 해당 형식의 데이터로 알아보게 합니다. .ascii는 문자열 끝에 NUL을 자동으로 붙이지 않으므로, 소스에 쓴 \0만 실제 0바이트로 들어갑니다.

### 163행
```asm
	.long	0					// Unknown
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. NB10 payload의 이 32비트 필드를 0으로 채웁니다. 원문에서 Unknown으로 표시한 자리이며 vmlinux 경로는 뒤의 ASCIZ에 들어갑니다.

### 164행
```asm
	.long	0					// Unknown2
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. NB10 payload의 두 번째 추가 32비트 필드도 0으로 채웁니다.

### 165행
```asm
	.long	0					// Unknown3
```
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. NB10 payload의 세 번째 추가 32비트 필드도 0으로 채워 경로 문자열 앞의 고정 크기 영역을 완성합니다.

### 167행
```asm
	.asciz	VMLINUX_PATH
```
빌드 시 정해진 vmlinux의 절대 경로를 NUL 종료 문자열로 넣습니다. 디버그 펌웨어가 심볼 파일 위치를 찾도록 제공하는 정보이며 실행 중 커널이 이 경로의 파일을 여는 명령은 아닙니다.

### 169행
```asm
	.set	.Lefi_debug_entry_size, . - .Lefi_debug_entry
```
NB10 서명·고정 필드·경로 문자열을 포함한 payload 크기를 계산합니다. 앞의 CodeView 디렉터리 SizeOfData에 들어갈 값입니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.

### 170행
```asm
#endif
```
DEBUG_EFI용 CodeView payload 생성을 마칩니다.

### 171행
```asm
#ifdef CONFIG_ARM64_BTI_KERNEL
```
커널 BTI 설정이 켜졌을 때 확장 이미지 특성 payload를 만듭니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

### 172행
```asm
.Lefi_dll_characteristics_ex:
```
앞의 EX_DLLCHARACTERISTICS 디렉터리가 파일 오프셋으로 가리킬 플래그 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

### 173행
```asm
	.long	IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT
```
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. FORWARD_CFI_COMPAT 비트를 기록하여 간접 분기 보호와 호환되는 이미지임을 로더에 알립니다. 여기서는 BTI 지원 빌드가 이 표시를 제공하며, 플래그를 기록하는 것 자체가 CPU의 BTI 검사를 켜는 명령은 아닙니다.

### 174행
```asm
#endif
```
BTI용 확장 특성 payload 생성을 마칩니다.

### 176행
```asm
	.balign	SEGMENT_ALIGN
```
헤더 끝을 SEGMENT_ALIGN인 64KiB 경계까지 정렬합니다. 앞서 선언한 .text 시작 위치와 섹션 정렬 조건을 실제 바이트 배치로 맞추는 단계입니다. .balign의 인자는 바이트 단위의 정렬 크기이며, 어셈블 단계에서 필요한 패딩을 넣습니다.

### 177행
```asm
.Lefi_header_end:
```
PE 헤더와 패딩이 끝나고 코드 영역이 시작하는 위치입니다. 코드 크기·BaseOfCode·SizeOfHeaders 등의 계산이 이 라벨을 기준으로 합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

### 178행
```asm
#else
```
CONFIG_EFI를 끈 이미지의 처리입니다. 위의 PE/COFF 헤더와 선택적 디버그 데이터를 만들지 않습니다.

### 179행
```asm
	.set	.Lpe_header_offset, 0x0
```
PE 헤더 오프셋을 0으로 정의하여 이 이미지에 EFI용 PE 헤더가 없음을 나타냅니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.

### 180행
```asm
#endif
```
EFI 지원 여부에 따른 헤더 생성 분기를 마칩니다.

### 181행
```asm
	.endm
```
공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다. 

## 설명한 뒤 함께 생각해 볼 질문

### Image 앞 64바이트가 PE 헤더 전체인가요?
공통 Image 헤더 뒤에 추가 PE/COFF 데이터가 이어집니다. 두 범위의 길이를 합쳐서 읽어야 합니다.

### MZ 서명은 그냥 문자열인가요?
이 위치는 CPU 명령으로도 읽힙니다. EFI 구성에서는 CCMP 명령의 바이트가 MZ 서명을 이루도록 사용합니다.

### EFI 진입점과 primary_entry는 같은가요?
EFI 로더는 헤더의 __efistub_efi_pe_entry를 사용합니다. 일반 Image 시작의 B primary_entry와 구분됩니다.
