DUJINLABS.COM

Linux v6.18.37 · ARM64

vdso-wrap.S — 말로 풀어 읽기

이 파일은 왜 필요한가요?

사용자 프로그램이 일부 커널 제공 기능을 매번 예외 진입 없이 호출할 수 있도록 vDSO라는 작은 ELF 이미지를 사용합니다. 이 파일은 그 기능을 구현하는 본체가 아니라, 별도로 빌드된 vDSO 파일을 커널 이미지에 넣는 포장 부분입니다.

명령·지시문·라벨을 원본 줄 번호에 맞춰 설명합니다. 주석과 빈 줄을 포함한 전체 원문은 아래에 따로 두었습니다. 매크로 정의와 조건부 코드는 모두 차례대로 실행되는 명령 목록이 아닙니다.

기존 코드 분석과 함께 읽기 · 원본 파일 · 설명 원고

먼저 알아둘 개념: 전처리·어셈블·링크·실행, 어셈블러 지시문, 정렬과 경계, 주소와 주소가 가리키는 내용

처음 읽을 때

파일을 포함하는 .incbin과 함수를 호출하는 BL은 전혀 다른 단계에서 동작합니다. 이 파일에서는 CPU 실행 순서보다 최종 이미지에 어떤 바이트가 들어가는지 봅니다.

더 깊이 살펴볼 때

페이지 정렬, 시작·끝 심볼, 포함된 ELF의 사용자 매핑 과정을 연결해 보세요. wrapper만으로 개별 vDSO 함수 구현까지 설명되지는 않습니다.

한 줄씩 레지스터와 메모리 상태 보기

각 코드 아래의 ‘행 그림’을 펼치면, 그 줄에서 읽는 값과 실행 전·후의 상태를 나란히 볼 수 있습니다. 여기서 ‘명령 직전’은 지금 보고 있는 명령이 실행되기 바로 전을 뜻합니다. 함수 진입 때나 소스의 바로 윗줄을 가리키는 말은 아닙니다. 앞서 실행한 명령이 값을 바꿨다면 그 결과가 들어 있습니다.

소스로 정해지는 상수, 호출자가 넘기는 값, 실제 실행에서 정해지는 주소를 구분해서 읽어야 합니다. 주소 숫자를 몰라도 무엇을 가리키는지와 어디서 받아 오는지는 설명할 수 있습니다. ‘명령 직전 x0의 값’처럼 일반 표기로 남은 곳은 한 명령의 동작을 나타내며, 특정 호출 경로의 값까지 확인했다는 뜻은 아닙니다.

일반 명령은 그 한 줄의 효과를, 매크로는 여러 명령을 묶은 주요 결과를 표시합니다. 함수 호출은 호출 직후의 PC와 LR까지만 나타냅니다. 메모리 접근은 정상적으로 끝나는 경우를 가정합니다. 생략된 항목까지 모두 보존된다는 뜻은 아닙니다.

화살표 →는 같은 항목의 전후 비교입니다. 메모리 칸에는 주소와 크기를 따로 적었습니다. SP가 줄어들면 낮은 주소 쪽을 사용하며, 화면의 위아래와 메모리 주소 방향은 별개입니다. 전처리·어셈블·링크 지시문은 CPU 실행과 구분합니다.

‘이전·다음 행’은 소스에 적힌 순서입니다. 분기·반복·호출이 있으므로 파일 위에서 아래로 실제 실행된다고 가정하지 않습니다. 값이 여러 줄에 걸쳐 이어지는 모습은 cpu-reset.S의 단계별 예제에서 보실 수 있습니다.

총 14개 줄에 그림을 붙였습니다. 기존 코드와 설명은 그 아래에서 이어집니다.

먼저 그림으로 살펴보기

64비트 vDSO 바이너리 포함
화살표: 처리 흐름입니다. 조건이 적힌 단계에서는 해당 조건에 따라 경로가 갈립니다.

섹션·정렬·범위 심볼

원본 1–24행

별도 vDSO 파일을 커널 이미지에 넣습니다

별도 vDSO 파일을 커널 이미지에 넣습니다의 세 단계 비교
화살표는 빌드 시 바이트 포함 또는 사용자 매핑 관계입니다. 세 단계가 이 .S 파일의 CPU 실행 순서는 아닙니다.
그림의 각 단계를 글로 읽기

1. vDSO 별도 생성

vDSO는 사용자 모드에서 실행할 코드와 정보를 담습니다.

2. 래퍼에서 포함

이 파일의 .incbin이 실행 중 다른 ELF를 호출하는 명령은 아닙니다.

3. 사용자에게 매핑

사용자 매핑은 이 래퍼만으로 끝나지 않습니다. 관련 C 코드가 연결합니다.

#include <linux/init.h>

부팅 때만 사용할 코드·데이터의 섹션 표기를 가져옵니다. 초기화가 끝난 뒤 해제 가능한 영역과 계속 남겨야 하는 영역을 링커가 구분하게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

8행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <linux/init.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <linux/linkage.h>

SYM_FUNC_START·SYM_CODE_START·SYM_INNER_LABEL 등의 심볼 선언 규칙을 가져옵니다. 함수인지 코드 내부 라벨인지, 다른 오브젝트에 보일 이름인지와 정렬·크기 정보를 빌드 도구에 전달하는 데 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

9행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <linux/linkage.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <linux/const.h>

