Linux v6.18.37 · 개념과 코드 읽기

Kernel module relocation과 instruction range

이 코드는 어떤 문제를 푸나요?

모듈을 빌드할 때 아직 정해지지 않은 주소는 적재 시점에 채웁니다. arm64·x86·RISC-V 모두 ELF 재배치를 쓰지만 명령어의 즉시값 위치와 표현 범위가 다릅니다. 대표로 arm64가 절대값, 위치 상대값, 4KiB 페이지 상대값을 계산하는 작은 함수를 읽습니다.

읽을 범위: v6.18.37 · arch/arm64/kernel/module.c · do_reloc 35–50행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

S+A와 P

val은 상위 경로에서 준비한 기호 값과 가산값이며 place는 값을 채울 위치 P입니다. 위치 상대 재배치는 둘의 차이를 사용합니다.

페이지 상대값

AArch64 ADRP 계열이 쓰는 4KiB 기준의 주소 차이입니다. 커널의 PAGE_SIZE가 반드시 4KiB여야 한다는 뜻이 아닙니다.

범위 검사

계산값을 얻는 것과 명령어의 비트 필드에 넣을 수 있는지 확인하는 것은 별개 단계입니다.

처음 읽을 때

예제로 S+A=0x5008, P=0x3004를 놓고 절대값과 차이를 계산해 보십시오. 이 값은 설명용 주소입니다.

더 깊이 살펴볼 때

부호 범위, 명령어 필드 분할, 원거리 분기를 위한 PLT가 어디서 처리되는지 연결해 보십시오.

그림으로 보는 변화

Kernel module relocation과 instruction range의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 재배치 종류 확인

ELF 재배치가 요구한 계산 종류를 선택합니다.

화살표는 계산 분기이며 런타임 함수 호출 이동이 아닙니다.

2. 주소 또는 차이 계산

절대값, P와의 차이, 4KiB 경계의 차이를 계산합니다.

주소 마스크는 데이터 복사가 아니라 수치 변환입니다.

3. 상위 처리로 전달

계산값을 돌려주면 호출자가 범위와 인코딩을 처리합니다.

이 함수의 반환만으로 명령 패치가 완료되지는 않습니다.

do_reloc를 한 줄씩 읽기

줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.

static u64 do_reloc(enum aarch64_reloc_op reloc_op, __le32 *place, u64 val)
{
	switch (reloc_op) {
	case RELOC_OP_ABS:
		return val;
	case RELOC_OP_PREL:
		return val - (u64)place;
	case RELOC_OP_PAGE:
		return (val & ~0xfff) - ((u64)place & ~0xfff);
	case RELOC_OP_NONE:
		return 0;
	}

	pr_err("do_reloc: unknown relocation operation %d\n", reloc_op);
	return 0;
}
static u64 do_reloc(enum aarch64_reloc_op reloc_op, __le32 *place, u64 val)

재배치 연산 종류, 패치 위치 place, 상위에서 준비한 기호 값 val을 받아 계산 결과를 반환합니다. __le32 포인터 타입은 명령어 저장 형식을 드러내지만 이 함수에서는 직접 쓰지 않습니다.

	switch (reloc_op) {

재배치가 요구한 계산 방식을 분기합니다.

	case RELOC_OP_ABS:

절대 주소 연산의 경우입니다.

		return val;

절대형은 전달받은 기호 값과 가산값의 결과 val을 그대로 사용합니다.

	case RELOC_OP_PREL:

패치 위치를 기준으로 하는 상대 주소 연산의 경우입니다.

		return val - (u64)place;

목적값에서 현재 패치 위치 P를 빼 상대 거리를 구합니다. place의 포인터 숫자를 u64로 바꾸어 계산합니다.

	case RELOC_OP_PAGE:

AArch64의 4KiB 페이지 상대 연산의 경우입니다.

		return (val & ~0xfff) - ((u64)place & ~0xfff);

목적지와 현재 위치에서 각각 하위 12비트를 지우고 두 경계 주소의 차이를 구합니다. Linux PAGE_SIZE와 ADRP의 4KiB 단위를 혼동하지 않습니다.

	case RELOC_OP_NONE:

아무 계산을 요구하지 않는 종류의 경우입니다.

	pr_err("do_reloc: unknown relocation operation %d\n", reloc_op);

알 수 없는 연산 값이 들어오면 진단 메시지를 남깁니다.

	return 0;

알 수 없는 경우도 이 helper는 0으로 반환합니다. 전체 로더가 재배치 형식과 범위를 검증하는 주변 경로까지 함께 봐야 합니다.

함께 생각해 볼 질문

do_reloc가 메모리에 값을 씁니까?

아닙니다. 이 함수는 값을 계산해 반환합니다.

0xfff를 지우는 이유는 무엇입니까?

AArch64 페이지 상대 주소 계산에서 하위 12비트를 제거해 4KiB 경계로 맞추기 때문입니다.

같은 ELF 재배치 번호를 세 ISA에 적용합니까?

아닙니다. 아키텍처별 ABI가 번호와 인코딩을 정합니다.

출처와 읽은 범위

Linux stable v6.18.37 · arch/arm64/kernel/module.c

해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고

맨 위로 ↑