ฉันควรทำอย่างไรหากสองไลบรารีมีฟังก์ชันที่มีชื่อเดียวกันซึ่งก่อให้เกิดความขัดแย้ง


98

ฉันควรทำอย่างไรหากฉันมีสองไลบรารีที่ให้ฟังก์ชันที่มีชื่อเทียบเท่ากัน


2
ห้องสมุดคงที่เหล่านี้หรือเชื่อมโยงแบบไดนามิก?
Alnitak

เราต้องการรายละเอียดเพิ่มเติม ... ชื่อเหล่านั้นถูกส่งออกหรือไม่? หรือใช้ภายในเท่านั้น? คุณสามารถเปลี่ยนชื่อได้หรือไม่?
Johannes Schaub - litb

มีการเชื่อมโยงแบบไดนามิกทั้งสองอย่าง ฉันไม่สามารถเปลี่ยนชื่อได้เนื่องจากฉันไม่ได้เป็นเจ้าของห้องสมุด
qeek

คำถามที่ดี แน่นอนมันจะไม่เป็นปัญหากับทั้งสองห้องสมุดถ้าสัญลักษณ์ทั้งหมดที่ถูกนำหน้าด้วยรหัสเฉพาะ (เช่นvorbis_..., sf_..., sdl_...) นี่คือสิ่งที่ C ++ ทำกับชื่อสัญลักษณ์สำหรับฟังก์ชันเนมสเปซ
Vortico

นี่เป็นคำถามที่น่าสนใจมาก แต่น่าเศร้าที่ไม่ชัดเจนเกินไปซึ่งเป็นสาเหตุของการมีคำตอบที่กว้างเกินไปมากเกินไป
yugr

คำตอบ:


53
  • หากคุณควบคุมอย่างใดอย่างหนึ่งหรือทั้งสองอย่าง: แก้ไขอย่างใดอย่างหนึ่งเพื่อเปลี่ยนชื่อและคอมไพล์ใหม่หรือเทียบเท่ากับดูBenและคำตอบที่ไม่รู้จักซึ่งจะทำงานได้โดยไม่ต้องเข้าถึงซอร์สโค้ด
  • หากคุณไม่ได้ควบคุมอย่างใดอย่างหนึ่งคุณสามารถรวมหนึ่งในนั้นขึ้นมาได้ นั่นคือการรวบรวมไลบรารีอื่น ( ลิงก์แบบคงที่ !) ที่ไม่ทำอะไรเลยนอกจากส่งออกสัญลักษณ์ทั้งหมดของต้นฉบับใหม่อีกครั้งยกเว้นสัญลักษณ์ที่ละเมิดซึ่งเข้าถึงได้ผ่านกระดาษห่อหุ้มที่มีชื่ออื่น เป็นอะไรที่ยุ่งยาก
  • เพิ่มในภายหลัง:เนื่องจาก qeek บอกว่าเขากำลังพูดถึงไลบรารีแบบไดนามิกโซลูชันที่Ferruccioและmouviciel แนะนำจึงน่าจะดีที่สุด (ดูเหมือนฉันจะมีชีวิตอยู่เมื่อนานมาแล้วเมื่อการเชื่อมโยงแบบคงที่เป็นค่าเริ่มต้นมันทำให้ความคิดของฉันเป็นสี)

แสดงความคิดเห็น: โดย "ส่งออก" ฉันหมายถึงการทำให้โมดูลที่ลิงก์ไปยังไลบรารีมองเห็นได้ --- เทียบเท่ากับexternคีย์เวิร์ดที่ขอบเขตไฟล์ วิธีการควบคุมนี้ขึ้นอยู่กับ OS และตัวเชื่อมโยง และมันก็เป็นสิ่งที่ผมมักจะต้องมองขึ้น


