libstdc ++를 정적으로 링크 : 문제가 있습니까?


92

상당히 오래된 libstdc ++ 버전과 함께 제공되는 Ubuntu 10.04를 실행하는 시스템에 GCC 4.7의 libstdc ++와 함께 Ubuntu 12.10에 빌드 된 C ++ 애플리케이션을 배포해야합니다.

현재 저는 -static-libstdc++ -static-libgcc이 블로그 게시물 인 Linking libstdc ++ statically에서 제안한대로를 사용 하여 컴파일하고 있습니다. 저자는 libstdc ++를 정적으로 컴파일 할 때 동적으로로드 된 C ++ 코드를 사용하지 말라고 경고합니다.이 코드는 아직 확인하지 않았습니다. 그래도 지금까지는 모든 것이 순조롭게 진행되고있는 것 같습니다. 우분투 10.04에서 C ++ 11 기능을 사용할 수 있습니다.

이 기사는 2005 년의 기사이며 그 이후로 많은 것이 변경되었을 것입니다. 그 조언은 여전히 ​​최신입니까? 내가 알아야 할 숨어있는 문제가 있습니까?


아니요, libstdc ++에 정적으로 링크하는 것은이를 의미하지 않습니다. 는 것을 의미했다면 다음에 아무 소용이 없을 것입니다 -static-libstdc++그냥 사용합니다 옵션-static
조나단 Wakely

@JonathanWakely -static은 kernel too old일부 우분투 1404 시스템에서 오류가 발생합니다. glibc.so는 kernel32.dll창에있는 것과 같 으며 운영 체제 인터페이스의 일부이므로 바이너리에 포함해서는 안됩니다. objdump -T [binary path]동적으로로드되는지 libstdc++.so여부 를 확인하는 데 사용할 수 있습니다 . golang 프로그래머의 경우 #cgo linux LDFLAGS: -static-libstdc++ -static-libgcc"C"를 가져 오기 전에 추가 할 수 있습니다.
bronze man

@bronzeman,하지만 우리는 대한 얘기 -static-libstdc++하지 -static그래서 libc.so정적으로 링크되지 않습니다.
Jonathan Wakely

1
@NickHutchinson 링크 된 블로그 게시물이 사라졌습니다. 이 SO 질문은 여기에서 관련 용어에 대한 인기 검색 히트입니다. 질문에 해당 블로그 게시물의 중요한 정보를 재현 할 수 있습니까? 또는 이동 위치를 알고있는 경우 새 링크를 제공 할 수 있습니까?
Brian Cain

1
인터넷 아카이브는있다 @BrianCain : web.archive.org/web/20160313071116/http://www.trilithium.com/...
롭 Keniger

답변:


136

그 블로그 게시물은 매우 정확하지 않습니다.

내가 아는 한 C ++ ABI 변경 사항은 GCC의 모든 주요 릴리스 (즉, 첫 번째 또는 두 번째 버전 번호 구성 요소가 다른 버전)에 도입되었습니다.

사실이 아니다. GCC 3.4 이후 도입 된 유일한 C ++ ABI 변경 사항은 이전 버전과 호환됩니다. 즉, C ++ ABI가 거의 9 년 동안 안정적이었습니다.

설상가상으로, 대부분의 주요 Linux 배포판은 GCC 스냅 샷을 사용하거나 GCC 버전을 패치하므로 바이너리를 배포 할 때 처리 할 GCC 버전을 정확히 알 수 없습니다.

배포판의 패치 된 GCC 버전 간의 차이는 사소하고 ABI가 변경되지는 않습니다. 예를 들어 Fedora의 4.6.3 20120306 (Red Hat 4.6.3-2)은 업스트림 FSF 4.6.x 릴리스와 ABI 호환되며 거의 모든 4.6과 호환됩니다. x 다른 배포판에서.

GNU / Linux에서 GCC의 런타임 라이브러리는 ELF 심볼 버전 관리를 사용하므로 객체와 라이브러리에 필요한 심볼 버전을 쉽게 확인할 수 있습니다. libstdc++.so이러한 심볼을 제공하는를 사용하면 작동합니다. 패치 된 버전이 약간 다른지 여부는 중요하지 않습니다. 배포판의 다른 버전에서.

