Hallo Forum,
ich wollte mal sehen wie eine Arduino Umgebung eigentlich das *.bin-File
zum flashen aus dem C-Code erzeugt. Jetzt weiß ich schon dass der
Compiler Position Independent Objectcode (*.o) erzeugt und der Linker
aus diesem und allen aufgerufenen Lib-Routinen ein Binary macht.
Nur was macht denn bei diesem Ablauf das erzeugen eines Archive Files
firmware.a? Und warum der Umweg über ELF und nicht direkt ein Binary?
Kann mir das jemand knapp erklären?
Hier der Ablauf:
Thomas schrieb:> ich wollte mal sehen wie eine Arduino Umgebung eigentlich das *.bin-File> zum flashen aus dem C-Code erzeugt
...
> was macht denn bei diesem Ablauf das erzeugen eines Archive Files> firmware.a?
Das scheint in der Tat eine Marotte der Arduino-Leute zu sein. Es gibt
keinen offensichtlichen Grund, warum man erst alle .o Files in eine
statische Library verfrachten sollte, um dann nachher das Executable
daraus zu linken.
> Und warum der Umweg über ELF und nicht direkt ein Binary?
Weil der Linker nur ELF kann. ELF ist das universelle Fileformat für
Executables und (dynamische) Libraries. Normalerweise sorgt ein
Laufzeitsystem dafür, den Inhalt des ELF Files in den Speicher zu laden
und Referenzen zu shared Libraries aufzulösen.
Da der µC kein Laufzeitsystem hat, wird in einem zweiten Schritt (per
objcopy) das ELF-File so in den Speicher geladen wie es das
Laufzeitsystem auch machen würde, und dann der Speicherinhalt als .hex
gedumpt.
Thomas schrieb:> Nur was macht denn bei diesem Ablauf das erzeugen eines Archive Files> firmware.a?>
1
> # Erzeugt archive-File?
2
> arm-none-eabi-ar rcs firmware.a main.o
3
>
Ich habe einen Verdacht, kann den aber nicht belegen. Ich arbeite nicht
mit Arduino und habe keine Lust mir einen kleinen Test zusammenzubauen.
Wenn mein Verdacht stimmt ist das ziemlich abgefuckt.
Aus der ar man-Page:
>> ar creates an index to the symbols defined in relocatable object>> modules in the archive when you specify the modifier s. Once created,>> this index is updated in the archive whenever ar makes a change to its>> contents (save for the q update operation). An archive with such an>> index speeds up linking to the library, and allows routines in the>> library to call each other without regard to their placement in the>> archive.
Es könnte sein, dass die ar benutzen um sich nicht um die Reihenfolge
der .o-Dateien auf der Linker-Kommandozeile kümmern zu müssen. So frei
nach dem Motto "Was interessieren uns Abhängigkeiten von .o Dateien,
geschweige denn zirkulare Referenzen?". Hauptsache alles linkt
irgendwie. Wie gesagt, abgefuckt.
Die o-Files sind keine fertigen Binaries - die endgültigen Adressen sind
noch nicht zugewiesen (wie auch, der Compiler weiß ja nicht, wo das
gerade übersetzte File später landet), das macht erst der Linker. Mit
Position-Independant-Code (PIC) hat das nichts zu tun - das ist ein
zusätzliches (optionales) Feature der Codegenerierung (z.B. statt
absolute Sprünge, relative Sprünge).
Die a-Files sind wirklich "Archive" - wie zip oder tar. Kannst du mit
"ar vt foo.a" reinschauen. Ein a-File kann einen zusätzlichen Index von
in den o-Files vorhanden Symbolen enthalten. Dieser Index ist aber
optional, kann nachträglich angelegt werden (s. ranlib) und dient nur
der Performance (und ein paar Nebensächlichkeiten).
Der Linker "linkt" alle angegeben o-Files zusammen (und weist dabei
Adressen zu). Wenn undefinierte Symbole übrig bleiben (z.B. strlen,
printf, ...), werden die angegebenen Libraries (a-Files) nach o-Files
durchsucht, in denen diese Symbole definiert sind. Das erste o-File, in
dem ein Symbol gefunden wird, wird aus dem Library extrahiert und
ebenfalls dazugelinkt.
Und hier kommt jetzt der Trick, warum einige Projekte (insb. größere mit
mehrern Unterverzeichnissen) ihre o-Files erstmal in ein Archive packen:
sie wissen nicht, welche o-Files alle vorhanden sind bzw welche im
endgültigen Programm wirklich benötigt werden!
Würde sie alle o-Files direkt in der Kommandozeile angeben, würden die
immer alle dazugelinkt, auch wenn sie nicht gebraucht werden. Sind sie
in einem a-File, werden vom Linker automatisch nur die rauspickt, die
benötigt werden. Als Bonus braucht der endgültige Linkerschritt nicht zu
wissen, welche o-Files alle vorhanden sind - ein a-File pro
(Unter-)Verzeichnis reicht.
Im Extremfall gibt es in der Kommandozeile gar keine o-Files mehr und
nur eine Referenz auf ein Startsymbol (z.B. "Reset_Handler"). Dieses
"undefinierte" Startsymbol führt dazu, dass der Startup-Code (in dem
"Reset_Handler" definiert ist) aus einem Library geholt wird, der
wiederrum hat eine Referenz auf "main", das auf "Setup" und "Loop", ...
Super. Das hab ich soweit verstanden!
Es scheint wohl zu sein als wird man damit auch das Problem von
mehrfachen Defintionen der gleichen Symbole los.
Probier ich nämlich statt dem firmware.a-File direkt das main.o File zu
linken bekomme ich folgende Fehler:
1
main.o: In function `_init':
2
:(.text+0x318): multiple definition of `_init'
3
/usr/lib/gcc/arm-none-eabi/4.9.3/armv7-m/crti.o:(.init+0x0): first defined here
4
5
main.o: In function `_fini':
6
:(.text+0x32c): multiple definition of `_fini'
7
/usr/lib/gcc/arm-none-eabi/4.9.3/armv7-m/crti.o:(.fini+0x0): first defined here
8
/usr/lib/gcc/arm-none-eabi/4.9.3/armv7-m/crtend.o:(.tm_clone_table+0x0): multiple definition of `__TMC_END__'
9
main.o::(.relocate+0xffffffffe001033c): first defined here
10
collect2: error: ld returned 1 exit status
Ich denke also meine main.o hat über ihr inkludiertes Header-File
('due_sam3x.init.h') schon ein Symbol '_init' gebraucht. Nun kommt aber,
woher auch immer, nochmal das Symbol '_init' vorbei. Da bricht dann der
Linker ab und verweigert seinen Dienst.
Mit dem a-File ist das wohl nicht passiert weil er das Symbol nur einmal
holt wenn er es braucht, korrekt?
Hier übrigens Programm:
Irgendjemand eine Idee wie ich jetzt um das Problem herum komme?
Für jemanden der simple 8-Bit AVR gewohnt ist, ist diese ganze ARM
Umgebung mit gefühlten tausenden Librarys die nötig sind ziemlich
kompliziert zu durchschauen.
Danke schonmal für die Hilfe übrigens!
Thomas
Thomas schrieb:> Hier übrigens Programm:#include> "/root/duebuildnew/install/due/include/due_sam3x.init.h"
Du hast die Arduino-IDE doch nicht unter root installiert, oder?
Ich habe die IDE garnicht installiert, sondern nur Stück für Stück die
bbhängigen Archive aus dem Boardmanager zusammengesucht :-)
Das ganze ist ein vServer der in kürze wieder platt gemacht wird.
Es gibt also dort:
a) Garkeine GUI für Arduino,
b) Keinerlei Serverprozesse die Verbindungen erlauben (bis auf SSH), und
c) ein sicheres root-Passwort
Ich denke das Risiko ist relativ gering, trotzdem danke für den Hinweis!
Thomas
Thomas schrieb:> und warum der Umweg über ELF und nicht direkt ein Binary?
Das .elf kann man debuggen, wenn Debug Symbole mit eingelinkt wurden.
Außerdem ist das das default Target für ld. ELF hätte auch den Vorteil,
dass man "Löcher" - also Lücken im Addressraum - benutzen kann. Das geht
z.B. auch mit Intel Hex, aber nicht mit binary.
Übrigens sollte man in der Linker Befehlszeile auch den CPU Typ mit
angeben, das braucht er zum Auswählen der richtigen Multilib-Variante.
Ich konnte das Problem jetzt lösen indem ich in der main.c aus:
>>> init_controller();
die entsprechenden einzelnen Kommandos gemacht habe:
>>> SystemInit();>>> if (SysTick_Config(SystemCoreClock / 1000)) while (1);>>> WDT_Disable(WDT);>>> __libc_init_array();
Nun linkt es und funktioniert auch !
Vielen Dank hier nochmal für die Hilfe!
Thomas
Thomas schrieb:> Ich habe die IDE garnicht installiert, sondern nur Stück für Stück die> bbhängigen Archive aus dem Boardmanager zusammengesucht :-)>> Das ganze ist ein vServer der in kürze wieder platt gemacht wird.> Es gibt also dort:> a) Garkeine GUI für Arduino,> b) Keinerlei Serverprozesse die Verbindungen erlauben (bis auf SSH), und> c) ein sicheres root-Passwort>> Ich denke das Risiko ist relativ gering, trotzdem danke für den Hinweis!
Dazu vielleicht noch als kleine Tipps: per SSH weder root- noch Logins
mit einem Paßwort erlauben, sondern nur als Benutzer per
zertifikatsbasierter Authentifizierung und sich dann erst mit "su -"
oder "sudo su -" eventuell notwendige Privilegien verschaffen.
Außerdem gibt es in Ubuntu (zumindest in der 16.04 LTS) ein Paket namens
"arduino-mk", das eine Reihe Beispiel-Makefiles für die Programmierung
von Arduinos auf der Kommandozeile enthält und alle nötigen
Abhängigkeiten mit (arduino-core mit den C++-Libraries, der Referenz und
den Beispielen, und über weitere Abhängigkeiten den gcc sowie gcc-avr)
installiert, da mußt Du nichts manuell zusammensuchen. Vermutlich findet
man das auch in Debian, Linux Mint und anderen Debian-basierten
Linux-Distributionen.