PLT
Procedure Linkage Table
외부 프로시저를 연결해주는 테이블.
PLT를 통해 다른 라이브러리에 있는 프로시저를 호출해 사용할 수 있다.
GOT
Global Offset Table
PLT가 참조하는 테이블. 프로시저들의 주소가 들어 있다.
함수를 호출하면(PLT를 호출하면)GOT로 점프하는 데
GOT에는 함수의 실제 주소가 쓰여 있다.
첫번째 호출이라면 GOT는 함수의 주소를 가지고 있지 않고,
어떤 과정을 거쳐 주소를 알아 낸다.
두번째 호출부터는 첫번째 호출 때 알아낸 주소로 바로 점프한다.
PLT와 GOT를 왜 사용하는지 알기 위해서는 먼저 Linker를 알아야 한다.
어떤 코드를 작성한다고 생각하자.
소스 안에는 printf함수를 호출하는 코드가 있고,
include한 헤더파일에는 printf 선언이 있다.
소스파일을 실행파일로 만들기 위해서 컴파일이라는 과정을 거쳐야 한다.
컴파일을 통해 오브젝트 파일이 생성된다.
하지만, 오브젝트 파일은 그 자체로 실행이 가능하지 않다.
printf의 구현 코드를 모르기 때문이다.
printf를 호출했을 때 어떤 코드를 실행해야 하는지, 우리가 작성한 코드만 가지고서는 아무것도 알 수 없다.
오브젝트 파일을 실행 가능하게 만들기 위해서는
printf의 실행코드를 찾아서 오브젝트 파일과 연결시켜야 한다.
printf의 실행코드는 printf의 구현 코드를 컴파일한 오브젝트 파일로,
이런 오브젝트 파일들이 모여있는 곳을 라이브러리라고 한다.
이렇게 라이브러리 등 필요한 오브젝트 파일들을 연결시키는 작업을 링킹(Linking)이라고 한다.
이렇게 링크 과정까지 마치면 최종적인 실행파일이 생긴다.
링크를 하는 방법에는 static과 dynamic 방식이 있다.
static link 방식은 파일 생성시 라이브러리 내용을 포함한 실행 파일을 만든다.
gcc 옵션중 static 옵션을 적용하면 static link 방식으로 컴파일 된다.
실행 파일 안에 모든 코드가 포함되기 때문에 라이브러리 연동 과정이 따로 필요 없고,
한 번 생성한 파일에 대해서 필요한 라이브러리를 따로 관리하지 않아도 되기 때문에 편하다는 장점이 있다.
하지만 파일 크기가 커지는 단점이 있고 동일한 라이브러리를 사용하더라도 해당 라이브러리를 사용하는
모든 프로그램들은 라이브러리의 내용을 메모리에 매핑시켜야 한다.
dynamic link 방식은 공유라이브러리를 사용한다.
라이브러리를 하나의 메모리 공간에 매핑하고 여러 프록램에서 공유하여 사용하는 것이다
실행파일 안에 라이브러리 코드를 포함하지 않으므로, static link 방식을 사용해 컴파일 했을 때에비해 파일 크기가 훨씬 작아진다.
실행시에도 상대적으로 적은 메모리를 차지한다
또한 라이브러리를 따로 업데이트를 할 수 있기 때문에 유연한 방법이다.
하지만 실행파일이 라이브러리에 의존해야 하기 때문에 라이브러리가 없으면 실행할 수 없다.
아무런 옵션도 주지 않는다면, 자동으로 Dynamic Link 방식으로 컴파일 한다.
Dynamic Link 방식으로 컴파일 했을 때 PLT와 GOT를 사용하게 된다.
Static Link 방식으로 컴파일 하면 라이브러리가 프로그램 내부에 있기 때문에 함수의 주소를 알아오는 과정이 필요하지 않지만,
Dynamic Link 방식으로 컴파일 하면 라이브러리가 프로그램 외부에 있기 때문에 함수의 주소를 알아오는 과정이 필요하다.
Dynamic Link 방식으로 프로그램이 만들어지면 함수를 호출할 때 PLT를 참조하게 된다.
PLT에서는 GOT로 점프를 하는데,
GOT에 라이브러리에 존재하는 실제 함수의 주소가 쓰여 있어서 이 함수를 호출하게 된다.
그런데 이때, 첫 호출이냐 아니냐에 따라 동작과정이 조금 달라진다.
두번째 호출이라면 GOT에 실제 함수의 주소가 쓰여 있지만, 첫번째 호출이라면 GOT에 실제함수의
주소가 쓰여있지 않다.
그래서 첫 호출시에는 Linker가 dl_resolve라는 함수를 사용해 필요한 함수의 주소를 알아오고,
GOT에 그 주소를 써준 후 해당 함수를 호출한다.