So verwenden Sie den ar-Befehl von Linux zum Erstellen statischer Bibliotheken

Bei der Softwareentwicklung ist der Befehl `ar` unter Linux ein wertvolles Werkzeug, um Funktionsbibliotheken zu erstellen. Dieses Tutorial führt Sie durch den Prozess der Erstellung, Modifizierung und Verwendung einer statischen Bibliothek in einem Programm, einschließlich anschaulichem Beispielcode.

Der Befehl `ar`, ein echter Veteran, existiert seit 1971. Sein Name leitet sich von seinem ursprünglichen Zweck ab: Archivdateien zu erzeugen. Eine Archivdatei dient als Container für mehrere andere Dateien. Diese Dateien können dem Archiv hinzugefügt, daraus entfernt oder extrahiert werden. Obwohl heutzutage andere Programme wie `tar` diese Aufgaben übernommen haben, wird `ar` weiterhin für bestimmte Zwecke genutzt.

Der primäre Anwendungsfall von `ar` liegt heute in der Erstellung statischer Bibliotheken, die in der Softwareentwicklung eingesetzt werden. Darüber hinaus wird `ar` zur Erstellung von Paketdateien wie den „.deb“-Dateien verwendet, die in Debian-basierten Linux-Distributionen, wie z.B. Ubuntu, verbreitet sind.

Wir werden uns den notwendigen Schritten zur Erstellung und Bearbeitung einer statischen Bibliothek widmen und zeigen, wie man sie in einem Programm einsetzt. Dafür benötigen wir eine konkrete Aufgabe für unsere Bibliothek. In unserem Fall soll die Bibliothek in der Lage sein, Textstrings zu verschlüsseln und zu entschlüsseln.

Es sei darauf hingewiesen, dass die hier gezeigte Verschlüsselung lediglich zu Demonstrationszwecken dient und nicht für sensible Daten eingesetzt werden sollte. Es handelt sich um eine sehr einfache Substitutionschiffre, bei der jeder Buchstabe durch den nächsten im Alphabet ersetzt wird (A wird zu B, B wird zu C, usw.).

Die Funktionen `cipher_encode()` und `cipher_decode()`

Wir arbeiten in einem Verzeichnis namens „library“ und erstellen später ein Unterverzeichnis namens „test“.

In diesem Verzeichnis befinden sich zwei Dateien. In der Datei `cipher_encode.c` definieren wir die Funktion `cipher_encode()`:


void cipher_encode(char *text)
{
 for (int i=0; text[i] != 0x0; i++) {
   text[i]++;
 }

} // end of cipher_encode

Die zugehörige Funktion `cipher_decode()` befindet sich in der Datei `cipher_decode.c`:


void cipher_decode(char *text)
{
 for (int i=0; text[i] != 0x0; i++) {
   text[i]--;
 }

} // end of cipher_decode

Dateien, die Anweisungen für ein Programm enthalten, werden als Quellcodedateien bezeichnet. Wir werden eine Bibliotheksdatei namens `libcipher.a` erstellen. Diese enthält die kompilierten Versionen unserer beiden Quellcodedateien. Zusätzlich erstellen wir eine Header-Datei namens `libcipher.h`, welche die Definitionen unserer beiden Funktionen beinhaltet.

Jeder, der die Bibliothek und die Header-Datei besitzt, kann die beiden Funktionen in seinen eigenen Projekten verwenden, ohne sie neu schreiben zu müssen. Sie können einfach die Funktionalitäten unserer Bibliothek wiederverwenden.

Kompilieren der Dateien `cipher_encode.c` und `cipher_decode.c`

Zum Kompilieren der Quellcodedateien verwenden wir `gcc`, den GNU-Standardcompiler. Die Option `-c` (compile, no link) weist `gcc` an, die Dateien zu kompilieren und danach zu stoppen. Dies erzeugt aus jeder Quellcodedatei eine sogenannte Objektdatei. Der `gcc`-Linker nimmt normalerweise alle Objektdateien und verknüpft sie zu einem ausführbaren Programm, was wir mit der Option `-c` vermeiden. Wir benötigen lediglich die Objektdateien.

