Jak používat příkaz ar Linuxu k vytváření statických knihoven

Při vývoji softwaru je příkaz ar v Linuxu neocenitelným nástrojem pro vytváření knihoven funkcí. Tento návod vám ukáže, jak krok za krokem vytvořit statickou knihovnu, provést její úpravy a začlenit ji do programu. Vše bude doprovázeno příklady kódu.

Příkaz `ar` je skutečný veterán, který se poprvé objevil již v roce 1971. Jeho název odkazuje na původní záměr použití – archivaci souborů. Archivní soubor je v podstatě kontejner, který sdružuje více souborů do jednoho. Uživatelé, kteří hledají tuto funkcionalitu dnes, se ale obracejí spíše na nástroje jako je `tar`.

Nicméně, příkaz `ar` si stále zachovává své specifické využití, zejména při vytváření statických knihoven, které jsou klíčové při vývoji softwaru. Mimo to, `ar` se také používá pro konstrukci balíčkových souborů, jako jsou například soubory „.deb“, které se využívají v distribucích Debian Linux a jeho odnožích, jako je Ubuntu.

V tomto průvodci se podíváme na kroky potřebné k sestavení a modifikaci statické knihovny. Dále si ukážeme, jak takovou knihovnu použít v programu. Pro demonstraci si vytvoříme knihovnu pro kódování a dekódování textových řetězců.

Je důležité zdůraznit, že se jedná o rychlý a jednoduchý příklad pro demonstraci. Nejedná se o zabezpečenou metodu šifrování pro citlivá data. Použijeme jednoduchou substituční šifru, kde se každé písmeno posune o jednu pozici v abecedě (A se stane B, B se stane C, atd.).

Funkce cipher_encode() a cipher_decode()

Pro naše účely si vytvoříme adresář pojmenovaný „library“. Později v něm vytvoříme podadresář „test“.

V adresáři „library“ budeme mít dva soubory. První, nazvaný `cipher_encode.c`, bude obsahovat funkci cipher_encode():

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

} // konec funkce cipher_encode

Druhý soubor, `cipher_decode.c`, bude obsahovat funkci cipher_decode():

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

} // konec funkce cipher_decode

Soubory obsahující programovací instrukce se nazývají zdrojové soubory. Naše knihovna `libcipher.a` bude obsahovat zkompilované verze těchto dvou zdrojových souborů. Dále si vytvoříme hlavičkový soubor `libcipher.h`, který bude obsahovat definice obou funkcí.

Každý, kdo bude mít k dispozici naši knihovnu a hlavičkový soubor, bude moci tyto funkce použít ve svých programech. Nemusí tak znovu vytvářet potřebné funkce, ale jednoduše využije kopie z naší knihovny.

Kompilace cipher_encode.c a cipher_decode.c

Ke kompilaci zdrojových souborů použijeme `gcc`, což je standardní GNU kompilátor. Použitím volby `-c` (kompilovat, ne linkovat) sdělujeme kompilátoru, aby zdrojové soubory zkompiloval, ale nepokračoval dále. Tímto způsobem vytvoříme tzv. objektové soubory. Linker `gcc` by běžně spojil všechny objektové soubory dohromady a vytvořil spustitelný program. My ale tento krok přeskočíme a vytvoříme pouze objektové soubory.

Nejprve zkontrolujeme, zda máme všechny soubory, které potřebujeme:

ls -l

Vypíše se seznam souborů a potvrdíme přítomnost obou zdrojových souborů. Nyní je zkompilujeme pomocí `gcc` do objektových souborů:

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

Pokud vše proběhne bez problémů, `gcc` nezobrazí žádný výstup.

Kompilací se vygenerují dva objektové soubory se stejným názvem jako zdrojové soubory, ale s příponou „.o“. Tyto soubory musíme přidat do knihovny.

ls -l

Vytvoření knihovny libcipher.a

Pro vytvoření souboru knihovny, který je vlastně archivní soubor, použijeme příkaz `ar`.

Použijeme volbu `-c` (vytvořit), volbu `-r` (přidat s nahrazením), pro přidání objektových souborů do knihovny a volbu `-s` (index) pro vytvoření indexu souborů uvnitř knihovny.

Knihovnu nazveme `libcipher.a`. Tento název zadáme na příkazové řádce spolu s názvy objektových souborů:

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

Po vypsání obsahu adresáře zjistíme, že jsme úspěšně vytvořili soubor `libcipher.a`.

