Forum: Compiler & IDEs Relozierbarer Code mit AVR-GCC möglich?


von Joerg W. (joergwolfram)


Lesenswert?

Hallo,

für das Nachfolgeprojekt von meinem ChipBasic wollte ich die Möglichkeit 
schaffen, dass man auch Anwenderprogramme für den AVR in C schreiben 
kann. Allerdings bin ich mir nicht sicher, ob das überhaupt möglich ist.
Denn dazu müsste man dem Compiler bestimmte Dinge vorgeben bzw. 
vorgaukeln. Das (Betriebs-) System selbst ist in ASM geschrieben und 
muss die Programme (mit derzeit maximal 9K Größe) an 8 verfügbare 
Programmplätze laden können. Ich habe zwar einige Erfahrung in C (GCC), 
aber halt nur auf dem PC.

1. Der Code muss relozierbar sein. Dazu habe ich bereits einen 
Relocator, der beim Speichern des Programmes in das Flash die Befehle 
CALL,JMP und LPM der entsprechenden Startadresse anpasst. Bei 
Tabellenzugriffen über das Z Register sehe ich dabei aber keine Chance. 
Es gibt zwar eine API-Funktion, die die Startadresse des Programmes 
ermittelt und im Z-Register zurückgibt aber ich weiss nicht ob sich die 
so nutzen lässt.

2. Der nutzbare RAM-Bereich muss begrenzbar sein, außerdem darf das 
Programm keinen eigenen Stack benutzen. Letzteres könne ich vielleicht 
realisieren, indem ich vom Relocator Direktzugriffe auf den Stackpointer
z.B. in NOP umwandle.

3. Es gibt mind. 6 Register (im Bereich R0-R15), die nicht genutzt 
werden dürfen da die Video-Engine sie benötigt.

4. Man müsste zumindest eine neue STDIO schreiben, damit die Ausgaben 
auch auf dem Bildschirm landen.

Ist so etwas überhaupt realisierbar ohne den Compiler zu modifizieren?

Jörg

von Hc Z. (mizch)


Lesenswert?

Joerg Wolfram schrieb:
> Ist so etwas überhaupt realisierbar ohne den Compiler zu modifizieren?

Nein.

Ein Beispiel:  Der Compiler bringt lokale Variable im Stack unter.  Dazu 
ist es nötig, dass er in den Stackpointer schreibt.

Aber auch andere Bedingungen sind nicht erfüllbar.

von Detlev T. (detlevt)


Lesenswert?

Hallo Joerg,

so ganz will mir nicht einleuchten, was das ganze bringen soll. Wäre es 
nicht einfacher, die Videoausgabe mit einer C-Schnittstelle zu versehen 
und eine Library zu erstellen, die der Anwender zu seinem Code linken 
kann?

Ansonsten ruft deine Anwendung nach einem RTOS-System, das u.a. 
einzelnen Prozessen eigene Stacks zuordnet. Das Relozieren könnte man 
dem gcc-Linker überlassen. Da musst du einmal schauen, welche 
Möglichkeiten einem Skripte ermöglichen und das Ganze in eine 
entsprechende toolchain integrieren. Vielleicht kann man z.B. den Linker 
dazu bringen, den Code für alle 8 Programmplätze zu erzeugen (und auch 
das RAM entsprechend aufzuteilen) und dein Loader pickt sich dann das 
passende Teil-Stück heraus.

Um das Sichern der Register für deine Videofunktion kommst du aber nicht 
herum, wenn du den Compiler nicht komplett neu schreiben willst.

Gruß, DetlevT

von Joerg W. (joergwolfram)


Lesenswert?

Danke erstmal für die schnelle Antwort.

Den Stack kann das Programm schon benutzen, aber eben halt "blind", nur 
mit PUSH/POP/CALL/RET.

Aber letztendlich muss es auch nicht gehen, es war nur so eine Idee...

Gruß Jörg

von Detlev T. (detlevt)


Lesenswert?

Hallo Joerg,

