2009-06-03 17 views
10

Ich habe ein "gefährliches" Programm in C++ geschrieben, das von einem Stack-Frame zu einem anderen hin und her springt. Das Ziel besteht darin, von der untersten Ebene eines Callstacks zu einem Aufrufer zu springen, etwas zu tun und dann wieder zurückzuspringen, wobei jedes dazwischen liegende Gespräch übersprungen wird.Was macht das PIC-Register (% ebx)?

Ich mache dies, indem ich manuell die Stack-Basisadresse (Einstellung %ebp) ändere und zu einer Label-Adresse springe. Es funktioniert völlig, mit gcc und icc beide, ohne irgendeine Stapelkorruption überhaupt. Der Tag, an dem das klappte, war ein kühler Tag.

Jetzt nehme ich das gleiche Programm und überschreibe es in C, und es funktioniert nicht. Insbesondere funktioniert es nicht mit gcc v4.0.1 (Mac OS). Sobald ich zu dem neuen Stapelrahmen gesprungen bin (wobei der Stapelbasiszeiger korrekt gesetzt wurde), werden die folgenden Anweisungen ausgeführt, die kurz vor einem Anruf zu fprintf sind. Der letzte Befehl aufgeführt hier abstürzt, dereferencing NULL:

lea 0x18b8(%ebx), %eax 
mov (%eax), %eax 
mov (%eax), %eax 

Ich habe einige Debug getan, und ich habe herausgefunden, dass durch Einstellen der %ebx Register manuell, wenn ich Stack-Frames wechseln (Wert unter Verwendung von I beobachtet, bevor er die Funktion an erster Stelle), repariere ich den Fehler. Ich habe gelesen, dass dieses Register sich mit "positionsunabhängigem Code" in gcc beschäftigt.

Was ist positionsunabhängiger Code? Wie funktioniert positionsunabhängiger Code? Worauf zeigt dieses Register?

+0

Sie können setjmp/longjmp in Betracht ziehen, um diese Funktionalität zu erhalten, ohne direkt mit% ebx zu mucken. –

+2

Im Allgemeinen ja, du hast Recht. In diesem Fall muss ich in der Lage sein, zu einem Anrufer zu springen, eine andere Funktion auszuführen und dann zurück zum Angerufenen zu springen. Mit setjmp/longjmp würde der Stapel des Angerufenen von der anderen Funktion überschrieben werden. –

Antwort

6

PIC ist Code, der dynamisch geladen wird, wenn er geladen wird. Nicht-PIC-Code verfügt über Sprung- und Aufrufadressen, die zu Verbindungszeit festgelegt werden. PIC hat eine Tabelle, die auf alle Orte verweist, an denen solche Werte existieren, ähnlich wie bei einer .dll.

Wenn das Bild geladen wird, aktualisiert der Loader diese Werte dynamisch. Andere Schemata verweisen auf einen Datenwert, der eine "Basis" definiert, und die Zieladresse wird durch Ausführen von Berechnungen auf der Basis entschieden. Die Basis wird normalerweise wieder vom Loader gesetzt.

Schließlich verwenden andere Schemata verschiedene Trampoline, die zu bekannten relativen Offsets aufrufen. Die relativen Offsets enthalten Code und/oder Daten, die von einem Loader aktualisiert werden.

Es gibt verschiedene Gründe, warum verschiedene Schemata gewählt werden. Einige sind schnell, wenn sie ausgeführt werden, sind aber langsamer zu laden. Einige sind schnell zu laden, haben aber weniger Laufzeit.

13

EBX verweist auf die globale Offset-Tabelle. Siehe this reference about PIC on i386. Der Link erklärt, was PIC ist, wie EBX verwendet wird.

+0

GOT - Globale Offset-Tabelle. –

+0

Das war eine gute Ressource; das Lesen ist absolut lohnenswert. –