Ubuntu Lucid 10.04 PC에서 무언가를 컴파일하면 glibc와 연결됩니다. Lucid는 2.11의 glibc를 사용합니다. 이전 glibc가있는 다른 PC에서이 바이너리를 실행하면 glibc 2.11이 없다는 명령이 실패합니다.
내가 아는 한 glibc는 기호 버전 관리를 사용합니다. gcc가 특정 기호 버전에 대해 링크하도록 강제 할 수 있습니까?
내 구체적인 사용에서 ARM 용 gcc 크로스 툴체인을 컴파일하려고합니다.
Ubuntu Lucid 10.04 PC에서 무언가를 컴파일하면 glibc와 연결됩니다. Lucid는 2.11의 glibc를 사용합니다. 이전 glibc가있는 다른 PC에서이 바이너리를 실행하면 glibc 2.11이 없다는 명령이 실패합니다.
내가 아는 한 glibc는 기호 버전 관리를 사용합니다. gcc가 특정 기호 버전에 대해 링크하도록 강제 할 수 있습니까?
내 구체적인 사용에서 ARM 용 gcc 크로스 툴체인을 컴파일하려고합니다.
답변:
glibc가 기호 버전 관리를 사용한다는 점에서 정확합니다. 궁금하다면 glibc 2.1에 도입 된 기호 버전 관리 구현이 여기 에 설명되어 있으며 여기 에 설명 된 Sun의 기호 버전 지정 체계의 확장입니다 . .
한 가지 옵션은 바이너리를 정적으로 링크하는 것입니다. 이것은 아마도 가장 쉬운 옵션 일 것입니다.
chroot 빌드 환경에서 또는 glibc- new => glibc- old를 사용하여 바이너리를 빌드 할 수도 있습니다. 크로스 컴파일러를 .
에 따르면 http://www.trevorpounds.com 블로그 게시물 이전 버전 화 기호 (glibc에)에 연결 , 같은 사용하여 너무 오래가 유효한 경우 이전 하나에 링크 할 수있는 기호를 강제로 할 수 있습니다 .symver
의사를 -op는 처음에 버전이 지정된 기호를 정의하는 데 사용됩니다. 다음 예는 블로그 게시물 에서 발췌 한 것 입니다.
다음 예제는 glibc의 realpath를 사용하지만 이전 2.2.5 버전과 연결되어 있는지 확인합니다.
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
__asm__(".symver realpath,realpath@GLIBC_2.2.5");
int main()
{
const char* unresolved = "/lib64";
char resolved[PATH_MAX+1];
if(!realpath(unresolved, resolved))
{ return 1; }
printf("%s\n", resolved);
return 0;
}
libc.a
계속 존재하지만 glibc는 권장되지 않지만 일부 경우에이를 지원 합니다 (Drepper) . 중요하지 않은 프로그램, 특히 NSS를 사용하는 모든 프로그램에 문제가 발생 합니다 (FAQ의 해결 방법 ).
-static 과 링크하십시오 . -static 과 연결할 때 하면 링커는 실행 파일 내에 라이브러리를 포함하므로 실행 파일은 더 커지지 만 프로그램이 시스템 대신 자체 라이브러리를 사용하기 때문에 이전 버전의 glibc가있는 시스템에서 실행할 수 있습니다. .
심볼 버전 관리 해킹만으로는 불가능 해 보이므로 한 단계 더 나아가 glibc를 직접 컴파일 해 보겠습니다.
이 설정은 작동 할 수 있으며 전체 GCC 도구 모음을 다시 컴파일하지 않고 glibc 만 다시 컴파일하므로 빠릅니다.
그것뿐만 호스트 C 런타임 객체는 사용하지만 이는 신뢰성없는 crt1.o
, crti.o
그리고 crtn.o
glibc에 의해 제공. https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location 에서 언급됩니다. 일이 멋진 추락하면 나는 놀라지 않을 것이다, 그래서 그 객체가, glibc는가에 의존 초기 설정을 그리고 놀랍도록 미묘한 방법.
보다 안정적인 설정은 아래의 설정 2를 참조하십시오.
glibc를 빌드하고 로컬에 설치합니다.
export glibc_install="$(pwd)/glibc/build/install"
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
mkdir build
cd build
../configure --prefix "$glibc_install"
make -j `nproc`
make install -j `nproc`
test_glibc.c
#define _GNU_SOURCE
#include <assert.h>
#include <gnu/libc-version.h>
#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>
atomic_int acnt;
int cnt;
int f(void* thr_data) {
for(int n = 0; n < 1000; ++n) {
++cnt;
++acnt;
}
return 0;
}
int main(int argc, char **argv) {
/* Basic library version check. */
printf("gnu_get_libc_version() = %s\n", gnu_get_libc_version());
/* Exercise thrd_create from -pthread,
* which is not present in glibc 2.27 in Ubuntu 18.04.
* /programming/56810/how-do-i-start-threads-in-plain-c/52453291#52453291 */
thrd_t thr[10];
for(int n = 0; n < 10; ++n)
thrd_create(&thr[n], f, NULL);
for(int n = 0; n < 10; ++n)
thrd_join(thr[n], NULL);
printf("The atomic counter is %u\n", acnt);
printf("The non-atomic counter is %u\n", cnt);
}
다음으로 컴파일하고 실행하십시오 test_glibc.sh
.
#!/usr/bin/env bash
set -eux
gcc \
-L "${glibc_install}/lib" \
-I "${glibc_install}/include" \
-Wl,--rpath="${glibc_install}/lib" \
-Wl,--dynamic-linker="${glibc_install}/lib/ld-linux-x86-64.so.2" \
-std=c11 \
-o test_glibc.out \
-v \
test_glibc.c \
-pthread \
;
ldd ./test_glibc.out
./test_glibc.out
프로그램은 예상 된 결과를 출력합니다.
gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674
https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location 에서 조정 된 명령 이지만 --sysroot
다음과 같이 실패했습니다.
cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install
그래서 제거했습니다.
ldd
출력 ldd
은 방금 빌드 한 및 라이브러리가 실제로 예상대로 사용되고 있는지 확인합니다 .
+ ldd test_glibc.out
linux-vdso.so.1 (0x00007ffe4bfd3000)
libpthread.so.0 => /home/ciro/glibc/build/install/lib/libpthread.so.0 (0x00007fc12ed92000)
libc.so.6 => /home/ciro/glibc/build/install/lib/libc.so.6 (0x00007fc12e9dc000)
/home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000)
gcc
이전에 언급,하지만 난 그것을 해결할 방법을 알고하지 않는 나쁜 나의 호스트 런타임 개체가 사용 된 것을 컴파일 디버그 출력 쇼, 예를 들어, 그것은 포함 :
COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
이제 다음과 같이 glibc를 수정 해 보겠습니다.
diff --git a/nptl/thrd_create.c b/nptl/thrd_create.c
index 113ba0d93e..b00f088abb 100644
--- a/nptl/thrd_create.c
+++ b/nptl/thrd_create.c
@@ -16,11 +16,14 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <stdio.h>
+
#include "thrd_priv.h"
int
thrd_create (thrd_t *thr, thrd_start_t func, void *arg)
{
+ puts("hacked");
_Static_assert (sizeof (thr) == sizeof (pthread_t),
"sizeof (thr) != sizeof (pthread_t)");
그런 다음 glibc를 다시 컴파일하고 다시 설치하고 프로그램을 다시 컴파일하고 다시 실행합니다.
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
hacked
예상대로 몇 번 인쇄 된 것을 볼 수 있습니다.
이것은 우리가 호스트가 아닌 컴파일 한 glibc를 실제로 사용했음을 확인합니다.
Ubuntu 18.04에서 테스트되었습니다.
이 설정 1의 대안이며, 그것은 내가 지금까지 달성 한 가장 올바른 설정입니다 : 모든 것을 내가 C 런타임과 같은 개체를 포함하여 관찰 할 수있는만큼 지금까지 올바른지 crt1.o
, crti.o
그리고 crtn.o
.
이 설정에서는 원하는 glibc를 사용하는 전체 전용 GCC 도구 모음을 컴파일합니다.
이 방법의 유일한 단점은 빌드 시간이 더 오래 걸린다는 것입니다. 그러나 나는 그 이하로 생산 설정을 위험에 빠뜨리지 않을 것입니다.
crosstool-NG 는 GCC, glibc 및 binutils를 포함하여 우리를 위해 소스에서 모든 것을 다운로드하고 컴파일하는 스크립트 세트입니다.
예, GCC 빌드 시스템이 너무 나빠서 별도의 프로젝트가 필요합니다.
crosstool-NG는 추가 플래그 없이 실행 파일 빌드를 지원하지 않기 때문에이 설정은 완벽 하지 않습니다.-Wl
. GCC 자체를 빌드했기 때문에 이상하게 느껴집니다. 그러나 모든 것이 작동하는 것처럼 보이므로 이것은 불편할뿐입니다.
crosstool-NG를 가져 와서 구성하십시오.
git clone https://github.com/crosstool-ng/crosstool-ng
cd crosstool-ng
git checkout a6580b8e8b55345a5a342b5bd96e42c83e640ac5
export CT_PREFIX="$(pwd)/.build/install"
export PATH="/usr/lib/ccache:${PATH}"
./bootstrap
./configure --enable-local
make -j `nproc`
./ct-ng x86_64-unknown-linux-gnu
./ct-ng menuconfig
내가 볼 수있는 유일한 필수 옵션은 올바른 커널 헤더를 사용하기 위해 호스트 커널 버전과 일치하도록 만드는 것입니다. 다음을 사용하여 호스트 커널 버전을 찾으십시오.
uname -a
나를 보여줍니다 :
4.15.0-34-generic
그래서 menuconfig
나는 :
Operating System
Version of linux
그래서 다음을 선택합니다.
4.14.71
첫 번째 동일하거나 이전 버전입니다. 커널이 이전 버전과 호환되므로 이전 버전이어야합니다.
이제 다음으로 빌드 할 수 있습니다.
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
이제 편집을 위해 약 30 분에서 2 시간을 기다립니다.
.config
우리는 생성하는 것이 ./ct-ng x86_64-unknown-linux-gnu
있다 :
CT_GLIBC_V_2_27=y
이를 변경하려면 다음을 menuconfig
수행하십시오.
C-library
Version of glibc
를 저장하고 .config
빌드를 계속합니다.
또는 자신의 glibc 소스를 사용하려면, 예를 들어 최신 git에서 glibc를 사용하려면 다음 과 같이 진행 하십시오 .
Paths and misc options
Try features marked as EXPERIMENTAL
: true로 설정C-library
Source of glibc
Custom location
: 예라고Custom location
Custom source location
: glibc 소스를 포함하는 디렉토리를 가리 킵니다.glibc는 다음과 같이 복제되었습니다.
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
원하는 툴체인을 구축했으면 다음을 사용하여 테스트하십시오.
#!/usr/bin/env bash
set -eux
install_dir="${CT_PREFIX}/x86_64-unknown-linux-gnu"
PATH="${PATH}:${install_dir}/bin" \
x86_64-unknown-linux-gnu-gcc \
-Wl,--dynamic-linker="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib/ld-linux-x86-64.so.2" \
-Wl,--rpath="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib" \
-v \
-o test_glibc.out \
test_glibc.c \
-pthread \
;
ldd test_glibc.out
./test_glibc.out
이제 올바른 런타임 개체가 사용되었다는 점을 제외하면 모든 것이 설정 1에서와 같이 작동하는 것 같습니다.
COLLECT_GCC_OPTIONS=/home/ciro/crosstool-ng/.build/install/x86_64-unknown-linux-gnu/bin/../x86_64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crt1.o
아래에 설명 된대로 crosstool-NG로는 불가능 해 보입니다.
재건 만하면
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
그런 다음 사용자 지정 glibc 소스 위치에 대한 변경 사항이 고려되지만 모든 것을 처음부터 빌드하므로 반복 개발에 사용할 수 없습니다.
우리가 할 경우 :
./ct-ng list-steps
빌드 단계에 대한 멋진 개요를 제공합니다.
Available build steps, in order:
- companion_tools_for_build
- companion_libs_for_build
- binutils_for_build
- companion_tools_for_host
- companion_libs_for_host
- binutils_for_host
- cc_core_pass_1
- kernel_headers
- libc_start_files
- cc_core_pass_2
- libc
- cc_for_build
- cc_for_host
- libc_post_cc
- companion_libs_for_target
- binutils_for_target
- debug
- test_suite
- finish
Use "<step>" as action to execute only that step.
Use "+<step>" as action to execute up to that step.
Use "<step>+" as action to execute from that step onward.
그러므로, 우리는 몇 가지 GCC 단계 얽혀 glibc는 단계가 볼 특히 libc_start_files
전에 제공 cc_core_pass_2
과 함께 가능성이 가장 비싼 단계 인 cc_core_pass_1
.
한 단계 만 빌드하려면 먼저 .config
초기 빌드 옵션에서 "중간 단계 저장"을 설정해야합니다 .
Paths and misc options
Debug crosstool-NG
Save intermediate steps
그런 다음 시도해 볼 수 있습니다.
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
하지만 안타깝게도 https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536에+
언급 된 필수 항목입니다.
그러나 중간 단계에서 다시 시작하면 설치 디렉토리가 해당 단계 동안의 상태로 재설정됩니다. 즉, 다시 빌드 된 libc가 있지만이 libc로 빌드 된 최종 컴파일러는 없습니다 (따라서 libstdc ++와 같은 컴파일러 라이브러리도 없음).
그리고 기본적으로 여전히 리빌드가 너무 느려서 개발에 적합하지 않으며 crosstool-NG를 패치하지 않고는 이것을 극복하는 방법을 알 수 없습니다.
또한 libc
단계 에서 시작 하면에서 소스를 다시 복사하지 않아이 Custom source location
방법을 사용할 수 없게되었습니다.
C ++ 표준 라이브러리에도 관심이 있다면 보너스 : GCC libstdc ++ C ++ 표준 라이브러리 소스를 편집하고 다시 빌드하는 방법은 무엇입니까?
musl-libc
C 런타임에 관한 한 또 다른 옵션입니다.
제 생각에는 가장 게으른 솔루션 (특히 최신 C / C ++ 기능 또는 최신 컴파일러 기능에 의존하지 않는 경우)이 아직 언급되지 않았으므로 여기에 있습니다.
여전히 지원하고 싶은 가장 오래된 GLIBC로 시스템을 구축하십시오.
이것은 chroot, KVM / Virtualbox 또는 docker와 같은 기술을 사용하여 오늘날 매우 쉽게 수행 할 수 있습니다. 심지어 PC에서 직접 이러한 오래된 배포판을 사용하고 싶지 않더라도 말입니다. 자세히, 소프트웨어의 최대 이식 가능한 바이너리를 만들려면 다음 단계를 따르는 것이 좋습니다.
샌드 박스 / 가상화 / ... 무엇이든 선택하고이를 사용하여 가상의 오래된 Ubuntu LTS를 얻고 기본적으로 포함 된 gcc / g ++로 컴파일하십시오. 그러면 GLIBC가 해당 환경에서 사용 가능한 것으로 자동 제한됩니다.
기본 라이브러리 외부의 외부 라이브러리에 의존하는 것을 피하십시오. 가능한 경우 libs / 함께 배송하십시오. 특히 이미지 로더, 멀티미디어 디코더 등과 같은 대부분의 자체 포함 된 라이브러리는 정적으로 제공하는 경우 다른 배포판에서 손상을 덜 일으킬 수 있습니다 (예 : 다른 주요 버전에있는 경우에만 손상이 발생할 수 있음).
이러한 접근 방식을 사용하면 완전히 정적 바이너리를 수행하지 않고 (glibc가이를 싫어하고 라이선스 문제를 유발할 수 있기 때문에 더 복잡한 프로그램에서 중단 될 수 있음), 설정없이 수동 기호 조정없이 이전 GLIBC 호환 바이너리를 얻을 수 있습니다. 사용자 지정 도구 모음, 사용자 지정 glibc 복사본 등