mal so eine Idee: Es muss ja nicht C und avr-gcc sein. Wenn du eine Art 
Programmier-Lern-System machen willst, kannst du mit deiner Erfahrung ja 
vielleicht einen Compiler für eine (einfache) Sprache selbst schreiben, 
der am Ende Assembler ausspuckt. Das wäre so eine Art Preprozessor, wo 
du die Umsetzung selbst unter Kontrolle hast. RJMP und RCALL muss man 
nicht relozieren und an die aktuelle Adresse kommt man z.B. via rcall 
und POP der Rücksprungadresse vom Stack.

Oder es kommt so eine Art p-Code heraus, den dein AVR interpretiert. 
Dann kann das Programm auch im RAM stehen.

Gruß, DetlevT

von Joerg W. (joergwolfram)


Lesenswert?

Hallo Detlev,

wenn ich aus dem Code eine dazulinkbare Library machen würde, würde ich 
vielleicht anderen einen Gefallen tun, aber mir selbst nur Steine in den 
Weg legen da ich selbst das System nicht nochmal in C nachprogrammieren 
möchte und auch nicht vorhabe die darauf laufenden Anwendungen in C zu 
programmieren.
Also müssen sich die Anwendungen nach dem System und seinen 
Schnittstellen richten und nicht umgekehrt. Es ging mir einfach darum, 
ob neben BASIC und ASM auch C zur Erstellung von Anwenderprogrammen 
möglich ist. Und das scheint halt nicht zu gehen.

Gruß Jörg

von Joerg W. (joergwolfram)


Lesenswert?

Nein, ein Programmierlern-System wird es nicht, eher eine Art "AVR-PAD". 
Aber die Idee mit dem P-Code ist gut, vielleich kann man ja aus C-Code 
mit einer Art "Compiler" den Bytecode erstellen, mit dem auch die 
Runtime des BASIC-"Interpreters" läuft.

Gruß Jörg

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joerg Wolfram schrieb:

> Den Stack kann das Programm schon benutzen, aber eben halt "blind", nur
> mit PUSH/POP/CALL/RET.

Genügt nicht, größere Datenmengen auf dem Stack benötigen eine
Stackpointermanipulation.  Alles, was du willst ist doch, dass
die Applikation sich den initialen Stackpointerwert nicht setzen
darf, das sollte möglich sein (durch Modifizieren des Startup-
Codes).

Im Prinzip müsstest du deine Applikation zur dynamischen Bibliothek
linken und dann einen Lader schreiben, der die verbliebenen Dinge
zur Laufzeit aktualisiert.  Das wird aber nicht ganz trivial sein,
außerdem hat für den AVR-GCC ganz sicher noch keiner die Optionen
-fpic und -fPIC tatsächlich implementiert.

Du bist dir aber auch im Klaren, dass du für die Applikationen
jedesmal aus dem Bootloaderbereich heraus den Flash-Inhalt beschreiben
musst, ja?

von (prx) A. K. (prx)


Lesenswert?

Joerg Wolfram schrieb:

> 1. Der Code muss relozierbar sein.

Möglicherweise ist das ELF-File relozierbar, indem die nötigen 
Relocations Record da noch drin sind - oder es lässt sich vielleicht 
beim Linken so einrichten. In diesem Fall würde es reichen, einen 
relozierenden Loader mit dem ELF-File an Stelle des Hex/Bin-Files zu 
füttern.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> oder es lässt sich vielleicht
> beim Linken so einrichten.

Genau das wäre der Sinn einer dynamischen Bibliothek (shared object).

Aber wie schon geschrieben, der Compiler müsste dafür position
independent code generieren können, und das wird für den AVR keiner
wirklich bis zu Ende implementiert haben.  Die Optionen -fpic und
-fPIC existieren zwar, aber es wird niemand garantieren können, dass
der sich ergebende Code tatsächlich zur Laufzeit relozierbar ist
noch dass der Compiler nicht mit einem internal compiler error
aussteigt (weil halt notwendige Teile vielleicht gar nicht implementiert
sind).

von (prx) A. K. (prx)


Lesenswert?

Jörg Wunsch schrieb:

> Aber wie schon geschrieben, der Compiler müsste dafür position
> independent code generieren können

Nicht unbedingt. Solcher -fPIC-Code ist ohne Modifikation verschiebbar.

