Header only : include하여 사용하는 라이브러리 (ex : stl, etc...)
Static library : 빌드 프로세스에서 링커가 연결해주는 라이브러리 (ex : .lib, .a)
Dynamic libiray : 동적 라이브러리, 2개의 종류로 나뉜다. (ex : .dll, .so)
스태틱 라이브러리의 생성 과정은 링커가 실행 파일을 만들기 전에 오브젝트 파일들을 묶어서 재배치(안쓰는 섹션 걸러내기) 되기 전 상태인 아카이브 파일을 만드는데 이 파일이 바로 스태틱 라이브러리(.lib) 파일이다.
아카이브(라이브러리) 파일과 실행 파일의 기준은
링크 되기 전(재배치 되기 전) 상태로 놔둔 오브젝트 파일들을 묶어 아카이브 파일을 만들면 이 파일이 스태틱 라이브러리가 되고, 링커가 재배치 한 오브젝트 파일 묶음은 실행 파일(.exe)이 된다.
즉, 오브젝트 파일 묶음의 재배치 유무에 따라 라이브러리 또는 실행 파일이 된다.
스태틱 라이브러리 역시 링크(사용) 하려면 스태틱 라이브러리의 실제로 쓰이는 섹션만 가져와서 relocation(재배치)하여 오브젝트 파일과 결합해 실행 파일을 만든다.
스태틱 라이브러리 (cat.lib)의 생성과 연결 방법은 다음과 같다.
cat.o 오브젝트 파일(cat.cpp + cat.h)을 라이브러리 파일로 만들려면 명령창에
ar -rs libcat.a cat.o
을 입력하면 libcat.a 스태틱 라이브러리 파일을 만들 수 있다.
그리고 이 스태틱 라이브러리(libcat.a)를 링킹하여 main.cpp을 빌드할려면
g++ main.cpp -L. -lcat
을 명령창에 입력하여 a.out 실행 파일이 생성된다.
(main.cpp를 빌드할 때 오프셋을 사용하여 libcat.a 라이브러리가 링크됬다.)
그리고 실행 파일 a.out을 실행하면 libcat 라이브러리에 있던 "meow"가 main에서 제대로 호출되는 걸 확인할 수 있다.
라이브러리를 사용하려면 caller가 되는 오브젝트 파일(.o)에서 사용할 라이브러리 파일(.lib)를 링킹하여 새로운 실행파일(.exe)를 빌드(생성)해야지 라이브러리의 함수/객체를 사용할 수 있다.
결론적으로 스태틱 라이브러리는 오브젝트 파일들을 모은 하나의 파일이며
사용법은 빌드 시에 라이브러리 파일을 링킹(연결)하여 실행파일을 만들어 사용한다.
다이나믹(공유) 라이브러리는 링크 타임이 아닌 프로세스 로드 타임 또는 런타임에 바인딩된다.
그래서 느리지만, 스태틱 라이브러리보다 실행 파일 용량이 적은 장점이 있다.
로드 타임 바인딩은 로더가 등장하는 로드 타임 시 동적 라이브러리를 바인딩하는 방식이다.
공유 라이브러리 용으로 오브젝트 파일을 만들 때는 추가적인 오프셋이 필요하다.
먼저 오브젝트 파일을 만들 때 SPIC(shared position Independent code) 옵션을 붙여 공유(다이나믹) 라이브러리용으로 오브젝트 파일을 만든다.
SPIC의 뜻은 Shared는 공유라는 뜻이고 PIC의 뜻은 위치 독립적 코드(절대 주소가 아닌 상대 주소)라는 뜻인데 공유 라이브러리는 스태틱 처럼 정해진 장소가 아닌 메모리의 어느 위치에나 배치 될 수 있기에(여러 프로세스에서 라이브러리를 사용한다면 해당 프로세스 기준 상대 주소로 설정하여 라이브러리를 배치해야할 필요성이 있기에) 라이브러리를 상대 주소로 설정하는 것이 필요하다.
g++ -foo.cpp -fPIC -c // SPIC를 적용한 오브젝트 파일 생성
g++ -shared foo.o -o libfoo.so // foo.o 오브젝트 파일을 기반으로 공유 라이브러리(fibfoo.so)를 생성한다.
그래서 이 SPIC로 만들어진 오브젝트 파일들을 모아서 공유 라이브러리(.so)를 만들 수 있다.
그리고 .so 파일과 이 라이브러리를 사용할 오브젝트 파일을 링킹하여 실행 파일을 만든다.
중요한 것은 스태틱 라이브러리와 다르게 실행 파일에 라이브러리 내용이 들어가는 게 아니라 라이브러리의 심볼 정보만 들어가게 된다.
이 심볼 정보를 토대로 로드 타임에 해당 라이브러리를 가져와서 프로그램에 바인딩된다.
동적 링킹으로 생성 된 실행 파일의 용량은 스태틱 라이브러리를 바인딩 했을 때보다 작으며
프로그램을 로드(실행)할 때 (링킹 프로세스에서 들어간)심볼 정보를 토대로 라이브러리(.so)를 불러오는 식으로 실행된다.
런타임 바인딩은 프로그램 실행 중(런타임)에 공유 라이브러리를 바인딩하는 방식을 말한다.
일단 런타임 바인딩을 사용하려면 <dlfcn.h> 헤더 파일의 추가한다.
라이브러리를 사용할 소스에서 라이브러리 경로가 지정된 핸들을 생성하고 (void * handle)
라이브러리 함수의 심볼을 불러올 함수 포인터를 생성한다. void (*fooPtr)()
그리고 함수 포인터를 통해 라이브러리의 함수를 호출하면 된다.
그 후 빌드를 통해 실행 파일을 생성하여 실행 할 수 있는데
특별한 점으론 런타임에 라이브러리를 바인딩하여 실행되기에 foo() 함수의 선언(Declaration)이 없어도 된다. (함수 포인터를 통해 심볼을 받아 호출되기 때문)
결과적으로 프로그램 실행 과정 중에
dlopen("./libfoo.so", RTLD_LAZY);
(void(*)())dlsym(handle, "_Z3foov");
(*fooPtr)();
공유 라이브러리의 런타임 바인딩 과정을 함수 포인터를 통해 소스 코드로 구현하였다.
다이나믹 라이브러리를 사용하는 대표적인 경우는 유저가 실행 파일을 가지고 있고
이 실행 파일의 업데이트가 필요할 때, 다이나믹 라이브러리만 바꿔(갱신해)준다면 실행 파일을
위한 Re-Build 과정을 생략할 수 있기에 이러한 경우 다이나믹 라이브러리를 사용하는 솔루션이
용이하다.