그러나 이것이 작동한다면 C ++ 코드 (또는 C ++ 런타임 지원을 사용하는 코드)는 동적으로 링크 될 수 없습니다.

이것은 사실이 아닙니다.

즉, 정적으로 연결 libstdc++.a 것이 하나의 옵션입니다.

라이브러리를 동적으로로드하는 경우 (를 사용하여 dlopen) 작동하지 않을 수있는 이유 는 종속 된 libstdc ++ 기호가 (정적으로) 링크 할 때 응용 프로그램에 필요하지 않았기 때문에 해당 기호가 실행 파일에 표시되지 않기 때문입니다. 이는 공유 라이브러리를 동적으로 링크하여 해결할 수 있습니다 libstdc++.so(이에 의존하는 경우 어쨌든 수행 할 수있는 올바른 작업입니다.) ELF 기호 삽입은 실행 파일에있는 기호를 공유 라이브러리에서 사용하지만 다른 것은 사용하지 않음을 의미합니다. 실행 파일에있는 파일은 libstdc++.so링크 된 모든 위치 에서 찾을 수 있습니다. 응용 프로그램이 사용하지 않는 경우dlopen 하지 않으면 그것에 대해 신경 쓸 필요가 없습니다.

또 다른 옵션 (그리고 내가 선호하는 옵션)은 libstdc++.so애플리케이션과 함께 최신 버전을 배포하고 기본 시스템 앞에 있는지 확인하는 것입니다. libstdc++.so이 작업 $LD_LIBRARY_PATH은 실행시 환경 변수를 사용하여 동적 링커가 올바른 위치를 찾도록 강제함으로써 수행 할 수 있습니다. 시간 또는 RPATH링크 시간에 실행 파일을 설정하여 . RPATH응용 프로그램이 작동하도록 올바르게 설정된 환경에 의존하지 않기 때문에 사용하는 것을 선호합니다 . 응용 프로그램을 '-Wl,-rpath,$ORIGIN'(셸이 확장하지 못하도록 작은 따옴표에 유의하십시오 $ORIGIN) 연결하면 실행 파일은 런타임에 발견되는 실행 파일과 동일한 디렉토리에 문제가 해결됩니다. (또 다른 옵션은 실행 파일을 넣고 최신 libstdc ++. so를RPATH 으로 $ORIGIN하는 자체 실행 파일과 같은 디렉토리에 공유 라이브러리를 찾기 위해 동적 링커를 알려줍니다. 새로운 것을 넣으면libstdc++.so/some/path/bin//some/path/lib/'-Wl,-rpath,$ORIGIN/../lib'실행 파일과 관련된 또는 다른 고정 위치 와 연결 하고 RPATH를에 상대적으로 설정 $ORIGIN)


8
특히 RPATH에 대한이 설명은 훌륭합니다.
nilweed

3
Linux에서 앱과 함께 libstdc ++를 제공하는 것은 나쁜 조언입니다. 이것이 가져 오는 모든 드라마를보기 위해 "steam libstdc ++"를위한 구글. 간단히 말해서, exe가 libstdc ++ (예 : radeon 드라이버)를 다시 dlopen하려는 외부 lib (예 : opengl)를로드하는 경우 해당 libs는 libstdc ++를 사용하게됩니다 . libstdc ++는 이미로드 되었기 때문입니다. 배고 있다. 그래서 당신은 원점으로 돌아 왔습니다.

7
@cap, OP는 시스템 libstdc ++가 오래된 배포판에 배포하는 것에 대해 구체적으로 묻습니다. Steam 문제는 시스템보다 오래된 libstdc ++. so를 번들로 묶었다는 것입니다 (아마도 번들로 묶었을 때 최신 버전 이었지만 배포판은 더 새로운 버전으로 이동했습니다). RPATH libstdc++.so.6가 설치시 번들 된 lib를 가리 키도록 설정된 심볼릭 링크를 포함하는 디렉토리를 가리 키도록함으로써 해결 될 수 있습니다 . Red Hat DTS에서 사용하는 것처럼 더 복잡한 혼합 연결 모델이 있지만 직접 수행하기는 어렵습니다.
조나단 Wakely

5
이봐 요, 역 호환 바이너리를 제공하는 모델에 "다른 사람이 libstdc ++ ABI 호환성을 유지하도록 신뢰"또는 "런타임에 libstdc ++를 조건부로 연결"을 포함하지 않으려면 죄송합니다. 그리고 거기에서 내가 무엇을 할 수 있는지, 나는 무례하지 않다는 것을 의미합니다. 당신이 memcpy@GLIBC_2.14 드라마를 기억한다면, 당신은 정말이 :)과의 신뢰의 문제가 나를 비난 할 수없는