นั่นเป็นความคิดแรกของฉันเช่นกัน แต่คุณจะไม่ลงเอยด้วยปัญหาการชนกันแบบเดียวกันหรือ? ในท้ายที่สุดโปรเจ็กต์ทั้งหมดจะต้องเชื่อมโยง - ในเวลาคอมไพล์ / ลิงก์หรือในเวลารัน - ซึ่งในเวลานั้นทั้งไลบรารีที่กระทำผิดจะต้องโหลดตามที่เป็น
Sniggerfardimungus

@unknown: Wrapper ต้องรวบรวมด้วยการเชื่อมโยงแบบคงที่และไม่ควรส่งออกสัญลักษณ์ที่ละเมิด จากนั้นคุณยังสามารถเชื่อมโยงกระดาษห่อหุ้มแบบไดนามิกได้ แก้ไขเพื่อความชัดเจนยิ่งขึ้นขอบคุณ
dmckee --- อดีตผู้ดูแลลูกแมว

หากปัญหาของ qeek เกิดจาก ddl และไม่ใช่ไลบรารีแบบคงที่จะสร้างไลบรารีใหม่ด้วย wrapper ได้อย่างไร เนื่องจากไลบรารี Wrapper จะต้องพันรอบฟังก์ชันในไลบรารีที่คุณไม่ต้องการเชื่อมโยงในตอนแรกแบบไดนามิก
jeffD

@dmckee - "ส่งออก" หมายความว่าอย่างไร

5
บางทีอาจมีคนให้ตัวอย่างง่ายๆของเทคนิคนี้? หนึ่ง exe สองไลบรารีแต่ละรายการมีหนึ่งฟังก์ชันที่มีชื่อเดียวกัน

54

เป็นไปได้ที่จะเปลี่ยนชื่อสัญลักษณ์ในอ็อบเจ็กต์ไฟล์โดยใช้objcopy --redefine-sym old=new file(ดู man objcopy)

จากนั้นเรียกใช้ฟังก์ชันโดยใช้ชื่อใหม่และเชื่อมโยงกับไฟล์ออบเจ็กต์ใหม่


2
ดี. การเพิ่มลงใน Makefile อาจเป็นเรื่องเล็กน้อย หากเคยมีการอัปเดตไลบรารีคาถา objcopy จะอัปเดตได้ง่ายกว่าโซลูชันอื่น ๆ
sigjuice

9
อย่าลืมเปลี่ยนชื่อสัญลักษณ์ในไฟล์ส่วนหัวด้วย
mouviciel

^ sed / awk / perl จะมีประโยชน์ในการเปลี่ยนชื่อสัญลักษณ์ในส่วนหัวโดยอัตโนมัติด้วย
Alex Reinking

16

ใน Windows คุณสามารถใช้LoadLibrary ()เพื่อโหลดหนึ่งในไลบรารีเหล่านั้นลงในหน่วยความจำจากนั้นใช้GetProcAddress ()เพื่อรับที่อยู่ของแต่ละฟังก์ชันที่คุณต้องการเรียกและเรียกใช้ฟังก์ชันผ่านตัวชี้ฟังก์ชัน

เช่น

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);

จะได้รับที่อยู่ของฟังก์ชันชื่อ bar ใน foo.dll และเรียกมัน

ฉันรู้ว่าระบบ Unix รองรับฟังก์ชันการทำงานที่คล้ายกัน แต่ฉันนึกชื่อไม่ออก


dlopen dlsymและdlclose. อย่างไรก็ตามการห่อหุ้มบน Unix อาจไม่มีประสิทธิภาพเท่ากับบน Windows
user877329


8

นี่คือความคิด เปิดหนึ่งในไลบรารีที่ละเมิดในตัวแก้ไขฐานสิบหกและเปลี่ยนการเกิดขึ้นทั้งหมดของสตริงที่ละเมิดเป็นอย่างอื่น จากนั้นคุณจะสามารถใช้ชื่อใหม่ในการโทรในอนาคตทั้งหมด

