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
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.
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
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.
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
Spätestens nach dem letzten Beitrag erkenne ich den Segen, der in einer Anmeldung liegt ;-) -> Der Edit-Button.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.