Forum: Compiler & IDEs Position Independent Code auf verschiedenen Plattformen


von rw (Gast)


Lesenswert?

Hallo,

wie setzt der GCC positionsunabhängigen Code auf verschiedenen 
Plattformen um? Beim AVR erscheint das relativ leicht, wenn sich die 
Sprunglänge in Grenzen hält, denn es gibt ja eine rjmp-, bzw. 
rcall-Anweisung.

Wie verhält es sich aber zum Beispiel beim MSP430? Wie ich sehe, ist 
sein R0-Register gleichzeitig der Program Counter. Außerdem hat er einen 
Adressierungsmodus der sich auf dieses R0-Register bezieht (AS=01). 
Heißt das, es gibt eine Instruktion, die als Operanden ein Offset zum PC 
akzeptiert und dann dort hin springt? Dann wäre das ja im Prinzip 
ähnlich zu rjmp beim AVR und die Erzeugung von positionsunabhängigen 
Code für den GCC ganz einfach.

Ich verstehe dann aber nicht, warum zum Beispiel dann in [1] bei der 
call-Anweisung gesagt wird, dass es keinen einfachen Weg für pc-relative 
Sprünge gibt.

Wie sieht es bei anderen Mikrocontrollerarchitekturen aus? Gibt es in 
jedem Fall einen Befehl, den man irgendwie für pc-relative Sprünge 
gebrauchen kann?

Mir geht es darum zu verstehen, wie einfach die Erzeugung von 
positionsunabhängigem Code ist und wieviel Overhead dadurch entsteht.

Vielen Dank
Richard

[1] http://mspgcc.sourceforge.net/manual/x223.html

von Wechselbalg (Gast)


Lesenswert?

Der genaue Terminus ist "relokatibel" bzw. "verschieblich". Auf Deutsch 
klingts dann ein bischen holprig. Das Fremdwort ist mir lieber.

Aber gut.

Ob ein Code relokatibel ist oder nicht, hängt davon ab, ob es 
Anweisungen gibt, die den Programmfluss so beeinflussen, das der Program 
Counter "relativ zum momentanen Stand" verändert wird. Das Gegenteil 
wäre, das der PC mit absoluten Adressen geladen wird. (Das die mit einer 
MMU dann wieder irgendwie relativ sind, ist eine andere Sache).

>Wie sieht es bei anderen Mikrocontrollerarchitekturen aus? Gibt es in
>jedem Fall einen Befehl, den man irgendwie für pc-relative Sprünge
>gebrauchen kann?
Da gibt es keine Regel. Manche haben es, manche nicht. Einen gewissen 
Vorzug hat das, wenn vornehmlich Tabellen abgearbeitet werden. Z.B. 
Basic-Token vergleiche oder Netzwerkadressen. Da kann man sicher 
Beispiele finden.

>Mir geht es darum zu verstehen, wie einfach die Erzeugung von
>positionsunabhängigem Code ist und wieviel Overhead dadurch entsteht.

Da wäre es gut zu wissen, zu welchem Zweck Du das verstehen willst.

Für einen Compilerbauer ist, der Unterschied: Oft gibt es Begrenzungen 
in der relativen Sprungweite. Das muss man berücksichtigen.

Als Benutzer hat man oft nicht die Wahl, ob ein Compiler 
ausschliesslich, bevorzugt, garnicht oder nur an Feiertagen keine, 
immer, wenn es geht usw. relative Sprünge benutzt.


Was Deinen Link betrifft, finde ich die Beschreibung widersprüchlich. 
Ich kenne mich mit MSP aber auch nicht aus.

Da steht was von "PC-relative" Adressierungsart: "the PC-relative 
addressing mode fetches a word and uses it as an absolute address. "
Das ist eben keine PC-relative Adressierung. Eben deswegen kann damit 
nicht relative Sprünge ausführen.

>Außerdem hat er einen
>Adressierungsmodus der sich auf dieses R0-Register bezieht (AS=01)

Der Call Befehl um den es Dir geht, gehört in die erste Zeile der ersten 
Tabelle. Daher ist das AS-Flag hier garnicht gefragt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