UPDATE: ฉันเพิ่งทำในตอนนี้และดูเหมือนว่าจะได้ผล แน่นอนฉันไม่ได้ทดสอบสิ่งนี้อย่างละเอียด - มันอาจจะไม่มากไปกว่าวิธีที่ดีจริงๆในการระเบิดขาของคุณด้วยปืนลูกซอง hexedit


จริงๆแล้วไม่ใช่วิธีแก้ปัญหาที่น่ากลัว แฮ็คนิดหน่อย แต่สิ่งที่คุณต้องทำคือเปลี่ยนสตริงในตารางสัญลักษณ์ ไม่มีอันตรายต่อการใช้งานจริงในสิ่งนั้น
Evan Teran

คุณอาจต้องการเปลี่ยนชื่อไลบรารีด้วย - เกรงว่าจะมีคนอื่นเข้ามาพยายามโหลดอีกครั้ง คุณจะเปลี่ยนจากความขัดแย้งหนึ่งไปเป็นหลายสิบหรือหลายร้อย =] ฉันชอบสิ่งนี้เกี่ยวกับ stackoverflow: เรามีคำตอบที่ผ่านการทดสอบสำหรับคำถามและมี 3 คะแนน คำตอบแรก (ไม่สมบูรณ์): 17. =]
Sniggerfardimungus

1
โอกาสในการเปลี่ยนชื่อมี จำกัด เนื่องจากคุณจะสามารถตั้งชื่อให้สั้นลงได้เท่านั้น นอกจากนี้บน Linux คุณจะมีปัญหาในการอัปเดตตารางแฮชของ ELF
yugr

8

หากคุณมีไฟล์. o มีคำตอบที่ดีที่นี่: https://stackoverflow.com/a/6940389/4705766

สรุป:

  1. objcopy --prefix-symbols=pre_string test.o เพื่อเปลี่ยนชื่อสัญลักษณ์ในไฟล์. o

หรือ

  1. objcopy --redefine-sym old_str=new_str test.o เพื่อเปลี่ยนชื่อสัญลักษณ์เฉพาะในไฟล์. o

7

สมมติว่าคุณใช้ linux ก่อนอื่นคุณต้องเพิ่ม

#include <dlfcn.h>

ประกาศตัวแปรตัวชี้ฟังก์ชันในบริบทที่เหมาะสมตัวอย่างเช่น

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

เช่นเดียวกับที่ Ferruccio ระบุไว้ในhttps://stackoverflow.com/a/678453/1635364ให้โหลดไลบรารีที่คุณต้องการใช้อย่างชัดเจนโดยดำเนินการ (เลือกแฟล็กที่คุณชื่นชอบ)

void* dlhandle;
void* sym;

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

อ่านที่อยู่ของฟังก์ชันที่คุณต้องการเรียกใช้ในภายหลัง

sym = dlsym(dlhandle, "conflicting_server_init");

กำหนดและส่งดังต่อไปนี้

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

โทรในลักษณะที่คล้ายกันมากกว่าแบบเดิม สุดท้ายยกเลิกการโหลดโดยดำเนินการ

dlclose(dlhandle);


6

คุณไม่ควรใช้ร่วมกัน ถ้าฉันจำไม่ผิดตัวเชื่อมโยงเกิดข้อผิดพลาดในกรณีดังกล่าว

ผมไม่ได้ลอง แต่วิธีการแก้ปัญหาอาจจะมีdlopen(), dlsym()และdlclose()ที่ช่วยให้คุณจัดการกับโปรแกรมห้องสมุดแบบไดนามิก หากคุณไม่ต้องการสองฟังก์ชันในเวลาเดียวกันคุณสามารถเปิดไลบรารีแรกใช้ฟังก์ชันแรกและปิดไลบรารีแรกก่อนที่จะใช้ไลบรารี / ฟังก์ชันที่สอง


