M. M. schrieb:
> Darf man fragen wo du das gelernt hast?
Ich habe für mehrere Architekturen (x86, Cortex-M3, Epiphany, RV32)
Startup-Code geschrieben, und mir wurde Assembler irgendwann zu blöd.
:-) Außerdem habe ich viel Startup-Code gelesen.
Ein Programm besteht grundsätzlich aus Code und Daten, die man
verschieden klassifiziert (unter anderem .text, .data, .bss, .rodata).
Der Linker weist diesen Programmstücken ihre endgültigen Adressen zu.
Das Linkerscript sagt dem Linker, was er wohin zu legen hat (Flash von
xxx bis yyy, RAM von qqq bis zzz, .text soll ins Flash, .data ins RAM,
uswusf.)
Die Aufgabe des Startup-Codes ist es, das System so weit zu
initialisieren, dass es den vom Compiler generierten Code ausführen
kann. Was genau notwendig ist, hängt von der Architektur und vom
Compiler ab. Startup-Code enthält eigentlich immer Assembler (nur die
Cortex-M kann man komplett in C starten), oder ist vollständig in
Assembler geschrieben.
Ein x86 startet im Real Mode (16 Bit), den musst du erstmal treten,
damit er 32-bittigen Code ausführen kann - oder er kommt aus einem
Bootloader wie GRUB, dann gilt dessen Dokumentation. Epiphany und ARM
starten im Interrupt-Kontext. Bei x86 und Epiphany ist die Startadresse
festgelegt, ein ARM liest sie aus dem Flash. Bei RISC-V ist nichts
festgelegt, es gilt die Dokumentation des spezifischen Cores bzw. der
Plattform. Und so weiter.
Für gcc muss mindestens der Stack funktionieren und ".bss" muss leer
sein, für eine libc solltest du noch die Konstruktoren ausführen (und
die Destruktoren, wenn du ordentlich bist). Das ist bei gcc wieder
architekturspezifisch (x86 ist etwas anders als die anderen Kinder).
Du musst dich an viele Dokumentationen halten, wenn du eine neue
Architektur/Plattform aus dem Sumpf ziehen willst. Andererseits brauchst
du natürlich nur das tun, was für deine Anwendung auch nötig ist - ich
kann z.B. auf eine funktionierende libc verzichten (gcc braucht aber
memcpy, memmove, memset und memcmp).