DUJINLABS.COM

Linux v6.18.37 · ARM64

efi-header.S — Image 안의 EFI PE/COFF 헤더

head.S가 이 파일을 포함합니다. efi_signature_nop는 Image 첫 명령을, __EFI_PE_HEADER는 EFI 로더가 읽을 PE/COFF 데이터를 만듭니다. 헤더 필드 사이의 화살표는 실행 순서가 아니라 파일 배치 순서입니다.

역할과 입력

  • CONFIG_EFI: EFI 헤더 생성 여부
  • 필드 값: 링커 심볼 차이와 PE 상수

원본 .S · 빌드 조건: Makefile · 명령·주소 표기 읽는 법

181줄 · SHA-256: aa9b5a1f28bd01da995ff4d2298181a49932a6bfb33c5148e294b4385f42278a

구조와 흐름

Image 안의 EFI PE/COFF 헤더
화살표: 파일·링크 배치 순서입니다. CPU 실행 순서가 아닙니다. 칸 크기는 실제 바이트 비율과 다릅니다.

MZ 서명인 첫 명령

CONFIG_EFI에서는 CCMP x18,#0,#0xd,pl을 배치합니다. 바이트가 MZ 서명을 이루지만 실제 NOP 명령은 아니어서 NZCV를 바꿀 수 있습니다. EFI를 끄면 NOP 하나가 남습니다.

efi-header.S · L1–L26
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Copyright (C) 2013 - 2017 Linaro, Ltd.
4 * Copyright (C) 2013, 2014 Red Hat, Inc.
5 */
6
7#include <linux/pe.h>
8#include <linux/sizes.h>
9
10	.macro	efi_signature_nop
11#ifdef CONFIG_EFI
12.L_head:
13	/*
14	 * This ccmp instruction has no meaningful effect except that
15	 * its opcode forms the magic "MZ" signature required by UEFI.
16	 */
17	ccmp	x18, #0, #0xd, pl
18#else
19	/*
20	 * Bootloaders may inspect the opcode at the start of the kernel
21	 * image to decide if the kernel is capable of booting via UEFI.
22	 * So put an ordinary NOP here, not the "MZ.." pseudo-nop above.
23	 */
24	nop
25#endif
26	.endm
L7
UEFI가 읽을 PE/COFF 헤더의 서명, ARM64 기계 종류, 섹션 속성 같은 상수를 가져옵니다. 이 파일의 .long·.short는 대부분 CPU 명령이 아니라 로더에게 제공할 헤더 필드를 만드는 데 사용됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L8
SZ_64K처럼 바이트 크기를 나타내는 상수를 가져옵니다. 이미지 섹션과 헤더의 정렬 크기를 읽을 때 단위를 분명하게 하기 위한 정의입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L10
커널 이미지 첫 명령을 만들 때 쓰는 매크로입니다. EFI 지원 이미지이면 명령 바이트의 시작이 MZ 서명이 되게 하고, 미지원 이미지이면 보통 NOP를 둡니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
L11
CONFIG_EFI는 ARM64 커널의 UEFI 부팅·인터페이스 지원 설정이며 EFI stub도 선택합니다. 이 설정이 켜진 이미지에는 UEFI 로더가 알아볼 MZ 서명을 첫 명령 바이트에 넣습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
L12
이미지 헤더 시작 주소를 표시합니다. 뒤의 PE 필드들이 사용하는 RVA는 이 시작에서 얼마 떨어졌는지로 계산합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L17
이 CCMP의 기계어 바이트가 MZ로 시작하도록 명령을 골랐습니다. 부팅 흐름에서는 의미 있는 계산 결과로 사용하지 않으며, 핵심 목적은 같은 바이트를 UEFI가 실행 파일 서명으로 인식하게 하는 것입니다. 실행하면 PL 조건(N=0)일 때 x18과 0을 비교해 NZCV를 갱신하고, 조건이 거짓이면 NZCV에 즉시값 0xd를 넣습니다. x18 자체는 바꾸지 않습니다.
L18
EFI를 지원하지 않는 빌드에서 사용할 첫 명령을 선택하는 분기입니다. UEFI 부팅 가능 이미지라는 MZ 표시를 넣지 않습니다.
L24
EFI 미지원 이미지의 첫 명령에 일반 NOP를 넣습니다. 이미지 시작 바이트로 EFI 부팅 가능 여부를 검사하는 부트로더가 잘못 판단하지 않도록 합니다.
L25
EFI 지원 여부에 따른 첫 명령 선택을 마칩니다.
L26
— 매크로 정의를 끝냅니다.

PE 파일 헤더와 선택 헤더

RVA는 Image 시작 .L_head를 기준으로 한 상대 주소입니다. AddressOfEntryPoint는 primary_entry가 아닌 __efistub_efi_pe_entry를 가리킵니다. .long은 4바이트, .short는 2바이트, .quad는 8바이트를 출력합니다. 여러 줄의 OR 식은 하나의 Characteristics 필드입니다.

