Hallo,
ich verwende auf einem ARM die newlib zusammen mit der gcc-basierten
Toolchain direkt von ARM. Das funktioniert soweit.
Aktuell arbeite ich mit einem eigenen Linkerskript und eigenem
Startup-Code und binde das Ganze entsprechend mit -nostartfiles
zusammen. Den konkreten Compiler binde ich aktuell so ein:
* In meinem Linkerskript baue ich die generischen .init/.fini-Sektionen
zusammen, aus crti.o/crtn.o und so weiter.
* Ich packe die .preinit_array, .init_array und .fini_array-Sektionen
mit ein.
* Im Startup-Code wird die .data-Sektion ins RAM reloziert und die
.bss-Sektion genullt.
* Danach werden die Funktionen aus der .preinit_array-Sektion
aufgerufen.
* Dann wird _init() aufgerufen.
* Dann die Funktionen aus der .init_array-Sektion.
* Schließlich ein Sprung in die main()-Funktion.
Nun gibt es in der newlib ja schon __libc_init_array, die sich um die
Initialisierer kümmert, und auch ein Startup-Code und ein Linkerskript
ist dabei. Grundsätzlich finde ich es nämlich reizvoll, nicht händisch
den Compiler zu integrieren, sondern möglichst viel von der Toolchain zu
verwerten.
Wie macht man das denn mit newlib?
Ich frage mich z.B., wie ich das mitgelieferte Linkerskript benutze,
denn irgendwie müsste ich ja zumindest ein Speicherlayout anpassen
(MEMORY{}-Abschnitt)...
Soweit ich weiß reloziert der Startup-Code aus der newlib auch die
.data-Sektion nicht ins RAM. Die .bss-Sektion dagegen wird genullt.
Ratio dahinter war, dass die .data-Sektion ja im Programmabbild drin ist
und daher vom dynamischen Linker mit geladen wird. Für .bss steht aber
nichts im Programmabbild, daher nullt der Startup das selbst. Den Linker
gibts natürlich bei bare-metal-ARM so nicht.
Danke und Grüße,
Nase
Nase schrieb:> Soweit ich weiß reloziert der Startup-Code aus der newlib auch die> .data-Sektion nicht ins RAM. Die .bss-Sektion dagegen wird genullt.> Ratio dahinter war, dass die .data-Sektion ja im Programmabbild drin ist> und daher vom dynamischen Linker mit geladen wird.
hmmm. Beim default Linker-Script kümmert sich eigentlich der Linker ums
richtige Script; deshalb gibt's auch diesen Zoo von .x, .xu, .xr, .xe,
.xn, .xbn die abhängig davon, welcher Link erfolgt (z.B. -r) verwendet
werden. Eine Übersicht, worin die sich unterscheiden, gibt das Template
bei Binutils: abhängig z.B. von $RELOCATING und $CONSTRUCTING
http://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=ld/scripttempl/armbpabi.sc;h=a12cd3d2343ecad9f66cda8179811f71a26a09b9;hb=HEAD
Wenn man hart ein Script vorgibt, kann so ein Mechanismus natürlich
nicht mehr wirken; da muss dann der Anwender wissen, was in seinem
Script steht und wann er es verwendet oder nicht.
Was durchaus praktikabel sein kann, ist ein Linker-Script zu
verwenden, und dies dann durch verschiedene memory.x zu parametrisieren.
Wenn der Compiler(treiber) mit ner Datei nix anfangen kann, gibt er sie
dem Linker weiter, und dieser interpretiert sie dann. Hier ein Beispiel
für avr, einfach das memory.x ohne weiteres bei arm-gcc mit angeben:
Johann L. schrieb:> So ist es dann übersichtlicher, wenn man mehrere> unterschiedliche aber hinreichend gleiche Devices und Links hat.
Das war schonmal ein wichtiger Hinweis...
So wie ich das Template da verstehe, eignet es sich aber eigentlich
garnicht für bare-metal-Anwendungen: Der Linkerscript-Zoo, der mit der
newlib für ARM mitkommt, geht offenbar davon aus, dass alles im
.text-Segment landet und vom LD ins RAM geladen wird.
Realistisch wäre ja für meine Anwendung, dass ich z.B. die .data-Sektion
zweimal einbaue, nämlich einmal als Platzhalter im RAM und einmal mit
den Daten im FLASH.
Was ist genau die Frage?
Ich hab ein ganz ähnliches Setup. ARM Cortex bare Metal. Linker Skript
und Start-up Code sind selbst gezimmert. Newlib hat damit erst Mal wenig
zu tun, oder?
Gehen tut es also und es macht immer dann Sinn wenn man etwas mehr
Kontrolle über den Aufbau haben möchte, z.b. für bootloader,
verschlüsselte sections die beim Start-up entschlüsselt werden,
effektive und effiziente Nutzung von CCM, Nutzung der remap Funktion,
die RAM im flash einblenden, Speicher mit verschiedenen cache Attributen
etc.
Mein Start-up Code ist übrigens in c und nicht in asm geschrieben.
Vereinfacht das ganze doch erheblich.
Karl schrieb:> Was ist genau die Frage?
Wie integriere ich den Compiler in mein Projekt?
> Ich hab ein ganz ähnliches Setup. ARM Cortex bare Metal. Linker Skript> und Start-up Code sind selbst gezimmert. Newlib hat damit erst Mal wenig> zu tun, oder?
Nunja, sie hat etwas damit zu tun, wie der Compiler mit seiner
Laufzeitumgebung zusammenkommt.
Aktuell arbeite ich auch mit einem eigenen Linkerskript. Das hat aber
zur Folge, dass ich mir recht mühsam zusammengesucht habe, welche
Sektionen der Compiler erzeugt und warum. Das z.B. meine ich mit
Compiler-Integration. Einen Teil gibt das ABI ja vor (bei ARM ist das
u.a. die .init_array-Sektion). Ein anderer Teil ist aber
Compiler-spezifisch, etwa dir .ctors-Sektion oder der ganze Relo-Kram.
Schließlich gibt es noch den Architektur- oder Boardspezifische Teil,
also beispielsweise die Speicherkonfiguration, die ja bestenfalls davon
abhängt, was auf die Platine gelötet wird.
Ich frage mich nun eben, wie ich wenigstens den Compiler-spezifischen
Kram loswerde.
Karl schrieb:> Mein Start-up Code ist übrigens in c und nicht in asm geschrieben.> Vereinfacht das ganze doch erheblich.
Gibt es da nicht ein Henne-Ei-Problem?
Im Prinzip könnte sich der C-Startup-Code ja schon auf Dinge verlassen,
die im Startup-Code erledigt werden, sprich genulltes .bss und geladenes
.data. Wenn der Compiler den C-Startup-Code übersetzt, weiß er ja nichts
davon, dass diese Zusicherungen noch garnicht erfüllt wurden. Bei der
Generierung sollte er ja (eigentlich korrekterweise) annehmen, dass die
Laufzeitumgebung schon steht.
Nase schrieb:> Gibt es da nicht ein Henne-Ei-Problem?>> Im Prinzip könnte sich der C-Startup-Code ja schon auf Dinge verlassen,> die im Startup-Code erledigt werden, sprich genulltes .bss und geladenes> .data.
Jein. Theoretisch hast du Recht aber rein praktisch gibt es keinen Grund
warum man für das nullen von .bss und initialisieren von .data lesenden
Zugriff auf das RAM brauchen sollte und deshalb wird auch (passender
C-Code vorrausgesetzt) kein Compiler auf die Idee kommen das zu tun.
Nase schrieb:> Gibt es da nicht ein Henne-Ei-Problem?
Nicht, dass ich wüsste.
> Im Prinzip könnte sich der C-Startup-Code ja schon auf Dinge verlassen,> die im Startup-Code erledigt werden, sprich genulltes .bss und geladenes> .data. Wenn der Compiler den C-Startup-Code übersetzt, weiß er ja nichts> davon, dass diese Zusicherungen noch garnicht erfüllt wurden. Bei der> Generierung sollte er ja (eigentlich korrekterweise) annehmen, dass die> Laufzeitumgebung schon steht.
Ja, aber der Startup Code nutzt nur Variablen auf dem Stack bzw. In
Registern und Read only Daten die der linker erzeugt. Bei denen ist lma
gleich vma.