Libraries

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

Dies soll ein Beitrag zum Erstellen und Benutzen von Libraries mit den Tools rund um GCC werden. Da das Erstellen von Libraries relativ plattformunabhängig ist, werde ich die 'native-tools', sprich gcc, ar, ld etc. ohne Target-Prefix (avr-/arm-elf-/...) erwähnen. Wenn ihr Verbesserungen und/oder Korrekturen kennt, dann fügt sie doch bitte gleich in den Artikel ein.

Was ist eine 'Library'

Eine Library (Bibliothek) ist eine Ansammlung von Funktionen, die bereits in kompilierter Form vorliegen. Sie besteht aus zwei Teilen: dem Archiv und dem Interface in Form von Headern. Ohne Header ist eine Library nicht verwendbar, da die Deklarationen (und damit das Interface) fehlen. Es gibt verschiedene Arten von Libraries: statische, dynamische und 'shared'.

In diesem Artikel soll vorerst nur auf statische Libraries für den C-Compiler eingegangen werden. In der Regel enden sie auf die Dateinamenerweiterung '.a'.

Bestehende Libraries

  • AVR libc function reference Beschreibung der in AVR-GCC enthaltenden Bilbiotheken
  • Procyon AVRlib AVRlib is a library of easy-to-use C functions for a variety of common and uncommon tasks using AVR processors

Organisation

Beim Erstellen einer Library sind einige Punkte zu beachten:

  • strikte/saubere Trennung von Codedefinition und -Deklaration bei Schnittstellen
  • eindeutige Namensgebung bei Sourcefiles (bsp.: 'up()' gehört nicht in 'down.c')
  • Optionale Funktionen in eigene Sourcefiles auslagern (Linker!)
  • ...

Damit der Linker später möglichst kleine Binaries erzeugen kann, sollten alle Funktionen, die nicht unmittelbar verheiratet sind, in eigene Sourcefiles ausgelagert werden. Der Linker kann später nur die Objekte aus dem Binary rauslassen, wenn auch wirklich keine der Funktionen in diesen Objekten verwendet wird!

Beispiel:

libmylibfunc1.c
#include "mylib.h"

int
libmylibfunc1(unsigned char cParam)
{
  int rVal = 0;

  rVal = (int)((unsigned int) cParam);
  return rVal;
}
libmylibfuncs.c
#include "mylib.h"

void
libmylibfunc2(void)
{
  for(;;)
    ;
}

void
libmylibfunc3(void)
{
  printf("called...\n");
  libmylibfunc2();
}
mylib.h
int libmylibfunc1(unsigned char);
void libmylibfunc2(void);
void libmylibfunc3(void);

Bei diesem Beispiel kann der Linker, je nach Verwendung, entscheiden, ob 'libmylibfunc1', 'libmylibfunc2' _und_ 'libmylibfunc3' oder keine der Funktionen im späteren Binary existieren. Wird 'libmylibfunc1' als einzige verwendet, so ist auch nur diese Funktion im späteren Binary vorhanden. Wird aber nur eine der beiden Funktionen aus libmylibfuncs.c verwendet, sind später _beide_ im Binary vorhanden! Das liegt daran, daß der Linker nur auf Objektebene selektieren kann.

Compileraufruf

Da eine Library lediglich kompiliert und assembliert, aber nicht gelinkt werden soll, muss das beim Compileraufruf berücksichtigt werden. Der Kommandozeilenschalter dafür ist '-c'.

$ gcc -c -ggdb -O2 -o libmylibfunc1.o libmylibfunc1.c

Archiv erstellen

Das Archivierungsprogramm 'ar' wird dazu verwendet, um die kompilierten Objektfiles zu einer Library zusammen zu fügen.

$ ar -rcs libmylib.a libmylibfunc1.o libmylibfuncs.o

Den Inhalt der frisch erzeugten Library kann man sich Beispielsweise mit 'nm' anzeigen lassen:

$ nm -s libmylib.a

Fertige Library

Es existieren jetzt zwei Dateien, die zum Export der Library benötigt werden:

  1. 'mylib.h', in der das Interface "Dokumentiert" ist.
  2. 'libmylib.a', die Library selber, in Kompilierter Form.

Verwenden

Hat man die Library jetzt in seinem Projektordner vorliegen, dann kann man (Beispielsweise) die Funktionen im Sourcecode verwenden:

main.c
#include "mylib.h"

int
main(void)
{
  return libmylibfunc1('A');
}

Kompilieren und linken mit:

$ gcc -ggdb -O2 -o projekt.elf main.c -Wl,-Map=projekt.map,--cref -L. -lmylib

Erklärungen:

-Wl,...
  • Dieser Kommandozeilenschalter sagt gcc, daß alles nachfolgende an den Linker weitergereicht werden soll. Bei jedem Komma beginnt eine neue Option.
-L.
  • ...erweitert den Librarysuchpfad um das aktuelle Verzeichnis
-lmylib
  • ...sagt dem Linker, daß er die Library 'libmylib.a' beim Linken verwenden soll (alle Libraries haben das Prefix 'lib', welches hier nicht angegeben werden darf!)

In der Datei 'projekt.map' kann man sich jetzt Beispielsweise anschauen, welche Objektfiles der libmylib.a wirklich hinzugefügt wurden. In unserem Beispiel ist lediglich das Objektfile 'libmylibfunc1.o' vorhanden. Wenn ihr jetzt ausprobieren wollt, was mit 'libmylibfuncs.o' passiert, dann müsst ihr im 'main.c' lediglich eine der Funktionen aus 'libmylibfuncs.c' verwenden und das 'projekt.elf' neu übersetzen.