Prüfen wir zuerst, ob die benötigten Dateien vorhanden sind:

ls -l

Die beiden Quellcodedateien befinden sich wie erwartet im Verzeichnis. Lassen Sie uns diese mit `gcc` in Objektdateien kompilieren:

gcc -c cipher_encode.c
gcc -c cipher_decode.c

Wenn alles korrekt abläuft, sollte `gcc` keine Ausgabe anzeigen.

Dies erzeugt zwei Objektdateien mit denselben Namen wie die Quellcodedateien, jedoch mit der Dateiendung „.o“. Diese Dateien werden wir später unserer Bibliotheksdatei hinzufügen.

ls -l

Erstellen der Bibliothek `libcipher.a`

Um die Bibliotheksdatei – welche eigentlich eine Archivdatei ist – zu erstellen, verwenden wir `ar`.
Wir nutzen die Option `-c` (create), um die Bibliotheksdatei anzulegen, die Option `-r` (add with replace), um die Dateien zur Bibliothek hinzuzufügen und die Option `-s` (index), um einen Index der enthaltenen Dateien zu erstellen.

Wir nennen unsere Bibliotheksdatei `libcipher.a`. Wir geben diesen Namen zusammen mit den Namen der Objektdateien, die der Bibliothek hinzugefügt werden sollen, in der Befehlszeile an:

ar -crs libcipher.a cipher_encode.o cipher_decode.o

Wenn wir die Dateien im Verzeichnis auflisten, sehen wir, dass wir nun die Datei `libcipher.a` haben.

ls -l

Mit der Option `-t` (Tabelle) von `ar` können wir die Module innerhalb der Bibliotheksdatei einsehen.

ar -t libcipher.a

Erstellen der Header-Datei `libcipher.h`

Die Datei `libcipher.h` wird in jedes Programm eingebunden, das die Bibliothek `libcipher.a` verwendet. Diese Datei muss die Definitionen der Funktionen der Bibliothek enthalten.

Um die Header-Datei zu erstellen, müssen wir die Funktionsdefinitionen in einen Texteditor, wie z.B. `gedit`, eingeben. Speichern Sie diese als „libcipher.h“ im gleichen Verzeichnis wie `libcipher.a`.


void cipher_encode(char *text);
void cipher_decode(char *text);

Verwenden der Bibliothek `libcipher`

Der beste Weg, unsere neue Bibliothek zu testen, ist die Erstellung eines kleinen Programms, das sie verwendet. Dazu erstellen wir zunächst ein neues Verzeichnis namens „test“.

mkdir test

Wir kopieren die Bibliotheks- und Header-Dateien in dieses neue Verzeichnis.

cp libcipher.* ./test

Dann wechseln wir in das neu erstellte Verzeichnis.

cd test

Prüfen wir, ob die beiden Dateien vorhanden sind.

ls -l

Jetzt müssen wir ein kleines Programm schreiben, das die Bibliothek verwendet und ihre korrekte Funktion nachweist. Geben Sie den folgenden Text in einen Editor ein und speichern Sie ihn als „test.c“ im Testverzeichnis:


#include <stdio.h>
#include <stdlib.h>

#include "libcipher.h"

int main(int argc, char *argv[])
{
 char text[]="etoppc.com loves Linux";

 puts(text);

 cipher_encode(text);
 puts(text);

 cipher_decode(text);
 puts(text);

 exit (0);

} // end of main

Der Programmablauf ist recht simpel:

Es bindet die Header-Datei `libcipher.h` ein, um die Definitionen der Bibliotheksfunktionen zu erhalten.

Es erstellt einen String namens „text“ mit dem Inhalt „etoppc.com loves Linux“.

Dieser String wird auf dem Bildschirm ausgegeben.

Anschließend wird die Funktion `cipher_encode()` aufgerufen, um den String zu verschlüsseln und wieder auszugeben.

Zuletzt wird `cipher_decode()` verwendet, um den String zu entschlüsseln und erneut auszugeben.

