RPATH 한번에 이해하기

KiJeong·2022년 8월 16일
2

Linux

목록 보기
7/8

RPATH 정의

rpath란 Run-time search path로, 위키피디아에 따르면 “rpath는 실행 파일이나 라이브러리에 하드코딩 된 런타임 검색 경로를 지정한다.” 고 되어있다.
rpath 는 바이너리 파일(shared library 또는 실행 파일)에 임베드된 경로이다. 이 경로는 바이너리가 런타임시에 링크해야 하는 라이브러리 중 최우선 검색 경로이다.

사용해야 하는 경우

고객의 상황에 따라 LD_LIBRARY_PATH 또는 /usr/lib 경로를 설정이 어려운 경우가 있다(보안상 이슈 등).
그럴 경우에, 배포하는 제품에서 필요한 shared library (편의상 libA.so 라고 명명) 위치를 설정하고, libA.so 라이브러리의 디펜던시 라이브러리들을 libA.so 라이브러리 기준으로 찾게 하려고 한다.

설정 방법

리눅스는 ELF 형식의 바이너리는 rpath 라고 부르는 "실행시 라이브러리를 찾을 경로 정보"를 컴파일 시점에 넣어줄 수 있으며 이 정보는 LD_LIBRARY_PATH 환경 변수 전에 참고된다. (참고)
컴파일할 때 Makefile에 $ORIGIN 키워드를 넣어 설정한다.

LDFLAGS += -Wl,-rpath,'$$ORIGIN/../lib'

$ORIGIN이라고 하면 Makefile의 규칙인 변수에 $를 적어주는 것으로 적용하므로 $$ 두개를 사용해야 한다.

$ORIGIN 키워드는 Linux, Solaris(SUN), HP-UX IA에 있지만 AIX에는 없는걸 확인했다.

1. $ORIGN 키워드란?

라이브러리가 생성된 후 런타임 링커가 프로그램을 시작시킬 때 $ORIGIN 키워드를 프로그램을 실행시킨 현재 경로로로 전환시킨다. (나: 영어는 프로그램이라 번역했지만 설명을 이어 보면 $ORIGIN이 설정된 shared library라고 볼수있겠다.)

The $ORIGIN token is kept inside your program after it's created and when the runtime linker starts to run your program it will replace $ORIGIN with the current path that your program was invoked from. (링크)

2. rpath 옵션은 무엇인가?

man ld 명령어로 -rpath 옵션 설명을 확인해봤다. -rpath 옵션을 사용하면 런타임시 shared object 위치를 알려준다고 되어있다.

-rpath=dir
Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly included in the link;

테스트 해보기

설정한 rpath 가 잘 동작하는지 확인해본다.

1. 빌드

컴파일할 때 Makefile에 $ORIGIN 키워드를 넣어 설정한다.

LDFLAGS += -Wl,-rpath,'$$ORIGIN/../lib2'

rpath를 $ORIGIN/../lib2 라고 설정했다. 디펜던시 라이브러리는 ../lib2 에 찾겠다는 뜻.

rpath 가 설정되었는지는 readelf -d 로 확인해본다.

# readelf -d A.so

Dynamic section at offset 0x1abc23 contains 28 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libB.so]
 0x0000000000000001 (NEEDED)             Shared library: [libC.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN/../lib2]

RUNPATH 가 잘 설정되어 있는 것을 확인한다

ldd 명령어로 디페던시 라이브러리 위치를 설정한 위치에 잘 찾고있는지 확인한다.

# ldd libA.so
   ...
   libB.so => /home/./../lib2/libB.so
   libC.so => /home/./../lib2/libC.so
   ...

2. 테스트 환경 설정

다음과 같이 세 디렉토리를 만든다.

  • test: libA.so 를 사용하는 test.c 파일
  • lib1: test.c 에 필요한 libA.so 파일이고, A.so의 디펜던시 라이브러리는 libB.so, libC.so 가 있다.
  • lib2: libA.so의 디펜던시 라이브러리들이 위치한 디렉토리로, libB.so, libC.so 라이브러리 파일.
    (예를들면 A.so가 사용하는 libssl.so, libcrypt.so 같은 서드파트 라이브러리가 될 수 있겠다.)
test
|_ test.c
lib1
|_ libA.so
lib2
|_ libB.so
|_ libC.so

3. gcc 실행

테스트 파일이 있는 위치에서 gcc 빌드해본다.
옵션 두개를 구분할 줄 알아야 한다.

  • -Wl,-rpath: 런타임시 필요한 라이브러리 (libA.so) 위치 설정 (런타임에 필요)
  • -L: 컴파일러가 링커에게 libA.so 위치를 알려줌 (컴파일시 필요)
gcc test.c -I. -Wl,-rpath,'$ORIGIN/../lib1' -L../lib1 -lA

0개의 댓글