Làm cách nào để liên kết đến một phiên bản glibc cụ thể?


110

Khi tôi biên dịch nội dung nào đó trên PC Ubuntu Lucid 10.04, nó sẽ được liên kết với glibc. Lucid sử dụng 2,11 glibc. Khi tôi chạy tệp nhị phân này trên một PC khác có glibc cũ hơn, lệnh không thành công khi nói rằng không có glibc 2.11 ...

Theo như tôi biết glibc sử dụng lập phiên bản biểu tượng. Tôi có thể buộc gcc liên kết với một phiên bản biểu tượng cụ thể không?

Trong mục đích sử dụng cụ thể của mình, tôi cố gắng biên dịch chuỗi công cụ chéo gcc cho ARM.


58
Đây là một trong những vấn đề linux thực sự khó chịu như giải pháp luôn luôn là "bạn không nên làm điều đó", tất nhiên có nghĩa là "nó không hoạt động và chưa ai sửa nó".
Timmmm

3
Mọi người phàn nàn về DLL hell trên Windows. Tôi nhớ Linux một số người hâm mộ đã cố gắng đưa nó lên như một ví dụ đặc biệt khủng khiếp từ thế giới Windows. Khi lần đầu tiên tôi chạy vào này thực hiện phát triển Linux hơn một thập kỷ trước tất cả tôi đã làm là chôn khuôn mặt của tôi trong tay tôi.
0xC0000022L

Câu trả lời:


69

Bạn đúng khi glibc sử dụng lập phiên bản biểu tượng. Nếu bạn tò mò, việc triển khai lập phiên bản biểu tượng được giới thiệu trong glibc 2.1 được mô tả ở đây và là một phần mở rộng của sơ đồ lập phiên bản biểu tượng của Sun được mô tả ở đây .

Một tùy chọn là liên kết tĩnh tệp nhị phân của bạn. Đây có lẽ là lựa chọn dễ dàng nhất.

Bạn cũng có thể tạo tệp nhị phân của mình trong môi trường xây dựng chroot hoặc sử dụng trình biên dịch chéo glibc- new => glibc- old .

Theo http://www.trevorpounds.com bài đăng trên blog Liên kết với các biểu tượng phiên bản cũ hơn (glibc) , có thể buộc bất kỳ biểu tượng nào được liên kết với biểu tượng cũ hơn miễn là nó hợp lệ bằng cách sử dụng cùng một .symvergiả -op được sử dụng để xác định các ký hiệu được tạo phiên bản ngay từ đầu. Ví dụ sau đây được trích từ bài đăng trên blog .

Ví dụ sau sử dụng đường dẫn thực của glibc, nhưng đảm bảo rằng nó được liên kết với phiên bản 2.2.5 cũ hơn.

#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;
}

18
glibc không hỗ trợ liên kết tĩnh - các chương trình glibc liên kết tĩnh không hoàn toàn hoạt động trên các hệ thống có các phiên bản libc khác nhau.
Hãy nhớ Monica

5
glibc libc.atiếp tục tồn tại, glibc hỗ trợ điều này trong một số trường hợp, mặc dù nó không được khuyến khích (Drepper) . Bạn sẽ gặp rắc rối với các chương trình không tầm thường, đặc biệt là bất cứ thứ gì sử dụng NSS (cách giải quyết trong Câu hỏi thường gặp ).
mr.spuratic

20

Liên kết với -static . Khi bạn liên kết với -static , trình liên kết sẽ nhúng thư viện bên trong tệp thực thi, do đó tệp thực thi sẽ lớn hơn, nhưng nó có thể được thực thi trên hệ thống có phiên bản glibc cũ hơn vì chương trình sẽ sử dụng thư viện của chính nó thay vì của hệ thống .


55
Thường thì lý do bạn muốn làm điều này là vì bạn đang phân phối một ứng dụng mã nguồn đóng. Trong trường hợp này, thường không được phép liên kết tĩnh vì lý do cấp phép (làm như vậy sẽ yêu cầu bạn giải phóng tất cả mã nguồn của mình) vì vậy bạn cần phải cẩn thận với -static.
Malvineous

3
Trong khi đó, ít nhất người ta thường có thể sử dụng musl-libc, nhưng với các chương trình C ++, mọi thứ có thể phức tạp hơn, vì vậy việc chỉ định một phiên bản ký hiệu có thể vẫn cần thiết.
0xC0000022L

16

Thiết lập 1: biên dịch glibc của riêng bạn mà không cần GCC chuyên dụng và sử dụng nó

Vì dường như không thể thực hiện được chỉ với các bản hack lập phiên bản biểu tượng, hãy tiến thêm một bước nữa và tự biên dịch glibc.

Thiết lập này có thể hoạt động và nhanh chóng vì nó không biên dịch lại toàn bộ chuỗi công cụ GCC, chỉ là glibc.

Nhưng nó không phải là đáng tin cậy như nó sử dụng các đối tượng máy chủ C runtime như crt1.o, crti.ocrtn.ođược cung cấp bởi glibc. Điều này được đề cập tại: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Những đối tượng đó thực hiện thiết lập sớm mà glibc dựa vào, vì vậy tôi sẽ không ngạc nhiên nếu mọi thứ gặp sự cố tuyệt vời và những cách vô cùng tinh tế.

