스택에 메모리가 저장되는 과정에 대해서..
글쓴이: quiz76 / 작성시간: 일, 2013/10/20 - 5:00오후
스택에 대해서 공부를하다가 막히는 예제가 있어서 질문드립니다.
#include <stdio.h> void f1(){ int i; printf("in func1\n"); } void f2(){ int j,*ptr; printf("f2 local : \t%p, \t%p\n", &j, &ptr); printf("in func2\n"); ptr = &j; *(ptr+2) = f1; } void f3(){ printf("Before invoke f2()\n"); f2(); printf("after invoke f2()\n"); } main(){ f3(); }
------------------------------------------------------
위의 코드를 스택메모리의 관점에서 보면..
main 함수에서 f3으로 분기를 하고
f3에서 f2로 분기를 하고
f2에서 stack 메모리에 지역변수 j와 ptr순서대로 하위 주소로 저장이 되고
j의 주소값을 ptr에게 넘겨주면서
ptr이 j를 가리키케 되는데요
그다음에 ptr+2(return address)에 f1의 주소값을 넣어주면서
f2의 함수가 끝나고 f1으로 분기하게 됩니다.
f1함수로 분기를 하고 지역변수 i가 스택에 저장되고 In_func1을 출력을하고
f1의 함수가 끝나면 리턴할곳이 없어서 세그멘테이션 오류가 나는데
해결하는 방법이 없을까요??
결과는
f1함수가 끝나면
After invoke f2() 출력하는 부분으로 가야합니다.
Forums:
할당하지않은 메모리를 이용하니까 문제가되는겁니다.
할당하지않은 메모리를 이용하니까 문제가되는겁니다. 함수호출과는 아무상관없습니다.f1은 호출한적도없습니다.
*(ptr+2) 부분은 힙 영역이 아니라 스택
*(ptr+2) 부분은 힙 영역이 아니라 스택 영익이므로 할당하지 않은 메모리는 아닙니다. 프로그램이 처음 실행될 때 고정된 크기(일반적으로 1 MB)로 할당됩니다.
그리고 질문자의 코드의 의도는 스택에 저장된 리턴 어드레스를 변조해서, f2()함수가 종료되고 f2()를 호출한 곳으로 실행 포인터가 옮겨가는게 아니라 f1()의 시작부분으로 옮겨지게 하는 것으로 보입니다.
콜스택 구조는 시스템 의존성이 있는 부분이라
콜스택 구조는 시스템 의존성이 있는 부분이라 프로그래밍 환경에 따라 다를 수 있습니다. 저는 인텔 CPU, 우분투 32 Bit에서 gcc로 다음처럼 해서 오류 없이 실행되었습니다.
윈도우즈 mingw 결과입니다.다음과 같은 결과을
윈도우즈 32비트 mingw 결과입니다.
다음과 같은 결과을 내고 나서 프로그램이 멈추네요.
peecky 님 결과와 마찬가지로 f3의 자리에 덮어쓰는 바람에 f3 로 돌아가지 못하고 끝납니다.
원하시는 결과를 얻으시려면, 리턴 주소와 필요한 값을 스택에 쌓고, 동시에 스택 포인터까지 조작해야 할 것입니다.
아무리 학습을 위한 코드라고 해도 C에서 이런 식으로
아무리 학습을 위한 코드라고 해도
C에서 이런 식으로 멋대로 메모리를 헤집는건 별로 바람직해 보이진 않습니다.
이런건 아예 어셈블러를 통해 학습하거나
혹은 오류가 없이 작성된 C 프로그램을 디버거 등으로 역어셈해서 읽어보는 정도로 그쳐야 합니다.
이미 컴파일된 코드를 해석해 보는 정도라면 몰라도,
메모리 상에 각 데이터들이 어떻게 배치될 것이라고 프로그래머가 자의적으로 예상하는건
위험해 보입니다.
C 자체로는 불가능합니다. 인라인 어셈블리로 CPU
C 자체로는 불가능합니다.
인라인 어셈블리로 CPU 레지스터를 직접 조작해야 하는데, 기계마다 다 다르게 동작할겁니다.
컴파일러가 inline 최적화를 하지 않았고, ARM
컴파일러가 inline 최적화를 하지 않았고, ARM 에서 수행된다면,
stack 은 단 1byte 도 사용하지 않는다에 100원 겁니다.
만약, 어떻게해서든 int j 가 스택을 사용하는 코드를 만들었다면,
gcc stack guard 에 걸리는 바람에 모든 플랫폼에서 SIGABRT 발생할 거라는 데 50원 겁니다.
stack기반 오버플로우?
f1() 함수를 call명령으로 호출한것이 아니기때문에
f1() 함수 종료시 수행되는 ret 명령의 리턴주소는 보장되지 않습니다.
함수호출규약을 흐트려(?) 조작할때 유지되도록 하는게 좋아보이는데요
f2()에서 리턴을 조작할때 f1()이 리턴할 주소도 같이 스택에 쌓아두는것도 방법이겠네요.
원리를 파악하는 목적에서 이런 연습을 해 보는 것은
원리를 파악하는 목적에서 이런 연습을 해 보는 것은 상관없지만, 실제로는 절대 해서는 안됩니다.
스택이 증가하는 방향, 스택 alignment 등 고려해야 될 내용이 무척 많습니다.
차라리 setjmp()/longjmp()를 쓰세요.
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://cinsk.github.io/cfaqs/
댓글 달기