강좌 & 팁
글 수 2,412
2012.10.09 13:35:03 (*.119.104.164)
43661
이번 산책길에서는
시스템 콜 함수에 전달되는
매계변수 처리와 반환 값 처리에 대하여 알아 보죠
open() 이라는 시스템 콜을 맨 페이지에서 찾아 보면
다음과 같은 형태의 함수형을 갖습니다.
int open (const char *FILENAME, int FLAGS[, mode_t MODE]);
이 함수는 FILENAME,FLAGS,MODE 세가지 매개변수를 갖고있습니다.
반환값은 int 값입니다.
이 함수를 호출할 경우 다음과 같은 형식으로 호출해야죠..
fd = open( "a.txt", O_RDWR ¦ O_CREAT ¦ O_EXCL, 0644);
SWI 명령을 이용해 함수를 호출할 경우 이전 산책길에서 보았듯이
mov r7, #1
swi 0
이렇게 하면 해당 open 함수를 분기할수는 있죠..
그런데 함수 뒤에 전달되는 인자들은 도대체 어떻게 전달해야 할까요?
예..
정답은 SWI와 시스템콜(4)에서 잠깐 언급한 APCS 규정을 이용합니다.
기억을 되살리기 위해서 다시 한번 언급하면
APCS 는 ARM Procedure Call Standard 의 약자입니다.
사실 APCS 는 구번이고
최신 버전은 또는 AAPCS 입니다.
이 단어는 Procedure Call Standard for the ARM Architecture 의 약자죠
별 차이는 없어 보입니다. ^^
그래서 그냥 계속 APCS 로 지칭하겠습니다. AAPCS 는 개인적으로 자꾸 까먹어서리.. ㅜㅜ
결국 개별적으로 컴파일된것과 어셈블된 것들을 함께 사용할 수 있도록 해주는 것이죠
전통적인 C 언어에서는 매개변수는 스택을 통해서 전달하도록 되어 있습니다.
하지만 메모리 접근은 프로세서의 속도 향상을 방해합니다.
그래서 조금 규칙을 바꾸어서
가급적 메모리 접근이 필요없는 레지스터를 이용합니다.
이때 규칙이 필요한데 이런 규칙을 지정한것이 APCS 입니다.
물론 그외의 규칙도 APCS 에 정의하고 있지만
여기서는 그냥 매개변수만 다루기로 하겠습니다.
APCS 규정을 들여다 보면 R0~R4 는 다음과 같은 규정이 있습니다.
r0 a1 argument 1/scratch register/result
r1 a2 argument 2/scratch register/result
r2 a3 argument 3/scratch register/result
r3 a4 argument 4/scratch register/result
이 규정은 R0부터 R3 까지를 매개변수(argument)로 사용한다는 것입니다.
예를 들어 다음과 같은 함수가 있다고 하면
sample_function( int a1, int a2, int a3, int a4 );
a1 은 r0 레지스터에 , a2는 r1 레지스터에 이런식으로 순서대로 대입하는 것입니다.
그러면 4개 이상은 어떻게 전달하냐구요?
5번째 매개변수 부터는 스택을 이용하는 겁니다.
그래서 가급적 시스템 콜은 4개 이상의 변수를 만들지 않는 것이 좋겠죠..
그래서 대부분의 시스템 콜은 4개 이하의 매개변수를 갖고 있습니다.
물론 ARM 에서 이야기 입니다.
i386 계열에서는 이런 규칙이 적용되지 않습니다.
(주의 실제 시스템 콜에서는 r4 를 사용할 때도 있습니다. )
맞나?
암에서 레지스터는 32 비트 크기를 가지게 됩니다.
그래서 대부분의 경우 매개변수를 적용시키는 것에 문제가 없습니다.
예를 들어 char 형도 실제로는 32 비트 레지스터를 사용합니다.
실제적인 처리를 할때 컴파일러가 char 형으로 다루도록 컴파일 될 뿐입니다.
그래서 ARM 같은 RISC 프로세서에서는 가장 빠른 처리는 32비트 형입니다.
8비트 형인 char 가 오히려 처리 속도는 늦어질 수 있습니다.
unsinged short *addr 같은 매개변수도 결국 주소를 담는 변수이므로
필요한 크기는 32 비트이면 충분하죠..
그런데 변수형에는 64비트가 있습니다.
APCS 의 규정에는 레지스터 할당 규칙만 있죠.
시스템 콜에서 64비트 데이터가 매개변수로 사용될 경우에 대한 규정은 없습니다.
이 규정은 EABI 규정에 있습니다.
EABI 규정을 보면 32 비트 데이터와 64비트 데이터가 같이 사용될때
64비트 데이터는 짝수 레지스터 번호로 시작해야 한다는 규정이 있습니다.
예를 들어
sample_function( int a1, long long a2 );
와 같은 함수의 경우 두번째 인자에 64비트 데이터 값이 전달되어야 합니다.
APCS 규칙에 의하면 a1은 R0 레지스터에 할당됩니다. 그러면 a2 는 r1 과 r2 를 사용하면 될까요?
EABI 규정에 짝수 레지스터를 사용한다고 되어 있죠?
그래서 이때는 다음과 같인 전달됩니다.
r0 : a1
r1 : 사용안함
r2 : a2 낮은 값
r3 : a2 높은 값
리틀엔디언으로 컴파일 되는가 빅엔디언으로 컴파일 되는가에 따라서
a2 를 r2, r3 에 어떻게 나누어 할당되는가가 다르게 됩니다.
위 경우는 리틀 엔디언일 경우 입니다.
이제 마지막으로 반환값은 어떻게 될까요?
예 r0 또는 r1 사용합니다.
32비트 데이터 크기까지는 r0 를 사용하고
64비트 데이터는 r0 와 r1 을 사용합니다.
그 이상의 크기는 없죠...
이렇게 시스템콜 함수는 매개변수와 반환값을 교환 합니다.
그런데 사실
지금까지 설명한 것은 그렇게 자세하게 알 필요가 없습니다.
gcc 컴파일러가 알아서 잘 해 줍니다.
그냥 어떻게 전달되는지에 대하여 궁금하신 분들을 위해서
걸었던 가벼운 산책 길입니다. ㅋㅋ