Когда ваше приложение связано, неразрешенные символы будут разрешаться с использованием предоставленных вами библиотек.Если вы не определили функцию, она будет неразрешенным символом во время компоновки, поэтому связанный в этом случае попытается разрешить этот символ с помощью librmath.Если один или несколько символов не могут быть разрешены, вы получите ошибку компоновщика.
Однако, если вы определите функцию в своем коде, она будет уже определена во время компоновки, поэтому не будетнеобходимо разрешить его, используя символы из внешних библиотек.
То, что вы не можете сделать, это определить один и тот же символ более одного раза в вашем приложении.
Редактировать : св другом ответе много споров, я привел практический пример.Я создал общий объект (аналог DLL в Windows), который определяет и экспортирует функцию 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();
}
Чтобы протестировать этот общий объект, я создал приложение, котороесвязан с ним:
//test.cpp
#include <iostream>
#include "lib.h"
void foo() {
std::cout << "From app\n";
}
int main() {
bar();
}
Я скомпилировал совместно используемый объект и приложение:
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
И когда я выполняю test
, задаю путь к библиотеке "."
, так что мой общий объект может быть загружен, я получаю следующий вывод:
matias@master:/tmp$ LD_LIBRARY_PATH="." ./test
Bar, calling foo
From app
Как видите, вызывается функция foo
, определенная в приложении (не общий объект).Вы можете сделать это в основном для каждого экспортируемого символа в общем объекте.
EDIT2 : я добавил еще одну экспортированную функцию в lib.h.Приложение теперь вызывает эту функцию, которая в итоге вызывает foo.Результат такой же, как и ожидалось.
EDIT3 : Хорошо, давайте углубимся.Это дамп из функции bar
:
Dump of assembler code for function bar@plt:
0x0804855c <+0>: jmp DWORD PTR ds:0x804a004
0x08048562 <+6>: push 0x8
0x08048567 <+11>: jmp 0x804853c
Если мы перейдем по адресу 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
.....
Как вы видите, он переходит в таблицу глобальных смещений.Вы можете прочитать о GOT здесь и здесь .Динамические символы (которые разрешаются во время выполнения) хранятся в этой таблице.Всякий раз, когда вы вызываете символ, который должен быть разрешен во время выполнения, вы фактически переходите к этой таблице, а затем переходите к адресу, который хранится в соответствующей записи этой таблицы.Поскольку приложение определяет foo
, GOT содержит адрес определения из test.cpp
, а не адрес в нашем общем объекте.
EDIT4 : Хорошо, последнее редактирование.Цитирование из документации:
Вам необходимо будет снабдить универсальный генератор случайных чисел
double unif_rand(void)
или использовать тот, который прилагается (и с динамической библиотекой или DLL, вам придетсяиспользуйте предоставленный (...)
В документации ясно сказано, что вы не можете предоставить собственную реализацию unif_rand
, если используете динамическую библиотеку. Поэтому я считаю, что то, на что я указална самом деле отвечает на ваш вопрос.