Tôi nên làm gì nếu hai thư viện cung cấp một hàm có cùng tên tạo ra xung đột?


94

Tôi nên làm gì nếu tôi có hai thư viện cung cấp các hàm có tên tương đương?


2
là các thư viện tĩnh hay được liên kết động?
Alnitak

chúng tôi cần thêm chi tiết ... những cái tên đó có được xuất khẩu không? hay chúng chỉ được sử dụng trong nội bộ? Bạn có thể thay đổi tên?
Johannes Schaub - litb

Chúng được liên kết động, cả hai. Tôi không thể thay đổi tên vì tôi không sở hữu thư viện.
qeek

Câu hỏi tuyệt vời. Tất nhiên nó sẽ không là một vấn đề với hai thư viện này nếu tất cả những biểu tượng được bắt đầu bằng một ID duy nhất (ví dụ như vorbis_..., sf_..., sdl_...). Về cơ bản đây là những gì C ++ thực hiện với các tên ký hiệu cho các hàm có không gian tên.
Vortico

Đây là một câu hỏi rất thú vị nhưng đáng buồn là quá không chính xác, đó là lý do có quá nhiều câu trả lời quá rộng.
yugr

Câu trả lời:


52
  • Nếu bạn kiểm soát một hoặc cả hai: chỉnh sửa một để thay đổi tên và biên dịch lại Hoặc tương đương xem câu trả lời của Benchưa biết sẽ hoạt động mà không cần truy cập vào mã nguồn.
  • Nếu bạn không kiểm soát một trong hai, bạn có thể quấn một trong số chúng lại. Đó là biên dịch một thư viện khác ( được liên kết tĩnh !) Không làm gì khác ngoài việc xuất lại tất cả các ký hiệu của bản gốc ngoại trừ ký hiệu vi phạm, được truy cập thông qua một trình bao bọc có tên thay thế. Thật là rắc rối.
  • Được thêm sau: Vì qeek nói rằng anh ấy đang nói về các thư viện động, nên các giải pháp được đề xuất bởi Ferrucciomouviciel có lẽ là tốt nhất. (Tôi dường như đã sống trong những ngày dài trước đây khi liên kết tĩnh là mặc định. Nó tô điểm cho suy nghĩ của tôi.)

Apropos nhận xét: Bằng cách "xuất", ý tôi là hiển thị các mô-đun liên kết tới thư viện --- tương đương với externtừ khóa ở phạm vi tệp. Điều này được kiểm soát như thế nào là phụ thuộc vào hệ điều hành và trình liên kết. Và nó là thứ mà tôi luôn phải tra cứu.


Đó cũng là suy nghĩ đầu tiên của tôi, nhưng bạn sẽ không gặp phải vấn đề va chạm tương tự chứ? Cuối cùng, toàn bộ dự án phải liên kết - tại thời gian biên dịch / liên kết hoặc tại thời điểm chạy - lúc đó cả hai thư viện vi phạm đều phải tải nguyên trạng.
Sniggerfardimungus

@unknown: Trình bao bọc phải được biên dịch với liên kết tĩnh và không được xuất biểu tượng vi phạm. Sau đó, bạn vẫn có thể liên kết động trình bao bọc. Đã chỉnh sửa để rõ ràng hơn, Cảm ơn.
dmckee --- cựu điều hành kitten

Nếu vấn đề của qeek là với ddl's chứ không phải thư viện tĩnh, thì làm cách nào để tạo một thư viện mới với một trình bao bọc? Vì, thư viện trình bao bọc sẽ phải tự động quấn quanh một hàm trong thư viện mà bạn không muốn liên kết ngay từ đầu.
jeffD

@dmckee - "xuất" nghĩa là gì?

4
có lẽ ai đó có thể cung cấp một ví dụ đơn giản về kỹ thuật này? Một exe, hai thư viện, mỗi thư viện chứa một hàm có cùng tên.

53

Có thể đổi tên các biểu tượng trong tệp đối tượng bằng cách sử dụng objcopy --redefine-sym old=new file(xem man objcopy).

Sau đó, chỉ cần gọi các hàm bằng tên mới của chúng và liên kết với tệp đối tượng mới.


2
Đẹp. Điều này sẽ là nhỏ nếu thêm vào một Makefile. Nếu các thư viện luôn được cập nhật, thì một câu thần chú phản đối sẽ dễ cập nhật hơn nhiều so với một số giải pháp khác.
sigjuice

9
Đừng quên đổi tên các ký hiệu trong tệp tiêu đề.
mouviciel

^ Sed / awk / perl sẽ hữu ích để tự động đổi tên các biểu tượng trong tiêu đề, quá
Alex Reinking

16

Trong Windows, bạn có thể sử dụng LoadLibrary () để tải một trong những thư viện đó vào bộ nhớ và sau đó sử dụng GetProcAddress () để lấy địa chỉ của từng hàm bạn cần gọi và gọi các hàm thông qua con trỏ hàm.

