DUJINLABS.COM

Linux v6.18.37 · ARM64

efi-rt-wrapper.S — EFI 호출용 스택 전환과 예외 복구

__efi_rt_asm_wrapper는 커널 스택을 보존한 뒤 EFI 전용 스택에서 펌웨어 함수를 호출합니다. 정상 복귀 경로와 펌웨어 실행 중 동기 예외가 발생했을 때의 복구 경로가 별도로 있습니다.

역할과 입력

  • x0: EFI 함수 주소 / x1: 오류 처리에 쓸 값
  • x2–x6: EFI 함수에 전달할 최대 다섯 인자

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

87줄 · SHA-256: fab9fb3a9d328033e53a1115d4e07866535ee3b10b4eab91f6da7e64d66e394e

구조와 흐름

EFI 호출용 스택 전환과 예외 복구
화살표: 처리 흐름입니다. 조건이 적힌 단계에서는 해당 조건에 따라 경로가 갈립니다.

EFI 호출 전후의 두 스택

EFI 호출 전후의 두 스택
화살표는 SP의 전환을 뜻합니다. 아래 칸의 숫자는 각 프레임 시작을 기준으로 한 바이트 오프셋입니다.

두 스택의 연결

커널 스택에 112바이트를 확보합니다. [SP+16]에 x1, [SP+24]에 x18, +32부터 x19–x28을 저장합니다. x29는 이 프레임을 가리킵니다. EFI 스택 맨 위의 16바이트에는 x18과 x29를 기록하여 예외 처리에서도 원래 스택을 찾을 수 있게 합니다.