C와 어셈블리 양쪽에서 쓸 정수 상수 표현을 가져옵니다. C용 형 접미사가 어셈블리 식에 그대로 들어가지 않게 하여 PAGE_SIZE 같은 공통 상수를 사용할 수 있게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

10행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <linux/const.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/assembler.h>

ARM64의 큰 주소 구성, CPU별 값 조회, 시스템 레지스터 처리, 동기화 등에 쓰는 어셈블리 매크로를 가져옵니다. 각 사용 위치에서 일반 명령 여러 개로 펼쳐지는 작업입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

11행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/assembler.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/page.h>

빌드가 선택한 페이지 크기와 정렬 단위를 가져옵니다. 4KiB를 항상 가정하지 않고 선택된 PAGE_SIZE로 표와 이미지 경계를 맞추는 데 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

12행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/page.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.globl vdso_start, vdso_end

AArch64 vDSO 원본의 시작·끝 기호를 다른 오브젝트에서 참조하도록 공개합니다. vdso.c가 이 기호로 원본 ELF를 찾고 페이지 수를 계산합니다.

14행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.globl vdso_start, vdso_end
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.section .rodata

AArch64 vDSO 원본 바이트를 커널 읽기 전용 데이터 영역에 넣습니다. 사용자에게 실행 가능하게 연결하는 권한과 주소는 vdso.c의 별도 매핑 경로에서 정합니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.

15행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.section .rodata
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.balign PAGE_SIZE

vDSO 이미지의 경계를 PAGE_SIZE에 맞춥니다. 커널이 이 원본을 페이지 단위로 사용자 주소 공간에 연결할 수 있게 하려는 정렬이며, 명령어 한 개의 길이만 맞추는 작업이 아닙니다. .balign의 인자는 바이트 단위의 정렬 크기이며, 어셈블 단계에서 필요한 패딩을 넣습니다.

16행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.balign PAGE_SIZE
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

vdso_start:

AArch64 vDSO ELF 원본 바이트의 시작 주소입니다. 사용자 프로세스에 vDSO 페이지를 매핑할 코드가 이 시작과 끝 기호로 원본 범위를 구합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

17행 그림 · 위치에 이름 붙이기
레이블
처리 전현재 배치 위치
이 줄의 효과vdso_start
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.incbin "arch/arm64/kernel/vdso/vdso.so"

별도로 빌드한 AArch64 프로그램용 vDSO ELF의 바이트를 커널 읽기 전용 영역에 포함합니다. vDSO는 시각 조회 같은 일부 서비스를 매번 시스템 호출로 진입하지 않고 사용자 공간에서 수행하게 하는 작은 공유 라이브러리입니다. 이 줄은 실행이나 사용자 매핑 자체가 아니라, 뒤의 매핑 코드가 사용할 원본 이미지를 담는 단계입니다.

18행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.incbin "arch/arm64/kernel/vdso/vdso.so"
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.balign PAGE_SIZE

vDSO 이미지의 경계를 PAGE_SIZE에 맞춥니다. 커널이 이 원본을 페이지 단위로 사용자 주소 공간에 연결할 수 있게 하려는 정렬이며, 명령어 한 개의 길이만 맞추는 작업이 아닙니다. .balign의 인자는 바이트 단위의 정렬 크기이며, 어셈블 단계에서 필요한 패딩을 넣습니다.

19행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.balign PAGE_SIZE
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

vdso_end:

페이지 경계까지 정렬한 AArch64 vDSO 원본의 끝입니다. 시작 주소와의 차이로 매핑할 이미지 크기를 계산할 수 있습니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

20행 그림 · 위치에 이름 붙이기
레이블
처리 전현재 배치 위치
이 줄의 효과vdso_end
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.previous

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다.

22행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.previous
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

emit_aarch64_feature_1_and

이 어셈블리 오브젝트에 AArch64 GNU property note를 출력하여 BTI/PAC 등 빌드가 선언한 제어 흐름 보호 속성을 링커에 전달합니다. 방금 포함한 vDSO ELF를 실행하거나 그 내부 명령을 수정하는 작업은 아닙니다.

24행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과emit_aarch64_feature_1_and
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

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

start와 end는 함수인가요?

이름을 붙인 바이트 범위의 경계입니다. C 코드가 포함된 이미지의 위치와 크기를 계산할 때 사용합니다.

end−start는 원래 파일 크기와 항상 같은가요?

끝에도 페이지 정렬이 있어 패딩이 포함될 수 있습니다.

여기서 vdso.so를 실행하나요?

빌드 시 바이트를 포함할 뿐입니다. 사용자 공간에 매핑하고 호출하는 과정은 별도 코드가 맡습니다.

주석을 포함한 전체 원문

Linux stable v6.18.37 · arch/arm64/kernel/vdso-wrap.S

1/* SPDX-License-Identifier: GPL-2.0-only */2/*3 * Copyright (C) 2012 ARM Limited4 *5 * Author: Will Deacon <[email protected]>6 */78#include <linux/init.h>9#include <linux/linkage.h>10#include <linux/const.h>11#include <asm/assembler.h>12#include <asm/page.h>1314	.globl vdso_start, vdso_end15	.section .rodata16	.balign PAGE_SIZE17vdso_start:18	.incbin "arch/arm64/kernel/vdso/vdso.so"19	.balign PAGE_SIZE20vdso_end:2122	.previous2324emit_aarch64_feature_1_and
맨 위로 ↑