C언어 가변인자함수 구현 문제입니다.
글쓴이: hscho / 작성시간: 토, 2014/12/20 - 3:23오후
void debug(const char *format, ...); void debug(const char *format, ...) { void *arg = (void *)&format + sizeof(void *); return; } int main(void) { // ERROR //debug("FORMAT", 1, "ABCDE", 3.141592f, 'A', 10); // 00000001 20000370 0000000A 00000000 400921FB 00000041 0000000A // -------- -------- -------- ----------------- -------- -------- // 1 ABCDE ? 3.141592 'A' 10 // 10? // OK //debug("FORMAT", 1, 0xFF, "ABCDE", 3.141592f, 8); // 00000001 000000FF 20000364 00000000 400921FB 00000008 // -------- -------- -------- ----------------- -------- // 1 0xFF "ABCDE" 3.141592 8 // OK //debug("FORMAT", 1, 2, 3, 4, 5); // 00000001 00000002 00000003 00000004 00000005 // -------- -------- -------- ----------------- // 1 2 3 4 5 // ERROR // debug("FORMAT", 0xA1A2A3, 1, 3.141592f, "ABCDE"); // 00A1A2A3 00000001 20000370 00000000 400921FB 20000370 // -------- -------- -------- ----------------- -------- // 0xA1A2A3 1 ? 3.141592 "ABCDE" // "ABCDE"? // ERROR //debug("FORMAT", 'A', 1, 3.141592f, "ABCDE", 0xA1A2A3); // 00000041 00000001 00A1A2A3 00000000 400921FB 20000378 00A1A2A3 // -------- -------- -------- ----------------- -------- -------- // 'A' 1 ? 3.141592 "ABCDE" 0xA1A2A3 // 0xA1A2A3? return 1; }
Compiler : GNU GCC 4.9
Target : ARM Cortex M3, 32bit
안녕하세요.
C언어 가변인자 함수 구현 중 위와 같은 문제가 있습니다.
<stdarg.h>를 사용하지 않고 직접 구현하려고 합니다.
void *arg = (void *)&format + sizeof(void *);
위 부분에서 arg로 가변인수 값을 획득하는데 이상해서 메모리를 확인해보니
어떤 경우는 메모리에 제대로 값이 있는 반면, 어떤 경우는 마지막 인자 값이 중간에 있는 경우가 있습니다.
어떤 문제 때문에 위의 상황이 나타나는지요?
Forums:
가변인자는 순수어셈블리로 구현할 수 밖에
가변인자는 순수어셈블리로 구현할 수 밖에 없습니다.
위의 함수의 경우 format인자가 r0에 저장되어 넘어오는데, &format으로 해당 변수의 주소를 획득하려고 할 경우 r0의 값을 스택에 저장하고 해당 위치의 주소를 이용하게 됩니다.
따라서 함수의 prologue에 의해 스택이 변경된다면 format의 주소를 이용하여 두번째 인자를 구할 수 없습니다(사실 두번째 인자도 스택에 담기지 않고 r1에 존재).
stdarg.h를 사용하지 않으시려는 이유가 따로 있으신가요?
메모리 제약 때문에 그렇습니다.그래서 C
메모리 제약 때문에 그렇습니다.
그래서 C 라이브러리에 의존적이지 않는 코드를 작성하려고 합니다.
stdarg.h의 va_list, va_arg 등도 전부 define 선언으로 작동하는 것으로 알고 있습니다.
stdarg.h를 사용해 봤으나 결국 호출된 함수에 넘겨저 온 값 자체가 잘못되었기 때문에 오류가 나고 있습니다.
고정된 첫번째 인자를 바탕으로 스택 다음 값을 계산하여 가변인자의 시작주소를 찾는 부분은 작동하지만,
caller 함수에서 callee 함수로 parameter를 전달할때 값을 잘못 전달한다는 생각입니다.
기본적으로 GNU GCC는 cdecl 방식으로 함수를 처리하니깐요.
stdarg.h의 va_*가 define으로
stdarg.h의 va_*가 define으로 구현되어있긴 하지만 올려주신 코드처럼 구현되어있지는 않고, `__builtin_va_*`와 같은 컴파일러 intrinsic으로 구현되어있습니다. 따라서 가변인자를 사용한다고 해서 용량을 더 많이 차지한다거나 하지는 않습니다.
또한 cdecl은 x86의 호출규약입니다. arm에서는 AAPCS를 참고하세요. 4번째 인자까지는 r0, r1, r2, r3를 이용하여 전달하고, 그 이상의 인자는 스택에 담아 전달합니다.
답변 감사합니다. stdarg.h로 구현/테스트
답변 감사합니다.
stdarg.h로 구현/테스트 하였습니다. 근데 다른 문제가 생겼습니다. CoOS라는 OS 사용하고 있는데 main 함수에서는 debug가 동작하지만, task로 들어가면 어떤 이유에서인지 다시 가변인자가 꼬여버리네요.
알려주신 AAPCS에 대해서 알아보겠습니다.
가변인자가 꼬인다는게 어떤 상황인지 좀 더 자세한
가변인자가 꼬인다는게 어떤 상황인지 좀 더 자세한 설명이 필요합니다.
추측해 보건대 가변 인자 진급에 의해 3.14...는
추측해 보건대
가변 인자 진급에 의해 3.14...는 double형으로 바뀌는데,
해당 플랫폼에서는 double형의 정렬제한이 64비트 단위인 모양입니다.
그러니까 매개변수의 순서와 위치에 따라 매개분수들 중간에 다른 값이 들어가는 거겠죠.
그리고 웬만하면 그냥 stdarg.h 쓰시길 권해드리는 바입니다.
다른 라이브러리는 대체가 가능하지만
이것만큼은 사용자가 어떻게 직접 작성해 볼만한 기능이 아닙니다.
가변인자 자체가 크게 오버헤드가 발생할 구조도 아니고요.
다소 무례한 말인지도 모르겠지만
가변 인자 및 함수호출과 관련된 모든 사항을 잘 이해하고 계시다고
생각되지 않습니다.
댓글 달기