efi-header.S · L27–L83
27
28	.macro	__EFI_PE_HEADER
29#ifdef CONFIG_EFI
30	.set	.Lpe_header_offset, . - .L_head
31	.long	IMAGE_NT_SIGNATURE
32	.short	IMAGE_FILE_MACHINE_ARM64		// Machine
33	.short	.Lsection_count				// NumberOfSections
34	.long	0 					// TimeDateStamp
35	.long	0					// PointerToSymbolTable
36	.long	0					// NumberOfSymbols
37	.short	.Lsection_table - .Loptional_header	// SizeOfOptionalHeader
38	.short	IMAGE_FILE_DEBUG_STRIPPED | \
39		IMAGE_FILE_EXECUTABLE_IMAGE | \
40		IMAGE_FILE_LINE_NUMS_STRIPPED		// Characteristics
41
42.Loptional_header:
43	.short	IMAGE_NT_OPTIONAL_HDR64_MAGIC		// PE32+ format
44	.byte	0x02					// MajorLinkerVersion
45	.byte	0x14					// MinorLinkerVersion
46	.long	__initdata_begin - .Lefi_header_end	// SizeOfCode
47	.long	__pecoff_data_size			// SizeOfInitializedData
48	.long	0					// SizeOfUninitializedData
49	.long	__efistub_efi_pe_entry - .L_head	// AddressOfEntryPoint
50	.long	.Lefi_header_end - .L_head		// BaseOfCode
51
52	.quad	0					// ImageBase
53	.long	SEGMENT_ALIGN				// SectionAlignment
54	.long	PECOFF_FILE_ALIGNMENT			// FileAlignment
55	.short	0					// MajorOperatingSystemVersion
56	.short	0					// MinorOperatingSystemVersion
57	.short	LINUX_EFISTUB_MAJOR_VERSION		// MajorImageVersion
58	.short	LINUX_EFISTUB_MINOR_VERSION		// MinorImageVersion
59	.short	0					// MajorSubsystemVersion
60	.short	0					// MinorSubsystemVersion
61	.long	0					// Win32VersionValue
62
63	.long	_end - .L_head				// SizeOfImage
64
65	// Everything before the kernel image is considered part of the header
66	.long	.Lefi_header_end - .L_head		// SizeOfHeaders
67	.long	0					// CheckSum
68	.short	IMAGE_SUBSYSTEM_EFI_APPLICATION		// Subsystem
69	.short	IMAGE_DLLCHARACTERISTICS_NX_COMPAT	// DllCharacteristics
70	.quad	0					// SizeOfStackReserve
71	.quad	0					// SizeOfStackCommit
72	.quad	0					// SizeOfHeapReserve
73	.quad	0					// SizeOfHeapCommit
74	.long	0					// LoaderFlags
75	.long	(.Lsection_table - .) / 8		// NumberOfRvaAndSizes
76
77	.quad	0					// ExportTable
78	.quad	0					// ImportTable
79	.quad	0					// ResourceTable
80	.quad	0					// ExceptionTable
81	.quad	0					// CertificationTable
82	.quad	0					// BaseRelocationTable
83
L28
UEFI가 ARM64 커널을 실행 파일로 적재하는 데 필요한 PE/COFF 헤더와 섹션표를 내보내는 매크로입니다. 헤더의 숫자들은 실행 중 계산되는 값이 아니라 조립·링크 단계에서 이미지에 기록됩니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
L29
CONFIG_EFI가 켜진 경우에만 실제 PE/COFF 헤더를 만듭니다. 꺼진 빌드는 아래 else에서 PE 헤더 오프셋을 0으로 설정합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
L30
이미지 시작 .L_head에서 현재 PE 헤더까지의 바이트 거리를 계산합니다. Image 헤더가 이 값을 통해 PE 헤더 위치를 알려줄 수 있게 합니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.
L31
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. PE 파일 서명인 PE와 두 NUL 바이트(0x00004550)를 기록합니다. 로더가 뒤의 바이트를 PE/COFF 헤더로 해석하는 기준입니다.
L32
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 대상 CPU 종류를 ARM64(0xaa64)로 기록합니다. UEFI 로더가 이 실행 이미지의 명령어 아키텍처를 구분합니다.
L33
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 뒤에서 계산할 PE 섹션 개수를 기록합니다. 이 파일에서는 .text와 .data 두 섹션의 설명이 이어집니다.
L34
이 .long은 CPU 명령이 아니라 어셈블러에게 4바이트 정수 데이터를 내보내도록 하는 지시문입니다. 상수 0은 어셈블 단계에서 바로 00 00 00 00으로 기록됩니다. 이 자리는 COFF TimeDateStamp 필드이며, 이 이미지에서는 빌드 시각을 넣지 않습니다.
L35
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. COFF 심볼표의 파일 위치를 0으로 기록합니다. 이 부팅 이미지에 별도의 COFF 심볼표를 제공하지 않는다는 뜻입니다.
L36
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. COFF 심볼표의 항목 수도 0으로 기록하여 앞의 심볼표 위치 0과 맞춥니다.
L37
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. optional header 시작부터 섹션표 직전까지의 크기를 기록합니다. 로더는 이 길이를 사용해 섹션표가 시작하는 위치를 찾습니다.
L38
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. COFF Characteristics 비트 조합의 첫 부분입니다. IMAGE_FILE_DEBUG_STRIPPED로 COFF 디버그 정보가 제거된 이미지임을 표시합니다. 아래의 선택적 PE 디버그 디렉터리와는 구분되는 필드입니다.
L39
같은 Characteristics 필드에 EXECUTABLE_IMAGE 비트를 더하여 실행 가능한 이미지임을 표시합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.
L40
LINE_NUMS_STRIPPED 비트까지 합쳐 COFF 소스 줄 번호 정보가 없음을 표시합니다. 세 줄은 각각 별도 필드가 아니라 OR로 합쳐진 하나의 16비트 필드를 만듭니다.
L42
PE optional header의 시작 위치입니다. 이름과 달리 실행 이미지의 적재 주소·크기·진입점 등 핵심 정보가 이 영역에 들어갑니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L43
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 64비트 PE32+ 형식을 나타내는 0x020b를 기록합니다. 로더가 이후 필드의 크기와 배치를 이 형식으로 해석하게 합니다.
L44
이 .byte은 실행 명령이 아니라 어셈블 단계에서 상수를 1바이트 정수로 기록하는 지시문입니다. PE MajorLinkerVersion 필드에 이 헤더가 정한 0x02를 기록합니다. Linux 커널의 주 버전 6을 의미하는 값은 아닙니다.
L45
이 .byte은 실행 명령이 아니라 어셈블 단계에서 상수를 1바이트 정수로 기록하는 지시문입니다. PE MinorLinkerVersion 필드에 0x14를 기록합니다. 바로 앞 값과 함께 헤더 형식상 링커 버전 필드를 채우며 Linux의 6.18.37 표시는 아닙니다.
L46
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. PE의 코드 크기를 헤더 끝부터 __initdata_begin 전까지의 길이로 기록합니다. 뒤의 .text 섹션이 차지하는 범위와 맞춘 값입니다.
L47
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. 링커가 계산한 PE 데이터 영역의 메모리 크기를 기록합니다. 원본 파일의 데이터 바이트 수와 메모리에 필요한 크기는 뒤의 .data 섹션에서 별도 필드로 구분합니다.
L48
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 별도 uninitialized-data 크기 필드는 0으로 둡니다. 뒤의 .data VirtualSize와 SizeOfRawData 차이로 파일에 없는 메모리 영역을 포함하므로 커널 전체에 BSS가 없다는 뜻은 아닙니다.
L49
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. UEFI가 처음 호출할 EFI stub의 efi_pe_entry 위치를 이미지 시작 기준 RVA로 기록합니다. 일반 부트로더가 Image의 첫 명령으로 들어오는 경로와 진입 방식이 다릅니다.
L50
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 코드 영역이 시작하는 헤더 끝의 위치를 이미지 시작 기준 오프셋으로 기록합니다.
L52
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE ImageBase 필드를 0으로 둡니다. 실제 적재 위치를 여기의 고정 가상 주소로 요구하지 않으며 각 위치는 이미지 기준 오프셋으로 표현합니다.
L53
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 메모리에 적재할 PE 섹션의 정렬을 SEGMENT_ALIGN, 즉 이 버전 ARM64에서는 64KiB로 지정합니다. 커널의 기본 페이지 크기를 선택하는 필드가 아닙니다.
L54
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 파일 안의 섹션 데이터 정렬을 PECOFF_FILE_ALIGNMENT인 512바이트로 기록합니다. 앞의 메모리 섹션 정렬과 서로 다른 조건입니다.
L55
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 요구 운영체제 주 버전 필드를 0으로 둡니다. UEFI 부팅용 이미지에서 특정 Windows 버전을 요구하는 값으로 사용하지 않습니다.
L56
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 요구 운영체제 부 버전 필드도 0으로 둡니다.
L57
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. Linux EFI stub 인터페이스의 주 버전값 3을 기록합니다. 이 버전부터는 비트마스크로도 해석되어 initrd의 EFI 장치 경로 제공과 initrd= 장치 경로 지정 기능 지원을 나타냅니다. Linux 커널 버전 6.18.37과는 별개입니다.
L58
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. EFI stub 인터페이스의 부 버전은 0으로 기록합니다. include/linux/pe.h는 이 필드를 0으로 유지하도록 정의합니다.
L59
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 요구 subsystem 주 버전 필드를 0으로 둡니다. 뒤의 Subsystem 종류 필드는 별도로 EFI application을 지정합니다.
L60
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 요구 subsystem 부 버전 필드도 0으로 둡니다.
L61
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE의 예약된 Win32VersionValue 필드를 0으로 채웁니다.
L63
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. 이미지 시작부터 _end까지의 전체 메모리 크기를 기록합니다. 로더는 코드와 데이터뿐 아니라 파일에 모두 들어 있지 않은 메모리 공간까지 고려해야 합니다.
L66
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 이미지 시작부터 실제 코드 앞까지의 길이를 헤더 전체 크기로 기록합니다. 정렬 때문에 추가된 헤더 뒤 패딩도 이 범위에 포함됩니다.
L67
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE CheckSum 필드는 0으로 둡니다. 이 필드가 0이라는 사실만으로 Secure Boot 서명 검사 여부를 판단할 수는 없습니다.
L68
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. Subsystem을 EFI_APPLICATION으로 지정합니다. UEFI는 이 커널 이미지의 EFI stub을 EFI 응용 프로그램 진입 규약으로 실행합니다.
L69
이 .short은 CPU 명령이 아니라 빌드할 때 값을 2바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. NX_COMPAT를 표시하여 실행 불가 메모리 보호와 양립하는 이미지임을 알립니다. 이 헤더 비트 자체가 ARM의 실행 금지 비트를 하드웨어에 설정하는 명령은 아닙니다.
L70
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE의 스택 예약 크기를 0으로 기록합니다. 이 필드는 Linux 태스크의 THREAD_SIZE를 지정하는 곳이 아닙니다.
L71
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE의 초기 스택 commit 크기도 0으로 기록합니다. 실제 커널 스택 구성은 부팅과 태스크 초기화 코드가 담당합니다.
L72
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE의 힙 예약 크기를 0으로 기록합니다. Linux의 커널 메모리 할당기 용량을 여기서 정하지 않습니다.
L73
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE의 초기 힙 commit 크기도 0으로 기록합니다.
L74
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 예약된 LoaderFlags 필드를 0으로 채웁니다.
L75
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 이 위치 뒤부터 섹션표 전까지의 길이를 8로 나눠 data directory 항목 수를 기록합니다. 각 항목이 32비트 위치와 32비트 크기로 이루어지므로 8바이트 단위입니다.
L77
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. 내보내기 테이블의 위치·크기를 모두 0으로 둡니다. 이 EFI 부팅 이미지에서 PE export table을 제공하지 않습니다.
L78
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. 가져오기 테이블도 비워 둡니다. UEFI 서비스는 PE DLL import 방식으로 연결하는 것이 아니라 펌웨어가 전달한 인터페이스를 사용합니다.
L79
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE resource table은 제공하지 않으므로 위치와 크기를 0으로 기록합니다.
L80
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE exception directory 항목을 비워 둡니다. 이는 ARM 예외 벡터가 없다는 뜻이 아니라 PE 형식의 해당 메타데이터를 제공하지 않는다는 뜻입니다.
L81
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. 원본 이미지의 인증서 테이블 항목을 0으로 둡니다. 이후 별도의 이미지 서명 도구가 추가하는 인증서 정보와 구분해야 합니다.
L82
이 .quad은 실행 명령이 아니라 어셈블 단계에서 상수를 8바이트 정수로 기록하는 지시문입니다. PE base relocation directory는 제공하지 않습니다. Linux 커널 자체의 재배치 처리 유무는 별도 코드와 설정에 따르며 이 항목만으로 결정되지 않습니다.