Um das Testprogramm zu erstellen, müssen wir die Datei `test.c` kompilieren und die Bibliothek einbinden. Die Option `-o` (Ausgabe) gibt `gcc` an, wie das erzeugte ausführbare Programm heißen soll.

gcc test.c libcipher.a -o test

Wenn `gcc` ohne weitere Meldung zur Eingabeaufforderung zurückkehrt, ist alles in Ordnung. Jetzt können wir unser Programm testen:

./test

Und wie erwartet zeigt das Programm den Klartext, dann den verschlüsselten Text und schließlich den entschlüsselten Text an. Dies beweist, dass unsere Bibliothek korrekt funktioniert.

Erfolg! Aber das muss nicht alles sein.

Hinzufügen eines weiteren Moduls zur Bibliothek

Fügen wir der Bibliothek eine weitere Funktion hinzu, die es dem Programmierer ermöglicht, die Bibliotheksversion anzuzeigen. Diese neue Funktion muss erstellt, kompiliert und der Bibliotheksdatei hinzugefügt werden.

Geben Sie den folgenden Code in einen Editor ein und speichern Sie ihn als `cipher_version.c` im Bibliotheksverzeichnis:


#include <stdio.h>

void cipher_version(void)
{
 puts("etoppc.com :: VERY INSECURE Cipher Library");
 puts("Version 0.0.1 Alphan");

} // end of cipher_version

Nun müssen wir die Definition der neuen Funktion in der Header-Datei `libcipher.h` ergänzen, sodass diese wie folgt aussieht:


void cipher_encode(char *text);
void cipher_decode(char *text);
void cipher_version(void);

Speichern Sie die geänderte Header-Datei `libcipher.h`.

Als nächstes muss die Datei `cipher_version.c` kompiliert werden, um eine Objektdatei `cipher_version.o` zu erhalten:

gcc -c cipher_version.c

Die neue Objektdatei `cipher_version.o` kann der Bibliothek `libcipher.a` nun hinzugefügt werden. Die Option `-v` (verbose) bewirkt, dass `ar` uns mitteilt, was es getan hat.

ar -rsv libcipher.a cipher_version.o

Die neue Objektdatei wird der Bibliotheksdatei hinzugefügt, und `ar` bestätigt dies mit der Ausgabe „a“, was für „hinzugefügt“ steht.

Mit der Option `-t` (Tabelle) können wir prüfen, welche Module sich jetzt in der Bibliotheksdatei befinden:

ar -t libcipher.a

Es befinden sich nun drei Module in unserer Bibliothek. Nutzen wir die neue Funktion!

Verwenden der Funktion `cipher_version()`

Entfernen wir die alte Bibliothek und Header-Datei aus dem Testverzeichnis, kopieren die neuen Dateien hinein und wechseln wieder ins Testverzeichnis:

Zuerst löschen wir die alten Versionen der Dateien.

rm ./test/libcipher.*

Dann kopieren wir die neuen Versionen in das Testverzeichnis.

cp libcipher.* ./test

Und wir wechseln ins Testverzeichnis.

cd test

Jetzt können wir unser Testprogramm `test.c` so anpassen, dass es die neue Bibliotheksfunktion nutzt.

Dazu fügen wir dem Programm eine neue Zeile hinzu, die `cipher_version()` aufruft. Wir platzieren diese vor der ersten `puts(text);` Zeile:


#include <stdio.h>
#include <stdlib.h>

#include "libcipher.h"

int main(int argc, char *argv[])
{
 char text[]="etoppc.com loves Linux";

 // new line added here
 cipher_version();

 puts(text);

 cipher_encode(text);
 puts(text);

 cipher_decode(text);
 puts(text);

 exit (0);

} // end of main

Speichern Sie die geänderte Datei als `test.c`. Wir können diese nun kompilieren und testen, ob die neue Funktion wie erwartet arbeitet.

gcc test.c libcipher.a -o test

Führen wir die neue Testversion aus:

