Opsi GCC -fPIC

Jawaban:

526

Kode Independen Posisi berarti bahwa kode mesin yang dihasilkan tidak tergantung pada lokasinya di alamat tertentu untuk dapat bekerja.

Misalnya lompatan akan dihasilkan sebagai relatif daripada absolut.

Pseudo-assembly:

PIC: Ini akan berfungsi apakah kode berada di alamat 100 atau 1000

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP

Non-PIC: Ini hanya akan berfungsi jika kode berada di alamat 100

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP

EDIT: Menanggapi komentar.

Jika kode Anda dikompilasi dengan -fPIC, kode ini cocok untuk dimasukkan ke dalam pustaka - pustaka harus dapat dipindahkan dari lokasi yang diinginkan dalam memori ke alamat lain, mungkin ada pustaka yang sudah dimuat di alamat pustaka yang Anda inginkan.

Erik
sumber
36
Contoh ini jelas, tetapi sebagai pengguna, apa bedanya jika saya membuat file labrary (.so) bersama tanpa opsi? Apakah ada beberapa kasus yang tanpa -fPIC lib saya tidak valid?
Narek
16
Ya, membangun perpustakaan bersama yang bukan PIC bisa menjadi kesalahan.
John Zwinck
92
Untuk lebih spesifik, perpustakaan bersama seharusnya dibagi antara proses, tetapi tidak selalu mungkin untuk memuat perpustakaan di alamat yang sama di keduanya. Jika kode tidak diposisikan independen, maka setiap proses akan memerlukan salinannya sendiri.
Simon Richter
19
@Narek: kesalahan terjadi jika satu proses ingin memuat lebih dari satu pustaka bersama di alamat virtual yang sama. Karena perpustakaan tidak dapat memprediksi perpustakaan apa yang bisa dimuat, masalah ini tidak dapat dihindari dengan konsep shared library tradisional. Ruang alamat virtual tidak membantu di sini.
Philipp
6
Anda dapat menghilangkan -fPICketika mengkompilasi program atau perpustakaan statis, karena hanya satu program utama yang akan ada dalam suatu proses, sehingga tidak ada relokasi runtime yang pernah diperlukan. Pada beberapa sistem, program masih membuat posisi independen untuk meningkatkan keamanan.
Simon Richter
61

Saya akan mencoba menjelaskan apa yang sudah dikatakan dengan cara yang lebih sederhana.

Setiap kali lib bersama dimuat, loader (kode pada OS yang memuat program apa pun yang Anda jalankan) mengubah beberapa alamat dalam kode tergantung pada tempat objek itu dimuat.

Dalam contoh di atas, "111" dalam kode non-PIC ditulis oleh loader saat pertama kali dimuat.

Untuk objek yang tidak dibagikan, Anda mungkin menginginkannya seperti itu karena kompiler dapat membuat beberapa optimasi pada kode itu.

Untuk objek yang dibagikan, jika proses lain ingin "menautkan" ke kode itu, ia harus membacanya ke alamat virtual yang sama atau "111" tidak masuk akal. tetapi ruang virtual itu mungkin sudah digunakan dalam proses kedua.

Roee Gavirel
sumber
Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.Saya pikir ini tidak benar jika dikompilasi dengan -fpic dan alasan mengapa -fpic ada yaitu untuk alasan kinerja atau karena Anda memiliki pemuat yang tidak dapat dipindahkan atau karena Anda memerlukan banyak salinan di lokasi yang berbeda atau karena banyak alasan lainnya.
robsn
Kenapa tidak selalu menggunakan -fpic?
Jay
1
@ Jay - karena diperlukan satu perhitungan lagi (alamat fungsi) untuk setiap panggilan fungsi. Jadi kinerja-bijaksana, jika tidak diperlukan lebih baik tidak menggunakannya.
Roee Gavirel
45

Kode yang dibangun ke dalam pustaka bersama biasanya adalah kode bebas posisi, sehingga pustaka bersama dapat dengan mudah dimuat di (kurang lebih) alamat apa pun di memori. The -fPICpilihan memastikan bahwa GCC menghasilkan kode tersebut.