디버그 디렉터리

디렉터리는 RVA로 참조되므로 섹션 선언에 포함되는 .init.rodata에 둡니다. CONFIG_DEBUG_EFI와 CONFIG_ARM64_BTI_KERNEL에 따라 각 항목을 추가하며, 실제 부가 데이터는 파일 오프셋으로 참조합니다. .previous로 헤더를 만들던 섹션에 돌아옵니다.

efi-header.S · L84–L125
84#if defined(CONFIG_DEBUG_EFI) || defined(CONFIG_ARM64_BTI_KERNEL)
85	.long	.Lefi_debug_table - .L_head		// DebugTable
86	.long	.Lefi_debug_table_size
87
88	/*
89	 * The debug table is referenced via its Relative Virtual Address (RVA),
90	 * which is only defined for those parts of the image that are covered
91	 * by a section declaration. Since this header is not covered by any
92	 * section, the debug table must be emitted elsewhere. So stick it in
93	 * the .init.rodata section instead.
94	 *
95	 * Note that the payloads themselves are permitted to have zero RVAs,
96	 * which means we can simply put those right after the section headers.
97	 */
98	__INITRODATA
99
100	.align	2
101.Lefi_debug_table:
102#ifdef CONFIG_DEBUG_EFI
103	// EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
104	.long	0					// Characteristics
105	.long	0					// TimeDateStamp
106	.short	0					// MajorVersion
107	.short	0					// MinorVersion
108	.long	IMAGE_DEBUG_TYPE_CODEVIEW		// Type
109	.long	.Lefi_debug_entry_size			// SizeOfData
110	.long	0					// RVA
111	.long	.Lefi_debug_entry - .L_head		// FileOffset
112#endif
113#ifdef CONFIG_ARM64_BTI_KERNEL
114	.long	0					// Characteristics
115	.long	0					// TimeDateStamp
116	.short	0					// MajorVersion
117	.short	0					// MinorVersion
118	.long	IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS	// Type
119	.long	4					// SizeOfData
120	.long	0					// RVA
121	.long	.Lefi_dll_characteristics_ex - .L_head	// FileOffset
122#endif
123	.set	.Lefi_debug_table_size, . - .Lefi_debug_table
124	.previous
125#endif
L84
CONFIG_DEBUG_EFI는 디버그 UEFI에서 사용할 이미지 디버그 정보를 넣는 설정입니다. CONFIG_ARM64_BTI_KERNEL은 커널의 간접 분기 도착점을 검사하는 BTI 지원 설정입니다. 둘 중 하나라도 켜면 PE debug directory를 만들어, 전자는 심볼 파일 위치를, 후자는 BTI 호환 특성을 전달합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
L85
이 .long은 CPU가 실행할 뺄셈 명령이 아니라 4바이트 정수 필드를 만드는 어셈블러 지시문입니다. .L_head는 이미지 시작점이고 .Lefi_debug_table은 디버그 디렉터리의 위치입니다. 디렉터리가 별도의 .init.rodata에 있으므로, 링크로 섹션 배치가 정해질 때 두 위치의 차이를 계산해 PE 헤더의 DebugTable RVA로 채웁니다. 부팅할 때 로더는 이미 기록된 이 오프셋을 읽어 디렉터리를 찾습니다.
L86
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 조건에 따라 생성한 debug directory 전체 크기를 기록합니다. DEBUG_EFI와 BTI 중 무엇을 켰는지에 따라 들어가는 항목 수가 달라집니다.
L98
디버그 디렉터리를 .init.rodata에 배치하도록 섹션을 바꿉니다. RVA로 참조되는 데이터는 PE 섹션이 덮는 범위에 있어야 하는데 헤더 자체는 섹션으로 선언되지 않기 때문입니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
L100
디버그 디렉터리를 4바이트 경계에 맞춥니다. 이곳의 .align 2는 2바이트가 아니라 2의 2승 바이트 정렬입니다. AArch64의 .align 인자는 바이트 수가 아니라 2의 지수입니다. 빌드할 때 필요한 패딩을 넣어 그 경계를 맞춥니다.
L101
PE data directory에서 가리킬 디버그 디렉터리 시작입니다. 아래 설정에 따라 CodeView 항목과 확장 이미지 특성 항목이 들어갑니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L102
디버그 정보를 포함한 EFI 빌드에서 CodeView 항목을 추가합니다. CONFIG_DEBUG_EFI는 EFI와 DEBUG_INFO에 의존하며, 디버그 펌웨어에서 vmlinux 심볼을 찾는 데 필요한 메타데이터를 제공합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
L104
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. CodeView 디버그 디렉터리 항목의 Characteristics는 0으로 둡니다. 선택적 속성을 추가하지 않습니다.
L105
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 이 디버그 항목의 타임스탬프를 0으로 기록합니다.
L106
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. CodeView 디렉터리 항목의 주 버전을 0으로 기록합니다.
L107
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. CodeView 디렉터리 항목의 부 버전을 0으로 기록합니다.
L108
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 디버그 정보 종류를 CodeView로 지정합니다. 뒤의 NB10 payload와 vmlinux 경로를 이 형식으로 해석하게 합니다.
L109
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 뒤에 배치할 CodeView payload의 전체 바이트 크기를 기록합니다.
L110
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. CodeView payload의 RVA는 0으로 둡니다. payload는 PE 섹션 바깥 헤더 뒤에 놓을 수 있으며 다음 FileOffset 필드로 파일에서 찾습니다.
L111
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 이미지 파일 시작부터 CodeView payload까지의 위치를 기록합니다. 메모리 RVA 대신 파일 오프셋으로 해당 데이터를 찾게 합니다.
L112
DEBUG_EFI가 추가하는 CodeView 디렉터리 항목을 마칩니다. BTI용 항목은 별도 조건으로 이어집니다.
L113
커널 BTI 설정이 켜졌으면 확장 이미지 특성을 알리는 디렉터리 항목을 추가합니다. BTI는 간접 분기가 허용된 명령 위치에 도착하는지 검사하는 기능입니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
L114
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 확장 이미지 특성 디렉터리의 Characteristics 필드를 0으로 둡니다.
L115
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 이 확장 특성 항목의 타임스탬프를 0으로 기록합니다.
L116
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 확장 특성 디렉터리의 주 버전을 0으로 기록합니다.
L117
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. 확장 특성 디렉터리의 부 버전을 0으로 기록합니다.
L118
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 디버그 디렉터리의 Type을 EX_DLLCHARACTERISTICS로 지정합니다. 아래 payload는 디버그 심볼 경로가 아니라 확장 이미지 호환 플래그로 해석됩니다.
L119
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 확장 이미지 특성 payload의 길이는 32비트 플래그 하나이므로 4바이트입니다.
L120
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 확장 특성 payload 역시 RVA를 0으로 두고 다음 파일 오프셋으로 참조합니다.
L121
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 헤더 뒤에 놓일 확장 특성 플래그의 파일 위치를 기록합니다.
L122
BTI 설정에 따른 확장 특성 디렉터리 항목을 마칩니다.
L123
실제로 생성한 디렉터리 시작부터 현재 위치까지의 차이를 크기로 계산합니다. 앞의 data directory 크기 필드는 이 값을 사용합니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.
L124
디버그 디렉터리를 만들기 전의 헤더 섹션으로 돌아갑니다. 이후 섹션표와 payload는 다시 이미지 헤더 쪽에 이어 붙습니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
L125
DEBUG_EFI 또는 BTI 때문에 추가한 디버그 디렉터리 생성을 마칩니다.

