Belirli bir glibc sürümüne nasıl bağlanabilirim?


111

Ubuntu Lucid 10.04 bilgisayarımda bir şey derlediğimde, glibc'ye bağlanıyor. Lucid, glibc'nin 2.11'ini kullanır. Bu ikili dosyayı daha eski bir glibc ile başka bir bilgisayarda çalıştırdığımda, komut glibc 2.11 olmadığını söyleyerek başarısız oluyor ...

Bildiğim kadarıyla glibc, sembol versiyonlama kullanıyor. Gcc'yi belirli bir sembol sürümüne bağlanmaya zorlayabilir miyim?

Somut kullanımımda, ARM için bir gcc çapraz araç zinciri derlemeye çalışıyorum.


58
Argh, çözümün her zaman "bunu yapmamalısın" olduğu gibi gerçekten sinir bozucu linux problemlerinden biri, ki bu tabii ki "işe yaramıyor ve henüz kimse düzeltmedi" anlamına geliyor.
Timmmm

4
İnsanlar Windows'ta DLL cehenneminden şikayet etti. Ben Linux'u hatırlıyorum bazı , Windows dünyadan özellikle korkunç örneği olarak bu konuyu açmak çalışıyor meraklıları. İçine ilk koştu zaman bu tek yaptığım on yıl önce yaptığını Linux geliştirme ellerimi yüzümü gömmek oldu.
0xC0000022L

Yanıtlar:


70

Glibc'nin sembol sürümlemesini kullandığı konusunda haklısınız. Merak ediyorsanız, glibc 2.1'de tanıtılan sembol sürümü oluşturma uygulaması burada açıklanmıştır ve Sun'ın burada açıklanan simge sürüm oluşturma şemasının bir uzantısıdır .

Bir seçenek, ikilinizi statik olarak bağlamaktır. Bu muhtemelen en kolay seçenektir.

İkili dosyanızı bir chroot oluşturma ortamında veya bir glibc- new => glibc- eski çapraz derleyici kullanarak da oluşturabilirsiniz .

Göre http://www.trevorpounds.com blog yayınına Yaşlı sürümlü Semboller (glibc) bağlanma , aynı kullanarak çok uzun geçerli olduğu gibi eski bir birine karşı bağlantılı olduğu herhangi bir sembol zorlamak mümkündür .symverpseudo -op, ilk etapta versiyonlanmış sembolleri tanımlamak için kullanılır. Aşağıdaki örnek, blog gönderisinden alınmıştır .

Aşağıdaki örnek, glibc'nin gerçek yolunu kullanır, ancak daha eski bir 2.2.5 sürümüne bağlı olduğundan emin olur.

#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 statik bağlamayı desteklemez - statik olarak bağlanmış glibc programları, farklı libc sürümleri olan sistemlerde genellikle çalışmaz.
Monica'yı

