다음 시나리오를 고려하십시오.
- 종속성이없는 공유 라이브러리 libA.so.
- 종속성으로 libA.so를 사용하는 공유 라이브러리 libB.so.
libB와 연결되는 바이너리 파일을 컴파일하고 싶습니다. 바이너리를 libB 와만 연결해야합니까? 아니면 libA와 연결해야합니까?
런타임에 대한 종속성에서 해결되지 않은 기호를 해결할 수 있도록 직접 종속성과 만 연결하는 방법이 있습니까?
라이브러리 libB 구현이 향후 변경되어 다른 종속성 (예 : libC, libD, libE)을 도입 할 수 있다는 사실이 걱정됩니다. 그게 문제가 될까요?
다시 말해:
- libA 파일 : a.cpp ah
- libB 파일 : b.cpp bh
- 주 프로그램 파일 : main.cpp
물론 b.cpp에는 ah가 포함되고 main.cpp에는 bh가 포함됩니다.
컴파일 명령 :
g++ -fPIC a.cpp -c
g++ -shared -o libA.so a.o
g++ -fPIC b.cpp -c -I.
g++ -shared -o libB.so b.o -L. -lA
다음 중 어떤 옵션을 사용해야합니까?
g++ main.cpp -o main -I. -L. -lB
또는
g++ main.cpp -o main -I. -L. -lB -lA
첫 번째 옵션을 사용할 수 없습니다. 링커는 라이브러리 libA의 해결되지 않은 기호에 대해 불평합니다. 하지만 좀 이상하게 들립니다.
매우 감사합니다.
-업데이트 된 댓글 :
바이너리를 링크하면 링커는 main과 libB의 모든 심볼을 확인하려고합니다. 그러나 libB에는 libA의 정의되지 않은 기호가 있습니다. 이것이 링커가 그것에 대해 불평하는 이유입니다.
그래서 libA 와도 연결해야합니다. 그러나 공유 라이브러리에서 해결되지 않은 기호를 무시하는 방법을 찾았습니다. 다음 명령 줄을 사용하여 수행해야하는 것 같습니다.
g++ main.cpp -o main -I. -L. -lB -Wl,-unresolved-symbols=ignore-in-shared-libs
여전히 -rpath
옵션 을 사용할 수있는 것 같습니다. 그러나 나는 그것을 조금 더 잘 이해할 필요가있다.
-Wl,-unresolved-symbols=ignore-in-shared-libs
옵션을 사용할 때 가능한 함정을 아는 사람이 있습니까?
-업데이트 된 댓글 2 :
-rpath
이 목적으로 사용해서는 안됩니다. 주어진 디렉토리에서 라이브러리를 강제로 찾는 것이 유용합니다. -unresolved-symbol
접근 방식은 훨씬 더 보인다.
다시 한 번 감사드립니다.
답변
당신은 이미 거기에있는 것 같습니다. 조사를 잘하셨습니다. 내가 그 뒤에있는 ‘이유’를 명확히하는 데 도움을 줄 수 있는지 보자.
링커가 수행하는 작업은 다음과 같습니다. 실행 파일 (위의 ‘main’)을 연결하면 해결되지 않은 기호 (함수 및 기타 항목)가 있습니다. 해결되지 않은 기호를 해결하기 위해 다음 라이브러리 목록을 살펴 봅니다. 그 과정에서 일부 기호가 libB.so에 의해 제공된다는 사실을 알게되었으므로 이제이 라이브러리에서 해당 기호가 해결되었음을 알립니다.
그러나 이러한 기호 중 일부가 실행 파일에서 아직 확인되지 않은 다른 기호를 사용한다는 사실도 발견하므로 이제 해당 기호도 해결해야합니다. libA.so에 연결하지 않으면 응용 프로그램이 불완전합니다. libA.so에 연결되면 모든 기호가 해결되고 연결이 완료됩니다.
보시다시피 -unresolved-symbols-in-shared-libs
,를 사용해도 문제가 해결되지 않습니다. 해당 기호가 런타임에 확인되도록 연기합니다. 그 이유 -rpath
는 런타임에 검색 할 라이브러리를 지정하는 것입니다. 해당 기호를 확인할 수 없으면 앱이 시작되지 않습니다.
하나 이상의 라이브러리에서 심볼을 제공하고 그중 하나에 연결하여 만족시킬 수 있기 때문에 라이브러리 종속성을 파악하는 것은 쉬운 일이 아닙니다.
여기에이 프로세스에 대한 또 다른 설명이 있습니다. 라이브러리가 링크 된 순서가 때때로 GCC에서 오류를 일으키는 이유는 무엇입니까?
답변
직접 종속성이있는 동적 링크의 경우 다음 뒤에-Wl,--as-needed
libs를 추가하는 데 사용할 수 있습니다 . -Wl,--as-needed
gcc main.c -o main -I. -L. -Wl,--as-needed -lB -lA
직접 종속성을 확인 위해 당신은 사용해야 readelf를 대신 LDD LDD는 간접 종속성을 보여주고 있기 때문이다.
$ readelf -d main | grep library
0x0000000000000001 (NEEDED) Shared library: [libB.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
ldd는 간접 종속성도 표시합니다.
$ LD_LIBRARY_PATH=. ldd ./main
linux-vdso.so.1 (0x00007fff13717000)
libB.so => ./libB.so (0x00007fb6738ed000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6734ea000)
libA.so => ./libA.so (0x00007fb6732e8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb673af0000)
cmake 를 사용하는 경우 직접 종속성 만 포함하도록 다음 행을 추가 할 수 있습니다.
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")
답변
또 다른 옵션은 libtool
당신은 변경하는 경우 g++
에 전화를 libtool --mode=compile g++
소스 코드를 컴파일하고 다음 libtool --mode=link g++
의 떨어져 응용 프로그램을 만들려면 libB
, 다음 libA
자동으로 연결됩니다.
답변
이것은 흥미로운 게시물입니다. 저도 이것으로 머리를 두 드렸지만 여기서 요점을 놓친 것 같습니다 ..
아이디어는 다음과 같습니다.
main.cpp =(depends)=> libB.so =(depends)=> libA.so
더 고려해 봅시다 ..
- a.cpp (그리고 거기에만)에서 클래스 / 변수를 정의합니다. “symA”라고 부르겠습니다.
- b.cpp (그리고 거기에서만)에서 클래스 / 변수를 정의하고 “symB”라고합시다.
- symB는 symA를 사용합니다.
- main.cpp는 symB를 사용합니다.
이제 libB.so와 libA.so는 위에서 설명한대로 컴파일되었습니다. 그 후에 첫 번째 옵션 이 작동합니다.
g++ main.cpp -o main -I. -L. -lB
당신의 문제는
main.cpp에서는 symA도 참조합니다.
나 맞아?
코드에서 기호를 사용하는 경우 해당 기호는 .so 파일에서 찾아야합니다.
상호 참조 공유 라이브러리 (즉, API 생성)의 전체 아이디어는 더 깊은 레이어의 심볼이 숨겨지고 (양파 껍질을 벗기는 것을 생각해보십시오) 사용되지 않는다는 것입니다. .. 즉, main.cpp에서 symA를 참조하지 말고 대신 symB 만 참조하십시오 (그리고 symB가 symA 만 참조하도록하십시오).