동적 라이브러리 로드

정적 라이브러리를 로드하는 시점

  • object 파일(.o 파일)을 링크할 때
  • 정적 라이브러리 로딩 / 정적 링크라고 함
  • 실행파일의 크기가 커짐
    • 라이브러리가 파일에 같이 끼어있기 때문
  • 라이브러리를 추가/제거하거나 기존에 사용되던 라이브러리가 업데이트됐을 경우 다시 프로그램을 컴파일해야 한다는 단점 존재

이러한 정적 링크의 단점을 극복하기 위해?

  • shared library라는 형식으로 라이브러리를 제작
  • 실행 시점(runtime)에 사용할 라이브러리를 연결하여 사용
  • 사용되는 라이브러리의 확장자는 .so (shared object)
    • 이후 언급되는 동적 라이브러리는 모두 so 파일 또는 shared object 파일로 통합하여 언급함

0. 준비사항

  • 대상 프로그램의 컴파일 시 ‘-ldl’ 옵션을 주어야 함

1. dlopen

#include <dlfcn.h>

void *dlopen(const char *filename, int flag);
  • 매개변수
    • filename : 로드할 동적 라이브러리 파일명
    • flag : so 파일을 메모리로 적재하는 방법 또는 시점과 관련된 설정값
      • (필수) RTLD_LAZY : shared object에 포함된 함수가 최초 호출되는 시점에 해당 so 파일이 로딩됨. shared object에 포함된 전역변수는 호출과 동시에 생성.
      • (필수) RTLD_NOW : dlopen()함수 호출과 동시에 loading
      • (이하 옵션, ' | ' 연산으로 필요한 flag 세팅)
      • RTLD_GLOBAL : loading한 이 라이브러리를 다른 프로그램에서도 사용
        • 이 공유 오브젝트에서 정의하는 심볼들이 이후 적재되는 공유 오브젝트들의 심볼 결정에 쓰일 수 있게 한다...라는데 이게 무슨 말일까?
      • RTLD_LOCAL : loading한 이 라이브러리는 이 프로그램에 한해서 사용 (default)
      • RTLD_NODELETE: dlclose()시에도 메모리에서 내리지 않는다.
        • 이후 dlopen()으로 그 오브젝트를 재적재하는 경우에 오브젝트의 정적 변수들이 다시 초기화되지 않는다.
      • RTLD_NOLOAD : dlopen()해서 메모리에 로딩하지 않는다. (라이브러리의 존재 여부 및 오류 체크용)
        • filename에 지정된 라이브러리 파일이 존재하는지, 또는 라이브러리 파일이 이미 본 프로그램에 로딩되어 있는지 여부를 확인하기 위해 사용
        • 만약 존재하지 않는다면 NULL이 반환되고, 이미 적재되어 있다면 해당 shared object에 대한 object handle을 반환한다.
      • RTLD_DEEPBIND: 이 라이브러리에 포함된 함수, 변수 등이 다른 라이브러리에 포함된 같은 이름의 함수, 변수들을 무시하고 우선 순위를 갖는다는 의미
  • 반환값
    • NULL
      • so 파일이 존재하지 않음
      • so 파일 로딩에 실패
    • NULL 아님
      • void* 형태의, shared object에 대한 handle 반환
      • so 파일을 정상적으로 로딩
      • so 파일이 이미 로딩되었음.
  • 이후 변화
    • 로딩된 shared object의 참조 카운트가 1 증가
void *lib_handle = dlopen(modulePath.c_str(), RTLD_LAZY);

2. dlsym

#include <dlfcn.h>

void *dlsym(void *handle, const char *symbol)
  • 매개변수

    • handle
      • dlopen에서 반환받은 shared object handle
    • symbol
      • shared object에서 찾고자 하는 함수명 또는 변수명
  • 반환값

    • NULL
      • symbol이 존재하지 않음
      • 에러의 경우 dlerror 함수를 호출하면 에러의 원인 파악 가능
    • NULL이 아님
      • 해당 symbol과 연관된 주소값이 반환
      • 이를 받아서 함수포인터 또는 변수로 저장
      • 이후 필요할 때 해당 함수/변수를 호출하여 사용하면 됨
int (*getData)() = (int (*)())dlsym(lib_handle, "getLibraryData");

3. dlclose

#include <dlfcn.h>

int dlclose(void *handle)
  • 매개변수

    • handle
      • dlopen에서 반환받은 shared object handle
  • 반환값

    • 0 : 정상적으로 close됨
    • 그 외의 값 : 에러 발생
      • 이 경우 dlerror로 에러 메시지 확인 필요
  • 이후 변화

    • 로딩된 shared object의 참조 카운트가 1 감소
    • 참조 카운트가 0이 되면 메모리에서 삭제
int ret = dlclose(lib_handle);
if ( ret != 0 ) {
    // dlerror 호출
}

4. dlerror

#include <dlfcn.h>

char *dlerror(void)
  • 매개변수

    • 없음
  • 반환값

    • 마지막 dlerror 호출 이후, dlopen 호출에서 발생한 에러 상황에 대한 사유를 문자열로 반환
    • 만약 에러가 발생한 적이 없을 경우 NULL이 반환됨
int ret = dlclose(lib_handle);
if ( ret != 0 ) {
    printf("Failed to close shared object : [reason : %s]\n", dlerror());
}