Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

3440
557
422296


[Linux][Kernel] gcc - asmlinkage란 부록

커널 소스를 보다보면 asmlinkage로 선언된 함수들을 볼 수 있습니다.

대표적으로 시스템 콜 핸들러를 들 수 있으며 다음 해더 파일에 정의되어 있습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/syscalls.h]
asmlinkage long sys_fork(void);
asmlinkage long sys_exit(int error_code);
asmlinkage long sys_read(unsigned int fd, char __user *buf, size_t count);
asmlinkage long sys_write(unsigned int fd, const char __user *buf,
  size_t count);

asmlinkage는 어셈블리 코드에서 직접 호출(링크)할 수 있다는 의미며 다음과 같이 커널 소스의 <include/linux/linkage.h>에 다음과 같이 정의되어 있습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/linkage.h]
#include <linux/compiler_types.h>
#include <linux/stringify.h>
#include <linux/export.h>
#include <asm/linkage.h>

#ifdef __cplusplus
#define CPP_ASMLINKAGE extern "C"
#else
#define CPP_ASMLINKAGE
#endif

#ifndef asmlinkage
#define asmlinkage CPP_ASMLINKAGE
#endif

어셈블리 코드에서 어떤 함수를 직접 호출할 수 있다는 것은 무엇을 의미할까요?
일반적으로 C 함수는 어셈블리 코드에서 별 어려움없이 호출할 수 있지만 함수의 인자를 넘기거나 리턴값을 받는 부분 등의 호출 규약(ARM Call Convention)이 필요합니다.

다음 코드에서 ARM 프로세스에서 sys_write() 함수에서 전달되는 인자를 확인합시다.
1 NSR:8028459C|E1A0C00D  sys_write:cpy     r12,r13
2 NSR:802845A0|E92DDBF0            push    {r4-r9,r11-r12,r14,pc}
3 NSR:802845A4|E24CB004            sub     r11,r12,#0x4     ; r11,r12,#4
4 NSR:802845A8|E24DD008            sub     r13,r13,#0x8     ; r13,r13,#8
5 NSR:802845AC|E52DE004            str     r14,[r13,#-0x4]!
6 NSR:802845B0|EBFA28DA            bl      0x8010E920       ; __gnu_mcount_nc
7 NSR:802845B4|E1A09001            cpy     r9,r1            ; r9,buf
8 NSR:802845B8|E1A08002            cpy     r8,r2            ; r8,count

7~8번째 줄 코드를 보면 r1와 r2 레지스터로 전달된 인자를 r9와 r8에 전달함을 알 수 있습니다.

하지만 x86 아키텍처에서는 때에 따라 레지스터, 스택 메모리 영역에 함수의 인자를 저장하여 함수도 전달하도록 지원합니다.

당연히 인자를 레지스터에 저장하여 넘기는 방식이 빠르기 때문에 (fastcall) 최적화 옵션을 켜고 컴파일하는 경우 인자를 레지스터를 통해 전달하도록 함수의 호출부와 구현부를 변경해 버릴 수 있습니다. (일반적인 최적화 방법) 이 경우 GCC를 통해 자동 생성되는 코드는 적절히 변환되므로 문제가 없지만 직접 작성한 어셈블리 코드에서 함수를 호출하는 경우 문제가 발생합니다.

이를 방지하기 위해 어셈블리 코드와 링크되는 함수는 인자를 (레지스터를 이용하지 않고) 스택을 이용해서 전달하도록
선언합니다. 이를 위해 asmlinkage 매크로를 함수 선언부에 지정해야 합니다.

asmlinkage 매크로를 선언한 <include/linux/linkage.h> 해더를 보면
<asm/linkage.h> 파일을 #include 하고 있습니다.

대부분의 아키텍처에서 이 파일은 asmlinkage를 정의하지 않지만 i386에서는 다음과 같이 정의했습니다.
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
#define FASTCALL(x) x __attribute__((regparm(3)))
#define fastcall __attribute__((regparm(3)))

regparm 속성은 레지스터를 이용하여 전달한 인자의 수를 지정합니다.
asmlinkage로 선언된 경우 모두 스택을 이용하고 (레지스터로 0개의 인자 전달) fastcall로 선언된 경우 최대 3개의 인자를 레지스터로 전달합니다.


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2





덧글

댓글 입력 영역