Es gibt aber auch EXE-Formate, die weiterhin Relokationsinformation 
enthalten. Damit wird es dem OS-Loader ermöglicht, den Code an jede 
beliebige Adresse zu laden, indem er in anhand dieser Relokationen Code 
und Daten anpasst. Solcher Code wird für eine präferierte Adresse 
gelinkt, kann aber auch mit leichtem Zusatzaufwand zum Ladezeitpunkt an 
jede andere geladen werden. Eine spezielle Codeerzeugung ist dabei nicht 
erforderlich. Ich glaube Windows arbeitet so, jedenfalls aber tat dies 
OS/2.

von Andreas F. (aferber)


Lesenswert?

Jörg Wunsch schrieb:
> Aber wie schon geschrieben, der Compiler müsste dafür position
> independent code generieren können, und das wird für den AVR keiner
> wirklich bis zu Ende implementiert haben.

Nein, muss er für diese Anwendung nicht.

Position Independent Code ist bei gängigen "grossen" Betriebssystemen 
nötig, damit alle inneren Referenzen in der Library (also z.B. Jumps 
bei Verzweigungen, aber noch vieles, vieles mehr) bereits beim Linken 
der Shared Library aufgelöst werden können (bzw. schon beim Assemblieren 
eine Konstante ergeben), und beim Laden der Lib nur noch Referenzen auf 
andere (externe) Shared Libraries reloziert werden müssen.

Dass man das so macht hat aber nichts damit zu tun, dass es anders 
garnicht funktionieren würde. Natürlich kann man grundsätzlich auch 
alle Relokationen auf den Zeitpunkt des Ladens der Lib verschieben.

Der Grund warum man stattdessen Position Independent Code verwendet 
(trotzdem er auf manchen Architekturen mit leichten Performanceeinbußen 
verbunden ist) ist, dass innere Referenzen so zahlreich und dicht im 
Code vorkommen, dass so gut wie keine Pages in der Lib mehr ohne 
Relokationen auskämen. Letzteres würde dazu führen, dass von dem 
"Shared" zumindest im Arbeitsspeicher nicht mehr viel übrigbleiben würde 
(da der Code durch die Relokation geändert wird, muss von der Page eine 
Kopie für den einzelnen Prozess angelegt werden), was natürlich dem 
ganzen Zweck von Shared Libaries zuwiderliefe.

Für die oben genannten Anforderungen ist das aber alles irrelevant, und 
deshalb wird kein PIC benötigt. Es müssen dann eben alle Relokationen 
beim Laden statt beim Linken endgültig durchgeführt werden. Evtl. kann 
man das noch dahingehend verfeinern (damit der Loader auf dem AVR 
kleiner wird), dass die eigentliche Relokation (z.B. mit Basisadresse 0) 
bereits vom Linker durchgeführt wird, aber (durch ein selbst zu 
schreibendes Tool) zusätzlich noch eine Tabelle erstellt wird, in der 
festgehalten wird, an welchen Stellen noch die Basisadresse zu addieren 
ist. Dann braucht sich der Loader zumindest nicht mehr mit dem Auflösen 
von Symbolen rumschlagen, sondern muss nur noch die Tabelle einmal 
durchgehen und überall den Offset addieren. Die relative Lage der 
Symbole untereinander kann sich ja nicht mehr ändern, es wird nur alles 
en bloc im Adressraum verschoben.

BTW kamen Shared Libraries im a.out-Format damals unter Linux auch ohne 
PIC aus. Stattdessen wurde dort die Ladeadresse der Lib bereits beim 
Linken der Lib endgültig festgelegt. Da der von der Lib verwendete 
Adressraum nicht mit anderen Libraries überlappen durfte, gab es eine 
zentrale Registry, über die man Adressraum für seine Library 
registrieren musste.

Letzteres würde quasi dem Vorschlag entsprechen, für jede mögliche 
Position eine eigene Kopie des Codes zu linken, und dann im Loader das 
passende Stück herauszupicken.

Andreas

von (prx) A. K. (prx)


Lesenswert?

Andreas Ferber schrieb:

> (da der Code durch die Relokation geändert wird, muss von der Page eine
> Kopie für den einzelnen Prozess angelegt werden), was natürlich dem
> ganzen Zweck von Shared Libaries zuwiderliefe.