efi-rt-wrapper.S · L1–L35
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Copyright (C) 2018 Linaro Ltd <[email protected]>
4 */
5
6#include <linux/linkage.h>
7#include <asm/assembler.h>
8
9SYM_FUNC_START(__efi_rt_asm_wrapper)
10	stp	x29, x30, [sp, #-112]!
11	mov	x29, sp
12
13	/*
14	 * Register x18 is designated as the 'platform' register by the AAPCS,
15	 * which means firmware running at the same exception level as the OS
16	 * (such as UEFI) should never touch it.
17	 */
18	stp	x1, x18, [sp, #16]
19
20	/*
21	 * Preserve all callee saved registers and preserve the stack pointer
22	 * value at the base of the EFI runtime stack so we can recover from
23	 * synchronous exceptions occurring while executing the firmware
24	 * routines.
25	 */
26	stp	x19, x20, [sp, #32]
27	stp	x21, x22, [sp, #48]
28	stp	x23, x24, [sp, #64]
29	stp	x25, x26, [sp, #80]
30	stp	x27, x28, [sp, #96]
31
32	ldr_l	x16, efi_rt_stack_top
33	mov	sp, x16
34	stp	x18, x29, [sp, #-16]!
35
L6
C 호출용 EFI 래퍼와 예외 복구 코드의 시작·끝을 선언할 linkage 매크로를 가져옵니다. 두 경로의 진입 방식이 달라 FUNC와 CODE 선언을 구분합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L7
전역 변수 값을 PC 상대 방식으로 읽는 ldr_l 등 ARM64 공통 매크로를 가져옵니다. 이 파일은 EFI 스택 상단 변수의 값을 찾을 때 이를 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L9
커널에서 UEFI 런타임 서비스를 호출할 때 스택과 보존 레지스터를 관리하는 진입점입니다. x0는 펌웨어 함수 주소, x1은 오류 메시지에 쓸 함수 이름이며 x2~x6이 실제 서비스 인자입니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
L10
112바이트의 커널 스택 프레임을 만들고 x29·LR을 저장합니다. 나머지 공간에는 함수 이름, x18, x19~x28을 보관하여 펌웨어 예외가 발생해도 커널 호출자로 복귀할 수 있게 합니다. 메모리에서는 sp + (-112) 위치가 첫 레지스터 x29에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x30에 대응합니다. 대괄호 뒤의 !는 주소 갱신을 뜻합니다. sp에 오프셋 -112을 더한 주소로 접근하고 그 주소를 sp에도 남깁니다.
L11
새 커널 스택 프레임 주소를 x29에 남깁니다. 잠시 EFI 전용 스택으로 바꿨다가 돌아올 때 이 값으로 원래 스택을 찾습니다. x29에 담기는 것은 SP의 주소값이며, SP와 스택 메모리의 내용은 그대로입니다.
L18
오류를 알릴 때 사용할 함수 이름 x1과 호출 전 x18을 보관합니다. x18은 플랫폼 전용 레지스터이므로 펌웨어가 바꾸면 안 되며, 호출 뒤 비교할 기준이 됩니다. 메모리에서는 sp + (16) 위치가 첫 레지스터 x1에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x18에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L26
커널의 x19·x20를 저장 프레임 +32에 보관합니다. 정상적인 펌웨어는 이를 보존하지만, 실행 도중 예외가 나면 정상 복귀 절차를 건너뛰므로 별도 복구 경로가 이 저장본을 사용합니다. 메모리에서는 sp + (32) 위치가 첫 레지스터 x19에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x20에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L27
커널의 x21·x22를 저장 프레임 +48에 보관합니다. 정상적인 펌웨어는 이를 보존하지만, 실행 도중 예외가 나면 정상 복귀 절차를 건너뛰므로 별도 복구 경로가 이 저장본을 사용합니다. 메모리에서는 sp + (48) 위치가 첫 레지스터 x21에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x22에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L28
커널의 x23·x24를 저장 프레임 +64에 보관합니다. 정상적인 펌웨어는 이를 보존하지만, 실행 도중 예외가 나면 정상 복귀 절차를 건너뛰므로 별도 복구 경로가 이 저장본을 사용합니다. 메모리에서는 sp + (64) 위치가 첫 레지스터 x23에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x24에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L29
커널의 x25·x26를 저장 프레임 +80에 보관합니다. 정상적인 펌웨어는 이를 보존하지만, 실행 도중 예외가 나면 정상 복귀 절차를 건너뛰므로 별도 복구 경로가 이 저장본을 사용합니다. 메모리에서는 sp + (80) 위치가 첫 레지스터 x25에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x26에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L30
커널의 x27·x28를 저장 프레임 +96에 보관합니다. 정상적인 펌웨어는 이를 보존하지만, 실행 도중 예외가 나면 정상 복귀 절차를 건너뛰므로 별도 복구 경로가 이 저장본을 사용합니다. 메모리에서는 sp + (96) 위치가 첫 레지스터 x27에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x28에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L32
부팅 때 할당한 EFI 런타임 전용 스택의 최상단 주소를 읽습니다. 펌웨어의 스택 사용을 현재 태스크의 일반 커널 스택에서 분리합니다. ldr_l은 ADRP로 심벌이 속한 페이지를 구한 뒤 LDR로 그 위치의 값을 읽는 매크로입니다. 주소를 구하는 adr_l과 달리 실제 메모리 접근이 있습니다.
L33
SP를 EFI 전용 스택으로 바꿉니다. 이후 펌웨어 함수와 그 내부 호출은 이 공간에 프레임을 만듭니다. 바뀌는 것은 SP가 담은 주소입니다. 원래 스택의 데이터가 새 주소로 복사되는 것은 아닙니다.
L34
EFI 스택 상단에 원래 x18과 커널 프레임 주소 x29를 기록합니다. 예외 처리 코드는 이 두 값을 통해 원래 태스크 스택과 shadow call stack 상태를 되찾습니다. 메모리에서는 sp + (-16) 위치가 첫 레지스터 x18에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x29에 대응합니다. 대괄호 뒤의 !는 주소 갱신을 뜻합니다. sp에 오프셋 -16을 더한 주소로 접근하고 그 주소를 sp에도 남깁니다.

정상 호출과 복귀

함수 주소는 x8에 옮기고 인자는 x0–x4로 재배치합니다. BLR 뒤에는 x29로 커널 SP를 복원하고 EFI 스택의 기록을 지웁니다. x0의 EFI 반환값은 유지합니다. 저장한 x18과 현재 x18을 비교하며 정상 경로에서는 펌웨어가 보존해야 하는 x19–x28을 다시 읽지 않습니다.

efi-rt-wrapper.S · L36–L57
36	/*
37	 * We are lucky enough that no EFI runtime services take more than
38	 * 5 arguments, so all are passed in registers rather than via the
39	 * stack.
40	 */
41	mov	x8, x0
42	mov	x0, x2
43	mov	x1, x3
44	mov	x2, x4
45	mov	x3, x5
46	mov	x4, x6
47	blr	x8
48
49	mov	x16, sp
50	mov	sp, x29
51	str	xzr, [x16, #8]			// clear recorded task SP value
52
53	ldp	x1, x2, [sp, #16]
54	cmp	x2, x18
55	ldp	x29, x30, [sp], #112
56	b.ne	0f
57	ret
L41
펌웨어 함수 주소를 x8에 보관합니다. 곧 x0를 첫 번째 서비스 인자로 바꿔야 하므로 분기 주소를 미리 옮겨 둡니다. MOV는 x8의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L42
래퍼의 x2를 펌웨어의 첫 인자 x0로 옮깁니다. 함수 포인터와 이름이라는 래퍼 전용 인자 두 개를 제외한 실제 인자를 전달합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L43
래퍼의 x3를 펌웨어의 두 번째 인자 x1로 옮깁니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L44
래퍼의 x4를 펌웨어의 세 번째 인자 x2로 옮깁니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L45
래퍼의 x5를 펌웨어의 네 번째 인자 x3로 옮깁니다. MOV는 x3의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L46
래퍼의 x6를 펌웨어의 다섯 번째 인자 x4로 옮깁니다. 이 구현이 다루는 런타임 서비스는 인자가 다섯 개 이하라 추가 스택 인자 이동이 필요하지 않습니다. MOV는 x4의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L47
x8의 UEFI 런타임 서비스를 EFI 전용 스택에서 호출합니다. 서비스의 efi_status_t 결과는 x0로 돌아오며, 래퍼는 이후 검사에서도 이 값을 보존합니다. BLR은 다음 명령의 주소를 LR(x30)에 기록하고 레지스터에 든 목적지로 분기합니다.
L49
돌아온 EFI 스택 주소를 x16에 남깁니다. 원래 태스크 스택으로 돌아간 뒤 EFI 스택에 기록한 복구용 포인터를 지우기 위해 사용합니다. x16에 담기는 것은 SP의 주소값이며, SP와 스택 메모리의 내용은 그대로입니다.
L50
x29에 보관했던 커널 프레임 주소로 SP를 복원합니다. 이후 프레임 해제와 함수 이름 읽기는 원래 태스크 스택에서 수행합니다. 바뀌는 것은 SP가 담은 주소입니다. 원래 스택의 데이터가 새 주소로 복사되는 것은 아닙니다.
L51
EFI 스택에 남긴 태스크 SP 값을 0으로 지웁니다. 현재 태스크가 아직 EFI 펌웨어를 실행 중이라는 잘못된 판정을 막기 위한 정리입니다. 실제로 x16 + (8) 주소의 메모리 8바이트를 xzr의 값으로 씁니다. 이 주소 형식은 기준 레지스터 x16 자체를 갱신하지 않습니다.
L53
원래 함수 이름을 x1에, 호출 전 x18을 x2에 읽습니다. x0에는 펌웨어 결과를 그대로 남겨 오류를 보고하더라도 반환할 수 있게 합니다. 메모리에서는 sp + (16) 위치가 첫 레지스터 x1에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x2에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L54
호출 전 x18과 펌웨어에서 돌아온 x18을 비교합니다. 플랫폼 레지스터 보존 규약을 어긴 펌웨어인지 확인합니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
L55
호출자의 프레임 포인터·LR을 복원하고 112바이트 스택 프레임을 해제합니다. 정상 반환과 오류 보고 양쪽이 같은 호출자에게 돌아갈 준비를 합니다. 메모리에서는 sp 위치가 첫 레지스터 x29에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x30에 대응합니다. 대괄호 밖의 오프셋 112은 후위 갱신입니다. 기존 sp 주소로 접근한 뒤 sp에 이 값을 더합니다.
L56
x18이 바뀌었다면 복원·오류 보고 경로로 갑니다. 같으면 바로 펌웨어 결과 x0를 반환합니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
L57
펌웨어의 반환 상태 x0를 바꾸지 않고 커널 호출자에게 돌려줍니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.

x18이 바뀐 경우

SHADOW_CALL_STACK 설정에서는 x18이 별도 반환 주소 스택을 가리킵니다. EFI 스택에 저장한 원래 값을 복구한 뒤 efi_handle_corrupted_x18로 tail call합니다. 이 B 명령은 LR을 바꾸지 않습니다.

efi-rt-wrapper.S · L58–L72
580:
59	/*
60	 * With CONFIG_SHADOW_CALL_STACK, the kernel uses x18 to store a
61	 * shadow stack pointer, which we need to restore before returning to
62	 * potentially instrumented code. This is safe because the wrapper is
63	 * called with preemption disabled and a separate shadow stack is used
64	 * for interrupts.
65	 */
66#ifdef CONFIG_SHADOW_CALL_STACK
67	ldr_l	x18, efi_rt_stack_top
68	ldr	x18, [x18, #-16]
69#endif
70
71	b	efi_handle_corrupted_x18	// tail call
72SYM_FUNC_END(__efi_rt_asm_wrapper)
L58
펌웨어가 x18을 손상시킨 경우 들어오는 오류 보고 경로의 시작입니다. shadow call stack을 사용하는 빌드는 먼저 x18을 복구합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L66
Shadow Call Stack은 함수의 복귀 주소를 일반 스택과 별도 공간에도 보관하여 덮어쓰기 공격에 대비하는 기능입니다. ARM64에서는 x18이 그 별도 스택의 포인터이므로, 이 설정이 켜졌다면 C 오류 보고 코드로 가기 전에 손상된 x18부터 복구해야 합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
L67
x18이 손상된 경로에서 EFI 전용 스택 상단 주소를 다시 읽습니다. 기존 x18 값은 믿을 수 없으므로 저장본을 찾을 주소부터 새로 구합니다. ldr_l은 ADRP로 심벌이 속한 페이지를 구한 뒤 LDR로 그 위치의 값을 읽는 매크로입니다. 주소를 구하는 adr_l과 달리 실제 메모리 접근이 있습니다.
L68
EFI 스택 상단의 16바이트 아래에 저장한 원래 x18을 되찾습니다. 이후 계측된 C 함수가 올바른 shadow call stack에 복귀 주소를 기록하도록 합니다.
L69
Shadow Call Stack용 포인터 복구는 여기까지입니다. 설정과 관계없이 x18 보존 규약 위반에 대한 오류 보고는 이어서 수행합니다.
L71
x0의 펌웨어 상태와 x1의 함수 이름으로 efi_handle_corrupted_x18을 꼬리 호출합니다. 이 함수는 펌웨어가 x18을 손상시켰다는 메시지를 남기고 원래 상태값을 그대로 반환합니다. B를 사용하므로 래퍼로 재복귀하지 않습니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
L72
(__efi_rt_asm_wrapper)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.

동기 예외 복구

efi.c의 efi_runtime_fixup_exception은 저장된 레지스터의 x0를 EFI_ABORTED, x30을 커널 프레임 주소, PC를 __efi_rt_asm_recover로 바꿉니다. 여기서 x30은 복귀 코드 주소가 아니라 SP 복구용 주소입니다. x19–x28과 FP/LR을 읽고 원래 호출자에게 반환합니다.

efi-rt-wrapper.S · L73–L87
73
74SYM_CODE_START(__efi_rt_asm_recover)
75	mov	sp, x30
76
77	ldr_l	x16, efi_rt_stack_top		// clear recorded task SP value
78	str	xzr, [x16, #-8]
79
80	ldp	x19, x20, [sp, #32]
81	ldp	x21, x22, [sp, #48]
82	ldp	x23, x24, [sp, #64]
83	ldp	x25, x26, [sp, #80]
84	ldp	x27, x28, [sp, #96]
85	ldp	x29, x30, [sp], #112
86	ret
87SYM_CODE_END(__efi_rt_asm_recover)
L74
펌웨어 실행 중 동기 예외를 커널이 복구하기로 결정했을 때 들어오는 별도 경로입니다. efi_runtime_fixup_exception이 PC를 여기로 바꾸고 x0에 EFI_ABORTED, x30에 저장된 태스크 SP를 마련합니다. 이 매크로는 정렬된 코드 시작 심벌을 정의하고 다른 오브젝트에서도 참조할 수 있게 합니다.
L75
여기서 x30은 보통의 복귀 주소가 아니라 예외 처리 코드가 넣어 준 태스크 스택 프레임 주소입니다. 이를 SP에 넣어 펌웨어 스택을 벗어납니다. 바뀌는 것은 SP가 담은 주소입니다. 원래 스택의 데이터가 새 주소로 복사되는 것은 아닙니다.
L77
EFI 런타임 스택 상단을 다시 찾아, 이제 펌웨어 실행을 벗어났다는 표시를 정리할 준비를 합니다. ldr_l은 ADRP로 심벌이 속한 페이지를 구한 뒤 LDR로 그 위치의 값을 읽는 매크로입니다. 주소를 구하는 adr_l과 달리 실제 메모리 접근이 있습니다.
L78
EFI 스택 끝의 복구용 태스크 SP 기록을 0으로 지웁니다. 정상 반환 경로와 동일하게 EFI 실행 중 상태를 해제합니다. 실제로 x16 + (-8) 주소의 메모리 8바이트를 xzr의 값으로 씁니다. 이 주소 형식은 기준 레지스터 x16 자체를 갱신하지 않습니다.
L80
정상적인 펌웨어 복귀를 거치지 못한 경로이므로, 호출 전에 프레임 +32에 보관한 x19·x20를 직접 되살립니다. 커널 호출자가 기대하는 callee-saved 상태를 복원하는 단계입니다. 메모리에서는 sp + (32) 위치가 첫 레지스터 x19에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x20에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L81
정상적인 펌웨어 복귀를 거치지 못한 경로이므로, 호출 전에 프레임 +48에 보관한 x21·x22를 직접 되살립니다. 커널 호출자가 기대하는 callee-saved 상태를 복원하는 단계입니다. 메모리에서는 sp + (48) 위치가 첫 레지스터 x21에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x22에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L82
정상적인 펌웨어 복귀를 거치지 못한 경로이므로, 호출 전에 프레임 +64에 보관한 x23·x24를 직접 되살립니다. 커널 호출자가 기대하는 callee-saved 상태를 복원하는 단계입니다. 메모리에서는 sp + (64) 위치가 첫 레지스터 x23에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x24에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L83
정상적인 펌웨어 복귀를 거치지 못한 경로이므로, 호출 전에 프레임 +80에 보관한 x25·x26를 직접 되살립니다. 커널 호출자가 기대하는 callee-saved 상태를 복원하는 단계입니다. 메모리에서는 sp + (80) 위치가 첫 레지스터 x25에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x26에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L84
정상적인 펌웨어 복귀를 거치지 못한 경로이므로, 호출 전에 프레임 +96에 보관한 x27·x28를 직접 되살립니다. 커널 호출자가 기대하는 callee-saved 상태를 복원하는 단계입니다. 메모리에서는 sp + (96) 위치가 첫 레지스터 x27에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x28에 대응합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L85
예외 전 커널 호출자의 프레임 포인터와 LR을 복원하고 전체 저장 프레임을 해제합니다. EFI 내부의 복귀 주소로 돌아가는 것이 아닙니다. 메모리에서는 sp 위치가 첫 레지스터 x29에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x30에 대응합니다. 대괄호 밖의 오프셋 112은 후위 갱신입니다. 기존 sp 주소로 접근한 뒤 sp에 이 값을 더합니다.
L86
예외 처리 코드가 준비한 EFI_ABORTED를 x0에 둔 채 원래 커널 호출자로 돌아갑니다. 커널의 C 예외 처리 쪽에서는 이후 EFI 런타임 서비스 사용도 비활성화합니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
L87
(__efi_rt_asm_recover)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.

매크로 정의와 확장

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

SYM_CODE_END

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

include/linux/linkage.h L305–L306

앞뒤 조건을 포함한 원문

#define SYM_CODE_END(name)				\
	SYM_END(name, SYM_T_NONE)

SYM_CODE_START

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

include/linux/linkage.h L278–L279

앞뒤 조건을 포함한 원문

#define SYM_CODE_START(name)				\
	SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)

SYM_FUNC_END

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

include/linux/linkage.h L248–L249

앞뒤 조건을 포함한 원문

#define SYM_FUNC_END(name)				\
	SYM_END(name, SYM_T_FUNC)

SYM_FUNC_START

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

arch/arm64/include/asm/linkage.h L18–L20

앞뒤 조건을 포함한 원문

#define SYM_FUNC_START(name)				\
	SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)	\
	bti c ;
include/linux/linkage.h L209–L210

앞뒤 조건을 포함한 원문

#define SYM_FUNC_START(name)				\
	SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)

상수·구조체 오프셋

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

asm-offsets.c의 생성 식

    맨 위로 ↑