Sangat disayangkan istilah perpustakaan statis dan perpustakaan dinamis keduanya berbentuk perpustakaan KATA KATA, karena selalu membuat pemrogram berpikir bahwa keduanya menunjukkan varian dari hal yang pada dasarnya sama. Hal ini hampir sama menyesatkannya dengan pemikiran bahwa lapangan bulutangkis dan mahkamah agung pada dasarnya adalah hal yang sama. Faktanya, hal ini jauh lebih menyesatkan, karena tidak ada orang yang berpikir bahwa lapangan bulutangkis dan mahkamah agung pada dasarnya adalah hal yang sama.
Adakah yang bisa menjelaskan perbedaan antara konten file perpustakaan statis dan bersama?
Mari kita gunakan contoh. Untuk melawan kabut lapangan bulu tangkis / mahkamah agung, saya akan menggunakan istilah teknis yang lebih akurat. Daripada perpustakaan statis saya akan mengatakan ar
arsip, dan sebagai ganti perpustakaan dinamis saya akan mengatakan objek bersama dinamis >, atau disingkat DSO.
Apa itu arsip ar
Saya akan membuat arsip ar
dimulai dengan tiga file ini:
foo.c
#include <stdio.h>
void foo(void)
{
puts("foo");
}
bar.c
#include <stdio.h>
void bar(void)
{
puts("bar");
}
limerick.txt
There once was a young lady named bright
Whose speed was much faster than light
She set out one day
In a relative way
And returned on the previous night.
Saya akan mengkompilasi kedua sumber C tersebut ke dalam file objek Position Independent:
$ gcc -c -Wall -fPIC foo.c
$ gcc -c -Wall -fPIC bar.c
Tidak perlu file objek yang ditujukan untuk arsip ar
dikompilasi dengan -fPIC
. Saya hanya ingin yang ini dikompilasi seperti itu.
Lalu saya akan membuat arsip ar
bernama libsundry.a
yang berisi file objek foo.o
dan bar.o
, ditambah limerick.txt
:
$ ar rcs libsundry.a foo.o bar.o limerick.txt
Arsip ar
tentu saja dibuat dengan ar
, pengarsip tujuan umum GNU. Jadi itu tidak dibuat oleh linker. Tidak ada keterkaitan yang terjadi. Begini cara ar
melaporkan isi arsip:
$ ar -t libsundry.a
foo.o
bar.o
limerick.txt
Berikut tampilan pantun jenaka dalam arsip:
$ rm limerick.txt
$ ar x libsundry.a limerick.txt; cat limerick.txt
There once was a young lady named bright
Whose speed was much faster than light
She set out one day
In a relative way
And returned on the previous night.
T. Apa gunanya meletakkan dua file objek dan sebuah limerick ASCII ke dalam arsip ar
yang sama?
A. Untuk menunjukkan bahwa saya bisa. Untuk menunjukkan bahwa arsip ar
hanya sekumpulan file.
Mari kita lihat apa yang file
buat dari libsundry.a.
$ file libsundry.a
libsundry.a: current ar archive
Sekarang saya akan menulis beberapa program yang menggunakan libsundry.a
dalam linkagenya.
fooprog.c
extern void foo(void);
int main(void)
{
foo();
return 0;
}
Kompilasi, tautkan, dan jalankan yang itu:
$ gcc -c -Wall fooprog.c
$ gcc -o fooprog fooprog.o -L. -lsundry
$ ./fooprog
foo
Itu kapal dory keren. Para linker rupanya tidak merasa terganggu dengan hadirnya limerick ASCII di libsundry.a
.
Alasannya adalah linker bahkan tidak mencoba menghubungkan limerick.txt
ke dalam program. Mari kita lakukan penautan lagi, kali ini dengan opsi diagnostik yang akan menunjukkan kepada kita file masukan apa yang ditautkan:
$ gcc -o fooprog fooprog.o -L. -lsundry -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
fooprog.o
(./libsundry.a)foo.o
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
Banyak perpustakaan default dan file objek di sana, namun satu-satunya file objek yang kami buat yang digunakan oleh linker adalah:
fooprog.o
(./libsundry.a)foo.o
Yang dilakukan linker dengan ./libsundry.a
hanyalah mengeluarkan foo.o
dari tasnya dan menautkannya ke dalam program. Setelah menghubungkan fooprog.o
ke dalam program, diperlukan definisi untuk foo
. Itu tampak di dalam tas. Ia menemukan definisi di foo.o
, jadi ia mengambil foo.o
dari tas dan menghubungkannya ke dalam program. Dalam menghubungkan fooprog
,
gcc -o fooprog fooprog.o -L. -lsundry
adalah hubungan yang persis sama dengan:
$ gcc -o fooprog fooprog.o foo.o
Apa yang file
katakan tentang fooprog
?
$ file fooprog
fooprog: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), \
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, \
for GNU/Linux 2.6.32, BuildID[sha1]=32525dce7adf18604b2eb5af7065091c9111c16e,
not stripped
Inilah program kedua saya:
foobarprog.c
extern void foo(void);
extern void bar(void);
int main(void)
{
foo();
bar();
return 0;
}
Kompilasi, tautkan, dan jalankan:
$ gcc -c -Wall foobarprog.c
$ gcc -o foobarprog foobarprog.o -L. -lsundry
$ ./foobarprog
foo
bar
Dan inilah linkagenya lagi dengan -trace
:
$ gcc -o foobarprog foobarprog.o -L. -lsundry -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
foobarprog.o
(./libsundry.a)foo.o
(./libsundry.a)bar.o
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
Jadi kali ini, file objek yang digunakan linker adalah:
foobarprog.o
(./libsundry.a)foo.o
(./libsundry.a)bar.o
Setelah menghubungkan foobarprog.o
ke dalam program, program perlu menemukan definisi untuk foo
dan bar
. Itu tampak di dalam tas. Ia menemukan definisi masing-masing di foo.o
dan bar.o
, jadi ia mengeluarkannya dari tas dan menghubungkannya ke dalam program. Dalam menghubungkan foobarprog
,
gcc -o foobarprog foobarprog.o -L. -lsundry
adalah hubungan yang persis sama dengan:
$ gcc -o foobarprog foobarprog.o foo.o bar.o
Menyimpulkan semua itu. Arsip ar
hanya sekumpulan file. Anda dapat menggunakan arsip ar
untuk menawarkan kepada linker sekumpulan file objek untuk memilih file yang diperlukan untuk melanjutkan linkage. Ini akan mengeluarkan file objek tersebut dari tas dan menghubungkannya ke file output. Sama sekali tidak ada kegunaan lain dari tas itu. Tas itu tidak memberikan kontribusi apa pun pada keterkaitannya.
Tas ini membuat hidup Anda sedikit lebih sederhana dengan menghindarkan Anda dari kebutuhan untuk mengetahui secara pasti file objek apa yang Anda perlukan untuk tautan tertentu. Anda hanya perlu tahu: Ya, mereka ada di dalam tas itu.
Apa itu DSO
Ayo buat satu.
foobar.c
extern void foo(void);
extern void bar(void);
void foobar(void)
{
foo();
bar();
}
Kami akan mengkompilasi file sumber baru ini:
$ gcc -c -Wall -fPIC foobar.c
lalu buat DSO menggunakan foobar.o
dan gunakan kembali libsundry.a
$ gcc -shared -o libfoobar.so foobar.o -L. -lsundry -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbeginS.o
foobar.o
(./libsundry.a)foo.o
(./libsundry.a)bar.o
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/usr/lib/gcc/x86_64-linux-gnu/5/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
Itu telah membuat DSO libfoobar.so
. Pemberitahuan: DSO dibuat oleh linker. Itu ditautkan seperti sebuah program ditautkan. Tautan libfoopar.so
terlihat sangat mirip dengan tautan foobarprog
, namun penambahan opsi -shared
memerintahkan linker untuk menghasilkan DSO, bukan program. Di sini kita melihat bahwa file objek yang digunakan oleh linkage adalah:
foobar.o
(./libsundry.a)foo.o
(./libsundry.a)bar.o
ar
sama sekali tidak memahami DSO:
$ ar -t libfoobar.so
ar: libfoobar.so: File format not recognised
Tapi file
melakukan:
$ file libfoobar.so
libfoobar.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), \
dynamically linked, BuildID[sha1]=16747713db620e5ef14753334fea52e71fb3c5c8, \
not stripped
Sekarang jika kita menautkan ulang foobarprog
menggunakan libfoobar.so
dan bukan libsundry.a
:
$ gcc -o foobarprog foobarprog.o -L. -lfoobar -Wl,-trace,--rpath=$(pwd)
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
foobarprog.o
-lfoobar (./libfoobar.so)
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
-lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so)
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
kami melihat
foobarprog.o
-lfoobar (./libfoobar.so)
bahwa ./libfoobar.so
itu sendiri telah ditautkan. Bukan beberapa file objek "di dalamnya". Tidak ada file objek apa pun di dalamnya. Dan bagaimana hal ini berkontribusi pada keterkaitan dapat dilihat pada ketergantungan dinamis program:
$ ldd foobarprog
linux-vdso.so.1 => (0x00007ffca47fb000)
libfoobar.so => /home/imk/develop/so/scrap/libfoobar.so (0x00007fb050eeb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb050afd000)
/lib64/ld-linux-x86-64.so.2 (0x000055d8119f0000)
Program ini keluar dengan ketergantungan runtime pada libfoobar.so
. Itulah yang dilakukan menghubungkan DSO. Kita dapat melihat ketergantungan runtime ini terpenuhi. Maka program akan berjalan:
$ ./foobarprog
foo
bar
sama seperti sebelumnya.
Fakta bahwa DSO dan program - tidak seperti arsip ar
- keduanya merupakan produk dari linker menunjukkan bahwa DSO dan program merupakan varian dari hal yang pada dasarnya sama. Output file
juga menyarankan hal itu. DSO dan program keduanya merupakan biner ELF yang dapat dipetakan oleh pemuat OS ke dalam ruang alamat proses. Bukan hanya sekantong file. Arsip ar
bukanlah biner ELF apa pun.
Perbedaan antara file ELF tipe program dan ELF tipe non-program terletak pada perbedaan nilai yang ditulis linker ke dalam struktur ELF Header dan struktur Program Headers format file ELF. Perbedaan ini memerintahkan pemuat OS untuk memulai proses baru ketika memuat file ELF tipe program, dan untuk menambah proses yang sedang dibangun ketika memuat file ELF non-program. Dengan demikian, DSO non-program akan dipetakan ke dalam proses program induknya. Fakta bahwa suatu program memulai proses baru mengharuskan suatu program memiliki titik masuk default tunggal yang akan dikontrol oleh OS: titik masuk tersebut adalah fungsi main
wajib dalam program C atau C++. Sebaliknya, DSO non-program tidak memerlukan satu titik masuk wajib. Itu dapat dimasukkan melalui salah satu fungsi global yang diekspor melalui pemanggilan fungsi dari program induk.
Namun dari sudut pandang struktur file dan konten, DSO dan program adalah hal yang sangat mirip. Mereka adalah file yang dapat menjadi komponen suatu proses. Suatu program harus menjadi komponen awal. DSO dapat menjadi komponen sekunder.
Perbedaan lebih lanjut masih umum dilakukan: DSO harus seluruhnya terdiri dari kode yang dapat direlokasi (karena tidak ada yang tahu pada waktu tautan di mana pemuat mungkin perlu menempatkannya di ruang alamat proses), sedangkan program terdiri dari kode absolut, selalu dimuat di alamat yang sama. Namun sebenarnya sangat mungkin untuk menautkan program yang dapat direlokasi:
$ gcc -pie -o foobarprog foobarprog.o -L. -lfoobar -Wl,--rpath=$(pwd)
Itulah yang dilakukan -pie
(Position Independent Executable) di sini. Kemudian:
$ file foobarprog
foobarprog: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), ....
file
akan mengatakan bahwa foobarprog
adalah DSO, meskipun juga masih berupa program:
$ ./foobarprog
foo
bar
Dan executable PIE mulai populer. Di Debian 9 dan distro turunannya (Ubuntu 17.04...) rantai alat GCC membuat program PIE secara default.
Jika Anda mendambakan pengetahuan mendetail tentang format file ar
dan ELF
, berikut adalah detailnya ar
format dan berikut detail format ELF.
mengapa tidak memiliki satu jenis file perpustakaan yang disertai dengan tanda kompiler yang menunjukkan bagaimana perpustakaan harus ditautkan (statis vs dinamis)?
Pilihan antara tautan dinamis dan statis sudah sepenuhnya dapat dikontrol oleh opsi tautan baris perintah, jadi tidak perlu meninggalkan arsip ar
atau DSO atau menciptakan perpustakaan jenis lain untuk mencapai hal ini. Jika linker tidak dapat menggunakan arsip ar
sebagaimana mestinya, hal ini akan menimbulkan ketidaknyamanan yang cukup besar. Dan tentu saja jika linker tidak dapat menghubungkan DSO, kita akan kembali ke sistem operasi zaman batu.
person
Mike Kinghan
schedule
05.11.2017