GNU/Linux >> Belajar Linux >  >> Linux

Mengapa dan bagaimana beberapa pustaka bersama dapat dijalankan, seolah-olah dapat dieksekusi?

Perpustakaan 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 satu saran tentang cara melakukannya, meskipun tidak berhasil untuk saya.

Inilah jawaban lain untuk pertanyaan serupa di S.O, yang tanpa malu-malu akan saya plagiat, sesuaikan, dan tambahkan sedikit penjelasan.

Pertama, sumber untuk pustaka contoh kita, 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 bahwa:

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

Di sini, kami mengkompilasi pustaka bersama (-fPIC ), tetapi memberi tahu penaut bahwa itu adalah file yang dapat dieksekusi biasa (-pie ), dan untuk membuat tabel simbolnya dapat diekspor (-Wl,-E ), sehingga dapat ditautkan dengan berguna.

Dan, meskipun file akan mengatakan itu adalah objek yang dibagikan, itu berfungsi sebagai yang dapat dieksekusi:

> ./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 extern menghemat kita harus membuat header. Sekarang kompilasi bahwa:

gcc program.c -L. -ltest

Sebelum kita dapat menjalankannya, kita perlu menambahkan path dari libtest.so untuk pemuat dinamis:

export LD_LIBRARY_PATH=./

Sekarang:

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

Dan ldd a.out akan menampilkan tautan ke libtest.so .

Perhatikan bahwa saya ragu ini adalah bagaimana sebenarnya glibc dikompilasi, karena mungkin tidak seportabel glibc itu sendiri (lihat man gcc sehubungan dengan -fPIC dan -pie sakelar), tetapi ini menunjukkan mekanisme dasar. Untuk detail sebenarnya, Anda harus melihat makefile sumber.


Mari selami jawaban dalam repo glibc acak di GitHub. Versi ini menyediakan „banner“ di file version.c .

Di file yang sama ada beberapa poin menarik:__libc_print_version fungsi yang mencetak teks ke stdout dan __libc_main (void) simbol yang didokumentasikan sebagai titik masuk. Jadi simbol ini dipanggil saat menjalankan perpustakaan.

Jadi bagaimana penaut atau kompiler tahu persis bahwa ini adalah fungsi titik masuk?

Mari selami makefile. Di flag linker ada yang menarik:

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

Jadi ini adalah flag linker untuk menyetel titik masuk perpustakaan. Saat membuat perpustakaan, Anda dapat memberikan -e function_name bendera untuk linker untuk mengaktifkan perilaku yang dapat dieksekusi. Apa fungsinya sebenarnya? Mari kita lihat manualnya (agak usang tetapi masih valid):

Bahasa perintah linker menyertakan perintah khusus untuk menentukan instruksi pertama yang dapat dieksekusi dalam file keluaran (titik masuknya). Argumennya adalah nama simbol:

ENTRY(simbol)

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

MASUK hanyalah 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).

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 awal yang ditentukan dalam file input Anda, Anda cukup menentukannya, memberinya nilai yang sesuai---

mulai =0x2020;

Contoh menunjukkan alamat absolut, tetapi Anda dapat menggunakan ekspresi apa pun. Misalnya, jika file objek masukan Anda menggunakan konvensi nama-simbol lain untuk titik masuk, Anda dapat menetapkan nilai dari simbol apa pun yang berisi alamat awal untuk memulai:

mulai =simbol_lain;

(dokumentasi saat ini dapat ditemukan di sini)

ld linker benar-benar membuat executable dengan fungsi titik masuk jika Anda memberikan opsi baris perintah -e (yang merupakan solusi paling populer), berikan simbol fungsi start , atau tentukan alamat simbol untuk assembler.

Namun harap dicatat bahwa itu tidak dijamin untuk bekerja dengan linker lain (saya tidak tahu apakah lld llvm memiliki bendera yang sama). Saya tidak tahu mengapa ini berguna untuk tujuan selain memberikan informasi tentang file SO.


Linux
  1. Bagaimana menangani perpustakaan dinamis dan statis di Linux

  2. Redis sebagai Cache:Cara Kerja dan Mengapa Menggunakannya

  3. Mengapa Data Penting Dan Bagaimana Cara Melindunginya

  1. Mengapa Beberapa Port Dilaporkan Oleh Nmap Difilter Dan Bukan Yang Lain?

  2. Cara kerja penautan dinamis, penggunaannya, dan bagaimana dan mengapa Anda membuat dylib

  3. Mengapa pustaka bersama di Linux dapat dieksekusi?

  1. Cara Menemukan Siapa yang Masuk Di Sistem Anda Dan Apa yang Mereka Lakukan

  2. Bagaimana cara memeriksa apakah perpustakaan bersama diinstal?

  3. Mengapa ada `/lib` dan `/lib64` tetapi hanya `/bin`?