rw schrieb:
> Hallo,
>
> wie setzt der GCC positionsunabhängigen Code auf verschiedenen
> Plattformen um?

Das ist plattformabhängig und im jeweiligen Backend implementiert -- 
oder auch nicht. Der Schalter zur Aktivierung ist -fpic bzw. -fPIC-

> Beim AVR erscheint das relativ leicht, wenn sich die
> Sprunglänge in Grenzen hält, denn es gibt ja eine rjmp-, bzw.
> rcall-Anweisung.

avr-gcc unterstützt weder positionsunabhängigen Code noch 
positionsunabhängige Daten. Beispiel:
1
void bar (void);
2
void * foo (void)
3
{
4
    return bar;
5
}

Der Returnwert von foo soll für einen indirekten Funktionsaufruf 
weiterverwendet werden. In foo wird aber die absolute Adresse von bar 
genommen, so daß der Code nicht relokatibel ist.

> Mir geht es darum zu verstehen, wie einfach die Erzeugung von
> positionsunabhängigem Code ist und wieviel Overhead dadurch entsteht.

Am einfachsten schaust du dir einen foo-gcc an, in dessen Toolchain es 
auch einen dynamischen Linker gibt. Das sind typischerweise solche, die 
für Controller auf welchen auch Linux läuft Code erzeugen (i386, ARM, 
AVR32, IA64, ...).

Die Adressierung ist aufwändiger und erfolgt zB über die GOT (global 
offset table)

Such einfach in den GCC-Quellen nach folgenden Makros:
    http://gcc.gnu.org/onlinedocs/gccint/PIC.html#PIC
bzw. nach flag_pic unterhalb von ./gcc/config

Dann hast du gleich die Targets die PIC implementieren und den Code dazu 
:-)

Johann

von Wechselbalg (Gast)


Lesenswert?

Hmm. Scheint, das ich da zum Teil was Falsches erklärt, zum Teil das 
Thema nicht getroffen habe. Bin wohl nicht auf dem aktuellen Stand.

von rw (Gast)


Lesenswert?

Hallo,

danke für die Informationen und für den Link. Ich bin allerdings ein 
wenig ob der Begriffsverwendungen in den vorangegangenen Beiträgen 
verwirrt.

@wechselbalg:r
Ernn ich mich nicht irre, sind doch alle Objektdateien relozierbar wenn 
sie frisch kompiliert wurden. Erst der Linker löst die in der 
Relokationstabellen (bei ELF) angegebenen Adressen in der .text-Sektion 
der Objektdateien auf. So habe ich zumindest die Dokumentation von ELF 
verstanden. Mir geht es hier wirklich um positionsunabhängigen Code, 
also Code, der so gebunden wird, dass er von beliebiger Adresse im ROM 
ausgeführt werden kann. Dieser braucht nicht mehr reloziert zu werden.

@Johann:
Danke für den Link. Ich werde in der gcc-Doku wohl mehrmals lesen 
müssen, um die Fülle von Informationen zu erfassen. Dynamisches Linken 
für 8Bit-Mikrocontroller wäre wohl etwas hoch gestochen, auch wenn es 
anscheinend verrückte Menschen gibt, die so etwas mehr oder weniger 
realisiert zu haben scheinen (Adam Dunkels bei Contiki).

Wenn ich das Konzept der Global Offset Tables richtig verstanden habe, 
finden sie nur Anwendung in Shared Objects, also Code-Teilen die 
gleichzeitig von mehreren "Prozessen" benutzt werden.  Während des 
Programmflusses ist lediglich der Abstand vom Modulbeginn zur gerade 
verwendeten GOT (für jeden Ausführungskontext existiert eine GOT) in 
irgend einem Register hinterlegt.
Für Funktionsaufrufe wird dann im Weiteren eine Procedure Lookup Table 
verwendet (PLT), die auch die Adressauflösung zur Laufzeit vornimmt. 
Habe ich das so richtig verstanden?