Jonathan Leffler
sumber
Mengapa pustaka bersama tidak dimuat di alamat mana pun di memori tanpa -fPICbendera dinyalakan? apakah itu tidak terkait dengan program? ketika program sedang berjalan, sistem operasi mengunggahnya ke memori. Apakah saya melewatkan sesuatu?
Tony Tannous
1
Apakah -fPICbendera digunakan, untuk memastikan lib ini dapat dimuat ke alamat virtual apa pun dalam proses yang menautkannya? maaf untuk komentar ganda 5 menit yang berlalu tidak dapat mengedit yang sebelumnya.
Tony Tannous
1
Bedakan antara membangun perpustakaan bersama (membuat libwotnot.so) dan menghubungkan dengan itu ( -lwotnot). Saat menghubungkan, Anda tidak perlu repot -fPIC. Dulu kasus ketika membangun perpustakaan bersama, Anda perlu memastikan -fPICdigunakan untuk semua file objek yang akan dibangun ke dalam perpustakaan bersama. Aturan mungkin telah berubah karena kompiler membangun dengan kode PIC secara default, hari ini. Jadi, apa yang kritis 20 tahun lalu, dan mungkin penting 7 tahun lalu, kurang penting akhir-akhir ini, saya percaya. Alamat di luar kernel o / s adalah 'selalu' alamat virtual '.
Jonathan Leffler
Jadi sebelumnya Anda harus menambahkan -fPIC. Tanpa melewati flag ini, kode yang dihasilkan saat membangun .so perlu dimuat ke alamat virtual tertentu yang mungkin sedang digunakan?
Tony Tannous
1
Ya, karena jika Anda tidak menggunakan flag PIC, kode tersebut tidak dapat dipindahkan dengan andal. Hal-hal seperti ASLR (pengacakan tata letak ruang alamat) tidak dimungkinkan jika kode tersebut bukan PIC (atau, setidaknya, sangat sulit untuk dicapai sehingga mereka secara efektif tidak mungkin).
Jonathan Leffler
21

Menambah lebih lanjut ...

Setiap proses memiliki ruang alamat virtual yang sama (Jika pengacakan alamat virtual dihentikan dengan menggunakan flag di OS linux) (Untuk lebih jelasnya Nonaktifkan dan aktifkan kembali tata letak ruang alamat hanya untuk saya sendiri )

Jadi jika salah satu exe tanpa tautan bersama (skenario Hipotetis), maka kita selalu dapat memberikan alamat virtual yang sama untuk instruksi as sama tanpa ada salahnya.

Tetapi ketika kita ingin menautkan objek bersama ke exe, maka kita tidak yakin alamat awal yang ditetapkan untuk objek bersama karena akan tergantung pada urutan objek yang dibagikan itu terhubung. Itulah yang dikatakan, asm instruksi di dalam. alamat virtual berbeda tergantung pada proses penautannya.

Jadi satu proses dapat memberikan alamat mulai ke. Jadi 0x45678910 di ruang virtualnya sendiri dan proses lainnya pada saat yang sama dapat memberikan alamat mulai dari 0x12131415 dan jika mereka tidak menggunakan pengalamatan relatif,. Jadi tidak akan bekerja sama sekali.

Jadi mereka selalu harus menggunakan mode pengalamatan relatif dan karenanya opsi fpic.

Ritesh
sumber
1
Terima kasih atas penjelasan addr virtual.
Hot.PxL
2
Adakah yang bisa menjelaskan bagaimana ini bukan masalah dengan perpustakaan statis, mengapa Anda tidak perlu menggunakan -fPIC di perpustakaan statis? Saya mengerti bahwa penautan dilakukan dalam waktu kompilasi (atau setelah benar-benar), tetapi jika Anda memiliki 2 perpustakaan statis dengan kode posisi tergantung, bagaimana mereka akan ditautkan?
Michael P
3
File objek @MichaelP memiliki tabel label posisi tergantung dan ketika file obj tertentu ditautkan semua label diperbarui sesuai. Ini tidak dapat dilakukan ke perpustakaan bersama.
Slava
16

Tautan ke fungsi di pustaka dinamis diselesaikan ketika pustaka dimuat atau saat dijalankan. Oleh karena itu, file yang dapat dieksekusi dan perpustakaan dinamis dimuat ke dalam memori ketika program dijalankan. Alamat memori tempat perpustakaan dinamis dimuat tidak dapat ditentukan sebelumnya, karena alamat tetap mungkin berbenturan dengan perpustakaan dinamis lain yang membutuhkan alamat yang sama.


