LD_DEBUG 환경 변수 (2012)
출처: Hacker News
원본 게시일: 2012년 4월 23일, 2019년 9월 10일 업데이트(문제 해결 도구 링크 추가), 2019년 10월 18일 업데이트(Jekyll로 이전), 2023년 4월 3일 업데이트(다른 도구 링크 추가)
많은 공유(동적) 라이브러리를 사용하는 대규모 시스템을 개발하면, 진단하기 어려운 짜증나는 버그가 발생할 수 있습니다. 이러한 버그는 시스템에 여러 버전의 라이브러리가 존재하고, 개발자가 의도한 버전이 아닌 “잘못된” 버전이 로드될 때 흔히 발생합니다.
Note Linux에서 링크 문제를 진단하기 위한 온라인 도구 를 제공하고 있습니다. 현재 전문가 시스템으로 구현 중이며, 작성 시점에도 이미 유용하게 사용할 수 있습니다.
예전에는 strace 명령어(man strace)와 함께 어떤 라이브러리가 접근되는지 확인하면서 비교적 효율적으로 문제를 디버깅했습니다. 하지만 공유 라이브러리 로딩 문제를 디버깅하는 덜 알려진 더 효율적인 방법이 있습니다: LD_DEBUG 환경 변수입니다.
LD_DEBUG 변수를 설정하면 Linux 동적 링커가 디버그 정보를 출력합니다. 이를 통해 대부분의 로딩 문제를 매우 빠르게 해결할 수 있습니다. 사용 가능한 옵션을 확인하려면 변수에 help를 지정하고 프로그램을 실행하면 됩니다.
LD_DEBUG=help cat
Valid options for the LD_DEBUG environment variable are:
libs display library search paths
reloc display relocation processing
files display progress for input file
symbols display symbol table processing
bindings display information about symbol binding
versions display version dependencies
all all previous options combined
statistics display relocation statistics
unused determined unused DSOs
help display this help message and exit
To direct the debugging output into a file instead of standard output
a filename can be specified using the LD_DEBUG_OUTPUT environment
variable.
Note 링크 문제를 다루는 데 유용한 다른 도구들도 많이 있습니다:
strace프로그램은 시스템 콜 전체를 보여주며, 동적 라이브러리 검색·열기 과정을 확인할 수 있습니다.ldd프로그램은 동적 라이브러리 의존성을 해석합니다.objdump -x YOURFILE | grep NEEDED명령은 프로그램이나 라이브러리 안에 어떤 다른 라이브러리가 필요한지 보여줍니다.patchelf프로그램을 사용하면 ELF 실행 파일의rpath를 쉽게 바꿔 내장 검색 순서를 조정할 수 있습니다.LD_PRELOAD환경 변수를 이용하면 동적 라이브러리를 손쉽게 교체할 수 있습니다.
추가 조언이 필요하신가요? 20년 이상의 경험을 바탕으로 저희가 도와드릴 수 있습니다 – 연락처: webs@bnikolic.co.uk
Note 이 변수는 Linux 전용입니다. Windows에서는 “Show Loader Snaps”를 활성화하면 비슷한 정보를 얻을 수 있습니다(https://abitofscotland.wordpress.com/2019/12/20/dll-load-failure/). gflags.exe 프로그램으로 해당 옵션을 켜고 windbg로 로그를 확인하면 됩니다. 예시:
gflags와windbg가 포함된 Windows SDK를 설치합니다.winget install Microsoft.WindowsSDK.10.0.19041(또는 Microsoft 웹사이트에서 다운로드)- 실행 파일에 ShowLoaderSnaps를 활성화합니다. 예: PowerShell에서
&"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags.exe" /i notepad.exe +sls notepad.exe를 WinDbg에서 실행합니다. 상세한 DLL 로딩 로그가 표시됩니다.
아래는 제 노트북에서 실행한 예시입니다:
LD_DEBUG=all cat
28504:
28504: file=libc.so.6 [0]; needed by cat [0]
28504: find library=libc.so.6 [0]; searching
28504: search path=/home/bnikolic/s/lib/tls/x86_64:/home/bnikolic/s/lib/tls:/home/bnikolic/s/lib/x86_64:/home/bnikolic/s/lib:tls/x86_64:tls:x86_64::/home/bnikolic/d/p/R-2.14.1bin/lib/R/lib/tls/x86_64:/home/bnikolic/d/p/R-2.14.1bin/lib/R/lib/tls:/home/bnikolic/d/p/R-2.14.1bin/lib/R/lib/x86_64:/home/bnikolic/d/p/R-2.14.1bin/lib/R/lib (LD_LIBRARY_PATH)
28504: trying file=/home/bnikolic/s/lib/tls/x86_64/libc.so.6
28504: trying file=/home/bnikolic/s/lib/tls/libc.so.6
28504: trying file=/home/bnikolic/s/lib/x86_64/libc.so.6
28504: trying file=/home/bnikolic/s/lib/libc.so.6
28504: trying file=tls/x86_64/libc.so.6
28504: trying file=tls/libc.so.6
28504: trying file=x86_64/libc.so.6
28504: trying file=libc.so.6
28504: trying file=/home/bnikolic/d/p/R-2.14.1bin/lib/R/lib/tls/x86_64/libc.so.6
28504: trying file=/home/bnikolic/d/p/R-2.14.1bin/lib/R/lib/tls/libc.so.6
28504: trying file=/home/bnikolic/d/p/R-2.14.1bin/lib/R/lib/x86_64/libc.so.6
28504: trying file=/home/bnikolic/d/p/R-2.14.1bin/lib/R/lib/libc.so.6
28504: search cache=/etc/ld.so.cache
28504: trying file=/lib/x86_64-linux-gnu/libc.so.6
28504:
28504: file=libc.so.6 [0]; generating link map
28504: dynamic: 0x00007fc38e525b40 base: 0x00007fc38e18c000 size: 0x00000000003a0368
28504: entry: 0x00007fc38e1ad420 phdr: 0x00007fc38e18c040 phnum: 10
28504:
28504: checking for version `GLIBC_2.4' in file /lib/x86_64-linux-gnu/libc.so.6 [0] required by file cat [0]
28504: checking for version `GLIBC_2.3' in file /lib/x86_64-linux-gnu/libc.so.6 [0] required by file cat [0]
28504: checking for version `GLIBC_2.3.4' in file /lib/x86_64-linux-gnu/libc.so.6 [0] required by file cat [0]
28504: checking for version `GLIBC_2.2.5' in file /lib/x86_64-linux-gnu/libc.so.6 [0] required by file cat [0]
28504: checking for version `GLIBC_2.3' in file /lib64/ld-linux-x86-64.so.2 [0] required by file /lib/x86_64-linux-gnu/libc.so.6 [0]
28504: checking for version `GLIBC_PRIVATE' in file /lib64/ld-linux-x86-64.so.2 [0] required by file /lib/x86_64-linux-gnu/libc.so.6 [0]
28504:
28504: relocation processing: /lib/x86_64-linux-gnu/libc.so.6 (lazy)
28504: symbol=_res; lookup in file=cat [0]
28504: symbol=_res; lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
28504: binding file /lib/x86_64-linux-gnu/libc.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `_res' [GLIBC_2.2.5]
28504: symbol=_IO_file_close; lookup in file=cat [0]
28504: symbol=_IO_file_close; lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
28504: binding file /lib/x86_64-linux-gnu/libc.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `_IO_file_close' [GLIBC_2.2.5]
28504: symbol=stderr; lookup in file=cat [0]
28504: binding file /lib/x86_64-linux-gnu/libc.so.6 [0] to cat [0]: normal symbol `stderr' [GLIBC_2.2.5]
28504: symbol=error_one_per_line; lookup in file=cat [0]
28504: symbol=error_one_per_line; lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
28504: binding file /lib/x86_64-linux-gnu/libc.so.6 [0] to /lib/x86_64-linux-gnu