====== 라이브러리 생성 및 사용하기 ======
지금의 프로그램은 워낙 방대해져서, 여기저기의 라이브러리들을 참조하여 만들어진다. 여기서는 실제 라이브러리를 만들어보고 이를 사용하는 방법에 대해 설명한다.
====== 예제 프로그램 ======
먼저 라이브러리를 만들기 위해 앞으로 사용할 예제 프로그램을 소개한다. 이해를 돕기위해 아래와 같이 아주 단순하게 만들었다.
===== test.c =====
#include
int test(void)
{
printf("test!!!!\n");
return 0;
}
===== test1.c =====
#include
int test1(void)
{
printf("test1111!!!!\n");
return 0;
}
===== test2.c =====
#include
int test2(void)
{
printf("test2222!!!!\n");
return 0;
}
====== 라이브러리 생성 ======
위의 예제 코드를 이용하여 각각 라이브러리를 만들어보겠다.
===== 정적 라이브러리(static library) 생성하기 =====
라이브러리를 만들기 위해서는 컴파일을 해야 한다.
#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 라는 정적 라이브러리를 생성했다.
== 정적 라이브러리(static library) 사용하기 ==
이제 앞서 만든 라이브러리를 사용해보자. main.c 라는 소스파일을 만들고, 여기서 앞서 만든 라이브러리에 정의된 함수를 호출해보고, 이를 참조하여 빌드해보겠다.
==== main.c ====
#include
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!!!!
위와 같이 빌드 시 참조했던 정적 라이브러리 파일을 삭제해도 전과 동일하게 실행된다.
===== 공유 라이브러리(shared library) 생성하기 =====
이제는 공유 라이브러리를 만들어 보자! 먼저 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 라는 공유 라이브러리가 만들어 진다.
===== 공유 라이브러리(shared library) 사용하기 =====
앞서 생성한 공유 라이브러리를 이용해 빌드해보자.
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 을 실행해보자.
----
{{indexmenu>:#1|skipns=/^(wiki|etc|diary|playground)$/ skipfile=/^(todays|about|guestbook)$/ nsort rsort}}
----