Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

10200
629
98817


[라즈베리파이] 시스템 콜 - 유저 공간 시스템 콜 발생 어셈블리 코드 분석 11장. 시스템 콜

유저 공간에서 시스템 콜을 발생하는 코드는 어셈블리로 구현되어 있습니다. 그래서 아키텍처별로 시스템 콜을 실행하는 동작이 다릅니다.

ARMv7 아키텍처(라즈베리파이)에서는 유저 공간에서 시스템 콜을 발생하는 동작은 다음과 같습니다. 
1. r0 ~ r5 레지스터에 시스템 콜로 전달할 인자 지정
2. r7 레지스터에 시스템 콜 번호 저장
3. "svc 0x00000000" 명령어 실행

이어서 GNU C 라이브러리 파일에서 실제 시스템 콜을 실행하는 어셈블리 코드를 봅시다.

유저 공간에서 write() 함수를 호출할 때 시스템 콜 발생 과정 분석하기
write() 함수를 호출했을 때 시스템 콜을 실행하는 코드를 소개합니다.
00000020 <__libc_write>:
1  20: e59fc060 ldr ip, [pc, #96] ; 88 <__libc_write+0x68>
2  24: e79fc00c ldr ip, [pc, ip]
3  28: e33c0000 teq ip, #0
4  2c: e52d7004 push {r7} ; (str r7, [sp, #-4]!)
5  30: 1a000005 bne 4c <__libc_write+0x2c>
6  34: e3a07004 mov r7, #4
7  38: ef000000 svc 0x00000000

6번째 줄 코드를 보면 r7 레지스터에 write() 함수에 대응하는 시스템 콜 번호인 4를 저장합니다.


위 어셈블리 코드를 보면 한 가지 의문이 생깁니다. 

    시스템 콜 번호는 어떻게 확인할까?

라즈베리파이에서 다음 해더 파일에서 __NR_write 매크로를 보면 인덱스가 4임을 알 수 있습니다.
[/usr/include/arm-linux-gnueabihf/asm/unistd-common.h]
#define __NR_write (__NR_SYSCALL_BASE+  4)

이 인덱스가 시스템 콜 번호입니다.


유저 공간에서 open() 함수를 호출할 때 시스템 콜 발생 과정 분석하기
이번에는 open() 함수를 호출하면 시스템 콜을 발생하는 어셈블리 코드를 보겠습니다.
00000020 <__libc_open>:
1  20: e59fc060 ldr ip, [pc, #96] ; 88 <__libc_open+0x68>
2  24: e79fc00c ldr ip, [pc, ip]
3  28: e33c0000 teq ip, #0
4  2c: e52d7004 push {r7} ; (str r7, [sp, #-4]!)
5  30: 1a000005 bne 4c <__libc_open+0x2c>
6  34: e3a07005 mov r7, #5
7  38: ef000000 svc 0x00000000

6번째 줄 코드를 보면 r7 레지스터에 5를 저장합니다. 이후 7번째 줄 코드에서 “svc 0x0” 명령어를 실행해 다음 동작을 수행합니다.  

    Supervisor Call, 즉 소프트웨어 인터럽트를 유발한다.

그러면 시스템 콜 5번의 정체는 어디서 확인할 수 있을까요? 
[/usr/include/arm-linux-gnueabihf/asm/unistd-common.h]
#define __NR_open (__NR_SYSCALL_BASE+  5)

라즈베리파이에서 위 해더 파일에서 __NR_open 매크로를 확인하면 인덱스가 5임을 알 수 있습니다. r7 레지스터에 저장하는 5란 정수는 open() 함수에 대한 시스템 콜 번호입니다.

      
write(), open() 함수를 호출하면 시스템 콜이 실행되고 커널 공간에서 sys_write() 와 sys_open() 함수를 호출할 것이라 예상합니다.

대부분 리눅스 저수준 표준 함수 이름과 시스템 콜 핸들러 함수는 일치하는 경우가 많지만 아닌 경우도 있습니다. 

exit() 함수를 예로 들겠습니다. exit() 함수를 호출하면 우리는 당연히 커널 공간에서 sys_exit() 함수가 시스템 콜 핸들러로 실행할 것이라 예상합니다. 

exit() 함수를 호출하면 libc.a 라이브러리 파일 내 다음 경로에 있는 _exit 레이블을 실행합니다.
[glibc/sysdeps/unix/sysv/linux/_exit.c]
00000000 <_exit>:
1    0: e92d4080 push {r7, lr}
2    4: e1a02000 mov r2, r0
3    8: e3a070f8 mov r7, #248 ; 0xf8
4    c: ef000000 svc 0x00000000

3~4번째 줄 코드를 보겠습니다.
3    8: e3a070f8 mov r7, #248 ; 0xf8
4    c: ef000000 svc 0x00000000

r7 레지스터에 시스템 콜 번호인 248를 저장합니다.

라즈베리파이에서 다음 해더 파일을 열어볼까요? 
[/usr/include/arm-linux-gnueabihf/asm/unistd-common.h]
#define __NR_exit_group (__NR_SYSCALL_BASE+248)

248에 대한 시스템 콜 함수 매크로는 __NR_exit_group입니다.

리눅스 커널 기준으로 sys_call_table 심볼에 있는 시스템 콜 테이블 248번에 해당하는 시스템 콜 핸들러는 sys_exit_group() 함수입니다. 프로세스를 종료할 때 프로세스가 속한 스레드 그룹까지 종료하는 경우가 많아서 sys_exit_group() 함수를 호출하는 것입니다.

참고로, GNU C 라이브러리 코드는 다음 경로에서 확인할 수 있습니다.
[https://code.woboq.org/userspace/glibc/stdlib/exit.c.html]
1 void
2 _exit (int status)
3 {
4  while (1)
5    {
6 #ifdef __NR_exit_group
7      INLINE_SYSCALL (exit_group, 1, status);
8 #endif
9      INLINE_SYSCALL (exit, 1, status);
10 #ifdef ABORT_INSTRUCTION
11      ABORT_INSTRUCTION;
12 #endif
13    }
14 }

7번째 줄 코드를 실행하면 r7 레지스터에 __NR_exit_group 시스템 콜 번호인 248을 저장하고 소프트웨어 인터럽트를 유발합니다. 

코드를 분석하고 실행 흐름을 예측하는 것은 중요합니다. 하지만 자신이 분석한 내용이 맞는지 리눅스 시스템에서 실제로 확인할 필요가 있습니다. 필자도 exit() 함수를 호출하면 248번 시스템 콜을 실행한다는 사실은 라즈베리파이에서 GDB 디버깅을 통해 확인했습니다. 디버깅을 안한 상태에서 sys_exit() 함수를 분석하면 시간을 허비할 뿐입니다.


이번 소절에서 유저 공간에서 시스템 콜을 발생하는 출발 지점을 살펴봤습니다. 이제 지금까지 설명한 내용을 정리해보겠습니다.

 첫째, 시스템 콜을 실행하는 주인공은 누구일까?
       프로세스이다.

 둘째, 시스템 콜을 유발하려면 어떤 ARM 어셈블리 명령어를 실행해야 할까?
       r7 레지스터에 시스템 콜 번호를 저장하고 'svc 0x0' 명령어를 실행한다.

 셋째, 시스템 콜을 유발하는 어셈블리 명령어는 CPU 아키텍처마다 같나?
     모두 다르다. CPU 아키텍처별로 각기 다른 명령어로 시스템 콜을 유발한다.

이번 절까지 유저 공간에서 시스템 콜을 발생하는 어셈블리 코드를 분석했습니다. 다음 절에서는 유저 공간에서 발생한 시스템 콜을 커널 공간에서 어떻게 처리하는지 살펴보겠습니다.


"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)


#Reference: 시스템 콜
시스템 콜 주요 개념 소개
유저 공간에서 시스템 콜은 어떻게 발생할까
시스템 콜 핸들러는 어떤 동작을 할까? 
시스템 콜 실행 완료 후 무슨 일을 할까?
시스템 콜 관련 함수  
시스템 콜 디버깅  


 

핑백

덧글

댓글 입력 영역