ví dụ

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

sẽ lấy địa chỉ của một thanh có tên hàm trong foo.dll và gọi nó.

Tôi biết các hệ thống Unix hỗ trợ chức năng tương tự, nhưng tôi không thể nghĩ ra tên của chúng.


dlopen dlsym, và dlclose. Tuy nhiên, tính năng đóng gói trên Unix có thể không hiệu quả như trên Windows.
user877329


8

Đây là một suy nghĩ. Mở một trong các thư viện vi phạm trong trình chỉnh sửa hex và thay đổi tất cả các lần xuất hiện của các chuỗi vi phạm thành một thứ khác. Sau đó, bạn sẽ có thể sử dụng các tên mới trong tất cả các cuộc gọi trong tương lai.

CẬP NHẬT: Tôi vừa làm điều đó ở đầu này và nó có vẻ hoạt động. Tất nhiên, tôi đã không kiểm tra kỹ lưỡng điều này - nó có thể không phải là một cách thực sự tốt để thổi bay chân bạn bằng một khẩu súng ngắn hexedit.


thực sự không phải là một giải pháp khủng khiếp. Hơi khó hiểu, nhưng tất cả những gì bạn đang làm là thay đổi các chuỗi trong bảng biểu tượng. Không có tác hại thực sự về chức năng trong đó.
Evan Teran

Bạn có thể cũng muốn đổi tên thư viện - kẻo có người khác đến cùng, cố tải lại thứ. Bạn sẽ đi từ xung đột này đến hàng chục hoặc hàng trăm. =] Tôi thích điều này về stackoverflow: chúng tôi có một câu trả lời đã được thử nghiệm cho một câu hỏi và nó có 3 phiếu bầu. Đầu tiên (không đầy đủ) câu trả lời: 17. =]
Sniggerfardimungus

1
Cơ hội đổi tên bị hạn chế vì bạn sẽ chỉ có thể đặt tên ngắn hơn . Ngoài ra trên Linux, bạn sẽ gặp khó khăn khi cập nhật bảng băm ELF.
yugr

7

Giả sử rằng bạn sử dụng linux, trước tiên bạn cần thêm

#include <dlfcn.h>

Khai báo biến con trỏ hàm trong ngữ cảnh thích hợp, ví dụ:

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

Giống như Ferruccio đã nêu trong https://stackoverflow.com/a/678453/1635364 , tải rõ ràng thư viện bạn muốn sử dụng bằng cách thực thi (chọn cờ yêu thích của bạn)

void* dlhandle;
void* sym;

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

Đọc địa chỉ của hàm bạn muốn gọi sau

sym = dlsym(dlhandle, "conflicting_server_init");

gán và truyền như sau

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

Gọi theo cách tương tự so với bản gốc. Cuối cùng, dỡ bỏ bằng cách thực thi

dlclose(dlhandle);


6

Bạn không nên sử dụng chúng cùng nhau. Nếu tôi nhớ không nhầm thì trình liên kết đã xảy ra lỗi trong trường hợp như vậy.

Tôi không cố gắng, nhưng một giải pháp có thể có dlopen(), dlsym()dlclose()cho phép bạn xử lý lập trình thư viện động. Nếu bạn không cần hai hàm cùng một lúc, bạn có thể mở thư viện đầu tiên, sử dụng hàm đầu tiên và đóng thư viện đầu tiên trước khi sử dụng thư viện / hàm thứ hai.


Cảm ơn. Không nghĩ về điều này. Mặc dù vậy, tôi muốn có cả hai cùng một lúc.
qeek

Điều gì sẽ xảy ra nếu tôi muốn sử dụng cả hai cùng một lúc?
QZHua

@QZHua: Các nhà cảm xạ khác (ví dụ: liên quan đến đổi tên biểu tượng) sẽ giải quyết vấn đề của bạn.
mouviciel

6

Nếu bạn có tệp .o ở đó, câu trả lời hay ở đây: https://stackoverflow.com/a/6940389/4705766

Tóm lược:

  1. objcopy --prefix-symbols=pre_string test.o để đổi tên các ký hiệu trong tệp .o

hoặc là

  1. objcopy --redefine-sym old_str=new_str test.o để đổi tên ký hiệu cụ thể trong tệp .o.

4

Vấn đề này là lý do c ++ có không gian tên. Không thực sự là một giải pháp tuyệt vời trong c cho 2 lib của bên thứ ba có cùng tên.

Nếu đó là một đối tượng động, bạn có thể tải rõ ràng các đối tượng được chia sẻ (LoadLibrary / dlopen / etc) và gọi nó theo kiểu đó. Ngoài ra, nếu bạn không cần cả hai lib cùng một lúc trong cùng một mã, bạn có thể làm điều gì đó với liên kết tĩnh (nếu bạn có tệp .lib / .a).

Tất nhiên, không có giải pháp nào trong số này áp dụng cho tất cả các dự án.


