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:
1 | OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr") |
2 | OUTPUT_ARCH(avr:103) |
3 | |
4 | FOO = 23; |
5 | __RODATA_PM_OFFSET__ = 0x9000; |
Die so definierten Symbole können dann im ld-Script verwendet werden, oder gleich ne
1 | MEMORY |
2 | { |
3 | } |
ins memory.x. So ist es dann übersichtlicher, wenn man mehrere unterschiedliche aber hinreichend gleiche Devices und Links hat.
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.
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.