2012-06-15 71 views
52

Hãy xem xét kịch bản sau đây:Liên kết với thư viện năng động với phụ thuộc

  • Shared Library libA.so, không phụ thuộc.
  • Thư viện được chia sẻ libB.so, với libA.so là sự phụ thuộc của nó.

Tôi muốn biên dịch tệp nhị phân có liên kết với libB. Tôi có nên liên kết nhị phân với libB chỉ hoặc với libA không?

Có cách nào để liên kết chỉ với các phụ thuộc trực tiếp, cho phép giải quyết các biểu tượng chưa được giải quyết khỏi các phụ thuộc trong thời gian chạy không?

Tôi lo lắng về việc thực thi thư viện libB có thể thay đổi trong tương lai, giới thiệu các phụ thuộc khác (ví dụ: libC, libD, libE). Tôi sẽ có vấn đề với điều đó?

Nói cách khác:

  • Liba file: a.cpp ah
  • file libB: b.cpp bh
  • file chương trình chính: main.cpp

Tất nhiên, b.cpp bao gồm ah và main.cpp bao gồm bh

lệnh Compilation:

g++ -fPIC a.cpp -c 
g++ -shared -o libA.so a.o 

g++ -fPIC b.cpp -c -I. 
g++ -shared -o libB.so b.o -L. -lA 

nào trong các tùy chọn dưới đây tôi nên sử dụng?

g++ main.cpp -o main -I. -L. -lB 

hoặc

g++ main.cpp -o main -I. -L. -lB -lA 

tôi không thể sử dụng tùy chọn đầu tiên. Trình liên kết phàn nàn về các ký hiệu chưa được giải quyết từ thư viện libA. Nhưng nó hơi lạ với tôi.

Cảm ơn rất nhiều.

- bình luận Cập nhật:

Khi tôi liên kết nhị phân, các mối liên kết sẽ cố gắng giải quyết tất cả các biểu tượng từ chính và libB. Tuy nhiên, libB có các ký hiệu không xác định từ libA. Đó là lý do tại sao người liên kết phàn nàn về điều đó.

Đó là lý do tại sao tôi cũng cần phải liên kết với libA. Tuy nhiên tôi tìm thấy một cách để bỏ qua các biểu tượng chưa được giải quyết từ các thư viện được chia sẻ. Hình như tôi nên sử dụng dòng lệnh sau đây để làm điều đó:

g++ main.cpp -o main -I. -L. -lB -Wl,-unresolved-symbols=ignore-in-shared-libs 

Hình như nó vẫn còn có thể sử dụng tùy chọn -rpath. Tuy nhiên tôi cần phải hiểu nó tốt hơn một chút.

Có ai biết bất kỳ cạm bẫy nào có thể xảy ra khi sử dụng tùy chọn -Wl,-unresolved-symbols=ignore-in-shared-libs không?

- bình luận Cập nhật 2:

-rpath không nên được sử dụng cho mục đích này. Nó rất hữu ích để buộc một thư viện được tìm thấy trong một thư mục nhất định. Cách tiếp cận -unresolved-symbol trông đẹp hơn nhiều.

Xin cảm ơn một lần nữa.

Trả lời

27

Dường như bạn đã ở hầu hết mọi cách. Thực hiện tốt với điều tra của bạn. Hãy xem liệu tôi có thể giúp làm rõ 'lý do' đằng sau nó hay không.

Đây là những gì mà trình liên kết đang thực hiện. Khi bạn liên kết thực thi của bạn ('chính' ở trên) nó có một số biểu tượng (chức năng và những thứ khác) mà chưa được giải quyết. Nó sẽ xem xét danh sách các thư viện theo sau, cố gắng giải quyết các ký hiệu chưa được giải quyết. Trên đường đi, nó thấy rằng một số biểu tượng được cung cấp bởi libB.so, do đó, nó lưu ý rằng chúng được giải quyết bởi thư viện này.

Tuy nhiên, nó cũng phát hiện ra rằng một số biểu tượng đó sử dụng các ký hiệu khác chưa được giải quyết trong tệp thực thi của bạn, do đó, bây giờ cũng cần phải giải quyết chúng. Nếu không có liên kết với libA.so, ứng dụng của bạn sẽ không đầy đủ. Khi nó liên kết với libA.so, tất cả các biểu tượng được giải quyết và liên kết hoàn tất.

Như bạn đã thấy, việc sử dụng -unresolved-symbols-in-shared-libs, không khắc phục được sự cố. Nó chỉ làm giảm nó để những biểu tượng được giải quyết tại thời gian chạy. Đó là những gì -rpath là dành cho: để chỉ định các thư viện được tìm kiếm trong thời gian chạy. Nếu những biểu tượng đó không thể giải quyết được thì ứng dụng của bạn sẽ không khởi động được. Nó không phải là một điều dễ dàng để tìm ra phụ thuộc thư viện bởi vì một biểu tượng có thể được cung cấp bởi nhiều hơn một thư viện và được hài lòng bằng cách liên kết với bất kỳ một trong số chúng.

Có một mô tả về quá trình này ở đây: Why does the order in which libraries are linked sometimes cause errors in GCC?

+1

Tôi biết đã lâu rồi, tôi quên đánh dấu nó là đã được giải quyết. Cảm ơn rất nhiều câu trả lời của bạn.] – Marcus

+0

