공부/JUN STUDY

PLT와 GOT 그리고 동적 라이브러리

JUNFUTURE 2022. 5. 18. 17:58

PLT와 GOT가 무엇이고 어떤 점이 다른지
동적 라이브러리를 이용한 함수 호출 방법은 어떻게 되는지 알아보자.

 

라이브러리 : 함수를 모아놓은 파일

정적 라이브러리 : 내가 만든 프로그램에 라이브러리 함수를 직접 삽입

동적 라이브러리 : 메모리에 별도로 라이브러리 파일을 적재한 뒤 함수 주소를 찾아 사용

PLT : 동적 라이브러리를 이용할때 함수 주소를 찾아 실행시켜주는 테이블

GOT : 함수의 실제 주소를 기록해둔 테이블

 

하나씩 살펴보면

라이브러리

라이브러리란 여러개의 함수가 담겨있는 파일이다.

어렵게 생각하지말자.

그냥 우리가 쓰는 printf, malloc과 같은 함수가 정의되어있는 파일이다.

 

링크

내가 만든 프로그램에서 사용하는 라이브러리 함수를 실제 연결해주는 행위.

이때 심볼함수정의의 차이점에 주목해서 이해하면 이해가 수월한데,

 

심볼은 헤더파일에 선언되어있는 함수의 프로토타입(선언부)라고 생각하면 되고

함수정의는 실제로 해당 함수가 구현되어있는 부분이라고 생각하면된다.

// Path: /usr/include/stdio.h
...
/* Write a string, followed by a newline, to stdout.
   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern int puts (const char *__s);
...

stdio.h 에는 (함수 선언부분. 함수 프로토타입)은 있지만, 함수 정의가 없다.

 

링킹은 이 심볼에 해당하는 함수의 정의를 연결시켜주는 행위이다.

즉, 우리가 #include<stdio.h> 헤더파일을 이용해 프로그래밍을 하고,

전처리->컴파일->어셈블을 하면 오브젝트 파일(.o)을 얻을 수 있다.

 

오브젝트 파일은 엄연히 실행가능한 프로그램(바이너리)이지만,

그 안에서 사용하고있는 함수의 정의가 없어서(링킹이 되어있지않아) 실행이 불가능하다.

 

이에 오브젝트 파일이 생성되고나면, 라이브러리 함수의 정의를 연결시켜주는 링킹이 필수적이다.

쉽게 생각해보면, 오브젝트 파일은 '아 오케이 이 함수를 쓸꺼라고?? ㅇㅋㅇㅋ 근데 그게 뭔데?'

인 상태이다. => 함수의 심볼은 기록되어있지만, 정의는 알지못하는 상태

 

링킹을 통해서 '아까 말했던 함수 이 내용이야 이대로 실행하면 돼~'하고 알려줄 수 있다.

이때 이 링킹을 하는 방법이 두가지가 있는데,

 

정적 링크

정적링크는 라이브러리 함수의 정의가 내가 만든 프로그램에 다 포함된다.

때문에 라이브러리 함수를 사용할때 실제로 그 함수를 호출하면된다. (간단하다)

동적 링크

동적링크는 라이브러리 함수의 정의가 내가 만든 프로그램에 포함되지 않는다.

그러니까 라이브러리가 내 프로그램과는 따로 메모리에 올라가는 것인데,

나의 프로그램에 포함된게 아니니까 단순히 호출로 필요한 함수를 사용할 수 없다.

 

그래서 필요한게 PLT/GOT이다.

PLT와 GOT는 동적 링크를 위해 필요한 테이블이다.

동적 링크는 메모리에 별도로 올라가있는 라이브러리에서 내가 원하는 함수의 정의가 어디있는지 찾아주는 과정이고, PLT와 GOT가 그런 과정을 수행해준다. (심볼에 따른 정의가 뭔지 알려줌, printf 쓸게. 어딨어? 아 여기여기)

 

말이 어려웠는데,

PLT와 GOT는 필요한 (동적 라이브러리의)함수의 주소를 찾아 실행하기위해 사용하는 테이블이다.

 

PLT

PLT는 동적 링크된 심볼의 주소를 찾고, 실행해준다.

 

라이브러리 함수를 호출했을때 실행 과정은 아래와 같다.

(PLT가 해주는 역할은 다음과 같다.)

 

1. 라이브러리에서 해당 심볼을 탐색함 (찾기)

2. 해당 함수의 정의를 발견

3. 그 주소로 실행흐름을 옮김 (실행)

 

이때 반복적으로 사용되는 라이브러리 함수의 경우 실제 주소를 또 찾는게 귀찮아서

GOT라는 테이블을 만들었다.

 

GOT

GOT는 PLT가 한번 찾은 실제 함수의 주소를 기록해두는 테이블이다.

 

라이브러리 함수를 맨 처음에 호출해서 실제주소를 찾는 과정을 runtime resolve라고 하는데,

runtime resolve 이후에 라이브러리 함수를 호출하면 runtime resolve 없이 바로 GOT에 적힌 주소로 점프한다.

 

PLT/GOT를 이용한 동적 라이브러리 함수 호출과정

오늘의 핵심이다.

그림으로 살펴보자.

 

1. puts 함수를 호출 함

2. puts<plt>+0으로 점프

3. plt -> got로 점프

4. (처음 호출) got에 puts<plt>+6 주소가 있음 -> puts+<plt>+6으로 점프 -> runtime resolve (호출 & GOT에 기록)

4. (다시 호출) got에 puts 실제 주소가 있음 -> puts 실제 주소로 점프 -> puts 실행

 

참조하는 테이블/주소가 많아 헷갈린다.