Bagaimana cara saya menautkan ke versi glibc tertentu?

110

Ketika saya mengkompilasi sesuatu di PC Ubuntu Lucid 10.04 saya, itu akan ditautkan ke glibc. Lucid menggunakan 2.11 glibc. Ketika saya menjalankan biner ini di PC lain dengan glibc yang lebih lama, perintah gagal mengatakan tidak ada glibc 2.11 ...

Sejauh yang saya tahu glibc menggunakan versi simbol. Dapatkah saya memaksa gcc untuk menautkan ke versi simbol tertentu?

Dalam penggunaan konkret saya, saya mencoba menyusun gcc lintas toolchain untuk ARM.

falstaff
sumber
58
Argh ini adalah salah satu masalah linux yang sangat mengganggu seperti di mana solusinya selalu "Anda tidak boleh melakukan itu", yang tentu saja berarti "tidak berhasil dan belum ada yang memperbaikinya".
Timmmm
3
Orang-orang mengeluh tentang neraka DLL pada Windows. Saya ingat beberapa penggemar Linux mencoba mengangkatnya sebagai contoh yang sangat mengerikan dari dunia Windows. Ketika saya berlari pertama ke ini melakukan pengembangan Linux lebih dari satu dekade lalu semua saya lakukan adalah membenamkan wajah di tangan saya.
0xC0000022L

Jawaban:

69

Anda benar karena glibc menggunakan versi simbol. Jika Anda penasaran, implementasi pembuatan versi simbol yang diperkenalkan di glibc 2.1 dijelaskan di sini dan merupakan perpanjangan dari skema pembuatan versi simbol Sun yang dijelaskan di sini .

Salah satu opsinya adalah menautkan biner Anda secara statis. Ini mungkin opsi termudah.

Anda juga bisa membangun biner Anda di lingkungan build chroot, atau menggunakan kompiler silang glibc- new => glibc- old .

Menurut entri blog http://www.trevorpounds.com Menghubungkan ke Simbol Versi Lama (glibc) , dimungkinkan untuk memaksa simbol apa pun untuk ditautkan dengan yang lebih lama selama itu valid dengan menggunakan .symverpseudo yang sama -op yang digunakan untuk mendefinisikan simbol berversi di tempat pertama. Contoh berikut dikutip dari postingan blog .

Contoh berikut menggunakan realpath glibc, tetapi pastikan itu ditautkan ke versi 2.2.5 yang lebih lama.

#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;
}
jschmier.dll
sumber
18
glibc tidak mendukung penautan statis - program glibc yang ditautkan secara statis tidak sepenuhnya berfungsi pada sistem dengan versi libc yang berbeda.
Ingat Monica
5
glibc libc.aterus ada, glibc mendukung ini dalam beberapa kasus, meskipun tidak disarankan (Drepper) . Anda akan mengalami masalah dengan program non-sepele, terutama apa pun yang menggunakan NSS (solusi di FAQ ).
mr. Spuratic
20

Tautkan dengan -static . Saat Anda menautkan dengan -static , linker akan menyematkan library di dalam file yang dapat dieksekusi, sehingga file yang dapat dieksekusi akan lebih besar, tetapi dapat dijalankan pada sistem dengan versi glibc yang lebih lama karena program akan menggunakan library itu sendiri, bukan pada sistem. .

Iacchus
sumber
55
Seringkali alasan Anda ingin melakukan ini sama sekali adalah karena Anda mendistribusikan aplikasi sumber tertutup. Dalam kasus ini, sering kali tidak diizinkan untuk menautkan secara statis untuk alasan lisensi (melakukan hal itu akan mengharuskan Anda melepaskan semua kode sumber Anda) jadi Anda perlu berhati-hati dengan -static.
Malvineous
3
Sementara itu setidaknya satu orang sering dapat menggunakan musl-libc, tetapi dengan program C ++ semuanya bisa menjadi lebih rumit, jadi menentukan versi simbol mungkin masih diperlukan.
0xC0000022L
16

Penyiapan 1: kompilasi glibc Anda sendiri tanpa GCC khusus dan gunakan

Karena tampaknya tidak mungkin untuk melakukan hanya dengan peretasan versi simbol, mari melangkah lebih jauh dan mengkompilasi sendiri glibc.

Penyiapan ini mungkin berfungsi dan cepat karena tidak mengompilasi ulang seluruh toolchain GCC, cukup glibc.

