Sistem panggilan apa yang digunakan untuk memuat pustaka di Linux?

23

Dalam straceoutput, jalur ke perpustakaan yang dapat dieksekusi panggilan dalam panggilan ke open(). Apakah ini panggilan sistem yang digunakan oleh executable yang terhubung secara dinamis? Bagaimana dengan dlopen()? open()bukan panggilan saya kira akan berperan dalam pelaksanaan program.

Melab
sumber

Jawaban:

33

dlopenbukan panggilan sistem, ini adalah fungsi perpustakaan di perpustakaan libdl . Hanya panggilan sistem yang muncul di strace.

Di Linux dan di banyak platform lain (terutama yang menggunakan format ELF untuk executable), dlopendilaksanakan dengan membuka perpustakaan target dengan open()dan memetakannya ke dalam memori mmap(). mmap()benar-benar bagian penting di sini, itu yang menggabungkan perpustakaan ke dalam ruang alamat proses, sehingga CPU dapat mengeksekusi kodenya. Tetapi Anda harus open()file sebelum Anda bisa mmap()!

Celada
sumber
2
"mmap () benar-benar bagian yang kritis": Dan kemudian linker dinamis harus melakukan relokasi, inisialisasi dan sebagainya (tetapi ini tidak terlihat pada level panggilan sistem).
ysdx
1
Karena memuat pustaka dilakukan oleh fungsi pustaka, saya pikir relevan untuk menambahkan bahwa executable itu sendiri dan ld-linuxdipetakan oleh kernel sebagai bagian dari execvepanggilan sistem.
kasperd
mmap sesuai jawaban ini. Perhatikan juga bahwa setelah "buka" di setiap perpustakaan, beberapa (832) byte dibaca sebelum panggilan mmap, saya berasumsi untuk memeriksa bahwa perpustakaan itu valid.
Johan
@kasperd Jadi, apakah kernel Linux mengetahui loader dinamis? Apakah itu menyebutnya saat aplikasi dijalankan? Atau apakah aplikasi itu sendiri melakukan itu? Jika yang terakhir, bagaimana executable lain memiliki akses ke memori aplikasi?
Melab
@Melab Ya, kernel mengetahui tautan dinamis. Kernel akan membaca path ke dynamic linker dari header executable. Dan kernel akan memetakan keduanya ke dalam memori. Saya tidak tahu apakah titik masuk di mana kendali transfer kernel pada awalnya berada di dalam tautan atau dapat dieksekusi. Jika saya mengimplementasikannya, saya mungkin akan memiliki kontrol transfer kernel ke titik entri di linker dengan alamat pengirim di stack menunjuk ke titik entri executable.
kasperd
5