ขอบคุณ. ไม่ได้คิดเรื่องนี้. แม้ว่าฉันอยากจะมีทั้งสองอย่างในเวลาเดียวกัน
qeek

จะเป็นอย่างไรถ้าฉันต้องการใช้ทั้งสองอย่างพร้อมกัน
QZHua

@QZHua: anwsers อื่น ๆ (เช่นการเปลี่ยนชื่อสัญลักษณ์) ควรแก้ปัญหาของคุณได้
mouviciel

4

ปัญหานี้เป็นสาเหตุที่ c ++ มีเนมสเปซ ไม่มีวิธีแก้ปัญหาที่ยอดเยี่ยมจริงๆใน c สำหรับ 2 บุคคลที่สาม libs ที่มีชื่อเดียวกัน

หากเป็นวัตถุแบบไดนามิกคุณอาจสามารถโหลดวัตถุที่ใช้ร่วมกันอย่างชัดเจน (LoadLibrary / dlopen / etc) และเรียกมันในรูปแบบนั้น อีกวิธีหนึ่งหากคุณไม่ต้องการ libs ทั้งสองพร้อมกันในรหัสเดียวกันคุณอาจทำอะไรบางอย่างด้วยการลิงก์แบบคงที่ (ถ้าคุณมีไฟล์. lib / .a)

แน่นอนว่าโซลูชันเหล่านี้ไม่สามารถใช้ได้กับทุกโครงการ


2
โอ้ใช่. สำหรับคำถามทั่วไปนี้ดูเหมือนจะเป็นคำตอบที่ดี อย่างไรก็ตาม - เนมสเปซนั้นยอดเยี่ยมหากคุณรวบรวมทุกอย่างเข้าด้วยกันในคอมไพเลอร์เดียวกัน ไชโยไม่มีชื่อปะทะกัน แต่ถ้าคุณได้รับไลบรารีในรูปแบบไบนารีและต้องการรวมเข้ากับคอมไพเลอร์อื่นก็ขอให้โชคดี กฎการรวมชื่อในไฟล์ออบเจ็กต์เป็นเพียงอุปสรรคแรก (ภายนอก "C" อาจช่วยได้ซึ่งจะยกเลิกความสมบูรณ์ของเนมสเปซ)
Tomasz Gandor

3

สาบาน? เท่าที่ฉันทราบไม่มีอะไรที่คุณสามารถทำได้หากคุณมีสองไลบรารีที่แสดงจุดเชื่อมโยงที่มีชื่อเดียวกันและคุณต้องเชื่อมโยงกับทั้งสอง


12
สาบานเป็นขั้นตอนแรกแน่นอน ไม่ต้องสงสัยเกี่ยวกับมัน
dmckee --- อดีตผู้ดูแลลูกแมว

1
"มีอะไรให้คุณทำไม่มาก" - ยังเกี่ยวข้องไหม คำตอบอื่น ๆ มีวิธีแก้ปัญหาที่แตกต่างกันมากมาย
yugr

2

คุณควรเขียนไลบรารีเสื้อคลุมรอบ ๆ หนึ่งในนั้น ไลบรารี Wrapper ของคุณควรแสดงสัญลักษณ์ที่มีชื่อเฉพาะและไม่เปิดเผยสัญลักษณ์ของชื่อที่ไม่ซ้ำ

ตัวเลือกอื่นของคุณคือเปลี่ยนชื่อฟังก์ชันในไฟล์ส่วนหัวและเปลี่ยนชื่อสัญลักษณ์ในไฟล์เก็บถาวรของไลบรารี

ไม่ว่าจะใช้ทั้งสองวิธีมันจะเป็นงานแฮ็ค



1

คำถามใกล้จะครบสิบปีแล้ว แต่มีการค้นหาใหม่ ๆ ตลอดเวลา ...