Alternativ wird der Adressraum einer bestimmten DLL/SharedLib bei ersten 
Laden global vergeben. Dann ist Sharing problemlos, aber dieser Teil des 
Adressraum ist dann auch bei Prozessen blockiert, die sie garnicht 
verwenden.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas Ferber schrieb:

> BTW kamen Shared Libraries im a.out-Format damals unter Linux auch ohne
> PIC aus.

Ja, aber das war ein ziemlicher Horror.  SunOS 4.x hat mit a.out
vorgemacht, dass man das auch besser machen kann. ;-)  Aber das
geht jetzt hier vom Thema weg.

Ich denke, dass das Ganze für einen AVR ohnehin etwas, naja,
oversized ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joerg Wolfram schrieb:
> Hallo,
>
> für das Nachfolgeprojekt von meinem ChipBasic wollte ich die Möglichkeit
> schaffen, dass man auch Anwenderprogramme für den AVR in C schreiben
> kann. Allerdings bin ich mir nicht sicher, ob das überhaupt möglich ist.

Was ist ein "Programm"? EIn fertig erzeugtes etwas? Routinen, gegeb die 
gelinkt wird und die innerhalb eines bestimmten Kontextes ausführbar 
sein sollen? ...?

> Denn dazu müsste man dem Compiler bestimmte Dinge vorgeben bzw.
> vorgaukeln. Das (Betriebs-) System selbst ist in ASM geschrieben und
> muss die Programme (mit derzeit maximal 9K Größe) an 8 verfügbare
> Programmplätze laden können. Ich habe zwar einige Erfahrung in C (GCC),
> aber halt nur auf dem PC.
>
> 1. Der Code muss relozierbar sein. Dazu habe ich bereits einen
> Relocator, der beim Speichern des Programmes in das Flash die Befehle
> CALL,JMP und LPM der entsprechenden Startadresse anpasst.

Das ist zu kurz gedacht, weil du so niemals alle Informationen hast, um 
die Ersetzungen zu machen. Bei einem LDI/SUBI/SBCI zB musst du wissen, 
ob er eine Zahl lädt/subtrahiert oder eine Adresse, und ob die Adresse 
eine RAM-, eine Flash- oder eine EEPROM-Adresse ist. Diese Informationen 
stehen im Objekt. Du kannst sie zB mit
1
> foo-objdump -rR file

anzeigen lassen. Im Endeffekt musst du die Aufgabe des Lokators 
übernehmen, und je nach RELOC (R_AVR_16, R_AVR_LO8_LDI, R_AVR_HI8_LDI, 
R_AVR_LO8_LDI_NEG, R_AVR_HI8_LDI_NEG, R_AVR_CALL, R_AVR_16_PM, ...) die 
richtige Ersetzung vornehmen. Weiters ist zu beachten, daß das Symbol, 
auf den sich der RELOC bezieht, zu unterschiedlichen Sections gehören 
kann, zb .text, .data, .progmem.data etc. Falls das Programm explizite 
Casts enthält, etwa weil es auf fest codierte EEPROM oder Flash-Adressen 
zugreift, ist das nicht erkennbar, weil dafür keine RELOCs erzeugt 
werden.

> Bei
> Tabellenzugriffen über das Z Register sehe ich dabei aber keine Chance.
> Es gibt zwar eine API-Funktion, die die Startadresse des Programmes
> ermittelt und im Z-Register zurückgibt aber ich weiss nicht ob sich die
> so nutzen lässt.

> 2. Der nutzbare RAM-Bereich muss begrenzbar sein, außerdem darf das
> Programm keinen eigenen Stack benutzen. Letzteres könne ich vielleicht
> realisieren, indem ich vom Relocator Direktzugriffe auf den Stackpointer
> z.B. in NOP umwandle.

