Apa yang harus saya lakukan jika dua pustaka menyediakan fungsi dengan nama yang sama yang menimbulkan konflik?

94

Apa yang harus saya lakukan jika saya memiliki dua pustaka yang menyediakan fungsi dengan nama yang setara?

qeek
sumber
2
apakah pustaka statis ini atau ditautkan secara dinamis?
Alnitak
kami memerlukan detail lebih lanjut ... apakah nama-nama itu diekspor? atau apakah mereka hanya digunakan secara internal? Bisakah kamu mengubah namanya?
Johannes Schaub - litb
Keduanya terhubung secara dinamis. Saya tidak dapat mengubah nama, karena saya tidak memiliki perpustakaan.
qeek
Pertanyaan bagus. Tentu saja tidak akan menjadi masalah dengan kedua pustaka ini jika semua simbol diawali dengan ID unik (mis vorbis_.... sf_..., sdl_...). Ini pada dasarnya adalah apa yang C ++ lakukan pada nama simbol untuk fungsi spasi nama.
Vortico
Ini adalah pertanyaan yang sangat menarik tetapi sayangnya terlalu tidak tepat yang menjadi alasan untuk memiliki terlalu banyak jawaban yang terlalu luas.
yugr

Jawaban:

52
  • Jika Anda mengontrol satu atau keduanya: edit satu untuk mengubah nama dan kompilasi ulang Atau dengan cara yang sama, lihat jawaban Ben dan tidak diketahui yang akan berfungsi tanpa akses ke kode sumber.
  • Jika Anda tidak mengontrol salah satu dari mereka, Anda dapat membungkus salah satunya. Itu adalah mengkompilasi pustaka lain ( terhubung secara statis !) Yang tidak melakukan apa pun kecuali mengekspor ulang semua simbol asli kecuali yang melanggar, yang dicapai melalui pembungkus dengan nama alternatif. Sungguh merepotkan.
  • Ditambahkan nanti: Karena qeek mengatakan dia berbicara tentang perpustakaan dinamis, solusi yang disarankan oleh Ferruccio dan mouviciel mungkin yang terbaik. (Sepertinya saya hidup di masa lalu ketika tautan statis adalah default. Itu mewarnai pemikiran saya.)

Apropos komentar: Dengan "ekspor" maksud saya untuk membuat terlihat modul yang menghubungkan ke perpustakaan --- setara dengan externkata kunci di ruang lingkup file. Bagaimana ini dikontrol tergantung pada OS dan linker. Dan itu adalah sesuatu yang harus selalu saya cari.

dmckee --- mantan moderator anak kucing
sumber
Itu juga pikiran pertamaku, tapi bukankah kamu akan berakhir dengan masalah tabrakan yang sama? Pada akhirnya, seluruh proyek harus menautkan - pada waktu kompilasi / penautan atau pada waktu berjalan - di mana kedua pustaka yang melanggar harus memuat apa adanya.
Sniggerfardimungus
@unknown: Pembungkus harus dikompilasi dengan tautan statis, dan tidak boleh mengekspor simbol yang melanggar. Kemudian Anda masih dapat menautkan pembungkus secara dinamis. Diedit untuk lebih jelasnya, Terima kasih.
dmckee --- kucing mantan moderator
Jika masalah qeek adalah dengan ddl dan bukan pustaka statis, bagaimana mungkin membuat pustaka baru dengan pembungkus? Karena, perpustakaan pembungkus harus secara dinamis membungkus fungsi di perpustakaan yang tidak ingin Anda tautkan sejak awal.
jeffD
@ dmckee - apa yang Anda maksud dengan "ekspor"?
4
Mungkinkah seseorang bisa memberikan contoh sederhana dari teknik ini? Satu exe, dua pustaka masing-masing berisi satu fungsi dengan nama yang sama.
53

Dimungkinkan untuk mengganti nama simbol dalam file objek menggunakan objcopy --redefine-sym old=new file(lihat man objcopy).

Kemudian panggil saja fungsi menggunakan nama barunya dan tautkan dengan file objek baru.

Ben
sumber
2
Bagus. Ini akan mudah untuk ditambahkan ke Makefile. Jika perpustakaan pernah diperbarui, mantera obyektif akan jauh lebih mudah untuk diperbarui daripada beberapa solusi lainnya.
sigjuice
9
Jangan lupa juga untuk mengganti nama simbol di file header.
mouviciel
^ sed / awk / perl akan berguna untuk mengotomatiskan penggantian nama simbol di header juga
Alex Reinking
16

Di bawah Windows, Anda dapat menggunakan LoadLibrary () untuk memuat salah satu pustaka tersebut ke dalam memori dan kemudian menggunakan GetProcAddress () untuk mendapatkan alamat setiap fungsi yang Anda perlukan untuk memanggil dan memanggil fungsi melalui penunjuk fungsi.

misalnya

