Binäre Daten zum Programm hinzufügen

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Will man beliebige Daten einem Programm hinzufügen (z.B. Bitmaps, Fonts, Seriennummern, ...) gibt es dafür mehrere Möglichkeiten.

Einfügen als C-Array

Die Daten werden mit einem Hilfsprogramm in ein C-Array umgewandelt und dann in die Anwendung einkompiliert. Zum Umwandeln von Binärdaten in ein C-Array gibt es bereits einige fertige Lösungen. So kann z.B. der kostenlose Hexeditor HxD eine Datei als C-Array exportieren. Mit Hilfe des Kommandozeilentools srec_cat ist dies ebenfalls möglich.

$ srec_cat my_data.bin -binary -output my_c_file.c -c-Array my_var_name -include

Es wird dann eine C-Datei erzeugt die ein Array my_var_name und einige Hilfsvariablen dazu enthält. Diese Datei wird dann ins Projekt eingebunden und ganz normal verwendet.

Beim avr-gcc ist noch darauf zu achten, dass die Daten — falls gewünscht — im Programmspeicher und nicht im RAM landen. Siehe AVR-GCC Tutorial

Vorteile
  • Einfach zu verwenden
  • Portabel
  • Typsicher
Nachteile
  • Zusätzliche Dateien (c/h) nötig
  • C-Datei bei großen Binärdateien sehr groß
  • Ein zusätzlicher Schritt beim Bauen nötig
  • Daten müssen zum Zeitpunkt des Kompilierens bekannt sein

objcopy

Mit objcopy wird aus der Binärdatei eine Objektdatei erstellt die zum Projekt gelinkt werden kann. Hier ein Beispiel wie das mit den GNU-Tools für den AVR gemacht wird.

$ avr-objcopy -I binary my_data.bin --binary-architecture avr4 -O elf32-avr my_data.o
$ avr-nm -t my_data.o 
000008c6 D _binary__home_mat_my_data_bin_end
000008c6 A _binary__home_mat_my_data_bin_size
00000000 D _binary__home_mat_my_data_bin_start

Die Objektdatei enthält dann drei Symbole, deren Namen allerdings etwas unhandlich sind und — je nach Aufruf von objcopy — auch noch den kompletten Pfad der Binärdatei enthalten. Es ist allerdings möglich, mit einem weiteren Aufruf von objcopy den Symbolen brauchbarere Namen zu verpassen. Dieses Datenpaket kann dann in C verwendet werden, wenn man folgende Zeile einfügt:

extern unsigned char _binary__home_mat_my_data_bin_start[];

Beim Linken des Projekts wird die Objekdatei dann mit zum Projekt hinzu gelinkt.

Vorteile
  • Keine zusätzliche Datei nötig
  • Keine manuell zu verwaltende Adresse
Nachteile
  • Nicht komplett portabel. Der Aufruf von objcopy ist je nach System immer etwas unterschiedlich.
  • Unhandliche Symbolnamen, mögliche Lösung siehe unten
  • Nicht typsicher
  • Daten müssen zum Zeitpunkt des Linkens bekannt sein

Standardmäßig gehen die Daten ins Datensegment (.data). Da man die (meist umfangreichen) Daten beim AVR im Flash haben möchte, erweitert sich die Kommandozeile für ein Makefile:

spDINER.o: TomsDiner.lpc		# Explizite Regel: Zieldatei: Quelldatei
	avr-objcopy -I binary \		# Eingabedatei binär
-O elf32-avr \					# Ausgabedatei ELF
--rename-section .data=.progmem.1,contents \	# Ausgabe-Sektion ".progmem" =
\						# in den unteren 64 K Flash vom Linker einbauen lassen
--redefine-sym _binary_$(subst .,_,$<)_start=$(@:.o=) \	# Das umständliche Startsymbol...
\						# durch Zieldateiname ohne ".o" ersetzen
-N _binary_$(subst .,_,$<)_end \	# Das Endesymbol löschen (optional)
-N _binary_$(subst .,_,$<)_size \	# Das Längensymbol löschen (optional)
$< \					# Eingabedatei
$@						# Ausgabedatei

Leider ist es mit avr-objcopy nicht möglich, dem (hier umbenannten) Start-Symbol (im Beispiel: spDINER) die Eigenschaft "STT_OBJECT" zu verpassen. (In der Symboltabelle erscheint dann bei avr-objdump -t ein "O".) Dadurch wird der Disassembler avr-objdump -d die Binärdaten als (sinnlosen) Kode disassemblieren anstatt die Bytes uninterpretiert auszuspucken. Patcht man mit einem Hex-Editor dieses Bit hinzu, funktioniert die Disassemblierung korrekt.

Achtung: Vergisst man beim Aufruf von objcopy die Zieldatei, wird die Quelldatei überschrieben!! Das ist fatal, wenn die Quelldatei keine Zwischendatei in einem Makefile darstellt.

.incbin

Die GNU Binutils bieten die Direktive .incbin mit welcher der Binärinhalt einer Datei eingefügt werden kann. Dazu legt man ein Assembler-Modul mydata.S an und includiert die gewünschte Datei:

In mydata.S

.section .progmem.data,"a",@progbits
.global my_data
.type   my_data,@object
my_data:
.incbin "maydata.bin"
.size my_data, .-my_data

Diese Assembler-Datei wird mit

$ gcc -c my_data.S

assembliert und die resultierende Object-Datei my_data.o zum Executable hinzugelinkt. Im C-Programm definiert man für der Zugriff auf die Daten den Prototyp

extern unsigned char my_data[];
Vorteile
  • Keine manuell zu verwaltende Adresse
  • Frei wählbarer Symbolname
  • Funktionierende Disassemblierung
Nachteile
  • Nur mit den GNU-Binutils anwendbar
  • Nicht typsicher
  • Extra Assembler-Datei notwendig
  • Daten müssen zum Zeitpunkt des Linkens bekannt sein

srec_cat

Wenn die Ausgabedatei des Compilers eine HEX-Datei ist (typisch bei µC Anwendungen) dann kann die Binärdatei mit Hilfe von srec_cat in die original Datei eingefügt werden. Bei dieser Methode kann dann allerdings nicht mehr über einen symbolischen Namen auf die Daten zugegriffen werden sondern es muss mit festen Adressen gearbeitet werden. Beispiel für eine Datei, die in die finale Intel-HEX-Datei beim Offset 0xF000 eingefügt wird.

srec_cat myprog.hex -intel my_data.bin -binary -offset 0xF000 -output my_output.hex -intel -address-length=2

In C kann dann mittels eines Pointers bzw. beim AVR mitteles den pgm_read-Funktionen auf diese Daten zugegriffen werden.

unsigned char *my_data = (unsigned char*) 0xF000;
Vorteile
  • Keine zusätzliche Datei nötig
  • Daten müssen zum Zeitpunkt des Linkens nicht bekannt sein (z.B. Seriennummer)
Nachteile
  • Fixe Adresse muss verwendet werden
  • Nicht typsicher