logo

English

이곳의 프로그래밍관련 정보와 소스는 마음대로 활용하셔도 좋습니다. 다만 쓰시기 전에 통보 정도는 해주시는 것이 예의 일것 같습니다. 질문이나 오류 수정은 siseong@gmail.com 으로 주세요. 감사합니다.

[shared lib] so 동적 라이브러리 만들기와 사용법 - 리눅스

by digipine posted Nov 01, 2017
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print
동적 적재 라이브러리는 프로그램이 시작할때가 아닌 다른 시기에 적재되는 라이브러리이다. 이것들은 플러그인이나 모듈을 구현할때 적합하다. 왜냐하면 그것들이 필요해질때까지 적재를 기다릴 수 있기 때문이다. 예를들어, PAM(Pluggable Authentication Modules)시스템은 관리자가 인증을 관리하는 것을 허용하기 위해 DL라이브러리를 사용한다. 이것들은 또한 때때로 코드를 머신 코드로 바꾸고 효율을 위해 멈추지 않고 컴파일된 코드로 만드는 인터프리터를 구현하는데 유용하다. 이것은 실시간의(just-in-time) 컴파일러나 MUD(multi-user dungeon)을 구현하는데에 유용하다.
 
리눅스에서, DL 라이브러리는 그들의 포멧의 관점에 있어서 특별하지 않다; 그들은 표준의 오브젝트 파일로 만들어지거나, 위에서 언급된 표준의 공유 라이브러리 파일로 만들어진다. 주요 차이점은 프로그램의 링크시나 시작시에 적재되지 않는다는 것이다; 대신, 라이브러리를 열거나, 심볼을 찾거나, 에러를 조정하거나, 라이브러리를 닫는 특별한 API가 있다. C 사용자들은 이런 API를 사용하기위해 dlfcn.h라는 헤더파일을 포함해야 한다.
 
리눅스에서 사용되는 인터페이스는 솔라리스에서 사용되는 인터페이스와 같다. 이것을 나는 ``dlopen()'' API라고 불를 것이다. 그러나, 이 같은 인터페이스가 모든 플랫폼에서 지원되는 것은 아니다; HP-UX는 shl_load()방법을 사용하고, 윈도우즈는 완전히 다른 인터페이스의 DLLs을 사용한다. 만약 당신이 폭넓은 포팅성이 목표라면, 당신은 이런 다른 플랫폼을 숨기는 라이브러리를 생각해야 할것이다. 하나의 방법은 모듈의 동적 적재를 지원하는 glib 라이브러리이다; 그것은 이 함수들의 포팅가능한 인터페이스를 구현하기위해 플랫폼의 동적 적재 루틴을 사용한다. 
 
http://developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html의 링크에서 glib에 대해 많은 것을 찾아볼 수 있다. glib 인터페이스가 이 문서에 잘 설명되있기 때문에 더 언급하지 않겠다. 다른 방법은 GNU libtool의 일부인 libltdl을 사용하는 것이다. 당신이 이것보다 더 기능적인것을 원한다면, CORBA ORB(Object Request Broker)를 찾아보아라.
 
1. dlopen()
dlopen 함수는 라이브러리를 열고 그것이 사용되도록 준비시켜준다. C에서 프로토타입은 다음과 같다:
  void * dlopen(const char *filename, int flag);
파일이름이 ``/''(즉, 절대경로)로 시작한다면 dlopen()은 즉시 사용하려 한다(라이브러리를 찾으려 하지 않는다). 그렇지 않으면, dlopen()은 다음과 같은 순서로 라이브러리를 찾는다:
 
    1.    사용자의 LD_LIBRARY_PATH의 환경변수의 콜론으로 구분지어진 디렉토리
    2.    (/etc/ld.so.conf에서 파생된) /etc/ld.so.cache에 명시되어있는 라이브러리의 목록
    3.    /lib, /usr/lib. 순서를 주의하라; 이것은 예전의 a.out로더가 사용한 순서의 역이다. 예전의 a.out로더는 프로그램을 로드할때 /usr/lib을 찾고 /lib을 찾았다(ld.so(8)의 man페이지를 참고하라). 보통은 어떤 디렉토리나 다른디렉토리 하나에만 라이브러리가 있기 때문에(둘다 없을수도 있다) 문제가 되지 않는다. 그리고 같은이름을 가진 다른 라이브러리는 잠재적인 위험을 가지고 있다.