1
Ồ vâng. Đối với câu hỏi chung này, đây có vẻ là một câu trả lời tốt. Tuy nhiên - không gian tên sẽ rất tuyệt nếu bạn biên dịch mọi thứ cùng nhau trong cùng một trình biên dịch. Hoan hô, không có đụng độ tên tuổi. Nhưng nếu bạn nhận được một thư viện ở dạng nhị phân và muốn tích hợp nó với một trình biên dịch khác, thì - chúc may mắn. Các quy tắc xáo trộn tên trong các tệp đối tượng chỉ là trở ngại đầu tiên ("C" bên ngoài có thể giúp ích, hoàn tác phần hoàn thiện của không gian tên).
Tomasz Gandor,

3

Xin thề? Theo như tôi được biết, bạn không thể làm gì nhiều nếu có hai thư viện để lộ các điểm liên kết có cùng tên và bạn cần liên kết với cả hai.


12
Thề chắc chắn là bước đầu tiên. Không có nghi ngờ gì về điều đó.
dmckee --- cựu điều hành kitten

1
"bạn không thể làm được gì nhiều" - điều này có còn phù hợp không? Các câu trả lời khác cung cấp nhiều giải pháp khác nhau.
yugr

2

Bạn nên viết một thư viện trình bao bọc xung quanh một trong số chúng. Thư viện trình bao bọc của bạn phải hiển thị các ký hiệu có tên duy nhất và không hiển thị các ký hiệu của các tên không phải duy nhất.

Tùy chọn khác của bạn là đổi tên tên hàm trong tệp tiêu đề và đổi tên ký hiệu trong kho lưu trữ đối tượng thư viện.

Dù bằng cách nào, để sử dụng cả hai, đó sẽ là một công việc hack.



1

Câu hỏi đã gần tròn một thập kỷ, nhưng luôn có những tìm kiếm mới ...

Như đã được trả lời, đối tượng với cờ --redefine-sym là một lựa chọn tốt trong Linux. Ví dụ: xem https://linux.die.net/man/1/objcopy để có tài liệu đầy đủ. Nó hơi rắc rối vì về cơ bản bạn đang sao chép toàn bộ thư viện trong khi thực hiện các thay đổi và mọi bản cập nhật đều yêu cầu công việc này được lặp lại. Nhưng ít nhất nó sẽ hoạt động.

Đối với Windows, tải động thư viện là một giải pháp và một giải pháp vĩnh viễn giống như giải pháp thay thế dlopen trong Linux. Tuy nhiên, cả dlopen () và LoadLibrary () đều thêm mã bổ sung có thể tránh được nếu vấn đề duy nhất là tên trùng lặp. Ở đây, giải pháp Windows thanh lịch hơn cách tiếp cận đối tượng: Chỉ cần nói với trình liên kết rằng các ký hiệu trong thư viện được biết đến với một số tên khác và sử dụng tên đó. Có một vài bước để làm điều đó. Bạn cần tạo tệp def và cung cấp bản dịch tên trong phần XUẤT KHẨU. Xem https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx (VS2015, cuối cùng nó sẽ được thay thế bằng các phiên bản mới hơn) hoặc http://www.digitalmars.com/ctg/ctgDefFiles.html(có thể lâu dài hơn) để biết chi tiết cú pháp đầy đủ của tệp def. Quá trình này sẽ là tạo tệp def cho một trong các thư viện, sau đó sử dụng tệp def này để tạo tệp lib và sau đó liên kết với tệp lib đó. (Đối với Windows DLL, tệp lib chỉ được sử dụng để liên kết chứ không phải thực thi mã.) Xem Cách tạo tệp .lib khi có tệp .dll và tệp tiêu đề cho quá trình xây dựng tệp lib. Ở đây, sự khác biệt duy nhất là thêm các bí danh.

Đối với cả Linux và Windows, đổi tên các hàm trong tiêu đề của thư viện có tên đang được đặt bí danh. Một tùy chọn khác sẽ hoạt động là, trong các tệp tham chiếu đến tên mới, thành #define old_name new_name, # bao gồm các tiêu đề của thư viện có các bản xuất đang được đặt bí danh, sau đó #undef old_name trong trình gọi. Nếu có nhiều tệp sử dụng thư viện, một giải pháp thay thế dễ dàng hơn là tạo một tiêu đề hoặc các tiêu đề bao bọc các định nghĩa, bao gồm và hoàn tác và sau đó sử dụng tiêu đề đó.

Hy vọng thông tin này là hữu ích!


0

Tôi chưa bao giờ sử dụng dlsym, dlopen, dlerror, dlclose, dlvsym, v.v., nhưng tôi đang xem trang man và nó đưa ra một ví dụ về cách mở libm.so và giải nén hàm cos. Dlopen có trải qua quá trình tìm va chạm không? Nếu không, OP có thể tải cả hai thư viện theo cách thủ công và gán tên mới cho tất cả các chức năng mà thư viện của anh ta cung cấp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.