Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

8179
1390
307630


[Arm프로세서] AAPCS: Armv7: 함수 반환형은 워드 단위로 지정 Armv7: 함수 호출 규약

함수를 반환하는 자료형은 워드 단위로 제한하는 것이 좋습니다. 워드형인 경우 반환값은 R0 레지스터에 저장하나, 데이터가 워드 범위를 벗어난 경우 R0와 R1레지스터에 반환값을 나눠서 처리하기 때문입니다.

[정보]
위에서 언급한 워드형이 무엇인지 알아봅시다. 워드(word)는 어셈블리 명령어로 연산 결과를 레지스터에 저장할 수 있는 데이터 단위입니다. 그렇다면 함수가 반환하는 타입이 무엇인지 파악하려면, 함수 선언부의 가장 앞 부분을 보면 됩니다. 예를 들어 이번 소절에서 소개한 add_func() 함수는 int 형의 인자를 반환합니다.

int add_func(int a, int b, int c, int d, int e);

대부분 함수가 반환하는 자료형은 워드 타입으로, int, unsinged int 혹은 포인터 형입니다. 
32비트 기반의 아키텍처에서 워드는 4바이트인데, 8바이트 포멧으로 함수가 반환하는 타입을 선언하면 워드 데이터 범위를 넘어서게 됩니다.

이번에도 예제 코드를 보면서 함수의 반환형을 워드 단위로 지정해야 하는 이유에 대해 더 알아봅시다.

unsigned long long add_func(int x, int y)
{
unsigned long long result = x + y;
printf("x:%d, y:%d \n", x, y);
return result;
}

위 함수를 보면 함수의 반환형이 unsigned long long입니다. 즉 8바이트(64비트)로 result를 반환하는데, 이 때 R0와 R1 레지스터에 걸쳐서 반환값이 저장됩니다. 여기서 "C 코드에서 이런 내용을 어떻게 확인할 수 있을까"라는 의문이 생깁니다. add_func() 함수를 어셈블리 명령어로 보면, 이런 의문을 해소할 수 있습니다. 

다음은 add_func() 함수의 반환형을 int 로 지정한 명령어[before]와 unsigned long long으로 지정한 명령어[after]입니다.

[before]: 함수 반환형(int)
01   104a0: e1a00003  mov r0, r3
02   104a4: e24bd004  sub sp, fp, #4
03   104a8: e8bd8800  pop {fp, pc}

[after]: 함수 반환형(unsigned long long)
04   10474: e1a00002  mov r0, r2
05   10478: e1a01003  mov r1, r3
06   1047c: e24bd004  sub sp, fp, #4
07   10480: e8bd8800  pop {fp, pc}

함수의 반환형을 워드 단위로 지정한 경우, 01번째 줄과 같이 R0 레지스터에 반환값을 저장합니다. 그런데 함수의 반환형이 워드 단위를 넘어서면 04~05번째 줄과 같이 R0와 R1 레지스터에 반환값을 저장합니다.

한 개의 명령어로 반환값을 R0 레지스터에 저장할 수 있는데, 명령어의 갯수가 2개로 늘었습니다. 이에 비례해서 add_func() 함수를 호출하는 구문도 어셈블리 명령어의 갯수가 늘어나게 됩니다.

정리하면, 되도록 함수의 반환형은 워드 단위로 지정하는게 바람직합니다.

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




덧글

댓글 입력 영역