dlopen()에서 flag의 값은 ``동적 라이브러리가 실행되면서 코드의 정의되지 않은 심볼을 처리하라''의 의미를 가진 RTLD_LAZY이거나, ``dlopen()이 리턴하기 전에 모든 정의되지 않은 심볼을 처리하고, 그것이 되지 않을 경우 실패하라''의 의미를 지닌 RTLD_NOW가 있다. RTLD_GLOBAL은 flag의 값속에 옵션으로 쓸수 있다. 의미는 라이브러리에 적재되는 외부 심볼들은 그 후에 적재되는 라이브러리에 의해서 가능하다는 것이다. 디버깅시에, RTLD_NOW를 쓰기를 원할것이다; RTLD_LAZY는 정의되지 않은 심볼이 있으면 알아볼수 없는 에러를 내기 때문이다. RTLD_NOW를 쓰면서 라이브러리를 여는 것은 약간의 시간이 더 걸린다(하지만 나중에 찾는 속도는 빨라진다); 만약 이것이 유저 인터페이스 문제를 일으킨다면, 당신은 나중에 RTLD_LAZY로 바꿀 수 있다.
 
라이브러리들이 서로 의존한다면(예를들어 X가 Y에 의존한다면), 당신은 의존당하는 것을 먼저 로드해야한다(이 예제에서 Y를 로드하고 X를 로드해야 한다).
dlopen()의 리턴 값은 다른 DL라이브러리 루틴이 사용하기에 애매모호하게 느껴지는 ``핸들''이다. dlopen()은 로드가 성공하지 못하거나 당신이 체크해야 할 필요가 있다고 생각하면 NULL을 리턴한다. dlopen()에 의해 같은 라이브러리가 한번이상 로드되면 같은 파일 핸들이 리턴된다.
라이브러리가 _init이라 이름지어진 루틴을 사용한다면(export), 그 코드는 dlopen()이 반환하기 전에 실행된다. 당신은 초기화 루틴을 구현하기위해 당신의 라이브러리에서 이 사실을 사용했을것이다.
 
2. dlerror()
에러는 dlerror()를 호출함으로써 보고될수 있다. dlerror()는 dlerror(), dlsym(), dlclose()중 마지막 부른것의 에러의 스트링을 반환한다. 하나의 이상한점은 dlerror()를 부르고 나서 dlerror()를 부르는 것은 그 사이에 다른 에러가 없으면 NULL을 반환한다는 것이다.
 
3. dlsym()
DL라이브러리를 사용하는 중요한 루틴은 dlsym이다. 이것은 열려진 라이브러리의 심볼의 값을 찾아준다. 이 함수는 다음과 같이 정의된다:
 void * dlsym(void *handle, char *symbol);
핸들은 dlopen에 의해 반환된 값이고, 심볼은 NIL로 끝나는 스트링이다. 당신이 할 수 있다면 있다면, dlsym()의 결과를 void* 포인터로 저장하지 마라. 왜냐하면, 당신은 사용할때마다 캐스트해서 사용해야 하기때문이다(그리고 다른사람들이 프로그램을 유지하는데에 더 적은 정보를 줄것이다).
dlsym()은 심볼이 없으면 NULL을 반환할 것이다. 만약 당신의 심볼에 NULL이나 0이 없다는 것을 알면 좋겠지만, 다음과 같은 잠재적인 문제가 있다: 당신이 받은 NULL이 에러인가, 아니면 심볼의 값이 NULL인가? 표준적인 해결법은 dlerror()를 부르고(전에 있던 에러들을 없앤다), dlsym()을 불러서 심볼의 값을 부르고, dlerror()를 불러서 에러가 났는지 체크한다. 단순한 코드는 다음과 같다:
 
 dlerror(); /* clear error code */
 s = (actual_type) dlsym(handle, symbol_being_searched_for);
 if ((err = dlerror()) != NULL) {
  /* handle error, the symbol wasn't found */
 } else {
  /* symbol found, its value is in s */
 }
 