ls -l

Pomocí volby `-t` (tabulka) s příkazem `ar` můžeme zobrazit moduly obsažené uvnitř souboru knihovny.

ar -t libcipher.a

Vytvoření hlavičkového souboru libcipher.h

Hlavičkový soubor `libcipher.h` bude součástí každého programu, který používá knihovnu `libcipher.a`. Tento soubor musí obsahovat definice funkcí, které se v knihovně nacházejí.

Pro vytvoření hlavičkového souboru musíme do textového editoru, jako je `gedit`, zadat definice funkcí. Soubor pojmenujeme „libcipher.h“ a uložíme ho do stejného adresáře jako soubor `libcipher.a`:

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

Použití knihovny libcipher

Nejspolehlivější způsob, jak otestovat naši knihovnu, je napsat program, který ji bude používat. Nejdříve vytvoříme adresář pojmenovaný „test“:

mkdir test

Následně do nového adresáře zkopírujeme knihovnu a hlavičkový soubor:

cp libcipher.* ./test

Nyní se přesuneme do nového adresáře:

cd test

Zkontrolujeme, že soubory byly zkopírovány:

ls -l

Nyní vytvoříme testovací program. Do editoru vložíme následující kód a uložíme jej jako „test.c“ v adresáři „test“:

#include 
#include 

#include "libcipher.h"

int main(int argc, char *argv[])
{
  char text[]="How-To Geek loves Linux";

  puts(text);

  cipher_encode(text);
  puts(text);

  cipher_decode(text);
  puts(text);

  exit (0);

} // konec funkce main

Program funguje takto:

  • Načte hlavičkový soubor `libcipher.h`, aby mohl používat funkce knihovny.
  • Vytvoří řetězec „text“ s hodnotou „How-To Geek loves Linux“.
  • Vytiskne tento řetězec na obrazovku.
  • Zavolá funkci `cipher_encode()` pro zakódování řetězce a vytiskne zakódovaný řetězec.
  • Zavolá funkci `cipher_decode()` pro dekódování řetězce a vytiskne dekódovaný řetězec.

Pro vygenerování spustitelného programu musíme zkompilovat program `test.c` a propojit ho s knihovnou. Volba `-o` (výstup) udává kompilátoru, jak se má generovaný spustitelný soubor jmenovat.

gcc test.c libcipher.a -o test

Pokud `gcc` po provedení příkazu tiše vrátí příkazový řádek, je vše v pořádku. Nyní otestujeme náš program:

./test

A vidíme očekávaný výstup. Program vytiskne původní text, zašifrovaný text a poté dešifrovaný text. Vše funguje správně a využívá funkce naší knihovny.

Úspěch! Ale proč se zastavit? Pojďme na to dál.

Přidání dalšího modulu do knihovny

Přidáme do knihovny další funkci. Bude se jednat o funkci, která zobrazí verzi knihovny. Vytvoříme novou funkci, zkompilujeme ji a přidáme do knihovny jako nový objektový soubor.

Do editoru vložíme následující řádky a uložíme je jako `cipher_version.c` v adresáři „library“:

#include 

void cipher_version(void)
{
  puts("How-To Geek :: VERY INSECURE Cipher Library");
  puts("Version 0.0.1 Alphan");

} // konec funkce cipher_version

Musíme také přidat definici nové funkce do hlavičkového souboru `libcipher.h`. Přidáme nový řádek na konec souboru tak, aby vypadal takto:

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

Upravený soubor `libcipher.h` uložíme.

Nyní musíme zkompilovat soubor `cipher_version.c`, abychom získali objektový soubor `cipher_version.o`:

gcc -c cipher_version.c

Vytvořili jsme soubor `cipher_version.o`. Nyní ho přidáme do knihovny `libcipher.a` pomocí následujícího příkazu. Volba `-v` (verbose) zajistí, že `ar` vypíše informaci o tom, co provedl:

ar -rsv libcipher.a cipher_version.o

Nový objektový soubor byl přidán do knihovny a `ar` zobrazí potvrzení. „A“ znamená, že modul byl „added“ – přidán.

Můžeme použít volbu `-t` (tabulka), abychom viděli, jaké moduly jsou v knihovně obsažené.

ar -t libcipher.a

V knihovně jsou nyní tři moduly. Pojďme využít novou funkci.

Použití funkce cipher_version()

