Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

8179
1390
307630


[Arm프로세서] AAPCS: Armv8: X30 링크 레지스터와 어셈블리 명령어 분석 Armv7: 함수 호출 규약

SP 레지스터보다 링크 레지스터인 X30은 '함수 호출'에서 가장 중요한 역할을 수행합니다. 함수를 호출한 다음에 복귀할 주소를 X30 레지스터가 저장하기 때문입니다.

우리가 함수를 작성하면 어디선가 함수를 호출할 것이라 예상합니다. 그런데 함수를 호출한 다음, 호출된 함수의 실행이 종료되면 (함수를 호출한 코드 기준으로) 바로 아랫 부분에 있는 코드가 실행됩니다.  

X30 레지스터와 관련된 예제 코드 분석

이번에는 아래와 같이 main() 함수의 코드(C 코드와 어셈블리 코드로 같이 변환)을 보면서 이 내용에 대해 조금 더 알아봅시다.

01 0x10000 <main>:
02 {
03 0x10000:   f81e0ffe    str x30, [sp, #-32]!
04     int res = _add_func(x, y);
05 0x10004:   52800061    mov w1, #0x3 // #3
06 0x10008:   52800040    mov w0, #0x2 // #2
07 0x1000c:   97ffffef    bl  20000 <add_func>
08 0x10010:   b9001fe0    str w0, [sp, #28]
09     printf("add res = %d \n", res);
...

위 코드의 핵심은 아래와 같이 04~08번째 줄 루틴입니다.

04     int res = _add_func(x, y);
05 0x10004:   52800061    mov w1, #0x3 // #3
06 0x10008:   52800040    mov w0, #0x2 // #2
07 0x1000c:   97ffffef    bl  20000 <add_func>
08 0x10010:   b9001fe0    str w0, [sp, #28]

C 코드 기준으로 04번째 줄은 add_func() 함수를 호출하는 코드인데, 어셈블리 코드 기준으로는 07번째 줄에서 add_func() 함수의 주소로 분기합니다.

[정보]
위 코드는 이해를 돕기 위해 C 코드와 어셈블리 명령어를 같이 표기한 것입니다.

그렇다면 07번째 줄에서 add_func() 함수의 주소로 분기한 다음에, add_func() 함수가 실행을 마치면 어느 주소에 있는 코드가 실행될까요? 

다음 코드와 같이 08번째 줄입니다.

07 0x1000c:   97ffffef    bl  20000 <add_func>
08 0x10010:   b9001fe0    str w0, [sp, #28]

이를 당연한 동작이라고 여기지만, 조금만 깊게 생각하면 누군가의 도움을 받아야만 가능하다는 사실을 알게 됩니다. 즉, add_func() 함수를 호출(함수의 주소로 분기)한 다음에 복귀할 주소를 누군가가 알고 있어야 이런 동작이 가능한 것입니다. 그런데 함수를 호출한 다음에 복귀할 주소를 저장하는 역할을 X30 레지스터가 수행합니다.

서브 루틴(함수)이 호출될 때 X30 레지스터의 동작 원리

이를 조금 더 일반화해서 다음과 같이 설명할 수 있습니다.  

   “어떤 코드에서 함수를 호출하면, Arm 코어는 함수가 실행을 끝낸 후 복귀할 주소를 
    X30 레지스터에 업데이트한다.”
 
이해를 돕기 위해 함수가 호출되면 Arm 아키텍처 관점으로 어떤 동작을 하는지를, 세부 단계 별로 나누면 다음과 같습니다. 

    1. 'bl [함수이름]' 포멧의 명령어를 실행한다.
    2. Arm 코어는 함수 실행을 마무리한 후 복귀할 주소를 X30 레지스터에 업데이트한다.
    3. 함수의 앞 부분에 X30 레지스터를 프로세스의 스택 공간에 푸시한다.
    4. 함수는 기능을 수행한다.
    5. 함수가 실행을 끝내면, ret 명령어를 실행해 스택 공간에 푸시된 X30 레지스터를 프로그램 카운터에 넣어준다.
    6. 함수를 호출한 다음 명령어가 위치한 주소로 복귀한다.

위 단계에서 가장 중요한 동작은 “'bl [함수이름]' 포멧의 명령어를 실행하면 Arm 코어는 하드웨어적으로 X30 레지스터에 함수 실행을 마무리한 후 복귀할 주소를 업데이트해준다”라고 말할 수 있습니다.  

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




덧글

댓글 입력 영역