4. dlclose()
dlopen()의 반대는 dlclose()로서, DL 라이브러리를 닫아준다. DL 라이브러리가 동적 파일 핸들의 링크 수를 관리하기 때문에, 동적 라이브러리는 dlopen이 성공한 만큼 모두 dlclose를 불러주기 전에 다 할당이 없어지지는 않는다. 따라서, 같은 프로그램이 같은 라이브러리를 여러번 불러주는것은 문제가 되지 않는다. 라이브러리가 할당이 없어진다면, 함수의 _fini가 불린다(존재한다면);  dlclose()는 성공하면 0을 리턴하고, 아니면 0이 아닌 값을 리턴한다; 어떤 리눅스 메뉴얼은 이것을 언급하고 있지 않다.
 
5. DL 라이브러리 예제
만약 so lib 이 이름이 libBoo.so 라면 다음과 같은 명령으로 동적 라이브러리를 생성한다.
 
    #include <stdio.h>
    
    int addone(int val)
    {
       return val + 1;
 
        }
gcc -fPIC -c file1.c
gcc -shared -\I, -soname,libBoo.so -o libBoo.so file1.o
 
    #include <stdlib.h>
    #include <stdio.h>
    #include <dlfcn.h>
 
    int main(int argc, char **argv) {
        void *handle;
        int (*add1)(int);
        char *error;
 
        handle = dlopen ("./libBoo.so", RTLD_LAZY);
        if (!handle) {
            fputs (dlerror(), stderr);
            exit(1);
        }
 
        add1 = dlsym(handle, "addone");
        if ((error = dlerror()) != NULL)  {
            fputs(error, stderr);
            exit(1);
        }
 
        printf ("%d\n", (*add1)(2));
        dlclose(handle);
    }
 
만약 이 프로그램의 파일이름이 "foo.c"라면, 다음과 같은 명령으로 프로그램을 만들 수 있을 것이다:
    gcc -o foo foo.c -ldl
TAG •

List of Articles
No. Subject Author Date Views
23 [C/C++] Random UUID String 생성 코드 digipine 2021.10.21 1302
22 소켓 통신을 이용한 HTTP 서버 개발 강의록 file digipine 2020.08.01 1482
21 C++ Atomic 클래스에 대해서 file 엉뚱도마뱀 2017.12.13 2139
20 C++ 컴파일 오류(error): variable 'std::istringstream sstream' has initializer but incomplete type digipine 2017.11.02 21077
19 fopen 파일 열기 모드 옵션 정리 digipine 2017.11.02 3894
18 make -j 옵션으로 컴파일 속도 최적화 하기 digipine 2017.11.01 2759
17 [Linux] Pthread 사용법, Thread 동기화 총정리 digipine 2017.11.01 294048
16 [linux] zlib build 방법 digipine 2017.11.01 1483
15 Linux C 언어로 Shell 명령어 실행하기 digipine 2017.11.01 22587
14 Introduce to Singly-linked List file digipine 2017.11.01 1288
» [shared lib] so 동적 라이브러리 만들기와 사용법 - 리눅스 digipine 2017.11.01 6434
12 Unix C/C++ Input and Output Function Reference digipine 2017.11.01 88072
11 wchar_t에 대하여 digipine 2017.11.01 7343
10 C 에서 Overloading 구현 digipine 2017.11.01 1790
9 C를 이용한 객체지향 프로그래밍 digipine 2017.11.01 568
8 Callback in C++ 와 Delegate 차이점 digipine 2017.11.01 2525
7 Solaris에서 pmap을 이용하여 백그라운드 프로세스 메모리 크기 구하기 digipine 2017.10.29 28598
6 Solaris 10에 개발 Tool (gcc,vim,gdb) 설치 digipine 2017.10.29 1257
5 brute-force 알고리즘을 이용한 패턴 위치 찾기 digipine 2017.10.29 1501
4 MD5 파일 변조 검사 관련 소스 (리눅스/Windows) digipine 2017.10.29 2613
Board Pagination Prev 1 2 Next
/ 2