Ada dua metode yang umum digunakan untuk menangani masalah ini:

1.Relokasi. Semua petunjuk dan alamat dalam kode dimodifikasi, jika perlu, agar sesuai dengan alamat pemuatan yang sebenarnya. Relokasi dilakukan oleh linker dan loader.

2.Kode bebas posisi. Semua alamat dalam kode relatif terhadap posisi saat ini. Objek yang dibagikan dalam sistem mirip Unix menggunakan kode independen posisi secara default. Ini kurang efisien daripada relokasi jika program berjalan untuk waktu yang lama, terutama dalam mode 32-bit.


Nama " kode posisi-independen " sebenarnya mengandung arti sebagai berikut:

  • Bagian kode tidak berisi alamat absolut yang membutuhkan relokasi, tetapi hanya alamat relatif sendiri. Oleh karena itu, bagian kode dapat dimuat di alamat memori arbitrer dan dibagi antara beberapa proses.

  • Bagian data tidak dibagi antara beberapa proses karena sering berisi data yang dapat ditulisi. Oleh karena itu, bagian data dapat berisi petunjuk atau alamat yang perlu dipindahkan.

  • Semua fungsi publik dan data publik dapat ditimpa di Linux. Jika suatu fungsi di executable utama memiliki nama yang sama dengan fungsi di objek bersama, maka versi di main akan diutamakan, tidak hanya ketika dipanggil dari utama, tetapi juga ketika dipanggil dari objek bersama. Demikian juga, ketika variabel global dalam main memiliki nama yang sama dengan variabel global dalam objek bersama, maka instance dalam utama akan digunakan, bahkan ketika diakses dari objek bersama.


Interposisi simbol yang disebut ini dimaksudkan untuk meniru perilaku perpustakaan statis.

Objek yang dibagikan memiliki tabel pointer ke fungsinya, yang disebut tabel prosedur linkage (PLT) dan tabel pointer ke variabelnya yang disebut global offset table (GOT) untuk mengimplementasikan fitur "override" ini. Semua akses ke fungsi dan variabel publik melewati tabel ini.

ps Apabila penghubungan dinamis tidak dapat dihindari, ada berbagai cara untuk menghindari fitur yang memakan waktu dari kode posisi-independen.

Anda dapat membaca lebih lanjut dari artikel ini: http://www.agner.org/optimize/optimizing_cpp.pdf

bruziuz
sumber
9

Tambahan kecil untuk jawaban yang sudah diposting: file objek yang tidak dikompilasi menjadi posisi independen dapat dipindahkan; mereka berisi entri tabel relokasi.

Entri ini memungkinkan pemuat (sedikit kode yang memuat program ke dalam memori) untuk menulis ulang alamat absolut untuk menyesuaikan dengan alamat pemuatan aktual dalam ruang alamat virtual.

Sistem operasi akan mencoba untuk membagikan satu salinan dari "shared object library" yang dimuat ke dalam memori dengan semua program yang ditautkan ke perpustakaan objek bersama yang sama.

Karena ruang alamat kode (tidak seperti bagian dari ruang data) tidak perlu bersebelahan, dan karena sebagian besar program yang menautkan ke perpustakaan tertentu memiliki pohon ketergantungan perpustakaan yang cukup tetap, ini berhasil sebagian besar waktu. Dalam kasus yang jarang terjadi di mana ada perbedaan, ya, mungkin perlu memiliki dua atau lebih salinan dari pustaka objek bersama dalam memori.

Jelas, setiap upaya untuk mengacak alamat beban pustaka antara program dan / atau contoh program (sehingga untuk mengurangi kemungkinan membuat pola yang dapat dieksploitasi) akan membuat kasus seperti itu umum, tidak jarang, sehingga ketika sistem telah mengaktifkan kemampuan ini, kita harus melakukan segala upaya untuk mengkompilasi semua pustaka objek bersama untuk posisi independen.

Karena panggilan ke pustaka-pustaka ini dari badan program utama juga akan dibuat dapat dipindahkan, ini membuat jauh lebih kecil kemungkinan bahwa pustaka bersama harus disalin.

pengguna1016759
sumber