Das Programm wird Stack benutzen; einfach schon deshalb, weil es Befehle 
wie CALL unr RET enthält. Falls es darum geht, daß keine Daten auf dem 
Stack angelegt werden bzw. über den Stack übergeben werden wird's 
schwierig. Weil du unter die stdio erwähnst: Funktionen mit variabler 
Argumentzahl übergeben ihre Parameter immer auf dem Stack. Zumindest 
im avr-gcc. Sobald die Adresse einer lokalen Variablen genommen wird, zu 
viele lokale Variablen gebraucht werden, deren Größe ungünstig ist (zB 
char[3]), solche "ungünstigen" Objekte als Funktions- oder 
Rückgabeparameter auftauchen oder Argumente bzw. Rückgabepparameter eine 
gewisse Anzahl übersteigen, wird ebenfalls Stack fällig. Stack wird auf 
jeden Fall gebraucht, wenn eine Funktion call-saved GPRs verwendet.

Zwar lassen sich auch recht komplexe Anwendungen "ohne Stack" schreiben 
(d.h. ohne daß ein Framepointer gebraucht wird) schreiben, aber dazu ist 
einiges an Erfahrung bzw. Arbeitsweise von avr-gcc nötig.

> 3. Es gibt mind. 6 Register (im Bereich R0-R15), die nicht genutzt
> werden dürfen da die Video-Engine sie benötigt.

Das kommt sich u.U mit dem avr-gcc ABI quer. R0 und R1 können bzw. 
dürfen nicht ausgeschlossen werden. Für Rn, n>=2 gilt: je kleiner die 
Register-Nummer, desto besser. Beim Übersetzen kann das GPR zwar mit 
-ffixed-n ausgeschlossen werden, allerdings ist das nicht ABI-kompatibel 
mit den Bibliotheken bzw. die Libs müssen mit ebendiesen Schaltern 
übersetzt werden. Zudem ist zu bedenken, daß, gcc um so mehr Stack 
verwenden wird, je mehr GPRs im entzogen werden.

von Joerg W. (joergwolfram)


Lesenswert?

Vielen Dank für die Antworten und die interessante Diskussion.

Ich versuche mal, ein bisschen Resümee für mich zu ziehen:

Kurz noch zum Ziel. Das Ganze soll eine recht universelle mobile 
Plattform werden und einige meiner nicht veröffentlichten Projekte zu 
einem Open Source Projekt zusammenführen. Ausgabe ist ein 320x240 LCD 
(mit Graustufen) aber auch VGA und TV (RGB/FBAS). Controller ist ein 
ATMega1284P, der auch später durch einen S12X ersetzt werden kann.
Als "Programme" bezeichne ich das, was man neuerdings "Apps" nennt. Dazu 
gibt es 8 Programmplätze im System, die jeweils 8K oder 9K (steht noch 
nicht ganz fest) Flash  belegen. Wenn dann jemand dich eine "App" in C 
programmieren will, sollte er es zumindest theoretisch können.

Zuallererst, das "Stack-Problem" sollte wahrscheinlich keines 
darstellen, solange nicht über andere Dinge als den Stackzeiger darauf 
zugegriffen werden muss. Mir geht es darum, dass beim Start des 
Programms ja schon ein Stack existiert und der auch am Programmende noch 
so existieren muss.

Für die Reloziererei ist es wahrscheinlich der einfachste Weg, das 
Programm genau für den Adressbereich zu kompilieren in dem es später 
laufen soll. Im Programmkopf der bei der HEX->BIN Konvertierung noch 
dazugefügt wird steht dann in einem Byte, an welche der 8 Programmplätze 
das Programm geladen werden kann.

Bei der Register-Thematik werde isch schauen, dass ich die reservierten 
Register (R2,R3 und R8-R11) zusammenlege.

Gruß Jörg

von Joerg W. (joergwolfram)


Lesenswert?

Da meine ursprüngliche Idee wohl nicht funktioniert, habe ich mir noch 
ein anderes Konzept überlegt. Da ich ja einen Mega1284P verwende, habe 
jetzt ich Dinge wie z.B. den Bildspeicher an das Ende des RAM gerückt 
und nutze die Vektortabelle im Bootloaderbereich.

- Das Programm kann an Adresse 0 beginnen
- Das Programm darf R2 - R5 nicht verwenden
- Port A und C dürfen nicht verwendet werden
- Speicherbereich wie ATMega 328, bei Bedarf auch 4K RAM
- Interrupts können nicht genutzt und dürfen nicht abgeschalten werden
- Das Programm kann einen eigenen Stack nutzen, die ISR braucht aber 10 
Bytes auf dem Stack