Tapi itu tidak dapat diandalkan karena menggunakan tuan C runtime objek seperti crt1.o, crti.o, dan crtn.odisediakan oleh glibc. Ini disebutkan di: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Objek tersebut melakukan penyiapan awal yang diandalkan glibc, jadi saya tidak akan terkejut jika ada yang macet dan cara yang sangat halus.

Untuk penyiapan yang lebih andal, lihat Penyiapan 2 di bawah ini.

Bangun glibc dan instal secara lokal:

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`

Penyiapan 1: verifikasi build

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

Kompilasi dan jalankan dengan 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

Program ini menghasilkan yang diharapkan:

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

Perintah diadaptasi dari https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location tetapi --sysrootmembuatnya gagal dengan:

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

jadi saya menghapusnya.

lddoutput mengonfirmasi bahwa lddpustaka dan yang baru saja kita buat benar-benar digunakan seperti yang diharapkan:

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

The gcckompilasi men-debug output yang menunjukkan bahwa host saya objek runtime yang digunakan, yang buruk seperti yang disebutkan sebelumnya, tapi saya tidak tahu bagaimana bekerja di sekitarnya, misalnya mengandung:

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

Penyiapan 1: modifikasi glibc

Sekarang mari kita memodifikasi glibc dengan:

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

Kemudian kompilasi ulang dan instal ulang glibc, dan kompilasi ulang dan jalankan kembali program kami:

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

dan kami melihat hackeddicetak beberapa kali seperti yang diharapkan.

Ini semakin menegaskan bahwa kami benar-benar menggunakan glibc yang kami kompilasi dan bukan yang host.

Diuji di Ubuntu 18.04.

Pengaturan 2: pengaturan murni crosstool-NG

Ini adalah sebuah alternatif untuk setup 1, dan itu adalah setup yang benar yang paling saya telah mencapai jauh: semuanya sudah benar sejauh yang saya dapat mengamati, termasuk C runtime objek seperti crt1.o, crti.o, dan crtn.o.

Dalam penyiapan ini, kami akan mengompilasi toolchain GCC khusus yang menggunakan glibc yang kami inginkan.

Satu-satunya downside ke metode ini adalah build akan memakan waktu lebih lama. Tetapi saya tidak akan mengambil risiko penyiapan produksi dengan kurang dari itu.

crosstool-NG adalah sekumpulan skrip yang mengunduh dan mengkompilasi segala sesuatu dari sumber untuk kami, termasuk GCC, glibc, dan binutils.

Ya, sistem build GCC sangat buruk sehingga kami memerlukan proyek terpisah untuk itu.

Penyiapan ini tidak sempurna karena crosstool-NG tidak mendukung pembuatan file yang dapat dieksekusi tanpa -Wltanda tambahan , yang terasa aneh karena kami telah membuat GCC itu sendiri. Tapi semuanya sepertinya bekerja, jadi ini hanya ketidaknyamanan.

Dapatkan crosstool-NG dan konfigurasikan:

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

Satu-satunya pilihan wajib yang dapat saya lihat, membuatnya cocok dengan versi kernel host Anda untuk menggunakan header kernel yang benar. Temukan versi kernel host Anda dengan:

uname -a

yang menunjukkan kepada saya:

4.15.0-34-generic

jadi di menuconfigsaya lakukan:

  • Operating System
    • Version of linux

jadi saya pilih:

4.14.71

yang merupakan versi pertama yang sama atau lebih lama. Itu harus lebih tua karena kernel kompatibel ke belakang.

Sekarang Anda dapat membangun dengan:

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

dan sekarang tunggu sekitar tiga puluh menit hingga dua jam untuk kompilasi.

Setup 2: konfigurasi opsional

Yang .configkami hasilkan ./ct-ng x86_64-unknown-linux-gnumemiliki:

CT_GLIBC_V_2_27=y

Untuk mengubahnya, menuconfiglakukan:

  • C-library
  • Version of glibc

simpan .config, dan lanjutkan dengan build.

Atau, jika Anda ingin menggunakan sumber glibc Anda sendiri, misalnya untuk menggunakan glibc dari git terbaru, lanjutkan seperti ini :

  • Paths and misc options
    • Try features marked as EXPERIMENTAL: disetel ke true
  • C-library
    • Source of glibc
      • Custom location: bilang iya
      • Custom location
        • Custom source location: arahkan ke direktori yang berisi sumber glibc Anda

di mana glibc diklon sebagai:

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

Penyiapan 2: ujilah

Setelah Anda membangun toolchain yang Anda inginkan, ujilah dengan:

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

Semuanya tampak berfungsi seperti di Setup 1, kecuali bahwa sekarang objek runtime yang benar digunakan:

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

Penyiapan 2: upaya kompilasi glibc efisien gagal

Tampaknya tidak mungkin dengan crosstool-NG, seperti yang dijelaskan di bawah.

Jika Anda hanya membangun kembali;

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

maka perubahan Anda pada lokasi sumber glibc khusus diperhitungkan, tetapi semuanya dibangun dari awal, sehingga tidak dapat digunakan untuk pengembangan berulang.

Jika kami melakukannya:

./ct-ng list-steps

ini memberikan gambaran yang bagus tentang langkah-langkah membangun:

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.

Oleh karena itu, kami melihat bahwa ada langkah glibc yang terkait dengan beberapa langkah GCC, terutama yang libc_start_filesmuncul sebelumnya cc_core_pass_2, yang mungkin merupakan langkah termahal bersama cc_core_pass_1.

Untuk membuat satu langkah saja, Anda harus terlebih dahulu menyetel opsi "Simpan langkah menengah" di .configopsi untuk versi awal:

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

dan kemudian Anda dapat mencoba:

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

tapi sayangnya, +diperlukan seperti yang disebutkan di: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

Namun perlu diperhatikan bahwa memulai ulang pada langkah menengah akan menyetel ulang direktori instalasi ke keadaan semula selama langkah itu. Yaitu, Anda akan memiliki libc yang dibangun kembali - tetapi tidak ada kompiler akhir yang dibangun dengan libc ini (dan karenanya, tidak ada perpustakaan kompiler seperti libstdc ++ juga).

dan pada dasarnya masih membuat rekondisi terlalu lambat untuk memungkinkan pengembangan, dan saya tidak melihat cara mengatasinya tanpa menambal crosstool-NG.

Selanjutnya, mulai dari libclangkah tersebut sepertinya tidak menyalin sumber lagi dari Custom source location, selanjutnya membuat metode ini tidak dapat digunakan.

Bonus: stdlibc ++

Bonus jika Anda juga tertarik dengan pustaka standar C ++: Bagaimana cara mengedit dan membuat ulang sumber pustaka standar libstdc ++ C ++ GCC?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
musl-libcadalah pilihan lain sejauh menyangkut runtime C.
0xC0000022L
0

Menurut pendapat saya, solusi paling malas (terutama jika Anda tidak bergantung pada fitur C / C ++ yang terbaru, atau fitur kompiler terbaru) belum disebutkan, jadi ini dia:

Cukup bangun sistem dengan GLIBC terlama yang masih ingin Anda dukung.

Ini sebenarnya cukup mudah dilakukan saat ini dengan teknologi seperti chroot, atau KVM / Virtualbox, atau buruh pelabuhan, bahkan jika Anda tidak benar-benar ingin menggunakan distro lama seperti itu langsung di pc manapun. Secara rinci, untuk membuat biner portabel maksimum dari perangkat lunak Anda, saya sarankan mengikuti langkah-langkah ini:

  1. Pilih saja racun kotak pasir / virtualisasi / ... apa pun, dan gunakan untuk mendapatkan LTS Ubuntu virtual yang lebih tua dan kompilasi dengan gcc / g ++ yang ada di sana secara default. Itu secara otomatis membatasi GLIBC Anda ke yang tersedia di lingkungan itu.

  2. Hindari bergantung pada lib eksternal di luar yang mendasar: seperti, Anda harus secara dinamis menautkan hal-hal sistem tingkat dasar seperti glibc, libGL, libxcb / X11 / wayland things, libasound / libpulseaudio, mungkin GTK + jika Anda menggunakannya, tetapi sebaliknya lebih disukai menghubungkan eksternal secara statis libs / kirimkan jika Anda bisa. Terutama sebagian besar lib yang berdiri sendiri seperti pemuat gambar, dekoder multimedia, dll. Dapat menyebabkan lebih sedikit kerusakan pada distro lain (kerusakan dapat disebabkan misalnya jika hanya ada di suatu tempat dalam versi utama yang berbeda) jika Anda mengirimkannya secara statis.

Dengan pendekatan itu, Anda mendapatkan biner lama yang kompatibel dengan GLIBC tanpa penyesuaian simbol manual, tanpa melakukan biner yang sepenuhnya statis (yang mungkin rusak untuk program yang lebih kompleks karena glibc membencinya, dan yang dapat menyebabkan masalah lisensi untuk Anda), dan tanpa pengaturan rantai alat khusus apa pun, salinan glibc khusus apa pun, atau apa pun.

ET
sumber