HMODULE lib = LoadLibrary("foo.dll");
void *p = GetProcAddress(lib, "bar");
// cast p to the approriate function pointer type (fp) and call it
(*fp)(arg1, arg2...);
FreeLibrary(lib);

akan mendapatkan alamat dari fungsi bernama bar di foo.dll dan memanggilnya.

Saya tahu sistem Unix mendukung fungsi serupa, tetapi saya tidak dapat memikirkan namanya.

Ferruccio
sumber
dlopen dlsym, dan dlclose. Namun, enkapsulasi pada Unix mungkin tidak seefektif Windows.
pengguna877329
8

Inilah pemikirannya. Buka salah satu pustaka yang melanggar di editor hex dan ubah semua kemunculan string yang menyinggung ke sesuatu yang lain. Anda kemudian dapat menggunakan nama baru di semua panggilan di masa mendatang.

UPDATE: Saya baru saja melakukannya di akhir ini dan tampaknya berhasil. Tentu saja, saya belum mengujinya secara menyeluruh - ini mungkin tidak lebih dari cara yang sangat baik untuk meledakkan kaki Anda dengan senapan hexedit.

Sniggerfardimungus
sumber
sebenarnya bukan solusi yang buruk. Agak hackish, tetapi yang Anda lakukan hanyalah mengubah string di tabel simbol. Tidak ada kerusakan fungsional yang nyata dalam hal itu.
Evan Teran
Anda mungkin juga ingin mengganti nama perpustakaan - jangan sampai ada orang lain yang datang, mencoba memuatnya lagi. Anda akan beralih dari satu konflik menjadi puluhan atau ratusan. =] Saya suka ini tentang stackoverflow: kami memiliki jawaban yang telah diuji untuk sebuah pertanyaan dan memiliki 3 suara. Jawaban pertama (tidak lengkap): 17. =]
Sniggerfardimungus
1
Mengganti nama kesempatan terbatas karena Anda hanya akan dapat membuat nama yang lebih pendek . Juga di Linux Anda akan kesulitan memperbarui tabel hash ELF.
yugr
7

Dengan asumsi Anda menggunakan linux, Anda perlu menambahkan file

#include <dlfcn.h>

Deklarasikan variabel penunjuk fungsi dalam konteks yang tepat, misalnya,

int (*alternative_server_init)(int, char **, char **);

Seperti yang dinyatakan Ferruccio di https://stackoverflow.com/a/678453/1635364 , muat secara eksplisit pustaka yang ingin Anda gunakan dengan menjalankan (pilih bendera favorit Anda)

void* dlhandle;
void* sym;

dlhandle = dlopen("/home/jdoe/src/libwhatnot.so.10", RTLD_NOW|RTLD_LOCAL);

Baca alamat fungsi yang ingin Anda panggil nanti

sym = dlsym(dlhandle, "conflicting_server_init");

menetapkan dan berperan sebagai berikut

alternative_server_init = (int (*)(int, char**, char**))sym;

Panggil dengan cara yang mirip dengan aslinya. Terakhir, bongkar dengan menjalankan

dlclose(dlhandle);
vraa
sumber
6

Anda tidak boleh menggunakannya bersama. Jika saya ingat dengan benar, linker mengeluarkan kesalahan dalam kasus seperti itu.

Saya tidak mencoba, tetapi mungkin ada solusinya dlopen(), dlsym()dan dlclose()yang memungkinkan Anda menangani pustaka dinamis secara terprogram. Jika Anda tidak membutuhkan dua fungsi pada saat yang sama, Anda dapat membuka pustaka pertama, menggunakan fungsi pertama dan menutup pustaka pertama sebelum menggunakan pustaka / fungsi kedua.

mouviciel
sumber
Terima kasih. Tidak memikirkan tentang ini. Meskipun demikian, saya ingin memiliki keduanya pada saat yang bersamaan.
qeek
Bagaimana jika saya ingin menggunakan keduanya secara bersamaan?
QZHua
@QZHua: Pengguna lain (mis., Melibatkan penggantian nama simbol) akan menyelesaikan masalah Anda.
mouviciel
6

Jika Anda memiliki file .o di sana, jawaban yang bagus di sini: https://stackoverflow.com/a/6940389/4705766

Ringkasan:

  1. objcopy --prefix-symbols=pre_string test.o untuk mengganti nama simbol dalam file .o

atau

  1. objcopy --redefine-sym old_str=new_str test.o untuk mengganti nama simbol tertentu dalam file .o.
Astaga
sumber
4

Masalah ini adalah alasan c ++ memiliki ruang nama. Sebenarnya tidak ada solusi hebat di c untuk 2 lib pihak ketiga yang memiliki nama yang sama.