두 섹션의 크기와 권한

.text는 읽기·실행, .data는 읽기·쓰기 속성을 가집니다. PE 섹션 이름과 ELF의 입력 섹션 구분은 다릅니다. .Lsection_count는 출력한 섹션 테이블 길이를 항목 크기 40으로 나눈 값입니다.

efi-header.S · L126–L157
126
127	// Section table
128.Lsection_table:
129	.ascii	".text\0\0\0"
130	.long	__initdata_begin - .Lefi_header_end	// VirtualSize
131	.long	.Lefi_header_end - .L_head		// VirtualAddress
132	.long	__initdata_begin - .Lefi_header_end	// SizeOfRawData
133	.long	.Lefi_header_end - .L_head		// PointerToRawData
134
135	.long	0					// PointerToRelocations
136	.long	0					// PointerToLineNumbers
137	.short	0					// NumberOfRelocations
138	.short	0					// NumberOfLineNumbers
139	.long	IMAGE_SCN_CNT_CODE | \
140		IMAGE_SCN_MEM_READ | \
141		IMAGE_SCN_MEM_EXECUTE			// Characteristics
142
143	.ascii	".data\0\0\0"
144	.long	__pecoff_data_size			// VirtualSize
145	.long	__initdata_begin - .L_head		// VirtualAddress
146	.long	__pecoff_data_rawsize			// SizeOfRawData
147	.long	__initdata_begin - .L_head		// PointerToRawData
148
149	.long	0					// PointerToRelocations
150	.long	0					// PointerToLineNumbers
151	.short	0					// NumberOfRelocations
152	.short	0					// NumberOfLineNumbers
153	.long	IMAGE_SCN_CNT_INITIALIZED_DATA | \
154		IMAGE_SCN_MEM_READ | \
155		IMAGE_SCN_MEM_WRITE			// Characteristics
156
157	.set	.Lsection_count, (. - .Lsection_table) / 40
L128
PE 섹션표 시작입니다. 각 섹션 항목은 40바이트이며, 로더에게 파일 범위·메모리 범위와 접근 속성을 알려줍니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L129
첫 PE 섹션 이름을 .text로 기록하고 8바이트 이름 필드의 나머지를 NUL로 채웁니다. .ascii는 문자열 끝에 NUL을 자동으로 붙이지 않으므로, 소스에 쓴 \0만 실제 0바이트로 들어갑니다.
L130
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. PE .text 섹션이 메모리에서 차지할 크기를 헤더 끝부터 __initdata_begin 직전까지로 지정합니다.
L131
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. PE .text 섹션의 이미지 기준 시작 위치를 기록합니다.
L132
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. 파일에서 가져올 .text 데이터 크기를 기록합니다. 이 섹션에서는 위의 메모리 크기와 같은 식을 사용합니다.
L133
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. 파일에서 .text 바이트가 시작하는 위치를 기록합니다. 이 이미지 배치에서는 RVA와 파일 오프셋이 같은 식으로 계산됩니다.
L135
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. 이 PE .text 섹션에는 별도 COFF relocation 목록을 두지 않으므로 그 파일 위치를 0으로 둡니다.
L136
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE .text의 COFF 줄 번호 테이블 위치를 0으로 둡니다. 설명 페이지의 소스 줄 번호와는 무관한 파일 형식 필드입니다.
L137
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. PE .text의 COFF relocation 항목 개수를 0으로 기록합니다.
L138
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. PE .text의 COFF 줄 번호 항목 개수도 0으로 기록합니다.
L139
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. PE .text의 속성에 코드 포함(CNT_CODE) 비트를 넣습니다. 이어지는 두 줄과 OR로 합쳐 하나의 32비트 필드를 만듭니다.
L140
같은 .text 속성에 읽기 허용을 추가합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.
L141
같은 .text 속성에 실행 허용을 추가합니다. 이 조합에는 쓰기 허용 비트가 포함되지 않습니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.
L143
두 번째 PE 섹션 이름을 .data로 기록하고 나머지 이름 필드를 NUL로 채웁니다. .ascii는 문자열 끝에 NUL을 자동으로 붙이지 않으므로, 소스에 쓴 \0만 실제 0바이트로 들어갑니다.
L144
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. PE .data가 메모리에서 차지하는 전체 크기를 기록합니다. 파일에서 읽어 올 데이터보다 클 수 있으므로 SizeOfRawData와 따로 둡니다.
L145
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. __initdata_begin을 .data 섹션의 이미지 기준 시작 위치로 기록합니다.
L146
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. PE .data에 대해 실제 파일에 들어 있는 바이트 수를 기록합니다. 메모리에만 필요한 뒤쪽 공간은 VirtualSize와 구분됩니다.
L147
이 .long은 4바이트 데이터 필드를 만드는 지시문입니다. 식에 필요한 심벌 값은 링크 단계에서 확정되어 이 필드에 들어가며, 부팅 중 CPU가 이 식을 명령으로 계산하지 않습니다. 파일에서 .data를 읽어 올 시작 오프셋을 기록합니다.
L149
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE .data의 별도 COFF relocation 목록 위치를 0으로 둡니다.
L150
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. PE .data의 COFF 줄 번호 테이블 위치도 0으로 둡니다.
L151
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. PE .data의 COFF relocation 항목 개수를 0으로 기록합니다.
L152
이 .short은 실행 명령이 아니라 어셈블 단계에서 상수를 2바이트 정수로 기록하는 지시문입니다. PE .data의 COFF 줄 번호 항목 개수도 0으로 기록합니다.
L153
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. PE .data의 속성에 초기화 데이터 포함 비트를 넣습니다. 뒤의 읽기·쓰기 비트와 합쳐 한 필드를 만듭니다.
L154
PE .data를 읽을 수 있음을 표시합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.
L155
PE .data에 쓰기 허용을 추가합니다. 이 조합에는 실행 허용 비트가 없어 .text와 다른 적재 권한을 표현합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.
L157
섹션표 전체 바이트 수를 항목 크기 40으로 나눠 섹션 개수를 계산합니다. 위에서 .text와 .data 두 항목을 만들었으므로 결과는 2입니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.