Odstraníme starou knihovnu a hlavičkový soubor z testovacího adresáře, zkopírujeme tam nové soubory a poté se opět přesuneme do testovacího adresáře.

Nejprve odstraníme staré verze souborů:

rm ./test/libcipher.*

Potom zkopírujeme nové verze do adresáře „test“:

cp libcipher.* ./test

Přesuneme se do adresáře „test“:

cd test

Nyní můžeme upravit program `test.c`, aby používal novou funkci. Přidáme do něj volání funkce `cipher_version()` před první řádek s `puts(text);`.

#include 
#include 

#include "libcipher.h"

int main(int argc, char *argv[])
{
  char text[]="How-To Geek loves Linux";

  // nový řádek přidán sem
  cipher_version();

  puts(text);

  cipher_encode(text);
  puts(text);

  cipher_decode(text);
  puts(text);

  exit (0);

} // konec funkce main

Upravený kód uložíme jako `test.c`. Nyní ho zkompilujeme a otestujeme, jestli nová funkce funguje:

gcc test.c libcipher.a -o test

Spustíme novou verzi programu:

Nová funkce funguje! Na začátku výstupu vidíme informace o verzi knihovny.

Může ale nastat problém.

Nahrazení modulu v knihovně

Toto není první verze knihovny, ale druhá. Číslo verze v našem programu je nesprávné. První verze knihovny neobsahovala funkci `cipher_version()`, ta je až v této. Měla by být verze „0.0.2“. Musíme funkci `cipher_version()` v knihovně nahradit opravenou verzí.

Naštěstí je to s příkazem `ar` velmi jednoduché.

Nejprve upravíme soubor `cipher_version.c` v adresáři „library“. Změníme text „Version 0.0.1 Alpha“ na „Version 0.0.2 Alpha“. Upravený kód by měl vypadat takto:

#include 

void cipher_version(void)
{
  puts("How-To Geek :: VERY INSECURE Cipher Library");
  puts("Version 0.0.2 Alphan");

} // konec funkce cipher_version

Uložíme soubor. Musíme ho znovu zkompilovat, abychom získali nový objektový soubor `cipher_version.o`:

gcc -c cipher_version.c

Nyní nahradíme existující modul `cipher_version.o` v knihovně novou zkompilovanou verzí.

K přidání nových modulů do knihovny jsme již použili volbu `-r` (přidat s nahrazením). Pokud ji použijeme na modul, který už v knihovně existuje, příkaz `ar` nahradí starou verzi novou. Volba `-s` (index) aktualizuje index knihovny a volba `-v` (verbose) nám řekne, co udělala.

ar -rsv libcipher.a cipher_version.o

Tentokrát `ar` hlásí, že nahradil modul `cipher_version.o`. „r“ znamená replaced – nahrazeno.

Použití aktualizované funkce cipher_version()

Nyní bychom měli použít naši upravenou knihovnu a zkontrolovat, zda funguje správně.

Zkopírujeme soubory knihovny do adresáře „test“:

cp libcipher.* ./test

Přesuneme se do adresáře „test“:

cd ./test

Musíme znovu zkompilovat testovací program s novou knihovnou:

gcc test.c libcipher.a -o test

A nyní můžeme náš program otestovat:

./test

Výstup z testovacího programu je nyní takový, jaký jsme očekávali. V řetězci verze se zobrazuje správné číslo verze a šifrovací i dešifrovací rutiny fungují.

Odstranění modulů z knihovny

Ačkoli se zdá škoda to udělat, zkusme odstranit soubor `cipher_version.o` z naší knihovny.

Pro tento účel použijeme volbu `-d` (smazat). Volba `-v` (verbose) nám umožní vidět, co příkaz `ar` provede. Nakonec použijeme `-s` (index) pro aktualizaci indexu v knihovně.

ar -dsv libcipher.a cipher_version.o

`ar` hlásí, že modul odstranil. „d“ znamená „deleted“ – smazáno.

Pokud se podíváme na moduly uvnitř knihovny, uvidíme, že máme opět jen dva moduly:

ar -t libcipher.a

Pokud se chystáte odstranit moduly z knihovny, nezapomeňte odstranit i jejich definice z hlavičkového souboru.

Sdílení kódu

Knihovny umožňují sdílení kódu praktickým, ale soukromým způsobem. Kdokoli, komu dáte knihovnu a hlavičkový soubor, může vaši knihovnu používat, aniž by měl přístup k vašemu zdrojovému kódu.