Der Zugriff auf das System müsste dann über entsprechende Bibliotheken 
erfolgen, die im Wesentlichen nur aus CALL-Aufrufen in den oberen 
Bereich bestehen. Auf die gleiche Weise kann ich dann auch den 
Rücksprung ins System realisieren.

Da ich mir nicht mehr sicher bin, ob ich das Ganze als Open Source 
veröffentlichen will, sollte es auch möglich sein, das System als "Black 
Box" mit definierter Schnittstelle zu betrachten.

Jörg

von Joerg W. (joergwolfram)


Lesenswert?

So, es gibt einen neuen "Zwischenstand". Ich werde das Ganze so halten, 
dass man die Einstellungen für den Mega644 verwenden kann:

- Die unteren 64K Flash sind frei, ebenso die unteren 2K EEPROM
- Die ersten 4K RAM sind frei
- Keyboard habe ich jetzt auf USART1 gelegt, USART0 ist frei

Es sind aber noch ein paar Fragen offen

1. macht der Assembler es mit, dass Unterprogrammaufrufe in den für den 
Mega644 nicht vorhandenenen Flash-Bereich erfolgen?

2. kann ich mit static FILE mystdout ... die Ausgabe einfach auf den 
Bildschirm umlenken?

3. gibt es eine ausführliche Doku, wie beim GCC die Parameterübergabe 
auf Register-/Stackebene erfolgt?

4. Besteht überhaupt Interesse an einem derartigen Projekt?

Jörg

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joerg Wolfram schrieb:

> 1. macht der Assembler es mit, dass Unterprogrammaufrufe in den für den
> Mega644 nicht vorhandenenen Flash-Bereich erfolgen?

Sollte dem Assembler egal sein.  Ist halt nur die Frage, wie du ihm
die Aufrufe dahin vermittelst.

> 2. kann ich mit static FILE mystdout ... die Ausgabe einfach auf den
> Bildschirm umlenken?

Ich kenne keinen AVR mit eingebautem Bildschirm. ;-)

stdio existiert, aber du musst dir schon die Doku dazu mal
durchlesen, und dann musst du dich drum kümmern, eine Backend-
Funktion zu schreiben, die die Zeichen aus dem AVR irgendwie zu
deinem Bildschirm bekommt.  Es existiert ein Beispielprogramm,
das für UART und HD44780-LCD zeigt, wie's geht.

> 3. gibt es eine ausführliche Doku, wie beim GCC die Parameterübergabe
> auf Register-/Stackebene erfolgt?

Ja, in der avr-libc-FAQ.

> 4. Besteht überhaupt Interesse an einem derartigen Projekt?

Keine Ahnung.

Auch Jörg. ;-)

von Joerg W. (joergwolfram)


Lesenswert?

Hallo Jörg,

danke für die Info. Wichtig ist erstmal, dass es prinzipiell gehen 
sollte. Die Aufrufe müssten dann wohl als CALL in Inline-Assembler 
erfolgen, ebenso ließe sich dort auch die Parameter- und 
Resultatübergabe unterbringen, also eine Art "Wrapper" um die 
eigentlichen ASM-API Funktionen.
Ob das jemand jemals realisiert, ist noch eine ganz andere Frage da ich 
selbst nur in ASM programmiere.

Jörg (TO)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joerg Wolfram schrieb:
> Die Aufrufe müssten dann wohl als CALL in Inline-Assembler
> erfolgen,

Nein, C kennt Funktionszeiger.

von Joerg W. (joergwolfram)


Lesenswert?

Gut, dann müsste halt der Wrapper im Assemblerbereich stehen. Da die 
"Schnittstellen" des compilierten C-Codes und die meiner ASM-Funktionen 
nicht miteinander kompatibel sind, wird so etwas auf jeden Fall 
notwendig sein. Um zum Beispiel einen Punkt zu setzen (PLOT), liegen bei 
mir die X-Koordinate im X-Register und die Y-Koordinate im Y Register 
und die Farbe in R20. Diese ganzen "internen Konventionen" will und muss 
ich auch weiter beibehalten, damit meine ASM-Module miteinander 
weitestgehend kompatibel bleiben.

Jörg

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.