Die neue Funktion arbeitet korrekt. Am Anfang der Ausgabe sehen wir die Bibliotheksversion.

Es könnte jedoch ein Problem geben.

Ersetzen eines Moduls in der Bibliothek

Dies ist nicht die erste Version der Bibliothek, sondern die zweite. Unsere Versionsnummer ist also falsch. Die erste Version hatte keine Funktion `cipher_version()`. Diese hier schon. Also sollte die Versionsnummer „0.0.2“ sein. Wir müssen die Funktion `cipher_version()` durch die korrigierte Version ersetzen.

Glücklicherweise ist dies mit `ar` sehr einfach.

Bearbeiten wir zunächst die Datei `cipher_version.c` im Bibliotheksverzeichnis und ändern den Text „Version 0.0.1 Alpha“ zu „Version 0.0.2 Alpha“. Die Datei sollte wie folgt aussehen:


#include <stdio.h>

void cipher_version(void)
{
 puts("etoppc.com :: VERY INSECURE Cipher Library");
 puts("Version 0.0.2 Alphan");

} // end of cipher_version

Speichern Sie die Datei. Diese muss erneut kompiliert werden, um eine neue Objektdatei `cipher_version.o` zu erstellen.

gcc -c cipher_version.c

Jetzt ersetzen wir die vorhandene `cipher_version.o` in der Bibliothek durch unsere neu kompilierte Version.

Wir haben zuvor die Option `-r` (add with replace) verwendet, um der Bibliothek neue Module hinzuzufügen. Wenn wir diese Option mit einem bereits in der Bibliothek vorhandenen Modul verwenden, ersetzt `ar` die alte durch die neue Version. Die Option `-s` (index) aktualisiert den Bibliotheksindex, und `-v` (verbose) bewirkt, dass `ar` uns mitteilt, was passiert ist.

ar -rsv libcipher.a cipher_version.o

Diesmal meldet `ar`, dass das Modul `cipher_version.o` ersetzt wurde. Das „r“ steht hier für „ersetzt“.

Verwenden der aktualisierten `cipher_version()`-Funktion

Nun testen wir die modifizierte Bibliothek und prüfen, ob sie korrekt funktioniert.

Kopieren wir die Bibliotheksdateien in das Testverzeichnis.

cp libcipher.* ./test

Wechseln wir ins Testverzeichnis.

cd ./test

Wir müssen unser Testprogramm mit der neuen Bibliothek neu kompilieren.

gcc test.c libcipher.a -o test

Und jetzt können wir unser Programm erneut testen.

./test

Die Ausgabe des Testprogramms entspricht unseren Erwartungen. Die korrekte Versionsnummer wird im Versionsstring angezeigt und die Verschlüsselungs- und Entschlüsselungsroutinen arbeiten weiterhin fehlerfrei.

Löschen von Modulen aus einer Bibliothek

Nach all dem ist es vielleicht schade, aber wir werden jetzt die Datei `cipher_version.o` aus der Bibliotheksdatei löschen.

Dazu verwenden wir die Option `-d` (delete). Zusätzlich nutzen wir `-v` (verbose), damit `ar` uns mitteilt, was getan wurde, und `-s` (index) um den Index in der Bibliotheksdatei zu aktualisieren.

ar -dsv libcipher.a cipher_version.o

`ar` bestätigt die Entfernung des Moduls mit der Ausgabe „d“ (gelöscht).

Wenn wir `ar` nun bitten, die Module in der Bibliotheksdatei aufzulisten, sehen wir, dass wir wieder bei zwei Modulen sind:

ar -t libcipher.a

Wenn Sie Module aus Ihrer Bibliothek löschen, denken Sie daran, auch deren Definitionen aus der Headerdatei der Bibliothek zu entfernen.

Teilen Sie Ihren Code

Bibliotheken ermöglichen es, Code auf eine praktische und dennoch private Art und Weise weiterzugeben. Jeder, dem Sie die Bibliotheksdatei und die Headerdatei übergeben, kann Ihre Bibliothek verwenden, während Ihr eigentlicher Quellcode privat bleibt.