5
glibc'ler var libc.aolmaya devam ediyor, glibc bunu bazı durumlarda desteklese de önerilmiyor (Drepper) . Önemsiz olmayan programlarla, özellikle NSS'yi kullanan her şeyle sorun yaşayacaksınız (geçici çözüm SSS'de ).
mr.spuratic

20

-Static ile bağlantı kurun . -Static ile bağlantı kurduğunuzda , bağlayıcı kitaplığı yürütülebilir dosyanın içine yerleştirir, böylece çalıştırılabilir daha büyük olur, ancak daha eski bir glibc sürümüne sahip bir sistemde çalıştırılabilir çünkü program, sistem yerine kendi kitaplığını kullanacaktır. .


55
Genellikle bunu yapmak istemenizin nedeni, kapalı kaynaklı bir uygulama dağıtmanızdır. Bu durumda, genellikle lisanslama nedenleriyle statik olarak bağlantı kurulmasına izin verilmez (bunu yapmak tüm kaynak kodunuzu serbest bırakmanızı gerektirir), bu nedenle -static konusunda dikkatli olmanız gerekir.
Malvineous

3
Bu arada en azından biri musl-libc'ye başvurabilir, ancak C ++ programları ile işler daha karmaşık hale gelebilir, bu nedenle bir sembol versiyonunun belirtilmesi yine de gerekli olabilir.
0xC0000022L

16

Kurulum 1: adanmış GCC olmadan kendi glibc'nizi derleyin ve kullanın

Sadece sembol versiyonlama hack'leriyle yapmak imkansız göründüğü için, bir adım daha ileri gidelim ve glibc'yi kendimiz derleyelim.

Bu kurulum işe yarayabilir ve tüm GCC araç zincirini yeniden derlemediği için hızlıdır, sadece glibc.

Bu gibi konakçı C çalışma zamanı gibi nesneleri kullanmaktadır Ancak güvenilir değildir crt1.o, crti.ove crtn.oglibc'nin sağladığı. Bu, şu adreste belirtilmiştir: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Bu nesneler, glibc'nin dayandığı erken kurulum yapar, bu yüzden işler harika bir şekilde çökerse şaşırmam ve müthiş incelikli yollar.

Daha güvenilir bir kurulum için aşağıdaki Kurulum 2'ye bakın.

Glibc'yi derleyin ve yerel olarak yükleyin:

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`

Kurulum 1: yapıyı doğrulayın

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

Aşağıdakilerle derleyin ve çalıştırın 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 beklenen çıktıları verir:

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

Komut, https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location'dan uyarlandı, ancak --sysrootşunlarla başarısız oldu:

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

bu yüzden kaldırdım.

lddçıktı, az önce oluşturduğumuz lddve kitaplıklarının aslında beklendiği gibi kullanıldığını doğrular :

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

gccDaha önce belirtildiği, ama bunu geçici bir çözüm için nasıl bilmiyorum kadar kötü benim ev sahibi çalışma zamanı nesneler kullanıldığını derleme ayıklama çıkışı gösterileri, örneğin içerdiği:

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

Kurulum 1: glibc'yi değiştirin

Şimdi glibc'yi şununla değiştirelim:

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

Ardından glibc'yi yeniden derleyin ve yeniden kurun ve programımızı yeniden derleyin ve yeniden çalıştırın:

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

ve hackedbeklendiği gibi birkaç kez basıldığını görüyoruz .

Bu ayrıca, ana bilgisayarı değil, derlediğimiz glibc'yi kullandığımızı doğrular.

Ubuntu 18.04'te test edildi.

Kurulum 2: crosstool-NG bozulmamış kurulum

Bu kurulum 1'e alternatif olduğunu ve ben kadar elde ettik en doğru kurgusunda: herşey C çalışma zamanı gibi nesneleri de dahil olmak üzere, gözlemlemek kadarıyla doğru olduğunu crt1.o, crti.ove crtn.o.

Bu kurulumda, istediğimiz glibc'yi kullanan tam adanmış bir GCC araç zinciri derleyeceğiz.

Bu yöntemin tek dezavantajı, yapımın daha uzun sürmesidir. Ama daha azıyla bir üretim kurulumunu riske atmam.

crosstool-NG , GCC, glibc ve binutils dahil her şeyi bizim için kaynaktan indiren ve derleyen bir dizi .

Evet, GCC inşa sistemi o kadar kötü ki bunun için ayrı bir projeye ihtiyacımız var.

Bu kurulum yalnızca mükemmel değildir çünkü crosstool-NG, ekstra -Wlbayraklar olmadan çalıştırılabilir dosyaların oluşturulmasını desteklemez. , bu da GCC'yi kendimiz oluşturduğumuz için garip geliyor. Ancak her şey çalışıyor gibi görünüyor, bu yüzden bu sadece bir rahatsızlık.

Crosstool-NG'yi alın ve yapılandırı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

Görebildiğim tek zorunlu seçenek, doğru çekirdek başlıklarını kullanmak için ana bilgisayar çekirdek sürümünüzle eşleşmesini sağlamaktır. Ana bilgisayar çekirdek sürümünüzü şu şekilde bulun:

uname -a

bana gösteriyor:

4.15.0-34-generic

böylece menuconfigyapmam:

  • Operating System
    • Version of linux

bu yüzden seçiyorum:

4.14.71

ilk eşit veya daha eski sürüm olan. Çekirdek geriye dönük olarak uyumlu olduğu için daha eski olması gerekir.

Şimdi şununla inşa edebilirsiniz:

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

ve şimdi derleme için yaklaşık otuz dakika ila iki saat bekleyin.

Kurulum 2: isteğe bağlı yapılandırmalar

.configBirlikte oluşturduğunu ./ct-ng x86_64-unknown-linux-gnuvardır:

CT_GLIBC_V_2_27=y

Bunu değiştirmek menuconfigiçin şunları yapın:

  • C-library
  • Version of glibc

kaydedin .configve oluşturmaya devam edin.

En son git den glibc kullanmak örneğin kendi glibc kaynağı kullanmak istiyorsanız Veya, devam böyle :

  • Paths and misc options
    • Try features marked as EXPERIMENTAL: true olarak ayarla
  • C-library
    • Source of glibc
      • Custom location: Evet de
      • Custom location
        • Custom source location: glibc kaynağınızı içeren bir dizine işaret edin

glibc şu şekilde klonlandı:

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

Kurulum 2: test edin

İstediğiniz araç zincirini oluşturduktan sonra, aşağıdakilerle test edin:

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

Artık doğru çalışma zamanı nesnelerinin kullanılması dışında her şey Kurulum 1'deki gibi çalışıyor gibi görünüyor:

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

Kurulum 2: verimli glibc yeniden derleme girişimi başarısız oldu

Crosstool-NG ile aşağıda açıklandığı gibi mümkün görünmemektedir.

Sadece yeniden inşa ederseniz;

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

daha sonra özel glibc kaynak konumunda yaptığınız değişiklikler hesaba katılır, ancak her şeyi sıfırdan oluşturarak onu yinelemeli geliştirme için kullanılamaz hale getirir.

Eğer yaparsak:

./ct-ng list-steps

derleme adımlarına güzel bir genel bakış sunar:

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.

bu nedenle, birçok GCC adımıyla iç içe geçmiş glibc adımları olduğunu görüyoruz, en önemlisi libc_start_filesdaha önce gelir cc_core_pass_2ve bu muhtemelen birlikte en pahalı adımdır cc_core_pass_1.

Yalnızca bir adım oluşturmak için, ilk derlemede "Ara adımları kaydet" .configseçeneğini ayarlamanız gerekir :

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

ve sonra deneyebilirsiniz:

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

ancak maalesef +şu adreste belirtildiği gibi gereklidir: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536

Ancak, ara bir adımda yeniden başlatmanın, kurulum dizinini o adımdaki durumuna sıfırlayacağını unutmayın. Yani, yeniden oluşturulmuş bir libc'ye sahip olacaksınız - ancak bu libc ile derlenmiş son derleyiciniz olmayacak (ve dolayısıyla libstdc ++ gibi derleyici kitaplıkları da yok).

ve temelde, yeniden inşayı geliştirme için mümkün olamayacak kadar yavaşlatıyor ve crosstool-NG'yi yamamadan bunun üstesinden nasıl geleceğimi bilmiyorum.

Dahası, adımdan başlayarak libckaynağın üzerine tekrar kopyalanmadı Custom source location, bu da bu yöntemi kullanılamaz hale getirdi.

Bonus: stdlibc ++

C ++ standart kitaplığıyla da ilgileniyorsanız bir avantaj: GCC libstdc ++ C ++ standart kitaplık kaynağı nasıl düzenlenir ve yeniden oluşturulur?


musl-libcC çalışma zamanı söz konusu olduğunda başka bir seçenektir.
0xC0000022L

0

Kanımca, en tembel çözüm (özellikle en son çıkan C / C ++ özelliklerine veya en son derleyici özelliklerine güvenmiyorsanız) henüz bahsedilmedi, işte burada:

Sadece, hala desteklemek istediğiniz en eski GLIBC ile sistemi oluşturun.

Bu kadar eski bir dağıtımı doğrudan herhangi bir bilgisayarda kullanmak istemeseniz bile, günümüzde chroot, KVM / Virtualbox veya docker gibi teknolojilerle bunu yapmak oldukça kolaydır. Ayrıntılı olarak, yazılımınızın maksimum taşınabilir ikili dosyası oluşturmak için şu adımları izlemenizi öneririm:

  1. Sandbox / sanallaştırma / ... zehrinizi seçin ve kendinize sanal olarak eski bir Ubuntu LTS elde etmek için kullanın ve varsayılan olarak içerdiği gcc / g ++ ile derleyin. Bu, GLIBC'nizi o ortamda bulunanla otomatik olarak sınırlar.

  2. Temel olanların dışındaki harici kitaplıklara bağlı olmaktan kaçının: örneğin, glibc, libGL, libxcb / X11 / wayland şeyler, libasound / libpulseaudio, muhtemelen GTK + gibi yer seviyesindeki sistem öğelerini dinamik olarak bağlamanız gerekir, aksi takdirde tercihen statik olarak harici libs / mümkünse onları birlikte gönderin. Özellikle görüntü yükleyiciler, multimedya kod çözücüler, vb. Gibi çoğunlukla kendi kendine yeten kitaplıklar, statik olarak gönderirseniz diğer dağıtımlarda daha az kırılmaya neden olabilir (örneğin, yalnızca farklı bir ana sürümde bir yerde mevcutsa kırılma meydana gelebilir).

Bu yaklaşımla, herhangi bir manuel sembol ayarlaması yapmadan, tamamen statik bir ikili program yapmadan (glibc bundan nefret ettiği için daha karmaşık programlar için bozulabilir ve sizin için lisans sorunlarına neden olabilir) ve ayar yapmadan eski GLIBC uyumlu bir ikili dosya elde edersiniz. herhangi bir özel araç zincirini, herhangi bir özel glibc kopyasını veya her neyse.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.