2017-12-07 2 views
2

Ich habe ein interessantes Kompilierungsproblem. Zuerst sehen Sie bitte den zu kompilierenden Code. Warum kann ich nicht mit -fPIE kompilieren, kann aber mit -fPIC?

$ ls 
Makefile main.c sub.c sub.h 
$ gcc -v 
... 
gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) 
## Makefile 
%.o: CFLAGS+=-fPIE #[2] 

main.so: main.o sub.o 
    $(CC) -shared -fPIC -o [email protected] $^ 
//main.c 
#include "sub.h" 

int main_func(void){ 
    sub_func(); 
    subsub_func(); 

    return 0; 
} 
//sub.h 
#pragma once 
void subsub_func(void); 
void sub_func(void); 
//sub.c 
#include "sub.h" 
#include <stdio.h> 
void subsub_func(void){ 
    printf("%s\n", __func__); 
} 
void sub_func(void){ 
    subsub_func();//[1] 
    printf("%s\n", __func__); 
} 

Und ich kompilieren dies und bekam einen Fehler wie unten

$ LANG=en make 
cc -fPIE -c -o main.o main.c 
cc -fPIE -c -o sub.o sub.c 
cc -shared -fPIC -o main.so main.o sub.o 
/usr/bin/ld: sub.o: relocation R_X86_64_PC32 against symbol `subsub_func' can not be used when making a shared object; recompile with -fPIC 
/usr/bin/ld: final link failed: Bad value 
collect2: error: ld returned 1 exit status 
make: *** [main.so] Error 1 

Und danach, modifizierte ich den Code (eine Zeile zu entfernen [ 1]/mit -fPIC anstelle von -PIE [2]) und dann erfolgreich kompiliert diese.

$ make #[1] 
cc -fPIE -c -o main.o main.c 
cc -fPIE -c -o sub.o sub.c 
cc -shared -fPIC -o main.so main.o sub.o 
$ make #[2] 
cc -fPIC -c -o main.o main.c 
cc -fPIC -c -o sub.o sub.c 
cc -shared -fPIC -o main.so main.o sub.o 

Warum dieses Phänomen passiert ist?

Ich habe gehört, dass das Aufrufen einer Funktion innerhalb eines Objekts über PLT erfolgt, wenn es mit -fPIC kompiliert wird, aber durch direktes Springen zu der Funktion, wenn es mit -fPIE kompiliert wird. Ich vermutete, dass der Funktionsaufruf-Mechanismus mit -fPIE von der Verschiebung Abstand nimmt. Aber ich würde gerne genaue und genaue Erklärung dafür wissen.

Würdest du mir helfen?

Vielen Dank, alle.

+1

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

+0

Danke. Ich habe das aus Japan geschrieben. – nutsman

Antwort

4

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.

Verwandte Themen