Cảm ơn các bạn vì câu hỏi tuyệt vời và câu trả lời hợp lý! Tôi đã gần như chính xác cùng một kịch bản. Tôi đang cố gắng để bọc các đối tượng chia sẻ openCV trong một đối tượng được chia sẻ tùy chỉnh. cho phép nói C.so là tùy chỉnh của tôi như vậy, và CV.so là một số openCV như vậy. Tôi đã liên kết thành công C.so chống lại CV.so, và tôi muốn liên kết một main.cpp (bạn nhận được nó!) Chống lại C.so, nhưng điều này ngoài việc sử dụng các đối tượng từ C.so, main.cpp sử dụng một đối tượng 'cv :: Mat' được định nghĩa trong CV.so .. Trong trường hợp này cho main.cpp để chạy, tôi có cần phải liên kết với cả C.so và CV.so ?? Tôi rất thích nó nếu ai đó làm sáng tỏ điều này. Cảm ơn bạn! :) –

+0

Tại sao trình liên kết không giải quyết tất cả các ký hiệu libA.so bắt buộc khi tạo libB.so? Ứng dụng chính không được liên kết đến libA vì nó phải trong suốt, ví dụ: gián tiếp bảo vệ bởi libB? – Wilson

11

Đối với liên kết động chỉ với phụ thuộc trực tiếp bạn có thể sử dụng -Wl,--as-needed với việc thêm libs sau-Wl,--as-needed:

gcc main.c -o main -I. -L. -Wl,--as-needed -lB -lA 

Đối với kiểm tra phụ thuộc trực tiếp bạn nên sử dụng readelf thay vì ldd vì ldd al vì vậy cho thấy các phụ thuộc gián tiếp.

$ readelf -d main | grep library 
0x0000000000000001 (NEEDED)    Shared library: [libB.so] 
0x0000000000000001 (NEEDED)    Shared library: [libc.so.6] 

ldd cũng cho thấy sự phụ thuộc gián tiếp:

$ LD_LIBRARY_PATH=. ldd ./main 
linux-vdso.so.1 (0x00007fff13717000) 
libB.so => ./libB.so (0x00007fb6738ed000) 
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6734ea000) 
libA.so => ./libA.so (0x00007fb6732e8000) 
/lib64/ld-linux-x86-64.so.2 (0x00007fb673af0000) 

Nếu bạn sử dụng cmake, bạn có thể thêm những dòng sau đây để chỉ bao gồm phụ thuộc trực tiếp:

set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}") 
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}") 
+0

Xin lỗi, tôi đã làm ví dụ của bạn là c programm thay vì C++. Vì vậy, tôi đã sử dụng trình biên dịch gcc. Nhưng nó cũng hoạt động với g ++. – user1047271

+0

Đó là thông tin thực sự hữu ích. Tôi không biết về việc sử dụng bản thân thay vì ldd. Cảm ơn rất nhiều. – Marcus

1

lựa chọn khác là để sử dụng libtool

Nếu bạn thay đổi cuộc gọi g++ thành libtool --mode=compile g++ để biên dịch mã nguồn và sau đó libtool --mode=link g++ để tạo ứng dụng tắt là libB, thì libA sẽ được liên kết tự động.

0

Đây là một bài thú vị - Tôi đã đập đầu của tôi với điều này là tốt, nhưng tôi nghĩ rằng bạn bỏ lỡ một điểm ở đây ..

Ý tưởng là như sau, phải không?

main.cpp =(depends)=> libB.so =(depends)=> libA.so 

Hãy tiếp tục xem xét rằng ..

  • Trong a.cpp (và chỉ có) bạn định nghĩa một lớp/biến, chúng ta hãy gọi nó là "SYMA"
  • Trong b.cpp (và chỉ có) bạn định nghĩa một lớp/biến, hãy gọi nó là "symB".
  • symB sử dụng SYMA
  • main.cpp sử dụng symB

Bây giờ, libB.so và libA.so đã được biên soạn như bạn mô tả ở trên. Sau đó, tùy chọn đầu tiên của bạn nên làm việc, ví dụ:

g++ main.cpp -o main -I. -L. -lB 

Tôi đoán rằng vấn đề của bạn bắt nguồn từ thực tế là

trong main.cpp bạn cũng tham khảo SYMA

Am Tôi đúng?

Nếu bạn sử dụng một biểu tượng trong mã của bạn, sau đó biểu tượng đó phải được tìm thấy trong một file .so

Toàn bộ ý tưởng của liên tham khảo thư viện chia sẻ (tức là tạo API), đó là những biểu tượng trong sâu các lớp được ẩn (nghĩ đến việc lột hành) và không được sử dụng. .. ví dụ: không tham khảo symA trong main.cpp của bạn, nhưng chỉ để symB thay thế (và để symB chỉ tham chiếu đến symA).

+0

Bạn hiểu nhầm nó. Rõ ràng bạn cần phải giải quyết các biểu tượng yêu cầu ngay lập tức. Vấn đề có liên quan đến các biểu tượng mà sự phụ thuộc của bạn có thể yêu cầu. Nó là phổ biến khi phát triển các thư viện được sử dụng bởi bên thứ 3 với người sử dụng thư viện phải cung cấp việc thực hiện các chức năng mà thư viện của bạn sử dụng. Bạn phát triển bằng cách sử dụng một bài sơ khai, không có phụ thuộc nào cả và trình tích hợp sẽ phát triển một lib để thay thế bài, sau đó liên kết mọi thứ với nhau. Trình tải sẽ phải giải quyết tất cả các phụ thuộc. Tuy nhiên, kịch bản biên dịch của bạn (và người dùng của bạn) phải tính đến nó. – Marcus

Các vấn đề liên quan