Wenn Sie eine Anwendung verknüpfen, werden nicht aufgelöste Symbole mithilfe der von Ihnen bereitgestellten Bibliotheken aufgelöst. Wenn Sie eine Funktion nicht definieren, wird sie während des Linkens ein ungelöstes Symbol sein. Daher versucht der Link in diesem Fall, das Symbol mithilfe von librmath aufzulösen. Wenn ein oder mehrere Symbole nicht aufgelöst werden können, erhalten Sie einen Linker-Fehler.
Wenn Sie jedoch die Funktion in Ihrem Code definieren, wird sie bereits während des Linkens definiert, so dass Sie sie nicht mit Symbolen aus externen Bibliotheken auflösen müssen.
Was Sie nicht tun können, ist das gleiche Symbol mehr als einmal in Ihrer Anwendung zu definieren.
Bearbeiten: Da gibt es eine Menge Debatte in einer anderen Antwort, habe ich ein praktisches Beispiel gemacht. Ich habe ein gemeinsam genutztes Objekt (ähnlich einer DLL in Windows) erstellt, die definiert und exportiert eine Funktion 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();
}
Um dieses gemeinsame Objekt zu testen, habe ich eine Anwendung erstellt, die damit verbunden ist, :
//test.cpp
#include <iostream>
#include "lib.h"
void foo() {
std::cout << "From app\n";
}
int main() {
bar();
}
ich habe beide zusammengestellt, das gemeinsame Ziel und die Anwendung:
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
und wenn ich test
ausführen, den Bibliothekspfad zu 01.239.140 Einstellung, so mein gemeinsames Objekt geladen werden kann, erhalte ich diese Ausgabe:
[email protected]:/tmp$ LD_LIBRARY_PATH="." ./test
Bar, calling foo
From app
Wie Sie sehen können, die foo
Funktion in der Anwendung definiert (nicht das Shared Object) aufgerufen wird. Sie können dies grundsätzlich für jedes exportierte Symbol in einem gemeinsamen Objekt tun.
EDIT2: Ich habe eine andere exportierte Funktion in lib.h. Die Anwendung ruft nun diese Funktion auf, die foo aufruft. Das Ergebnis ist wie erwartet dasselbe.
EDIT3: Ok, lass uns tiefer gehen.Dies ist die Müllhalde von Funktion bar
:
Dump of assembler code for function [email protected]:
0x0804855c <+0>: jmp DWORD PTR ds:0x804a004
0x08048562 <+6>: push 0x8
0x08048567 <+11>: jmp 0x804853c
Wenn wir gehen 0x804a004
Adresse:
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
.....
Wie Sie sehen können, ist es an den Global Offset Tabelle springen. Sie können über die GOT here und here lesen. Dynamische Symbole (die zur Laufzeit aufgelöst werden) werden in dieser Tabelle gespeichert. Wenn Sie ein Symbol aufrufen, das zur Laufzeit aufgelöst werden soll, springen Sie tatsächlich zu dieser Tabelle und springen dann zu der Adresse, die im entsprechenden Eintrag der Tabelle gespeichert ist. Da die Anwendung foo
definiert, enthält der GOT die Adresse der Definition von test.cpp
, nicht die in unserem gemeinsamen Objekt.
EDIT4: Okay, letzte Änderung. Zitiert aus der Dokumentation:
Sie müssen dem einheitlichen Zufallszahlengenerator
double unif_rand(void)
oder verwenden Sie die mitgelieferten (und mit einer dynamischen Bibliothek oder DLL angeben, den Sie haben die man verwenden geliefert (...)
die Dokumentation sagt klar, dass Sie nicht, dass Sie Implementierung von unif_rand
besitzen zur Verfügung stellen kann, wenn Sie die dynamische Bibliothek verwenden. Deshalb glaube ich, dass das, was ich darauf hinwies, tatsächlich ans Wers deine Frage.
Meine Vermutung wäre, dass die Funktion def in einer #ifdef ist, so dass, wenn Sie es definieren, Ihre zuerst durch den Linker zuerst gefunden wird, so dass die bereits dort würde ignoriert werden. – baash05