Skip to content

Ld

GNU 링커 (또는 GNU ld)는 유닉스 명령어 ld에 대한 GNU 프로젝트의 구현이다. GNU ld는 소프트웨어의 컴파일 시에 생성되는 목적 파일들로부터 실행 파일(또는 라이브러리)를 생성하는 링커를 실행한다. 링킹 프로세스에 대한 더 많은 권한을 행사하기 위해 링커 스크립트는 GNU ld로 전달된다. GNU 링커는 GNU 바이너리 유틸리티(binutils)의 한 부분이다.

이름 "ld"의 기원으로 여겨지는 것들로는 "LoaD" 그리고 "Link eDitor"가 있다.

GNU 링커는 자유 소프트웨어이며 GNU 일반 공중 사용 허가서 하에 배포된다.

Category

GNU Linker

BSD Linker

Mac OSX등에서 사용된다. GCC에서는 Darwin Options로 확인할 수 있다. 1

Flags

-headerpad {size}
Specifies the minimum space for future expansion of the load commands. Only useful if intend to run install_name_tool to alter the load commands later. Size is a hexadecimal number.
-headerpad_max_install_names
Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. Only useful if intend to run install_name_tool to alter the load commands later.
실행파일의 Header 정보가 install_name_tool등으로 변경될 수 있으니, MAXPATHLEN으로 정의된 최대 공간을 확보한다. (정확한 확인 필요)
-compatibility_version {number}
Specifies the compatibility version number of the library. When a library is loaded by dyld, the compatibility version is checked and if the program's version is greater that the library's version, it is an error. The format of number is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. If the compatibility version number is not specified, it has a value of 0 and no checking is done when the library is used. This option is also called -dylib_compatibility_ver-sion for compatibility.
라이브러리의 호환성 버전 번호를 지정한다. {number}의 형식은 X[.Y[.Z]]이며, 수치에 대한 제약사항은 아래와 같다.
0 < X <= 65535
[Optional] 0 <= .Y <= 255
[Optional] 0 <= .Z <= 255
-current_version {number}
Specifies the current version number of the library.
라이브러리의 현재 버전을 지정한다.
-search_paths_first
This is now the default (in Xcode4 tools). When processing -lx the linker now searches each directory in its library search paths for libx.dylib' thenlibx.a' before the moving on to the next path in the library search path.

ld 프로그램의 섹션에 대해

부트로더나 기타 펌웨어 작업을 하다보면 ld 스크립트를 조작하게된다. 보통 복사를 하여 사용하지만 조금의 이해만 있으면 쉽게 조작할 수 있다.

  • .text: 코드 영역.
  • .bss: 함수밖에 존재하고 초기값을 갖지 않는 변수들의 영역, 초기값이 0으로 설정된 변수라도 bss 영역에 포함되지 않는다.
  • .data: 함수밖에 존재하고 초기값을 갖는 변수들의 영역, 초기값이 0인 변수도 이곳에 포함된다.
  • .rodata: 읽기 전용 변수, const 라고 선언된 변수.
  • .got: Global Offset Table, 공유라이브러리 관련 시작주소.

LD_LIBRARY_PATH vs LIBRARY_PATH

두 줄 요약:

  • LD_LIBRARY_PATH - 프로그램 시작 시 검색되며,
  • LIBRARY_PATH - 링크 시 검색됩니다.

참고로:

  • DYLD_LIBRARY_PATH - LIBRARY_PATHmacOS 버전.
  • DYLD_FALLBACK_LIBRARY_PATH - 위의, DYLD_LIBRARY_PATH 에서 라이브러리를 찾지 못할 경우 검색할 경로.

자세한 내용은 하단의 #rpath 항목 참조.

링크 순서와 관련된 예제는 아래와 같다.

main.c:

#include <temp.h>
int main()
{
    return temp();
}

temp.h:

int temp();

temp.c:

#include <stdio.h>
int temp()
{
    printf("Hello, World!\n");
    return 0;
}

각각 Archive로 작성했다고 가정한다.

$ ar cr main.a main.o
$ ar cr temp.a temp.o

이 때, 아래와 같이 링크할 경우 에러가 발생된다.

