리눅스 함수 호출 과정이 어떻게 될까?
간단한 printf 함수로 테스트를 해봤습니다.
#include <stdio.h>
int main(){ printf("test~~~"); }
다음 프로그램을 작성 후 gdb로 까보면..
(gdb) disas main
Dump of assembler code for function main:
0x0804841b <+0>: lea ecx,[esp+0x4]
0x0804841f <+4>: and esp,0xfffffff0
0x08048422 <+7>: push DWORD PTR [ecx-0x4]
0x08048425 <+10>: push ebp
0x08048426 <+11>: mov ebp,esp
0x08048428 <+13>: push ecx
0x08048429 <+14>: sub esp,0x4
0x0804842c <+17>: sub esp,0xc
0x0804842f <+20>: push 0x80484d0
0x08048434 <+25>: call 0x80482f0 <printf@plt>
0x08048439 <+30>: add esp,0x10
0x0804843c <+33>: mov ecx,DWORD PTR [ebp-0x4]
0x0804843f <+36>: leave
0x08048440 <+37>: lea esp,[ecx-0x4]
0x08048443 <+40>: ret
End of assembler dump.
다음과 같이 plt 영역의 printf 주소를 찾아볼 수 있습니다.
저 주소에는 뭐가 들어있을까요?
(gdb) x/3i 0x80482f0
0x80482f0 <printf@plt>: jmp DWORD PTR ds:0x804a00c
0x80482f6 <printf@plt+6>: push 0x0
0x80482fb <printf@plt+11>: jmp 0x80482e0
음 뭔가 또 점프를 하는 것을 볼 수 있습니다. 다시 따라가보면..
(gdb) x/1x 0x804a00c
0x804a00c <printf@got.plt>: 0x080482f6
이번엔 got에 왔네요;
지금까지의 flow를 그려보면 다음과 같습니다.
그림 넘넘 잘그리네용
아무튼 다시 결국 되돌아와선 0x80482e0에 다시 점프하게되는데!
이 0x80482e0이 dl_runtime_resolve라고 하는 영역입니다.
여기서 dl은 dynamic linker라고 하는데 좀있다가 자세하게 설명 드릴거구, 일단은 dl_runtime_resolve는 공유 라이브러리에서 특정 함수의 주소를 가져오는 역할을 하는구나 하고만 생각해주시면 될 것 같아요.
그림이 점점 더러워지네요 아무튼 dl_runtime_resolve로 점프하게되면 got 위치에 특정 함수 주소를 찾아서 저장시켜줍니다. 특정 함수라 함은 여기서 printf()가 되겠지요..
이 과정이 수행되고 나면 got 영역에 삽입된 함수를 호출하게 됩니다.
여기까지 정리해서 설명해드리면..
우선 제일 처음 나왔던 printf의 PLT 주소는 GOT영역의 주소를 가리키고 있습니다.
그 GOT 영역으로 트레이싱하게 되면 또 다시 어떤 주소가 들어있는데 이 주소는 다시 PLT 영역으로 돌아오게됩니다. 이는 dl_runtime_resolver로 점프하기 위함이죠.
인자 하나를 가지고 dl_runtime_resolver로 점프하게되면 got의 위치에 함수의 주소를 찾아 저장하고 그 함수를 호출한 뒤 다시 복귀하게 됩니다.
왜 이런 과정을 하는걸까?
같은 함수 호출을 여러 번 하게 되는 경우 일일이 라이브러리 주소를 가져와야 하기 때문에 got에 라이브러리 주소를 저장함으로써 이를 해결하는 것이라 보시면 됩니다.
실제로 printf 함수를 두 번 호출하게 되면 어떻게 될까용
root@ubuntu:~/study# cat pltgot.c
#include <stdio.h>
int main(){ printf("test~~~"); printf("re-hi!”); }
(gdb) disas main
Dump of assembler code for function main:
0x0804841b <+0>: lea 0x4(%esp),%ecx
0x0804841f <+4>: and $0xfffffff0,%esp
0x08048422 <+7>: pushl -0x4(%ecx)
0x08048425 <+10>: push %ebp
0x08048426 <+11>: mov %esp,%ebp
0x08048428 <+13>: push %ecx
0x08048429 <+14>: sub $0x4,%esp
0x0804842c <+17>: sub $0xc,%esp
0x0804842f <+20>: push $0x80484e0
0x08048434 <+25>: call 0x80482f0 <printf@plt>
0x08048439 <+30>: add $0x10,%esp
0x0804843c <+33>: sub $0xc,%esp
0x0804843f <+36>: push $0x80484e8
0x08048444 <+41>: call 0x80482f0 <printf@plt>
0x08048449 <+46>: add $0x10,%esp
0x0804844c <+49>: mov -0x4(%ebp),%ecx
0x0804844f <+52>: leave
0x08048450 <+53>: lea -0x4(%ecx),%esp
0x08048453 <+56>: ret
End of assembler dump.
참고로 \n을 사용하시게되면 인자를 넣지 않는 한 puts로 바뀔거에요
암튼 한번 printf를 호출한 다음 부분을 break걸고 got 영역을 확인해보면..
(gdb) disas main
Dump of assembler code for function main:
0x0804841b <+0>: lea ecx,[esp+0x4]
0x0804841f <+4>: and esp,0xfffffff0
0x08048422 <+7>: push DWORD PTR [ecx-0x4]
0x08048425 <+10>: push ebp
0x08048426 <+11>: mov ebp,esp
0x08048428 <+13>: push ecx
0x08048429 <+14>: sub esp,0x4
0x0804842c <+17>: sub esp,0xc
0x0804842f <+20>: push 0x80484e0
0x08048434 <+25>: call 0x80482f0 <printf@plt>
0x08048439 <+30>: add esp,0x10
0x0804843c <+33>: sub esp,0xc
=> 0x0804843f <+36>: push 0x80484e8
0x08048444 <+41>: call 0x80482f0 <printf@plt>
0x08048449 <+46>: add esp,0x10
0x0804844c <+49>: mov ecx,DWORD PTR [ebp-0x4]
0x0804844f <+52>: leave
0x08048450 <+53>: lea esp,[ecx-0x4]
0x08048453 <+56>: ret
End of assembler dump.
(gdb) x/i 0x80482f0
0x80482f0 <printf@plt>: jmp DWORD PTR ds:0x804a00c
(gdb) x/x 0x804a00c
0x804a00c <printf@got.plt>: 0xb7e50130
(gdb) x/2i 0xb7e50130
0xb7e50130 <__printf>: push ebx
0xb7e50131 <__printf+1>: call 0xb7f28e95 <__x86.get_pc_thunk.bx>
다음과 같이 첫 printf 호출 당시에 dl_runtime_resolve 함수에 의해 got 테이블에 printf 주소가 입력되었다는 사실을 알 수 있었습니다.
각 실행파일마다 이러한 영역은 readelf 명령을 통해 확인해 볼 수 있습니다.
먼가 본의아니게 늦게 언급하게됬는데
plt는 procedure linkable table의 약자이고 got는 global offset table의 약자입니다. 의미를 풀어놓으니 확실히 위에서 plt와 got가 어떻게 쓰였는지 매치가 될 것 같네용 ㅎ
'공부' 카테고리의 다른 글
malloc chunk (0) | 2015.10.28 |
---|---|
힙 (0) | 2015.10.26 |
dynamic linker (0) | 2015.10.18 |
vdso (0) | 2015.10.17 |
syscall 목록 (0) | 2015.10.16 |