콘텐츠로 건너뛰기
Home » 정적/공유 라이브러리 만드는 법

정적/공유 라이브러리 만드는 법

정적 라이브러리

정적 라이브러리는 컴파일된 코드, 즉 오브젝트들의 모음이라고 이해할 수 있다. 컴파일러로 생성한 오브젝트 파일을 ar 명령을 사용해서 하나의 파일로 아카이브를 만들면 정적 라이브러리를 생성할 수 있다.

정적 라이브러리를 사용할 때에는 -L ./ -lmylib 과 같은 식으로 -L, -l 스위치를 사용해서 라이브러리 경로와 파일을 지정하여 컴파일하면 된다. 이렇게 컴파일한 프로그램에는 라이브러리가 실행파일에 통째로 포함되기 때문에 라이브러를 함께 배포할 필요가 없다. (대신 그만큼 실행파일의 크기가 그만큼 커진다.)

sqlite3 소스를 받아서 아래와 같은 식으로 정적 라이브러리를 만든 후 실행파일을 만들 수 있다. (물론 한 번에 컴파일하고 빌드할 수도 있지만.. 방법만 살펴본다는 차원에서…)

ar 명령에서 r 옵션은 기존 라이브러리에 새로운 오브젝트를 추가한다는 뜻이며, c 옵션은 아카이브가 없으면 새로 만든다는 뜻이다. 생성되는 아카이브의 확장자는 .a 를 쓰는데, 윈도 환경에서는 .lib 을 사용하는 경우도 많은 것 같다.

  • lib{라이브러리이름}.a
  • {라이브러리이름}.lib
$ gcc -c sqlite3.c
$ ar rc libsqlite3.a sqlite3.o
$ gcc shell.c -L ./ -lsqlite3 -o sqlite3.exe

윈도에서도 mingw32-x64를 사용해서 동일하게 정적 라이브러리를 만들 수 있다. 정적 라이브러리를 포함하여 컴파일할 때에 -l 옵션의 라이브러리 이름은 확장자와 파일 이름앞의 lib 을 생략한 부분의 라이브러리 이름을 사용한다. (만약 “sqlite3.lib” 이라고 아카이브를 만든 경우에도 -lsqlite3 로만 파라미터를 주면 정상적으로 컴파일된다.)

공유 라이브러리

정적 라이브러리를 만들었던 소스는 코드의 변경 없이 공유 라이브러리로도 만들 수 있다. 즉, 정적 라이브러리와 공유 라이브러리는 컴파일하는 방법만 차이가 있을 뿐 라이브러리를 만들기 위한 소스에는 아무런 차이가 없다.

<Linux/Unix>
$ gcc -fPIC -c sqlite3.c
$ gcc -shared -W1,-soname,libsqlite3.so.1 -o libsqlite3.so.1.0.1 sqlite3.o
$ cp libsqlite3.so.1.0.1 /usr/local/lib
$ ln -s /usr/local/lib/libsqlite3.so.1.0.1 /usr/local/lib/libsqlite3.so

<Windows>
$ clang -fPIC -c sqlite3.c
$ clang -shared sqlite3.dll

리눅스일 때의 명령이 보다 더 복잡해 보이는데, 이는 라이브러리를 빌드하고 설치까지 하는 과정을 포함하기 때문에 그러하다. 이름이 복잡해 보이는 것은 버전을 이름에 추가했기 때문이며, /usr/local/lib/ 디렉토리에 libsqlite3.so 라는 이름으로 심볼릭 링크를 만들어 주었다. 추후에 소스가 변경되었다면 libsqlite3.so.1.0.2 와 같은 식으로 빌드하고 심볼릭 링크만 새 버전의 라이브러리로 바꿔주면 라이브러리의 버전 업그레이드를 할 수 있다.

윈도에서는 .dll 파일로 만든 후 %PATH% 경로에 해당 파일을 옮기거나 복사해두면 사용할 수 있다.

리눅스와 윈도의 공유 라이브러리 개념은 좀 다른데, 윈도에서 dll 은 “동적 연결 라이브러리”로 항상 동적 라이브러리이다. 대신에 리눅스에서는 동적 라이브러리와 공유 라이브러리가 구분된다. 동적 라이브러리는 프로그램이 실행되는 중에 라이브러리 내의 특정한 함수가 필요한 시점에 해당 라이브러리를 적재하고, 호출할 함수의 심볼을 찾아서 호출해야 한다. 따라서 동적 라이브러리를 사용하려는 프로그램의 호출부의 코드가 정적 라이브러리를 사용하는 것과 다르다.

하지만 리눅스의 공유 라이브러리는 정적 라이브러리와 동일한 호출부의 코드를 갖는다. 다만, 공유 라이브러리에 포함된 오브젝트 파일들이 실행파일과 별도의 위치에 분리되어 있는 것이다. 공유 라이브러리는 파일만 분리된 형태의 응용 프로그램의 일부이며, 따라서 실행파일이 로드된 직후에 바로 로드된다. 그리고 프로그램 코드는 정적 라이브러리와 똑같은 방식으로, 즉 프로그램 내부에 호출하려는 함수가 모두 존재하는 형태로 사용된다.

동적 라이브러리 사용

동적라이브러리는 공유라이브러리와 완전히 같은 방식으로 만들어진다. 차이점은 라이브러리를 사용하는 코드에서 접근하는 방법이다. 리눅스에서는 이러한 라이브러리를 호출하기 위한 방법으로 다음과 같은 함수들을 제공하고 있다.

#include <dlfcn.h>

void *dlopen(const char* filename, int flag);
const char* dlerror(void);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);

윈도의 공유 라이브러리 사용 방법은 좀 다르다고 하는데, 여기까지는 내가 쓸 일이 없어서 따로 찾아보지는 않았다. 필요한 일이 생기면 그 때 내용을 좀 더 찾아봐야겠다. 그럼 이만.