Mengapa dan bagaimana beberapa perpustakaan bersama dapat dijalankan, seolah-olah itu adalah executable?

56

Pada sistem Linux 32-bit, gunakan ini

$ /lib/libc.so.6

dan pada sistem 64-bit ini

$ /lib/x86_64-linux-gnu/libc.so.6

dalam sebuah shell, berikan output seperti ini:

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

Mengapa dan bagaimana hal ini terjadi, dan bagaimana mungkin melakukan hal yang sama di perpustakaan bersama lainnya?

Saya melihat /usr/libuntuk menemukan executable, dan saya menemukan /usr/lib/libvlc.so.5.5.0. Menjalankannya menyebabkan kesalahan segmentasi . : - /

Ho1
sumber
Selain semua jawaban berikut, ingatan saya adalah jika Anda mengatur bit x pada perpustakaan bersama, itu (mungkin masih) mungkin untuk memuatnya dari executable bahkan dengan bit r jelas. Itu pernah dianggap sebagai praktik keamanan yang baik untuk membuang bit dari dunia pada executable sistem dan perpustakaan. Karena open source yang tersebar luas, ini hanya berlaku untuk direktori / bin / ls direktori anonim lagi. Bagi saya, meninggalkan set x bit tampak seperti wajah dari praktik lama itu.
Joshua

Jawaban:

53

Pustaka itu memiliki main()fungsi atau titik masuk yang setara, dan dikompilasi sedemikian rupa sehingga berguna baik sebagai objek yang dapat dieksekusi maupun sebagai objek bersama.

Berikut ini satu saran tentang cara melakukan ini, meskipun tidak berhasil untuk saya.

Berikut ini jawaban lain untuk pertanyaan serupa tentang SO , yang tanpa malu-malu saya akan menjiplak, mengubah, dan menambahkan sedikit penjelasan.

Pertama, sumber misalnya perpustakaan kami, test.c:

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

Kompilasi itu:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

Di sini, kami sedang mengkompilasi pustaka bersama ( -fPIC), tetapi memberi tahu linker bahwa itu adalah executable biasa ( -pie), dan membuat tabel simbolnya dapat diekspor ( -Wl,-E), sehingga dapat dihubungkan dengan berguna.

Dan, meskipun fileakan mengatakan itu adalah objek bersama, itu berfungsi sebagai executable:

> ./libtest.so 
./libtest.so: Hello!

Sekarang kita perlu melihat apakah itu benar-benar dapat dihubungkan secara dinamis. Contoh program program.c,:

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

Menggunakan externsave, kita harus membuat header. Sekarang kompilasi itu:

gcc program.c -L. -ltest

Sebelum kita dapat menjalankannya, kita perlu menambahkan path libtest.sountuk loader dinamis:

export LD_LIBRARY_PATH=./

Sekarang:

> ./a.out
Test program.
./a.out: Hello!

Dan ldd a.outakan menunjukkan tautan ke libtest.so.

Perhatikan bahwa saya ragu ini adalah bagaimana glibc sebenarnya dikompilasi, karena mungkin tidak se portable glibc itu sendiri (lihat man gccberkaitan dengan -fPICdan -pieswitch), tetapi ini menunjukkan mekanisme dasar. Untuk detail nyata Anda harus melihat sumber makefile.

goldilocks
sumber
1
Jawaban yang bagus, terima kasih! :-) Saya mencoba menggunakan nmdi shared library, tapi itu bukan versi debug. Jadi, mengapa libvlcdan yang lainnya macet?
Ho1
1
Karena sebagian besar pustaka bersama tidak dimaksudkan untuk dieksekusi, GNU libcadalah pengecualian.
goldilocks
Saya menemukan dua lainnya: lddan libpthread.
Ho1
@ Ho1 ld.sospesial dalam hal lain. Dalam batas tertentu, ini lebih merupakan executable nyata daripada executable yang terhubung secara dinamis normal.
Random832
1
Opsi-opsi di atas walaupun membuat library executable-shared-library, tetapi tidak lengkap dalam arti, itu menandai kesalahan, ketika beberapa executable mencoba untuk menautkan ini. Referensi sampel terperinci ditambahkan di sini: unix.stackexchange.com/a/479334/152034
parasrish
21

Mari selami jawaban dalam repo glibc acak di github. Versi ini menyediakan "banner" di file version.c.

Dalam file yang sama ada beberapa poin menarik: __libc_print_versionfungsi yang menyediakan pencetakan untuk stdin teks dan simbol __libc_main (void)yang sama yang didokumentasikan sebagai titik masuk. Jadi simbol ini disebut saat menjalankan perpustakaan.

Jadi bagaimana linker / compiler tahu bahwa ini adalah fungsi entry point?

Mari selami makefile . Di bendera tautan ada bendera yang menarik:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

Jadi ini adalah bendera penghubung untuk mengatur titik masuk di perpustakaan. Saat membangun perpustakaan, Anda dapat menyediakan -e function_nametautan yang membuat perilaku yang dapat dieksekusi. Apa itu sebenarnya? Mari kita lihat manualnya (agak tanggal tetapi masih valid) :

Bahasa perintah linker termasuk perintah khusus untuk mendefinisikan instruksi yang dapat dieksekusi pertama dalam file output (titik masuknya). Argumennya adalah nama simbol:

ENTRY (simbol)

Seperti penetapan simbol, perintah ENTRY dapat ditempatkan sebagai perintah independen dalam file perintah, atau di antara definisi bagian dalam perintah BAGIAN - apa pun yang paling masuk akal untuk tata letak Anda.

ENTRY hanya salah satu dari beberapa cara untuk memilih titik masuk. Anda dapat menunjukkannya dengan salah satu cara berikut (ditampilkan dalam urutan prioritas menurun: metode yang lebih tinggi dalam daftar menimpa metode yang lebih rendah ke bawah).

the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 

Misalnya, Anda dapat menggunakan aturan ini untuk menghasilkan titik masuk dengan pernyataan penugasan: jika tidak ada simbol mulai didefinisikan dalam file input Anda, Anda dapat dengan mudah mendefinisikannya, menetapkan nilai yang sesuai ---

mulai = 0x2020;

Contoh ini menunjukkan alamat absolut, tetapi Anda dapat menggunakan ekspresi apa pun. Misalnya, jika file objek input Anda menggunakan konvensi nama-simbol lain untuk titik entri, Anda bisa menetapkan nilai simbol apa pun yang berisi alamat mulai untuk memulai:

mulai = other_symbol;

(dokumentasi saat ini dapat ditemukan di sini )

Benar-benar ldlinker tidak membuat executable dengan fungsi titik masuk jika Anda menyediakannya dengan opsi baris perintah -e(solusi paling praktis), memberikan simbol fungsi start, atau menyuntikkan alamat simbol di assembler.

Namun harap dicatat bahwa itu jelas tidak dijamin untuk bekerja dengan linker lain (saya tidak tahu apakah lld llvm memiliki flag yang sama). Mengapa ini harus bermanfaat untuk tujuan selain memberikan info tentang file tersebut, saya tidak tahu.

IBr
sumber
1
Jika python itu akan memberikan unit test.
Erik Aronesty