Wenn ja, scheint es mir, als wären hardwareunterstütze PC-relative 
Sprünge gar nicht nötig, weil die Sprungadresse immer zur Laufzeit 
berechnet werden kann, nämlich folgendermaßen:
Das PIC-Modul kennt zur Laufzeit seine eigene Startadresse im 
Programmspeicher und den Abstand zur gerade gültigen GOT. Außerdem wurde 
durch den Linker festgelegt, unter welchem Index in der PLT die 
gewünschte Funktion eingetragen ist. Damit ließe sich die absolute 
Adresse dieses Eintrags auch zur Laufzeit berechnen, dort hin springen 
und dann von dort aus weiterspringen (oder die dort gespeicherte Adresse 
laden und dann springen). Das würde jetzt aber bedeuten, dass auch 
modulinterne Sprünge immer mindestens eine zusätzliche Addition 
erfordern, bevor gesprungen werden kann. Das wäre also ein erheblicher 
Aufwand, aber würde erklären, warum im Zusammenhang mit ELF Shared 
Objects immer von einem signifikanten Laufzeitverlust die Rede ist.

Wie ich mir das jetzt ausgemalt habe, steigt der Aufwand also mindestens 
um eine Addition und eventuell ein paar Load&Store-Instruktionen. Das 
klingt mir irgendwie zu einfach. Möglicherweise muss ich noch einmal ELF 
Shared Objects studieren.

Ich würde mich freuen, wenn jemand einschätzen könnte, wie nah der eben 
geschriebene Erguss der Realität steht.

Herzlichen Dank
Richard

von R. W. (rickw)


Lesenswert?

Spätestens nach dem letzten Beitrag erkenne ich den Segen, der in einer 
Anmeldung liegt ;-) -> Der Edit-Button.

von Иван S. (ivan)


Lesenswert?

Hallo rw,

rw schrieb:
> Wie verhält es sich aber zum Beispiel beim MSP430? Wie ich sehe, ist
> sein R0-Register gleichzeitig der Program Counter. Außerdem hat er einen
> Adressierungsmodus der sich auf dieses R0-Register bezieht (AS=01).
> Heißt das, es gibt eine Instruktion, die als Operanden ein Offset zum PC
> akzeptiert und dann dort hin springt?

Zumindest das User Guide nennt solch einen Adressierungsmodus:
"Symbolic mode: ADDR (PC + X) points to the operand. X is stored in the 
next word. Indexed mode X(PC) is used." Beim Symbolic Mode handelt es 
sich also um einen Spezialfall des Indexed Mode. Weiter aus der 
Beschreibung zu bedingten Sprüngen: "Conditional jumps support program 
branching relative to the PC and do not affect the status bits. The 
possible jump range is from −511 to +512 words relative to the PC value 
at the jump instruction. The 10-bit program-counter offset is treated as 
a signed 10-bit value that is doubled and added to the program counter".

> Dann wäre das ja im Prinzip ähnlich zu rjmp beim AVR und die Erzeugung
> von positionsunabhängigen Code für den GCC ganz einfach.

Dazu kann ich nichts sagen, da ich weder die Architektur AVR noch die 
Internas des GCCs kenne.

> Ich verstehe dann aber nicht, warum zum Beispiel dann in [1] bei der
> call-Anweisung gesagt wird, dass es keinen einfachen Weg für pc-relative
> Sprünge gibt.

Steht doch dort: "Call: [...]the PC-relative addressing mode fetches a 
word and uses it as an absolute address". Was der 430er also beim 
Springen kann, kann er in Verbindung mit CALL einfach nicht.
Call funktioniert beim MSP430 nämlich so:
CALL dst: SP − 2 → SP, PC+2 → @SP, dst → PC
JMP hingegen so: PC + 2 × offset −> PC

> Wie sieht es bei anderen Mikrocontrollerarchitekturen aus?

Von Architektur zu Architektur verschieden.

> Gibt es in jedem Fall einen Befehl, den man irgendwie für pc-relative
> Sprünge gebrauchen kann?

Nein.

hth, Iwan

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.