Khi bạn đăng ký được liên kết, các biểu tượng chưa được giải quyết sẽ được giải quyết bằng cách sử dụng thư viện bạn cung cấp. Nếu bạn không định nghĩa một hàm, nó sẽ là một biểu tượng chưa được giải quyết trong quá trình liên kết, do đó, liên kết sẽ cố gắng giải quyết biểu tượng đó bằng cách sử dụng librmath, trong trường hợp này. Nếu không thể giải quyết một hoặc nhiều biểu tượng, bạn sẽ gặp lỗi liên kết.
Tuy nhiên, nếu bạn xác định hàm trong mã của mình, nó sẽ được xác định trong quá trình liên kết, vì vậy sẽ không cần phải giải quyết nó bằng các biểu tượng từ thư viện bên ngoài.
Điều bạn không thể làm là xác định cùng một biểu tượng nhiều lần trong ứng dụng của bạn.
Chỉnh sửa: Vì có rất nhiều cuộc tranh luận trong một câu trả lời khác, tôi đã thực hiện một ví dụ thực tế. Tôi đã tạo một đối tượng chia sẻ (tương tự như một DLL trong cửa sổ), trong đó xác định và xuất khẩu một hàm foo
:
//lib.h
extern "C" {
void foo();
void bar();
};
//lib.cpp
#include <iostream>
#include "lib.h"
void foo() {
std::cout << "From lib\n";
}
void bar() {
std::cout << "Bar, calling foo\n";
foo();
}
Để kiểm tra đối tượng chia sẻ này, tôi đã tạo một ứng dụng được liên kết với nó :
//test.cpp
#include <iostream>
#include "lib.h"
void foo() {
std::cout << "From app\n";
}
int main() {
bar();
}
tôi đã biên soạn cả hai, các đối tượng chia sẻ và ứng dụng:
g++ lib.cpp -o libtest.so -Wall -fPIC -shared -Wl,--export-dynamic -Wl,-soname,libtest.so -Wl,-z,defs
g++ test.cpp -o test -L. -ltest
và khi tôi thực hiện test
, thiết lập các đường dẫn thư viện để 01.239., vì vậy đối tượng chia sẻ của tôi có thể được nạp, tôi nhận được kết quả này:
[email protected]:/tmp$ LD_LIBRARY_PATH="." ./test
Bar, calling foo
From app
Như bạn có thể thấy, foo
chức năng được xác định trong ứng dụng (không phải là đối tượng chia sẻ) được gọi. Bạn về cơ bản có thể làm điều này cho mỗi biểu tượng xuất khẩu trong một đối tượng được chia sẻ.
EDIT2: tôi đã thêm một hàm được xuất khác trong lib.h. Ứng dụng bây giờ gọi hàm này, kết thúc bằng cách gọi hàm foo. Kết quả là như nhau, như mong đợi.
EDIT3: Ok, chúng ta hãy đi sâu hơn.Đây là bãi từ chức năng bar
:
Dump of assembler code for function [email protected]:
0x0804855c <+0>: jmp DWORD PTR ds:0x804a004
0x08048562 <+6>: push 0x8
0x08048567 <+11>: jmp 0x804853c
Nếu chúng tôi đi đến quyết 0x804a004
:
Dump of assembler code for function _GLOBAL_OFFSET_TABLE_:
0x08049ff4 <+0>: or BYTE PTR [edi+0x804],bl
0x08049ffa <+6>: add BYTE PTR [eax],al
0x08049ffc <+8>: add BYTE PTR [eax],al
0x08049ffe <+10>: add BYTE PTR [eax],al
.....
Như bạn thấy, nó nhảy đến Bảng Global Offset. Bạn có thể đọc về GOT here và here. Ký hiệu động (được giải quyết trong thời gian chạy) được lưu trữ trong bảng này. Bất cứ khi nào bạn gọi một biểu tượng cần được giải quyết khi chạy, bạn thực sự chuyển sang bảng này, và sau đó chuyển đến địa chỉ được lưu trữ trong mục tương ứng của bảng này. Kể từ khi ứng dụng xác định foo
, GOT chứa địa chỉ của định nghĩa từ test.cpp
, không phải là một trong đối tượng được chia sẻ của chúng tôi.
EDIT4: OK, chỉnh sửa cuối cùng. Trích dẫn từ tài liệu:
Bạn sẽ cần phải cung cấp đồng phục bộ tạo số ngẫu nhiên
double unif_rand(void)
hoặc sử dụng một trong những cung cấp (và với một thư viện động hoặc DLL bạn sẽ phải sử dụng một trong những cung cấp (...)
Tài liệu rõ ràng nói rằng bạn không thể cung cấp cho mình việc triển khai unif_rand
nếu bạn đang sử dụng thư viện động. Do đó, tôi tin rằng những gì tôi đã chỉ ra, thực sự là ans đặt câu hỏi của bạn.
Dự đoán của tôi là chức năng def nằm trong #ifdef để nếu bạn định nghĩa nó, thì máy của bạn được tìm thấy bởi trình liên kết đầu tiên, vì vậy cái đã có ở đó sẽ bị bỏ qua. – baash05