Để có thiết lập đáng tin cậy hơn, hãy xem Thiết lập 2 bên dưới.

Xây dựng glibc và cài đặt cục bộ:

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`

Thiết lập 1: xác minh bản dựng

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);
}

Biên dịch và chạy với 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

Chương trình đưa ra kết quả mong đợi:

gnu_get_libc_version() = 2.28
The atomic counter is 10000
The non-atomic counter is 8674

Lệnh được điều chỉnh từ https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location nhưng --sysrootkhông thành công với:

cannot find /home/ciro/glibc/build/install/lib/libc.so.6 inside /home/ciro/glibc/build/install

vì vậy tôi đã gỡ bỏ nó.

lddđầu ra xác nhận rằng lddvà các thư viện mà chúng tôi vừa xây dựng đang thực sự được sử dụng như mong đợi:

+ 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)

Đầu gccra gỡ lỗi biên dịch cho thấy rằng các đối tượng thời gian chạy máy chủ của tôi đã được sử dụng, điều này không tốt như đã đề cập trước đó, nhưng tôi không biết cách giải quyết nó, ví dụ: nó chứa:

COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o

Thiết lập 1: sửa đổi glibc

Bây giờ hãy sửa đổi glibc với:

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)");

Sau đó biên dịch lại và cài đặt lại glibc, biên dịch lại và chạy lại chương trình của chúng tôi:

cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh

và chúng tôi thấy hackedđược in một vài lần như mong đợi.

Điều này khẳng định thêm rằng chúng tôi thực sự sử dụng glibc mà chúng tôi đã biên dịch chứ không phải máy chủ.

Đã thử nghiệm trên Ubuntu 18.04.

Thiết lập 2: Thiết lập nguyên sơ crosstool-NG

Đây là một thay thế cho thiết lập 1, và nó là thiết lập đúng nhất mà tôi đã đạt được cho đến nay: mọi thứ đều đúng như xa như tôi có thể quan sát, bao gồm thời gian chạy C đối tượng như crt1.o, crti.o, và crtn.o.

Trong thiết lập này, chúng tôi sẽ biên dịch một chuỗi công cụ GCC chuyên dụng đầy đủ sử dụng glibc mà chúng tôi muốn.

Nhược điểm duy nhất của phương pháp này là quá trình xây dựng sẽ lâu hơn. Nhưng tôi sẽ không mạo hiểm thiết lập sản xuất với bất kỳ thứ gì ít hơn.

crosstool-NG là một bộ tập lệnh tải xuống và biên dịch mọi thứ từ nguồn cho chúng tôi, bao gồm GCC, glibc và binutils.

Vâng, hệ thống xây dựng GCC quá tệ nên chúng tôi cần một dự án riêng cho việc đó.

Thiết lập này không hoàn hảo chỉ vì crosstool-NG không hỗ trợ xây dựng tệp thực thi mà không có -Wlcờ bổ sung , điều này cảm thấy kỳ lạ vì chúng tôi đã tự xây dựng GCC. Nhưng mọi thứ dường như hoạt động, vì vậy đây chỉ là một sự bất tiện.

Nhận crosstool-NG và định cấu hình nó:

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

Tùy chọn bắt buộc duy nhất mà tôi có thể thấy, là làm cho nó khớp với phiên bản hạt nhân máy chủ của bạn để sử dụng các tiêu đề hạt nhân chính xác. Tìm phiên bản hạt nhân máy chủ của bạn bằng:

uname -a

cho tôi thấy:

4.15.0-34-generic

vì vậy trong menuconfigtôi làm:

  • Operating System
    • Version of linux

vì vậy tôi chọn:

4.14.71

là phiên bản đầu tiên bằng hoặc cũ hơn. Nó phải cũ hơn vì hạt nhân tương thích ngược.

Bây giờ bạn có thể xây dựng với:

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

và bây giờ đợi khoảng ba mươi phút đến hai giờ để biên dịch.

Thiết lập 2: cấu hình tùy chọn

Cái .configmà chúng tôi đã tạo ./ct-ng x86_64-unknown-linux-gnucó:

CT_GLIBC_V_2_27=y

Để thay đổi điều đó, menuconfighãy làm:

  • C-library
  • Version of glibc

lưu .configvà tiếp tục với bản dựng.

Hoặc, nếu bạn muốn sử dụng nguồn glibc của riêng mình, ví dụ: sử dụng glibc từ git mới nhất, hãy tiến hành như sau :

  • Paths and misc options
    • Try features marked as EXPERIMENTAL: đặt thành true
  • C-library
    • Source of glibc
      • Custom location: nói có
      • Custom location
        • Custom source location: trỏ tới thư mục chứa nguồn glibc của bạn

trong đó glibc được sao chép thành:

git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28

Thiết lập 2: thử nghiệm

Khi bạn đã xây dựng chuỗi công cụ mà bạn muốn, hãy thử nghiệm nó với:

#!/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

Mọi thứ dường như hoạt động như trong Thiết lập 1, ngoại trừ việc bây giờ các đối tượng thời gian chạy chính xác đã được sử dụng:

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

Thiết lập 2: nỗ lực biên dịch lại glibc hiệu quả không thành công

Điều này dường như không khả thi với crosstool-NG, như được giải thích bên dưới.

Nếu bạn chỉ xây dựng lại;

env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`