$ gcc temp.a main.a
main.a(main.o):main.c:(.text+0xe): undefined reference to `temp'
collect2.exe: error: ld returned 1 exit status

아래와 같이 순서를 바꿔야 한다. 이 때, 아래와 같이 링크할 경우 에러가 발생된다.

$ gcc main.a temp.a

정리하면, {참조하는 라이브러리} -> {구현된 라이브러리} 순서로 링크해야 한다.

순서에 신경쓰고싶지 않다면 --start-group--end-group를 사용하면 된다.

ld.so는 어떻게 공유 라이브러리를 찾아갈까

  • 기본적으로 /lib또는 /use/lib찾아봄. 또한 /etc/ld.so.conf에 지정된 경로를 찾아보며 계정마다 생성해준 LD_LIBRARY_PATH에 지정된 경로를 검색.
  • 새롭게 추가되는 라이브러리는 /etc/ld.so.conf.d디렉터리 아래에 관련 경로를 추가하면 된다. 2
  • 주의점: /etc/ld.so.conf에 지정된 경로를 찾아보는데, 내용을 변경하거나 시스템 라이브러리를 업그레이드 또는 설치하는 경우, ld.so가 경로를 탐색할 때 사용하는 공유 라이브러리 캐시인 /etc/ld.so/chche를 생성하기 위해 ldconfig명령 반드시 실행해야 한다.

라이브러리 로드 순서 확인

LD_DEBUG환경변수를 files로 설정한 후 원하는 파일을 실행하면 된다.

$ LD_DEBUG=files /bin/ls

Linker의 검색 경로 목록

아래의 방법중 하나를 선택하면 된다.

ldconfig -v 2>/dev/null | grep -v ^$'\t'
ld --verbose | grep SEARCH_DIR | tr -s ' ;' \\012
gcc -print-search-dirs | sed '/^lib/b 1;d;:1;s,/[^/.][^/]*/\.\./,/,;t 1;s,:[^=]*=,:;,;s,;,;  ,g' | tr \; \\012
gcc -Xlinker -v
gcc -Xlinker --verbose
gcc --print-search-dirs

검색된 라이브러리 목록

아래의 방법중 하나를 선택하면 된다.

ldconfig -v
ldconfig -p

soname

유닉스 운영체제에서, soname이란 공유 오브젝트 파일 내의 한 필드를 말한다. soname은 버전 하위 호환성 정보를 시스템에 제공한다. 요컨데, 한 프로그램이 1.0 버전의 공유 오브젝트를 요청했으나, 시스템은 2.0 버전만 갖고 있다고 할 때, 공유 오브젝트 내의 soname 필드를 보고 시스템은 1.0 버전 대신 2.0 버전의 공유 오브젝트를 쓸 수 있는지 파악할 수 있다.

How to use

GCC Version:

$ gcc -shared -Wl,-soname,libtest.so.1.0 test.o

clang Version:

$ clang -dynamiclib test.o -o libtest.1.0.dylib -install_name,libtest.1.0.dylib -current_version 1.0.6 -compatibility_version 1.0

rpath

rpath is a term in programming which refers to a run-time search path hard-coded in an executable file or library, used during dynamic linking to find the libraries the executable or library requires.

한마디로, 실시간 라이브러리 검색 경로이다. Linux 동적 링커는 이러한 경로를 사용하여 필요한 공유 객체를 찾는다. RUNPATH로 불리기도 한다.

리눅스 계열은 readelf 으로, darwin 계열은 otool 같은 도구를 사용하여 확인할 수 있다.

How to use

아래와 같은 방법들로 확인하거나 수정할 수 있다.

$ objdump -x {file} | grep RPATH
$ readelf -d {file} | head -20
$ otool -D {file} ## to view the install name of a dylib
$ otool -L {file} ## to view the dependencies (<code>@rpath</code>가 붙는다.)
$ otool -l {file} | grep LC_RPATH -A2 ## to view the RPATHs
$ install_name_tool -id ... ## to change an install name
$ install_name_tool -change ... ## to change the dependencies
$ install_name_tool -rpath ... -add_rpath ... -delete_rpath ...  ## to change RPATHs

How to assign

아래와 같이, rpath를 할당하면 된다. {dir}부분에 디렉토리 경로를 입력하면 된다.

참고로 #BSD Linker방법으로 사용하면 #GNU Linker에서 호환된다.

MacOSX RPath

Linux의 LD_LIBRARY_PATH와 동일한 역할을 하는 DYLD_LIBRARY_PATH를 사용하여 rpath를 지정할 수 있다. 단, 반드시 export해야 한다. 참고로 라이브러리를 찾지 못할 경우 DYLD_FALLBACK_LIBRARY_PATH를 참조한다.

사용할 일이 많지는 않겠지만 상용 유닉스의 경우 아래와 같은 환경 변수를 사용합니다.

  • Solaris: LD_LIBRARY_PATH
  • AIX: LIBPATH
  • HP-UX: SHLIB_PATH

Install name

(install_name으로 이 항목에 접근할 수 있다.)

런타임에 링커가 해당 dynamic library를 찾을 수 있도록 알려주기 위해 dynamic library 내에 embedded 된 경로 이름 이다.

@executable_path
Useful for frameworks embedded inside applications, because it allows you to specify the location of the framework relative to the application’s executable
만약 여러분이 어플리케이션을 작성할 때 어떤 프레임워크를 /Library 또는 그런 비슷한 위치에 설치할 수 없고 어플이케이션 내에 임베드(embed)해야 한다면 인스톨 네임(install name)을 절대 경로로 설정한다는 것은 난감한 문제가 된다.
이런 경우의 Mac의 해결책은 @executable_path이다. 라이브러리의 인스톨 네임이 @executable_path로 시작하도록 설정하면, 해당 문자열은 실행 파일이 로드된 경로로 치환된다.
예를 들어, Bar.appFoo.framework와 링크한다고 할때, Bar.app/Applications에 설치되었다면, @executable_path/Applications/Bar.app/Contents/MacOS 로 확장된다. 만약 여러분이 프레임워크를 Contents/frameworks에 포함시키려고 한다면, Foo.frameworks의 인스톨 네임을 @executable_path/../Frameworks/Foo.framework/Versions/A/Foo로 설정하기만 하면된다.
동적 링커는 이 경로를 /Applications/Bar.app/Contents/MacOS/../Frameworks/Foo.framework/Versions/A/Foo로 확장하여 프레임워크를 찾게된다.
@loader_path
Available from Mac OS X 10.4 Tiger onwards; useful for frameworks embedded inside plug-ins, because it allows you to specify the location of the framework relative to the plug-in’s code (remember, plug-ins may not actually know where they are going to be installed, relative to the application, so knowing @executable_path doesn’t help us in this case)
실행파일의 위치만으로는 부족할 때가 있다. 또다른 프레임워크를 임베드하는 프레임워크나 플러그인을 생각해 보자. 말하자면, Foo.frameworkBaz.framework를 임베드하는 경우다. Baz.framework가 로드되록 요청하는 것은 Foo.framework이지만, 동적 링커는 @executable_pathBar.app의 경로를 사용하게 되며, 이렇게 해서는 정상적으로 동작할 수 없게 된다.
Mac OS 10.4 부터 제공되는 @loader_path 를 사용하면된다. 이 값은 대상 라이브러리가 로드되게 만든 것(프레임워크이든 플러그인이든)의 마지막 경로 컴포넌트(last component)를 제외한 전체 경로이다.
@rpath
@rpath instructs the dynamic linker to search a list of paths in order to locate the framework
이론적으로는 위에 설명한 것들로 충분해야하겠지만, 실제에 있어서는 문제가 되는 경우가 있다. 그 문제란 라이브러리의 하나의 복사본은 한가지 방법만으로 사용될 수 밖에 없다는 것이다. 만약 여러분이 Foo.framework가 어떤 어플리케이션에 임베드되어서도 동작하고, /Libary/Frameworks에 설치되어서도 동일하게 동작하기를 원한다고 할때, 여러분은 가각의 인스톨 네임을 가진 두 가지 버전의 프레임워크를 배포해야 한다. 또는 설치과정에서 install_name_tool 이란 명령어로 인스톨 네임을 수정해야한다. 결국 가능은 하지만 역시 난감한 문제라고 할 수 있다.
Mac OS 1.05 부터 @rpath 를 사용하여 이 문제를 해결할 수 있다. 인스톨 네임이 @rpath 로 시작하면, 동적 링커는 라이브러리 위치를 기록한 리스트를 검색하는데, 이 리스트는 어플리케애션내에 포함되기 때문에 프레이워크가 빌드 될 때가 아닌 어플리케이션이 빌드되는 시점에 만들어진다. 따라서 하나의 프레임워크의 복사본을 어플리케이션이 만들어지는 시점에 다양한 용도로 사용할 수 있게된다.
이렇게 동작하도록 하기위해서는 Foo.framework의 인스톨 네임은 @rpath/Foo.framework/Versions/A/Foo 로 설정되어야한다. Foo.framework를 임베드하는 어플리케이션은 -rpath @executable_path/../Frameworks 옵션을 빌드 시점에 링커에게 전달한다. 이렇게 하면 동적링커가 @rpath 대신에 주어진 경로로 프레임워크를 찾도록 만든다. Foo.framework/Library/Frameworks에 설치하는 어플리케이션은 -rpath /Library/Frameworks 링크 옵션을 사용하여 동적 링커가 해당 경로에서 찾도록 만들면 된다. 어떤 이유에서 위의 두 가지 방법을 빌드시점에 결정하지 않겠다면 그냥 위의 두 가지 옵션 모두를 링커에 전달하면 동적링커는 프레임워크를 찾기위해 두 경로에서 모두 시도하게 된다.

How to use

$ clang foo.c -dynamiclib -install_name @executable_path/../foo/libfoo.dylib -o libfoo.dylib

Documentation

Ns-3_Application_Note_-_linker-problems.pdf
링커와 관련된 문제점 정리.

See also

Favorite site

References


  1. 3.18.10 Darwin Options 

  2. System Library 추가 및 관리 (ldconfig 명령, /etc/ld.so.conf.d 파일) 

  3. Glibc_-_shared_library_symbol_version.pdf 

  4. Out_of_Bedlam_-_Mac_OS_X_Linking_and_Install_Names.pdf 

  5. Using_static_and_shared_libraries_across_platforms.pdf 

  6. Korea.gnu.org.zip - manual/release/ld/ld-sjp/ld-ko_toc.html