6
'-Wl, -rpath, $ ORIGIN'을 사용해야했습니다 (rpath 앞에 '-'가 있음). 편집이 적어도 6 자 이상이어야합니다 때문에 .... 대답을 편집 할 수 없습니다
user368507

11

Jonathan Wakely의 탁월한 답변에 추가 된 한 가지 추가 사항은 dlopen ()이 문제가되는 이유입니다.

GCC 5의 새로운 예외 처리 풀 ( PR 64535PR 65434 참조)로 인해 libstdc ++에 정적으로 링크 된 라이브러리를 dlopen 및 dlclose하면 매번 풀 객체의 메모리 누수가 발생합니다. 따라서 dlopen을 사용할 가능성이 있다면 libstdc ++를 정적으로 링크하는 것은 정말 나쁜 생각 인 것 같습니다. 이것은 PR 65434에 언급 된 양성 누출과는 대조적으로 실제 누출 입니다.


1
이 함수 __gnu_cxx::__freeres()는 풀 개체의 내부 버퍼를 해제하기 때문에이 문제에 대해 적어도 약간의 도움을 제공하는 것 같습니다. 그러나 나에게는이 함수에 대한 호출이 우연히 나중에 던져진 예외와 관련하여 어떤 의미를 갖는지 명확하지 않습니다.
phlipsy

3

RPATH에 관한 Jonathan Wakely의 답변에 대한 추가 기능 :

RPATH는 문제의 RPATH가 실행중인 응용 프로그램 의 RPATH 인 경우에만 작동합니다 . 자체 RPATH를 통해 라이브러리에 동적으로 연결되는 라이브러리가있는 경우 라이브러리의 RPATH를로드하는 응용 프로그램의 RPATH가 덮어 씁니다. 이는 애플리케이션의 RPATH가 라이브러리의 RPATH와 동일하다는 것을 보장 할 수없는 경우에 발생하는 문제입니다. 예를 들어 종속성이 특정 디렉토리에있을 것으로 예상하지만 해당 디렉토리가 애플리케이션의 RPATH의 일부가 아닌 경우입니다.

예를 들어 GCC 4.9의 libstdc ++. so.x에 동적으로 연결된 종속성이있는 App.exe 응용 프로그램이 있다고 가정 해 보겠습니다. App.exe는 RPATH를 통해이 종속성을 해결합니다.

App.exe (RPATH=.:./gcc4_9/libstdc++.so.x)

이제 GCC 5.5의 libstdc ++. so.y에 동적으로 연결된 종속성이있는 다른 라이브러리 Dependency.so가 있다고 가정 해 보겠습니다. 여기서 종속성은 라이브러리의 RPATH를 통해 해결됩니다.

Dependency.so (RPATH=.:./gcc5_5/libstdc++.so.y)

App.exe가 Dependency.so를로드 할 때 라이브러리의 RPATH를 추가하거나 앞에 추가하지 않습니다 . 전혀 참조하지 않습니다. 고려되는 유일한 RPATH는 실행중인 응용 프로그램의 RPATH이거나이 예에서 App.exe입니다. 즉, 라이브러리가 gcc5_5 / libstdc ++. so.y에는 있지만 gcc4_9 / libstdc ++. so.x에는없는 기호에 의존하는 경우 라이브러리가로드되지 않습니다.

내가 과거에 이러한 문제를 직접 겪었 기 때문에 이것은 경고의 한마디와 같습니다. RPATH는 매우 유용한 도구이지만 구현에는 여전히 몇 가지 문제가 있습니다.


따라서 공유 라이브러리에 대한 RPATH는 무의미합니다! 그리고 저는 그들이 지난 20 년 동안이 점에서 Linux를 약간 개선하기를 바랐습니다.
Frank Puck