ตามที่ตอบไปแล้ว objcopy ที่มีแฟล็ก --redefine-sym เป็นตัวเลือกที่ดีใน Linux ดูตัวอย่างเช่นhttps://linux.die.net/man/1/objcopyสำหรับเอกสารฉบับเต็ม มันค่อนข้างอึดอัดเล็กน้อยเนื่องจากคุณกำลังคัดลอกไลบรารีทั้งหมดในขณะที่ทำการเปลี่ยนแปลงและการอัปเดตทุกครั้งจำเป็นต้องมีการทำงานนี้ซ้ำ แต่อย่างน้อยก็ควรใช้งานได้

สำหรับ Windows การโหลดไลบรารีแบบไดนามิกเป็นวิธีแก้ปัญหาและแบบถาวรเช่นเดียวกับทางเลือก dlopen ใน Linux อย่างไรก็ตามทั้ง dlopen () และ LoadLibrary () เพิ่มรหัสพิเศษที่สามารถหลีกเลี่ยงได้หากปัญหาเดียวคือชื่อที่ซ้ำกัน วิธีการแก้ปัญหาของ Windows มีความสง่างามมากกว่าวิธีการ objcopy: เพียงบอกผู้เชื่อมโยงว่าสัญลักษณ์ในไลบรารีรู้จักชื่ออื่นและใช้ชื่อนั้น มีไม่กี่ขั้นตอนในการทำ คุณต้องสร้างไฟล์ def และแปลชื่อในส่วน EXPORTS โปรดดูที่https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015 ในที่สุดก็จะถูกแทนที่ด้วยเวอร์ชันที่ใหม่กว่า) หรือhttp://www.digitalmars.com/ctg/ctgDefFiles.html(อาจจะถาวรกว่า) สำหรับรายละเอียดไวยากรณ์ทั้งหมดของไฟล์ def กระบวนการนี้คือการสร้างไฟล์ def สำหรับหนึ่งในไลบรารีจากนั้นใช้ไฟล์ def นี้เพื่อสร้างไฟล์ lib จากนั้นเชื่อมโยงกับไฟล์ lib นั้น (สำหรับ Windows DLL ไฟล์ lib จะใช้สำหรับการเชื่อมโยงเท่านั้นไม่ใช่การเรียกใช้โค้ด) ดูวิธีสร้างไฟล์. lib เมื่อมีไฟล์. dll และไฟล์ส่วนหัวสำหรับขั้นตอนการสร้างไฟล์ lib ข้อแตกต่างเพียงอย่างเดียวคือการเพิ่มนามแฝง

สำหรับทั้ง Linux และ Windows ให้เปลี่ยนชื่อฟังก์ชันในส่วนหัวของไลบรารีที่มีการใช้นามแฝง อีกทางเลือกหนึ่งที่ควรใช้คือในไฟล์ที่อ้างถึงชื่อใหม่ไปที่ # กำหนด old_name new_name # รวมส่วนหัวของไลบรารีที่มีการเอ็กซ์พอร์ตถูกใช้นามแฝงจากนั้น #undef old_name ในตัวเรียก หากมีไฟล์จำนวนมากที่ใช้ไลบรารีทางเลือกที่ง่ายกว่าคือการสร้างส่วนหัวหรือส่วนหัวที่ล้อมรอบการกำหนดรวมถึงและอันเดอร์แล้วใช้ส่วนหัวนั้น

หวังว่าข้อมูลนี้จะเป็นประโยชน์!


0

ฉันไม่เคยใช้ dlsym, dlopen, dlerror, dlclose, dlvsym ฯลฯ แต่ฉันกำลังดู man page และมันให้ตัวอย่างของการเปิด libm.so และแยกฟังก์ชัน cos dlopen ผ่านกระบวนการมองหาการชนหรือไม่? หากไม่เป็นเช่นนั้น OP สามารถโหลดไลบรารีทั้งสองด้วยตนเองและกำหนดชื่อใหม่ให้กับฟังก์ชันทั้งหมดที่ไลบรารีมีให้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.