Die nur Codegenerierung Differenz zwischen -fPIC
-fPIE
und für den Code in dem Anruf von sub_func
zu subsub_func
gezeigt ist. Mit -fPIC
geht dieser Anruf durch den PLT; mit -fPIE
ist es ein direkter Anruf. Bei der Montage (cc -S
) Dumps, sieht das wie folgt aus:
--- sub.s.pic 2017-12-07 08:10:00.308149431 -0500
+++ sub.s.pie 2017-12-07 08:10:08.408068650 -0500
@@ -34,7 +34,7 @@ sub_func:
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
- call [email protected]
+ call subsub_func
leaq __func__.2258(%rip), %rsi
leaq .LC0(%rip), %rdi
movl $0, %eax
In unverbundenen Objektdateien, es ist eine Änderung der Verschiebungstyp:
--- sub.o.dump.pic 2017-12-07 08:13:54.197775840 -0500
+++ sub.o.dump.pie 2017-12-07 08:13:54.197775840 -0500
@@ -22,7 +22,7 @@
1f: 55 push %rbp
20: 48 89 e5 mov %rsp,%rbp
23: e8 00 00 00 00 callq 28 <sub_func+0x9>
- 24: R_X86_64_PLT32 subsub_func-0x4
+ 24: R_X86_64_PC32 subsub_func-0x4
28: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 2f <sub_func+0x10>
2b: R_X86_64_PC32 .rodata+0x14
2f: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 36 <sub_func+0x17>
Und auf dieser Architektur, wenn Sie eine gemeinsam genutzte Bibliothek verlinken Mithilfe von cc -shared
ermöglicht der Linker nicht, dass die Eingabeobjektdateien R_X86_64_PC32
Umlagerungen enthalten, die auf globale Symbole ausgerichtet sind. Dies ist der Fehler, den Sie bei der Verwendung von -fPIE
anstelle von -fPIC
beobachtet haben.
Jetzt fragen Sie sich wahrscheinlich warum direkte Anrufe innerhalb einer gemeinsam genutzten Bibliothek sind nicht erlaubt. In der Tat sind sie sind zulässig, aber nur wenn der Aufrufer nicht global ist. Wenn Sie zum Beispiel subsub_func
mit static
deklariert haben, würde das Aufrufziel vom Assembler aufgelöst werden und es würde überhaupt keine Verschiebung in der Objektdatei geben, und wenn Sie es mit __attribute__((visibility("hidden")))
deklarierten, würden Sie eine R_X86_64_PC32
Umlagerung aber den Linker erhalten würde es erlauben, weil der Angerufene nicht mehr aus der Bibliothek exportiert wird. Aber in beiden Fällen wäre subsub_func
nicht mehr von außerhalb der Bibliothek aufrufbar.
Jetzt Sie fragen sich wahrscheinlich, was es über globale Symbole bedeutet, dass Sie sie über die PLT aus einer gemeinsam genutzten Bibliothek aufrufen müssen.Dies hat mit einem Aspekt der ELF-Symbolauflösungsregeln zu tun, den Sie vielleicht überraschend finden: Jedes globale Symbol in einer gemeinsam genutzten Bibliothek kann entweder durch die ausführbare Datei oder eine frühere Bibliothek in der Verknüpfungsreihenfolge überschreiben. Konkret, wenn wir lassen Sie Ihre sub.h
und sub.c
allein aber main.c
wie diese lesen machen:
//main.c
#include "sub.h"
#include <stdio.h>
void subsub_func(void) {
printf("%s (main)\n", __func__);
}
int main(void){
sub_func();
subsub_func();
return 0;
}
so ist es nun den offiziellen ausführbarer Eintrittspunkt in ihm aber auch eine zweite Definition von subsub_func
bekam, und wir kompilieren sub.c
in eine gemeinsam benutzte Bibliothek und main.c
in eine ausführbare Datei, die es, und führen sie das ganze, wie diese
$ cc -fPIC -c sub.c -o sub.o
$ cc -c main.c -o main.o
$ cc -shared -Wl,-soname,libsub.so.1 sub.o -o libsub.so.1
$ ln -s libsub.so.1 libsub.so
$ cc main.o -o main -L. -lsub
$ LD_LIBRARY_PATH=. ./main
der Ausgang sein wird, ruft
subsub_func (main)
sub_func
subsub_func (main)
Das heißt, beide der Anruf main
-subsub_func
, und der Anruf von sub_func
, in der Bibliothek, zu subsub_func
wurden in der ausführbaren Datei auf die Definition aufgelöst. Damit das möglich ist, muss der Anruf von sub_func
durch den PLT gehen.
Sie können dieses Verhalten mit einem zusätzlichen Linker-Switch -Bsymbolic
ändern.
$ cc -shared -Wl,-soname,libsub.so.1 -Wl,-Bsymbolic sub.o -o libsub.so.1
$ LD_LIBRARY_PATH=. ./main
subsub_func
sub_func
subsub_func (main)
nun der Anruf von sub_func
wird auf die Definition in der Bibliothek aufgelöst. In diesem Fall kann mit sub.c
mit -fPIE
anstelle von -fPIC
kompiliert werden, aber ich empfehle Ihnen nicht, dies zu tun. Es gibt andere Effekte der Verwendung von -fPIE
anstelle von -fPIC
, wie z. B. Ändern, wie der Zugriff auf thread-local storage getan werden muss, und diese können nicht mit -Bsymbolic
umgehen.
FYI Ich habe die Yen-Zeichen in Ihrem Code in Backslashes geändert, weil ich stark vermutete Backslashes im Kontext beabsichtigt waren. Die Substitution von Yen-Zeichen für Backslashes ist charakteristisch für einen sehr alten Kludi (die japanische nationale Variante von ASCII), den ich für längst ausgestorben hielt, aber vielleicht nicht ganz? Schreibst du aus Japan? Wie dem auch sei, dies hat keinen Einfluss auf den Inhalt Ihrer Frage, aber es könnte andere Leser verwirrt haben, die nicht so alt sind wie ich. – zwol
Danke. Ich habe das aus Japan geschrieben. – nutsman