thì các thay đổi của bạn đối với vị trí nguồn glibc tùy chỉnh được tính đến, nhưng nó xây dựng mọi thứ từ đầu, khiến nó không thể sử dụng được để phát triển lặp đi lặp lại.

Nếu chúng ta làm:

./ct-ng list-steps

nó cung cấp một cái nhìn tổng quan tốt đẹp về các bước xây dựng:

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.

do đó, chúng tôi thấy rằng có các bước glibc đan xen với một số bước GCC, đáng chú ý nhất là libc_start_filestrước cc_core_pass_2đó, đây có thể là bước đắt nhất cùng với cc_core_pass_1.

Để xây dựng chỉ một bước, trước tiên bạn phải đặt .configtùy chọn "Lưu các bước trung gian" trong tùy chọn cho bản dựng nội dung:

  • Paths and misc options
    • Debug crosstool-NG
      • Save intermediate steps

và sau đó bạn có thể thử:

env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`

nhưng rất tiếc, +yêu cầu như đã đề cập tại: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

Tuy nhiên, lưu ý rằng việc khởi động lại ở bước trung gian sẽ đặt lại thư mục cài đặt về trạng thái mà nó có trong bước đó. Tức là, bạn sẽ có một libc được xây dựng lại - nhưng không có trình biên dịch cuối cùng nào được xây dựng với libc này (và do đó, không có thư viện trình biên dịch nào như libstdc ++).

và về cơ bản vẫn làm cho việc xây dựng lại quá chậm để có thể phát triển được và tôi không biết cách khắc phục điều này mà không cần vá crosstool-NG.

Hơn nữa, bắt đầu từ libcbước dường như không sao chép lại nguồn từ đó Custom source location, khiến cho phương pháp này không thể sử dụng được.

Phần thưởng: stdlibc ++

Một phần thưởng nếu bạn cũng quan tâm đến thư viện chuẩn C ++: Làm thế nào để chỉnh sửa và xây dựng lại nguồn thư viện chuẩn GCC libstdc ++ C ++?


musl-libclà một tùy chọn khác liên quan đến thời gian chạy C.
0xC0000022L

0

Theo ý kiến ​​của tôi, giải pháp lười biếng nhất (đặc biệt nếu bạn không dựa vào các tính năng C / C ++ mới nhất hoặc các tính năng trình biên dịch mới nhất) vẫn chưa được đề cập, vì vậy đây là:

Chỉ cần xây dựng trên hệ thống có GLIBC cũ nhất mà bạn vẫn muốn hỗ trợ.

Điều này thực sự khá dễ thực hiện ngày nay với các công nghệ như chroot, KVM / Virtualbox hoặc docker, ngay cả khi bạn không thực sự muốn sử dụng trực tiếp một bản phân phối cũ như vậy trên bất kỳ máy tính nào. Về chi tiết, để tạo bản nhị phân di động tối đa cho phần mềm của bạn, tôi khuyên bạn nên làm theo các bước sau:

  1. Chỉ cần chọn hộp cát / ảo hóa / ... độc của bạn, và sử dụng nó để tạo cho mình một Ubuntu LTS ảo cũ hơn và biên dịch với gcc / g ++ mà nó có trong đó theo mặc định. Điều đó tự động giới hạn GLIBC của bạn với GLIBC có sẵn trong môi trường đó.

  2. Tránh phụ thuộc vào các lib bên ngoài bên ngoài những thứ cơ bản: chẳng hạn như bạn nên liên kết động các nội dung hệ thống cấp đất như glibc, libGL, libxcb / X11 / wayland things, libasound / libpulseaudio, có thể là GTK + nếu bạn sử dụng nó, nhưng nếu không thì tốt hơn là liên kết tĩnh bên ngoài libs / gửi chúng cùng nếu bạn có thể. Đặc biệt là hầu hết các lib độc lập như bộ tải hình ảnh, bộ giải mã đa phương tiện, v.v. có thể ít gây ra lỗi hơn trên các bản phân phối khác (có thể gây ra lỗi, ví dụ như nếu chỉ xuất hiện ở đâu đó trong một phiên bản chính khác) nếu bạn chuyển chúng tĩnh.

Với cách tiếp cận đó, bạn nhận được tệp nhị phân tương thích với GLIBC cũ mà không cần bất kỳ chỉnh sửa ký hiệu thủ công nào, mà không cần thực hiện nhị phân tĩnh hoàn toàn (có thể bị hỏng đối với các chương trình phức tạp hơn vì glibc ghét điều đó và có thể gây ra vấn đề cấp phép cho bạn) và không cần thiết lập lên bất kỳ chuỗi công cụ tùy chỉnh nào, bất kỳ bản sao glibc tùy chỉnh nào hoặc bất kỳ thứ gì.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.