부가 데이터와 헤더 끝

DEBUG_EFI이면 NB10 정보와 VMLINUX_PATH를 출력합니다. BTI 설정에서는 확장 특성 값을 추가합니다. SEGMENT_ALIGN까지 정렬한 위치가 .Lefi_header_end입니다. EFI 비활성 경로에서는 PE 헤더 오프셋을 0으로 정의합니다.

efi-header.S · L158–L181
158
159#ifdef CONFIG_DEBUG_EFI
160.Lefi_debug_entry:
161	// EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY
162	.ascii	"NB10"					// Signature
163	.long	0					// Unknown
164	.long	0					// Unknown2
165	.long	0					// Unknown3
166
167	.asciz	VMLINUX_PATH
168
169	.set	.Lefi_debug_entry_size, . - .Lefi_debug_entry
170#endif
171#ifdef CONFIG_ARM64_BTI_KERNEL
172.Lefi_dll_characteristics_ex:
173	.long	IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT
174#endif
175
176	.balign	SEGMENT_ALIGN
177.Lefi_header_end:
178#else
179	.set	.Lpe_header_offset, 0x0
180#endif
181	.endm
L159
DEBUG_EFI가 켜졌을 때 디렉터리가 가리킬 CodeView payload를 만듭니다. 디렉터리 구조 자체와 그 구조가 가리키는 데이터의 위치는 서로 다릅니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
L160
CodeView 디버그 payload의 시작 주소입니다. 앞의 SizeOfData와 FileOffset은 이 위치와 크기를 사용합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L162
CodeView의 NB10 형식을 나타내는 네 문자 서명을 넣습니다. 뒤에 붙는 필드와 파일 경로를 해당 형식의 데이터로 알아보게 합니다. .ascii는 문자열 끝에 NUL을 자동으로 붙이지 않으므로, 소스에 쓴 \0만 실제 0바이트로 들어갑니다.
L163
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. NB10 payload의 이 32비트 필드를 0으로 채웁니다. 원문에서 Unknown으로 표시한 자리이며 vmlinux 경로는 뒤의 ASCIZ에 들어갑니다.
L164
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. NB10 payload의 두 번째 추가 32비트 필드도 0으로 채웁니다.
L165
이 .long은 실행 명령이 아니라 어셈블 단계에서 상수를 4바이트 정수로 기록하는 지시문입니다. NB10 payload의 세 번째 추가 32비트 필드도 0으로 채워 경로 문자열 앞의 고정 크기 영역을 완성합니다.
L167
빌드 시 정해진 vmlinux의 절대 경로를 NUL 종료 문자열로 넣습니다. 디버그 펌웨어가 심볼 파일 위치를 찾도록 제공하는 정보이며 실행 중 커널이 이 경로의 파일을 여는 명령은 아닙니다.
L169
NB10 서명·고정 필드·경로 문자열을 포함한 payload 크기를 계산합니다. 앞의 CodeView 디렉터리 SizeOfData에 들어갈 값입니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.
L170
DEBUG_EFI용 CodeView payload 생성을 마칩니다.
L171
커널 BTI 설정이 켜졌을 때 확장 이미지 특성 payload를 만듭니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
L172
앞의 EX_DLLCHARACTERISTICS 디렉터리가 파일 오프셋으로 가리킬 플래그 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L173
이 .long은 CPU 명령이 아니라 빌드할 때 값을 4바이트 정수로 기록하는 지시문입니다. 어셈블러가 확정할 수 있는 상수·같은 섹션 안의 차이는 어셈블 단계에서 계산합니다. FORWARD_CFI_COMPAT 비트를 기록하여 간접 분기 보호와 호환되는 이미지임을 로더에 알립니다. 여기서는 BTI 지원 빌드가 이 표시를 제공하며, 플래그를 기록하는 것 자체가 CPU의 BTI 검사를 켜는 명령은 아닙니다.
L174
BTI용 확장 특성 payload 생성을 마칩니다.
L176
헤더 끝을 SEGMENT_ALIGN인 64KiB 경계까지 정렬합니다. 앞서 선언한 .text 시작 위치와 섹션 정렬 조건을 실제 바이트 배치로 맞추는 단계입니다. .balign의 인자는 바이트 단위의 정렬 크기이며, 어셈블 단계에서 필요한 패딩을 넣습니다.
L177
PE 헤더와 패딩이 끝나고 코드 영역이 시작하는 위치입니다. 코드 크기·BaseOfCode·SizeOfHeaders 등의 계산이 이 라벨을 기준으로 합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L178
CONFIG_EFI를 끈 이미지의 처리입니다. 위의 PE/COFF 헤더와 선택적 디버그 데이터를 만들지 않습니다.
L179
PE 헤더 오프셋을 0으로 정의하여 이 이미지에 EFI용 PE 헤더가 없음을 나타냅니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.
L180
EFI 지원 여부에 따른 헤더 생성 분기를 마칩니다.
L181
— 매크로 정의를 끝냅니다.

