Perbedaan antara perpustakaan statis dan perpustakaan dinamis mengabaikan cara penggunaannya oleh linker/loader

Saya memahami bagaimana perpustakaan statis/dinamis digunakan oleh linker/loader.

  1. Namun mengapa tidak memiliki satu jenis file perpustakaan yang disertai dengan tanda kompiler yang menunjukkan bagaimana perpustakaan harus ditautkan (statis vs dinamis)?
  2. Dengan fakta sederhana bahwa kita memiliki perpustakaan statis dan dinamis, saya berasumsi file-file ini memiliki konten spesifik yang masing-masing memungkinkan tautan statis dan dinamis. Adakah yang bisa menjelaskan perbedaan antara konten file perpustakaan statis dan bersama?

person rahul    schedule 04.11.2017    source sumber


Jawaban (4)


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
comment
Jawaban yang luar biasa!! Salut! - person Zack Zhu; 12.03.2020

Karena keduanya adalah hal yang sangat berbeda. Perpustakaan statis hanyalah kumpulan kode objek yang dihasilkan kompiler. Perpustakaan dinamis saling terhubung.

person user207421    schedule 04.11.2017

  1. Format perpustakaan dinamis, yang dimuat pada saat run-time, ditentukan oleh penulis sistem operasi. Format perpustakaan statis diatur oleh penulis rantai alat. Seringkali ada beberapa tumpang tindih antara kelas-kelas pemrogram tersebut, namun mereka cenderung lebih memilih untuk mempertahankan pemisahan masalah.
  2. Pemuat run-time perlu mengetahui ukuran gambar yang akan dimuat, mungkin beberapa ukuran tumpukan dan segmen data serta nama dan titik masuk fungsi di DLL. Linker perlu mengetahui lebih banyak tentang masing-masing objek (fungsi/data) yang diarsipkan di perpustakaan statis. Hal-hal seperti tanda tangan fungsi, tipe data, ukuran benda, inisialisasi, cakupan akses.

Setiap OS dan rangkaian alat memiliki persyaratan spesifik dan riwayat revisinya masing-masing, sehingga tidak praktis untuk menggambarkan tata letak file yang tepat untuk semuanya di sini. Lihat dokumentasi OS dan rantai alat untuk detailnya.

person jwdonahue    schedule 04.11.2017

Pustaka yang dapat dibagikan cenderung dikompilasi menjadi kode yang tidak bergantung pada posisi. Artinya, misalnya, transfer kendali menggunakan pengalamatan relatif PC. Hal ini karena mereka mungkin dipetakan ke posisi yang berbeda di ruang alamat program klien yang berbeda.

Pustaka yang dapat dibagikan harus memiliki pemisahan yang jelas antara kode+data hanya-baca, dan data baca/tulis. Dengan cara ini hanya satu salinan dari bagian read-only yang perlu dimuat ke dalam memori untuk semua proses klien.

Jika program klien A mengacu pada perpustakaan B dan C, sedangkan perpustakaan B juga mengacu pada C, maka Anda harus memastikan bahwa semuanya dibangun dengan pengaturan kemampuan berbagi yang konsisten. Misalnya, jika C dibuat dalam versi yang dapat dibagikan dan tidak dapat dibagikan, dan B dibuat dapat dibagikan dengan versi C yang dapat dibagikan, maka A juga harus dibuat dengan versi B dan C yang dapat dibagikan. Jika ia mencoba menggunakan versi yang tidak dapat dibagikan versi C, hal ini kemungkinan akan menyebabkan masalah pembangunan, karena konflik dengan versi C yang dapat dibagikan yang digunakan oleh B.

Dokumen ini oleh Ulrich Drepper (mantan pengelola perpustakaan libc GNU) menjelaskan lebih detail daripada yang ingin Anda ketahui tentang perpustakaan yang dapat dibagikan.

person Lawrence D'Oliveiro    schedule 04.11.2017