지금의 프로그램은 워낙 방대해져서, 여기저기의 라이브러리들을 참조하여 만들어진다. 여기서는 실제 라이브러리를 만들어보고 이를 사용하는 방법에 대해 설명한다.
먼저 라이브러리를 만들기 위해 앞으로 사용할 예제 프로그램을 소개한다. 이해를 돕기위해 아래와 같이 아주 단순하게 만들었다.
#include <stdio.h> int test(void) { printf("test!!!!\n"); return 0; }
#include <stdio.h> int test1(void) { printf("test1111!!!!\n"); return 0; }
#include <stdio.h> int test2(void) { printf("test2222!!!!\n"); return 0; }
위의 예제 코드를 이용하여 각각 라이브러리를 만들어보겠다.
라이브러리를 만들기 위해서는 컴파일을 해야 한다.
#gcc -fPIC -c test.c test1.c test2.c #ls test.c test.o test1.c test1.o test2.c test2.o #file test.o test.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
빌드 후에 보면, 각각 오브젝트 파일(.o)이 생성된 것을 알 수 있다. 이 파일들은 실행(excutable)할 수 없다(빌드시 -c 옵션). 이 파일들을 사용하여 static library 를 만들어 보겠다.
아래와 같이 Makefile 을 작성한다.
CC=gcc all: lib static lib: gcc -fPIC -c test.c test1.c test2.c static: ar r mytest.a *.o clean: rm -f test *.o *.a
오브젝트 파일들을 모아 mytest.a 라는 정적 라이브러리를 생성했다.
이제 앞서 만든 라이브러리를 사용해보자. main.c 라는 소스파일을 만들고, 여기서 앞서 만든 라이브러리에 정의된 함수를 호출해보고, 이를 참조하여 빌드해보겠다.
#include <stdio.h> int main(void) { printf("here is static!!!\n"); test(); test1(); test2(); return 0; }
아래와 같이 Makefile 을 수정하였다.
CC=gcc all: lib static lib: gcc -fPIC -c test.c test1.c test2.c static: ar r mytest.a *.o gcc main.c mytest.a -o static-main clean: rm -f test *.o *.a static-main
빌드 하면, main 이라는 실행파일이 생성된 것을 볼 수 있다.
#./static-main here is static!!! test!!!! test1111!!!! test2222!!!! #rm -f mytest.a #./static-main here is static!!! test!!!! test1111!!!! test2222!!!!
위와 같이 빌드 시 참조했던 정적 라이브러리 파일을 삭제해도 전과 동일하게 실행된다.
이제는 공유 라이브러리를 만들어 보자! 먼저 Makefile 을 살펴보자.
CC=gcc all: lib static shared lib: gcc -fPIC -c test.c test1.c test2.c static: ar r mytest.a *.o gcc main.c mytest.a -o static-main shared: gcc -shared -Wl,-soname,libmytest.so -o libmytest.so *.o // 추가 clean: rm -f static-main shared-main *.o *.a /lib/x86_64-linux-gnu/libmytest.so
빌드하면 libmytest.so 라는 공유 라이브러리가 만들어 진다.
앞서 생성한 공유 라이브러리를 이용해 빌드해보자.
CC=gcc all: lib static shared lib: gcc -fPIC -c test.c test1.c test2.c static: ar r mytest.a *.o gcc main.c mytest.a -o static-main shared: gcc -shared -Wl,-soname,libmytest.so -o libmytest.so *.o gcc main.c -L/root/test-0606 -lmytest -o shared-main // 추가 clean: rm -f static-main shared-main *.o *.a /lib/x86_64-linux-gnu/libmytest.so
빌드시에 참조할 공유라이브러리의 경로와 파일명을 옵션 파라미터를 사용하여 지정해주어야 한다. 이때 주의할 것은 라이브러리의 이름으로 앞의 lib 와 뒤의 .so 를 뺀 나머지의 이름, 즉 mytest 가 라이브러리의 이름이다.
빌드되면, shared-main 파일이 생성된다. 이 파일을 실행해보자!
#./shared-main ./shared-main: error while loading shared libraries: libmytest.so: cannot open shared object file: No such file or directory
아래와 같은 에러메세지를 볼 수 있다. 무엇이 잘못된 것일까?
원인을 찾기 위해 strace 라는 프로그램을 사용하자.
#strace ./shared-main execve("./shared-main", ["./shared-main"], [/* 35 vars */]) = 0 brk(0) = 0x1b32000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff59eef9000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("tls/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("tls/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/root/test-0606/tls/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/root/test-0606/tls/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/root/test-0606/tls/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/root/test-0606/tls", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/root/test-0606/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/root/test-0606/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/root/test-0606/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/root/test-0606", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=83059, ...}) = 0 mmap(NULL, 83059, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff59eee4000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/tls/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/lib/x86_64-linux-gnu/tls/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/tls/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/lib/x86_64-linux-gnu/tls", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/lib/x86_64-linux-gnu/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/lib/x86_64-linux-gnu", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0 open("/usr/lib/x86_64-linux-gnu/tls/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib/x86_64-linux-gnu/tls/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64-linux-gnu/tls/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib/x86_64-linux-gnu/tls", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64-linux-gnu/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib/x86_64-linux-gnu/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64-linux-gnu/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib/x86_64-linux-gnu", {st_mode=S_IFDIR|0755, st_size=61440, ...}) = 0 open("/lib/tls/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/lib/tls/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/lib/tls/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/lib/tls", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/lib/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/lib/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/lib/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/lib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 open("/usr/lib/tls/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib/tls/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/usr/lib/tls/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib/tls", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib/x86_64", 0x7fff01fd9020) = -1 ENOENT (No such file or directory) open("/usr/lib/libmytest.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) stat("/usr/lib", {st_mode=S_IFDIR|0755, st_size=16384, ...}) = 0 writev(2, [{"./shared-main", 13}, {": ", 2}, {"error while loading shared libra"..., 36}, {": ", 2}, {"libmytest.so", 12}, {": ", 2}, {"cannot open shared object file", 30}, {": ", 2}, {"No such file or directory", 25}, {"\n", 1}], 10./shared-main: error while loading shared libraries: libmytest.so: cannot open shared object file: No such file or directory ) = 125 exit_group(127) = ? +++ exited with 127 +++
출력된 내용을 보면, libmytest.so 라는 공유라이브러리를 찾지 못하는 것이다. 리눅스 시스템에서는 라이브러리를 특정 디렉토리에 저장하고 이 경로에 PATH 를 지정한다. 따라서 공유라이브러리로 빌드된 프로그램은 어디서든 실행이 가능하다.
이 경로가 어디인지 확인하기 위해서, '/etc/ld.so.conf' 파일을 보자. 여기서 다시 '/etc/ld.so.conf.d/x86_64-linux-gnu.conf' 파일을 보면, '/lib/x86_64-linux-gnu/' 아래에 PATH 가 걸린 것을 알 수 있다.
이제 앞서 만든 공유 라이브러리 파일(libmytest.so)을 PATH 가 걸린 곳으로 복사하자.
CC=gcc all: lib static shared lib: gcc -fPIC -c test.c test1.c test2.c static: ar r mytest.a *.o gcc main.c mytest.a -o static-main shared: gcc -shared -Wl,-soname,libmytest.so -o libmytest.so *.o gcc main.c -L/root/test-0606 -lmytest -o shared-main mv libmytest.so /lib/x86_64-linux-gnu/ // 추가 clean: rm -f static-main shared-main *.o *.a /lib/x86_64-linux-gnu/libmytest.so
이제 다시 실행해보자.
#./shared-main here is static!!! test!!!! test1111!!!! test2222!!!! #rm /lib/x86_64-linux-gnu/libmytest.so rm: 일반 파일 `/lib/x86_64-linux-gnu/libmytest.so'를 제거할까요? y root@ubuntu:~/test-0606# ./shared-main ./shared-main: error while loading shared libraries: libmytest.so: cannot open shared object file: No such file or directory
제대로 실행된다. 이 상태에서 공유 라이브러리를 삭제해보자. 실행 에러가 발생하는 것을 알 수 있다.
위의 방법은 PATH 를 잡기 위해 기존의 PATH 가 걸린 곳으로 공유 라이브러리 파일을 복사했다. 하지만, 복사할 필요없이 현재(공유라이브러리가 있는 경로)위치를 바로 PATH 가 걸리게 할 수도 있다.
아래와 같이 쉘에서 실행하자.
#export LD_LIBRARY_PATH=`echo $LD_LIBRARY_PATH`:`pwd`
그리고 다시 shared-main 을 실행해보자.