Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

9365
557
421924


[Arm프로세서] AAPCS: Armv8: SP(스택 포인터) 레지스터의 세부 동작 Armv8: 함수 호출 규약

SP는 스택 포인터(Stack Pointer) 레지스터라고 하는데, Armv8 아키텍처에서는 SP_ELn로 표기합니다.

SP_ELn 레지스터와 익셉션 레벨

6장에서는 SP은 프로세스의 스택 공간 내에서 업데이트된다고 했는데, Armv8 아키텍처에서 명시된 SP_ELn은 어떤 개념인지 조금 헷갈립니다. SP_ELn은 쉽게 풀어서 다음 표와 같이 설명할 수 있습니다.

표 7.6 SP_ELn 레지스터와 익셉션 레벨

SP_EL0은 유저 프로세스가 유저 공간에서 실행될 때 접근하는 스택 포인터이고, SP_EL1은 프로세스가 커널 공간에서 실행될 때 접근되는 스택 포인터라고 볼 수 있습니다.

SP_ELn에 대한 정의를 Arm 아키텍처의 관점으로 알아 봤습니다. 그런데 SP_ELn 레지스터의 내용을 읽으면 읽을 수록 의문이 더 생기는 경우가 많습니다. 운영체제에 대한 내용을 빼고 Arm 아키텍처를 설명하기 때문입니다. 

이번에는 다음 표를 보면서 운영체제의 입장에서 SP_ELn의 정체에 대해 조금 더 알아봅시다.


표 7.7 운영체제와 SP_ELn 레지스터

먼저 표의 가장 윗 부분에 보이는 유저 프로세스가 접근하는 SP_ELn에 대해서 알아봅시다. 

유저 프로세스는 유저 공간과 커널 공간에 따로 스택 공간이 존재합니다. 대부분 유저 프로세스는 익셉션 레벨이 EL0인 유저 공간에서 실행하는데, SP_EL0 레지스터가 가리키는 유저 공간 내의 스택을 사용합니다. 

또한 유저 프로세스는 유저 공간에서 시스템 콜을 발생하면, 커널 공간으로 진입해 익셉션 레벨 EL1으로 실행되며, 커널 공간에 있는 스택 공간에 접근합니다. 이 때 SP_EL1 레지스터가 가리키는 커널 공간 내의 스택을 사용합니다. 유저 프로세스는 유저 공간과 커널 공간을 넘나들면서 실행되므로, SP_EL0와 SP_EL1 레지스터를 엑세스합니다.

이어서 표의 가운데 부분을 보겠습니다. 커널 프로세스라고 명시돼 있는데, 커널 프로세스는 커널 공간에 있는 스택만 접근하므로 SP_EL1 레지스터만 엑세스합니다. 커널 프로세스는 시스템 콜을 통해 유저 애플리케이션과 상호 동작을 하지 않습니다.

마지막으로 표의 가장 아랫 부분에 보이는 하이퍼바이저 프로세스를 보겠습니다. 하이퍼 바이저 프로세스는 EL2 내의 하이퍼바이저 내의 스택에만 접근하므로 SP_EL2 레지스터만 엑세스합니다.

[정보]
익셉션 레벨 별로 SP_ELn에 접근하는 권한은 하이퍼바이저 장에서 다룹니다.


이번 절에서 어셈블리 명령어와 함께 업데이트되는 레지스터는 유저 애플리케이션이 구동되는 익셉션 레벨0을 기준으로 설명합니다.

stp 명령어를 실행하면 SP 레지스터와 프로세스의 스택은 어떻게 바뀔까? 

Armv8 아키텍처에서 SP 레지스터가 업데이트되려면 stp 명령어가 실행되야 합니다. stp 명령어를 분석할 때는 레지스터와 스택 메모리 공간과 함께 조금 입체적으로 시야를 넓히면 이해가 빠릅니다. 

이점을 유념하면서 다음 어셈블리 코드 분석을 통해 SP 레지스터가 어떻게 업데이트되는지 분석해 봅시다.

01 0x20000 <add_func>:
02 {
03 0x20000:   a9bf7bf3    stp x19, x30, [sp, #-16]!
04     int result = x + y;
05 0x20004:   0b010013    add w19, w0, w1
06     printf("result: %d\n", result);
07 0x20008:   2a1303e1    mov w1, w19
08 0x2000c:   f0003ea0    adrp    x0, 188000 
09 0x20010:   91182000    add x0, x0, #0x608
10 0x20014:   97ed3258    bl  1fe948 <printf>
11 }
12 0x20018:   2a1303e0    mov w0, w19
13 0x2001c:   a8c17bf3    ldp x19, x30, [sp], #16
14 0x20020:   d65f03c0    ret

add_func() 함수의 주소로 분기하면 가장 먼저 몇 번째 줄이 실행될까요? 03번째 줄이 실행됩니다.

03번째 줄은 'stp x19, x30, [sp, #-16]!' 명령어가 보이는데, 이는 x19와 x30 레지스터를 스택에 푸시하는 동작입니다. 이 명령어를 실행하면 두 가지 동작을 동시에 수행합니다.

   * x19와 x30 레지스터의 값이 스택에 푸시된다.
   * SP 레지스터가 0x10 만큼 감소된다.

SP 레지스터는 프로세스의 스택 메모리 공간을 같이 떠올리면서 분석해야, 세부 동작 원리를 정확히 이해할 수 있습니다.

Armv8 아키텍처: AAPCS(함수호출 규약)

   ❑ 스택과 관련된 명령어 
      * stp 명령어  
      * sub 명령어  
      * ldp 명령어  
   ❑ 브랜치와 복귀 명령어
      * bl 명령어  
      * RET 명령어
AAPCS와 C 코드 최적화


Written by <디버깅을 통해 배우는 리눅스 커널의 구조와 원리> 저자




핑백

덧글

댓글 입력 영역