Jika itu adalah objek dinamis, Anda mungkin dapat memuat objek bersama secara eksplisit (LoadLibrary / dlopen / etc) dan memanggilnya dengan cara itu. Bergantian, jika Anda tidak memerlukan kedua lib secara bersamaan dalam kode yang sama, Anda mungkin dapat melakukan sesuatu dengan tautan statis (jika Anda memiliki file .lib / .a).

Tak satu pun dari solusi ini berlaku untuk semua proyek, tentu saja.

Brian Mitchell
sumber
1
Oh ya. Untuk pertanyaan umum ini sepertinya jawaban yang bagus. Namun - namespace itu keren jika Anda mengkompilasi semuanya dalam kompiler yang sama. Hore, tidak ada bentrokan nama. Tetapi jika Anda mendapatkan perpustakaan dalam bentuk biner, dan ingin mengintegrasikannya dengan kompiler lain, maka - semoga berhasil. Aturan penguraian nama dalam file objek hanyalah penghalang pertama ("C" eksternal mungkin membantu, yang membatalkan efek ruang nama).
Tomasz Gandor
3

Bersumpah? Sejauh yang saya ketahui, tidak banyak yang dapat Anda lakukan jika Anda memiliki dua pustaka yang mengekspos titik tautan dengan nama yang sama dan Anda perlu menautkan keduanya.

Vatine
sumber
12
Bersumpah jelas merupakan langkah pertama. Tidak diragukan lagi.
dmckee --- kucing mantan moderator
1
"tidak banyak yang bisa kamu lakukan" - apakah ini masih relevan? Jawaban lain memberikan banyak solusi berbeda.
yugr
2

Anda harus menulis pustaka pembungkus di sekitar salah satunya. Perpustakaan pembungkus Anda harus mengekspos simbol dengan nama unik, dan tidak mengekspos simbol nama non-unik.

Pilihan Anda yang lain adalah mengganti nama fungsi di file header, dan mengganti nama simbol di arsip objek perpustakaan.

Bagaimanapun, untuk menggunakan keduanya, ini akan menjadi pekerjaan hack.

James Caccese
sumber
1

Pertanyaannya mendekati satu dekade, tetapi ada pencarian baru sepanjang waktu ...

Seperti yang sudah dijawab, objcopy dengan flag --redefine-sym adalah pilihan yang baik di Linux. Lihat, misalnya, https://linux.die.net/man/1/objcopy untuk dokumentasi lengkap. Ini sedikit kikuk karena pada dasarnya Anda menyalin seluruh pustaka sambil membuat perubahan dan setiap pembaruan mengharuskan pekerjaan ini diulang. Tapi setidaknya itu harus berhasil.

Untuk Windows, memuat pustaka secara dinamis adalah solusi dan solusi permanen seperti alternatif dlopen di Linux. Namun baik dlopen () dan LoadLibrary () menambahkan kode tambahan yang dapat dihindari jika satu-satunya masalah adalah nama duplikat. Di sini solusi Windows lebih elegan daripada pendekatan objcopy: Cukup beri tahu linker bahwa simbol di pustaka dikenal dengan nama lain dan gunakan nama itu. Ada beberapa langkah untuk melakukannya. Anda perlu membuat file def dan memberikan terjemahan nama di bagian EKSPOR. Lihat https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015, pada akhirnya akan diganti dengan versi yang lebih baru) atau http://www.digitalmars.com/ctg/ctgDefFiles.html(mungkin lebih permanen) untuk detail sintaks lengkap dari file def. Prosesnya adalah membuat file def untuk salah satu perpustakaan kemudian menggunakan file def ini untuk membuat file lib dan kemudian menautkan dengan file lib itu. (Untuk Windows DLL, file lib hanya digunakan untuk penautan, bukan eksekusi kode.) Lihat Cara membuat file .lib saat memiliki file .dll dan file header untuk proses pembuatan file lib. Di sini satu-satunya perbedaan adalah menambahkan alias.

Untuk Linux dan Windows, ganti nama fungsi di header pustaka yang namanya dialias. Opsi lain yang seharusnya berfungsi adalah, dalam file yang merujuk ke nama baru, #define old_name new_name, #include headers library yang ekspornya sedang dialias, lalu #undef old_name di pemanggil. Jika ada banyak file yang menggunakan pustaka, alternatif yang lebih mudah adalah membuat tajuk atau tajuk yang membungkus definisi, penyertaan, dan undef, lalu gunakan tajuk itu.

Semoga info ini bermanfaat!

Jim Monte
sumber
0

Saya tidak pernah menggunakan dlsym, dlopen, dlerror, dlclose, dlvsym, dll., Tapi saya melihat halaman manual, dan ini memberikan contoh membuka libm.so dan mengekstrak fungsi cos. Apakah dlopen melalui proses mencari tabrakan? Jika tidak, OP bisa memuat kedua perpustakaan secara manual dan menetapkan nama baru ke semua fungsi yang disediakan perpustakaannya.

Sniggerfardimungus
sumber