dlopen tidak ada hubungannya dengan perpustakaan bersama seperti yang Anda pikirkan. Ada dua metode memuat objek yang dibagikan:

  1. Anda memberi tahu penghubung waktu kompilasi (ld, meskipun biasanya itu dipanggil melalui kompiler) bahwa Anda ingin menggunakan fungsi dari pustaka bersama tertentu. Dengan pendekatan ini, Anda harus tahu apa nama perpustakaan akan ketika linker waktu kompilasi dijalankan, tetapi Anda dapat memanggil fungsi perpustakaan seolah-olah mereka secara statis terhubung ke program Anda. Ketika aplikasi dijalankan, penghubung dinamis, run-time (ld.so) akan dipanggil tepat sebelum mainfungsi dipanggil, dan mengatur ruang proses aplikasi sehingga aplikasi akan menemukan fungsi perpustakaan. Ini melibatkan open()ing, yang kemudian mmap(), diikuti dengan mengatur beberapa tabel pencarian.
  2. Anda memberi tahu tautan waktu kompilasi yang ingin Anda tautkan libdl, dari mana Anda kemudian (menggunakan metode pertama) dapat memanggil dlopen()dandlsym()fungsi. Dengan dlopen Anda mendapatkan pegangan ke perpustakaan, yang kemudian dapat Anda gunakan dengan dlsym untuk menerima pointer fungsi ke fungsi tertentu. Metode ini jauh lebih rumit untuk programmer daripada metode pertama (karena Anda harus melakukan setup secara manual, daripada memiliki linker melakukannya secara otomatis untuk Anda), dan juga lebih rapuh (karena Anda tidak mendapatkan kompilasi -pengecekan waktu bahwa Anda memanggil fungsi dengan tipe argumen yang benar seperti yang Anda dapatkan di metode pertama), tetapi keuntungannya adalah Anda dapat memutuskan objek yang dibagikan untuk dimuat saat runtime (atau bahkan apakah akan memuatnya sama sekali), membuat antarmuka ini dimaksudkan untuk fungsionalitas jenis plugin. Akhirnya, antarmuka dlopen juga kurang portabel daripada cara lain, karena mekanismenya bergantung pada implementasi yang tepat dari dynamic linker (karenanya libtool'slibltdl, yang mencoba untuk menghilangkan perbedaan ini).
Wouter Verhelst
sumber
menarik; jadi perpustakaan yang dimuat secara dinamis lebih baik disebut perpustakaan yang terhubung secara dinamis, karena memuat file biner ke dalam memori bukan bagian yang sulit, membuat alamat yang digunakan di dalamnya masuk akal. Ketika saya meminta memuat perpustakaan dinamis, saya sebenarnya meminta untuk menautkan (atau membatalkan tautan) perpustakaan ke (atau keluar dari) ruang alamat saya.
Dmitry
4

Saat ini, sebagian besar sistem operasi menggunakan metode untuk perpustakaan bersama yang diperkenalkan pada akhir 1987 oleh SunOS-4.0. Metode ini didasarkan pada pemetaan memori melalui mmap ().

Mengingat fakta bahwa pada awal 1990-an, Sun bahkan menyumbangkan kode lama berbasis a.out (Solaris pada waktu itu sudah berbasis ELF) kepada orang-orang FreeBSD dan bahwa kode ini kemudian diserahkan ke banyak sistem lain (termasuk Linux) , Anda mungkin mengerti mengapa tidak ada perbedaan besar antara platform.

schily
sumber
3

ltrace -Sanalisis contoh minimal menunjukkan bahwa mmapdigunakan dalam glibc 2.23

Di glibc 2.23, Ubuntu 16.04, berjalan latrace -Spada program minimal yang digunakan dlopendengan:

ltrace -S ./dlopen.out

menunjukkan:

dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "\177ELF\002\001\001", 832)                              = 832
SYS_brk(0)                                                           = 0x244c000
SYS_brk(0x246d000)                                                   = 0x246d000
SYS_fstat(3, 0x7fff42f9ce30)                                         = 0
SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128)                   = 54
SYS_mmap(0, 0x201028, 5, 2050)                                       = 0x7f1c323fe000
SYS_mprotect(0x7f1c323ff000, 2093056, 0)                             = 0
SYS_mmap(0x7f1c325fe000, 8192, 3, 2066)                              = 0x7f1c325fe000
SYS_close(3)                                                         = 0
SYS_mprotect(0x7f1c325fe000, 4096, 1)                                = 0

jadi kami segera melihat bahwa dlopenpanggilan open+ mmap.

Alat luar biasa ini ltracemelacak panggilan perpustakaan dan panggilan sistem, dan karenanya sempurna untuk memeriksa apa yang terjadi dalam kasus ini.

Analisis yang lebih dekat menunjukkan bahwa openmengembalikan deskriptor file 3(yang berikutnya gratis setelah stdin, out dan err).

readkemudian menggunakan file descriptor itu, tetapi TODO mengapa mmapargumen dibatasi hingga empat, dan kita tidak bisa melihat mana fd digunakan di sana karena itu adalah argumen ke-5 . stracemenegaskan seperti yang diharapkan itu 3adalah satu, dan tatanan alam semesta dipulihkan.

Jiwa yang berani juga dapat menjelajah ke kode glibc, tetapi saya tidak dapat menemukan mmapsetelah grep cepat dan saya malas.

Diuji dengan contoh minimal ini dengan membuat boilerplate di GitHub .

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
sumber
2

stracelaporan panggilan sistem (yaitu fungsi yang diimplementasikan langsung oleh kernel). Pustaka dinamis bukan fungsi kernel; dlopenadalah bagian dari pustaka C, bukan kernel. Pelaksanaan dlopenpanggilan akan open(yang merupakan panggilan sistem) untuk membuka file perpustakaan sehingga dapat dibaca.

cjm
sumber
5
Panggilan perpustakaan dapat dilihat menggunakan ltrace.
kasperd
@kasperd ltrace -Ssempurna untuk menganalisis ini karena ia juga menampilkan syscalls
Ciro Santilli 新疆 改造 中心 法轮功 六四 六四