매크로 정의와 확장

각 정의는 v6.18.37 원문입니다. 역슬래시 인자는 사용 위치의 값으로 치환되며, 안쪽 매크로와 조건부 블록은 이후 단계에서 다시 처리됩니다.

IMAGE_FILE_EXECUTABLE_IMAGE

아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.

include/linux/pe.h L105–L105

앞뒤 조건을 포함한 원문

#define IMAGE_FILE_EXECUTABLE_IMAGE		0x0002 /* File is executable (i.e. no unresolved external references) */

IMAGE_FILE_LINE_NUMS_STRIPPED

아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.

include/linux/pe.h L106–L106

앞뒤 조건을 포함한 원문

#define IMAGE_FILE_LINE_NUMS_STRIPPED		0x0004 /* Line nunbers stripped from file */

IMAGE_SCN_MEM_EXECUTE

아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.

include/linux/pe.h L216–L216

앞뒤 조건을 포함한 원문

#define IMAGE_SCN_MEM_EXECUTE	0x20000000 /* can be executed as code */

IMAGE_SCN_MEM_READ

아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.

include/linux/pe.h L217–L217

앞뒤 조건을 포함한 원문

#define IMAGE_SCN_MEM_READ	0x40000000 /* readable */

IMAGE_SCN_MEM_WRITE

아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.

include/linux/pe.h L218–L218

앞뒤 조건을 포함한 원문

#define IMAGE_SCN_MEM_WRITE	0x80000000 /* writeable */

__INITRODATA

출력 섹션을 초기화용 읽기 전용 데이터 .init.rodata로 바꿉니다.

include/linux/init.h L97–L97

앞뒤 조건을 포함한 원문

#define __INITRODATA	.section	".init.rodata","a",%progbits

상수·구조체 오프셋

S_*·THREAD_*·FREGS_* 등의 구조체 오프셋은 빌드한 C 구조체 배치에서 생성됩니다. 숫자를 고정하지 않고 원래 기호를 사용했습니다.

asm-offsets.c의 생성 식

맨 위로 ↑