2

동적 glibc에 의존하지 않는지 확인해야 할 수도 있습니다. 실행 ldd하여 결과 실행과 동적 종속성을주의 (libc의 / libm의 / libpthread의 USAL 용의자).

추가 연습은이 방법론을 사용하여 관련 C ++ 11 예제를 빌드하고 실제 10.04 시스템에서 결과 바이너리를 실제로 시도하는 것입니다. 대부분의 경우 동적 로딩으로 이상한 작업을 수행하지 않는 한 프로그램이 작동하는지 또는 충돌하는지 즉시 알 수 있습니다.


1
동적 glibc에 따라 어떤 문제가 있습니까?
닉 허친슨

나는 적어도 얼마 전에 libstdc ++가 glibc에 대한 의존성을 암시했다고 믿습니다. 오늘날 상황이 어디에 있는지 잘 모르겠습니다.
Alexander L. Belikoff

9
libstdc ++는 glibc에 의존 printf하지만 (예 : iostreams는으로 구현 됨 ) Ubuntu 10.04의 glibc가 최신 libstdc ++에 필요한 모든 기능을 제공하는 한 동적 glibc에 의존하는 데 문제가 없습니다. 사실 절대 링크하지 않는 것이 좋습니다. 정적 glibc가하기
조나단 Wakely에게

1

Jonathan Wakely의 답변에 다음을 추가하고 싶습니다.

주위 재생 -static-libstdc++리눅스에, 나는의 문제에 직면했습니다 dlclose(). 정적으로 연결된 애플리케이션 'A'가 libstdc++있고 libstdc++런타임에 플러그인 'P'에 동적으로 연결된로드 한다고 가정 합니다. 괜찮아. 그러나 'A'가 'P'를 언로드하면 세그멘테이션 오류가 발생합니다. 내 가정은 언로드 후 libstdc++.so'A'가 더 이상 관련 기호를 사용할 수 없다는 것 libstdc++입니다. 'A'와 'P'가 모두에 정적으로 연결되어 libstdc++있거나 'A'가 동적 으로 연결되고 'P'가 정적으로 연결되어 있으면 문제가 발생하지 않습니다.

요약 : 애플리케이션이에 동적으로 링크 될 수있는 플러그인을로드 / 언로드하는 경우 libstdc++앱도 동적으로 링크 되어야합니다. 이것은 제 관찰 일 뿐이며 귀하의 의견을 듣고 싶습니다.


1
이것은 아마도 libc 구현을 혼합하는 것과 비슷할 것입니다 (즉, 응용 프로그램 자체는 musl-libc에 정적으로 링크되는 반면 glibc를 동적으로 링크하는 플러그인에 동적으로 링크). musl-libc의 저자 인 Rich Felker는 그러한 시나리오의 문제는 glibc 메모리 관리 (사용 sbrk)가 특정 가정을하고 한 프로세스 내에서 혼자있을 것으로 예상한다는 것입니다. 그래도 특정 glibc 버전 또는 기타.
0xC0000022L

그리고 사람들은 여전히 ​​단일 프로세스 내에서 libc ++ / libc의 여러 독립 사본을 처리 할 수있는 Windows 힙 인터페이스의 장점을 보지 못합니다. 그런 사람들은 소프트웨어를 설계해서는 안됩니다.
Frank Puck

@FrankPuck은 Windows와 Linux 모두에 상당한 양의 경험을 가지고 있습니다. MSVC가 할당 자 사용 방법과 방법을 결정하는 당사자 인 경우 "Windows"가 수행하는 방식이 도움이되지 않는다고 말할 수 있습니다. Windows에서 힙으로 보는 주요 이점은 비트와 조각을 나누어주고 한 번에 해제 할 수 있다는 것입니다. 그러나 MSVC를 사용하면 다른 VC 런타임 (릴리스 대 디버그 또는 정적으로 대 동적 링크)에 의해 할당 된 포인터를 전달할 때와 같이 위에서 설명한 문제가 여전히 많이 발생합니다. 따라서 "Windows"는 면역이 아닙니다. 두 시스템 모두주의를 기울여야합니다.
0xC0000022L
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.