Produkty Novinky Články Návody Kontakty

Křížový kompilátor

Dnešní doba vývojářům aplikací pro embedded zařízení vysloveně přeje. Svědčí o tom i to, že křížové nástroje (cross-tools) se pomalu ale jistě stávají nedílnou součástí standardních linuxových distribucí v podobě instalačních balíčků. Na druhou stranu stálicí mezi volně dostupnými křížovými nástroji třetích stran je vývojové prostředí Sourcery G++ Lite. A právě o tomhle prostředí, resp o nástrojích, které nabízí, budou následující odstavce.
Společnost CodeSourcery (nyní Mentor Graphics) nabízí své integrované vývojové prostředí Sourcery G++ pro vývoj aplikací v jazyce C/C++ pro řadu platforem a v různých úrovních vybavenosti. My se zaměříme na edici Lite pro operační systém Linux, konkrétně pro cílovou architekturu ARM. Tato edice nabízí zdarma kompletní sadu vývojových nástrojů pro příkazovou řádku (kompilátor, debugger a binutils). Součástí edice Lite však není grafická nádstavba, která by s těmito nástroji tvořila integrované vývojové prostředí. Toto omezení lze ovšem snadno vyřešit použitím freewarového integrovaného vývojového prostředí Eclipse CDT.
Vedle sady křížových nástrojů je součástí vývojového prostředí Sourcery G++ také sada standardních systémových knihoven a jejich hlavičkových souborů. Těchto sad knihoven je ve vývojovém prostředí k dispozici vlastně několik, protože Sourcery G++ podporuje více architektur procesorů ARMInformationArchitektura procesorů ARM určuje mimo jiné i použitou instrukční sadu. O tom ale více v dalším textu.
Naším cílem ovšem není vychvalovat společnost CodeSourcery a její produkty. My vás chceme seznámit s praktickým používáním vývojových nástrojů této společnosti. Proto vás provedeme instalací prostředí Sourcery G++ na linuxový desktop, vysvětlíme si jak kompilátoru přikázat, aby překládal zdrojové kódy pro vaši konkrétní platformu, a na základě zkušeností uvedených v části o nativním kompilátoru si ukážeme, kde křížový kompilátor gcc z prostředí Sourcery G++ hledá při překladu hlavičkové soubory a systémové knihovny. Protože i tuhle znalost využijeme, např. když si budeme chtít sestavit vlastní linuxovou distribuci pro naše embedded zařízení a budeme potřebovat nakopírovat tu správnou sadu systémových knihoven.

Instalace

Vývojové prostředí Sourcery G++ Lite pro platformu ARM a pro cílový OS GNU/Linux lze stáhnout ze stránek firmy CodeSourceryInformationhttp://www.codesourcery.com/sgpp/lite/arm/portal/release1600. Tato verze vývojového prostředí je určena pro kompilaci jádra, modulů jádra (ovladačů) a samozřejmě pro vývoj aplikací, které poběží v prostředí OS Linux na cílovém zařízení.
Postup:
  1. Z uvedené adresy si stáhneme instalační balíček pro OS Linux v podobě komprimovaného archivního souboru *.tar.bz2.tar
  2. Přepneme se na uživatele root:
    $ su
    
  3. Vytvoříme si cílový adresář pro instalaci Sourcery G++ v adresáři /usr/local:
    # mkdir /usr/local/SourceryG++
    
  4. Do tohoto adresáře rozbalíme instalační archiv:
    # tar -xf arm-2010.09-50-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2.tar -C /usr/local/SourceryG++ 
    
  5. Nyní máme v adresáři /usr/local/SourceryG++ podadresář arm-2010.09/, kde je nainstalováno vývojové prostředí SourceryG++. Bohužel stejný název je zvolen i pro ostatní varianty vývojového prostředí (jiné cílové OS), takže jej pro jistotu přejmenujeme na arm-none-linux-gnueabi:
    # cd /usr/local/SourceryG++
    # mv arm-2010.09/ arm-none-linux-gnueabi/
    
  6. Teď musíme zajistit, aby bylo možné spustit libovolný nainstalovaný křížový nástroj, např.: křížový kompilátor, z jakéhokoliv adresáře. Tedy abychom mohli na příkazovou řádku napsat přímo arm-none-linux-gnueabi-gcc a nemuseli vždy uvádět celou cestu. To lze zajistit více způsoby.
    My zvolíme ten nejjednodušší - vytvoříme symbolické odkazy na všechny nástroje a umístíme je do adresáře /usr/local/bin. Což je jeden z výchozích adresářů, které jsou prohledávány, když je zadán příkaz bez určení cesty. Provedeme to takto:
    # cd /usr/local/bin
    # cp -s /usr/local/SourceryG++/arm-none-linux-gnueabi/bin/* .
    
    Další možností je, že trvale přidáme cestu /usr/local/SourceryG++/arm-none-linux-gnueabi/bin do proměnné PATH v našem uživatelském profilu. V editoru přidáme do souboru .bash_profile nebo .profile v našem domovském adresáři tento řádek:
    PATH="$PATH:/usr/local/SourceryG++/arm-none-linux-gnueabi/bin"
    
    Po opětovném přihlášení bude proměnná PATH obsahovat i přidanou cestu.
  7. Přepneme se zpátky na běžného uživatele:
    # exit
    
  8. Ověříme si, že lze spustit kompilátor:
    $ arm-none-linux-gnueabi-gcc -v
    
    Pokud vše funguje, tak bychom měli vidět výpis nastavení kompilátoru. Pokud ne, tak ověříme, platnost symbolických odkazů v /usr/local/bin a hodnotu proměnné PATH, kde musí být uveden i adresář /usr/local/bin.

Nastavení

U křížových nástrojů je zvykem, že je jejich název rozšířen o prefix. Ten je jednak odlišuje od nativních nástrojů a jednak umožňuje rychle určit pro jakou platformu a pro jaký operační systém jsou dané nástroje určeny. Křížové nástroje vývojového prostředí Sourcery G++ pro platformu ARM a OS GNU/Linux mají proto prefix arm-none-linux-gnueabi-. Křížový kompilátor gcc má tedy úplný název arm-none-linux-gnueabi-gcc. Přídomek eabi je kvůli tomu, že tyto nástroje vytváří spustitelné a objektové soubory dle specifikace binárního aplikačního rozhraní ARM EABI.
Pro maximální využití výkonu procesoru použitého v cílovém zařízení si musíme zjistit, jakou architekturu procesorů ARMInformationArchitekturou procesorů ARM je určena i instrukční sada (dále jen architekturu), a jaké procesorové jádroInformationHezký přehled architektur a procesorových jader firmy ARM je zde [EN]: http://en.wikipedia.org/wiki/ARM_architecture#ARM_cores daný procesor implementuje. Podle toho pak zvolíme nastavení křížového kompilátoru, které dovolí maximální optimalizaci kódu pro cílový procesor. Vezměme si například mikrokontrolér AT91SAM9260 firmy Atmel. Ten implementuje procesorové jádro ARM926EJ-S a podporuje architekturu ARMv5TEJ, respektive ARMv5TE. J značí podporu běhu javovského bytového kódu, a proto kompilátor gcc nemá explicitní podporu přímo pro tuto architekturu.
Z návoduInformationhttp://www.codesourcery.com/sgpp/lite/arm/portal/doc11470/gcc.pdf pro kompilátor gcc z vývojového prostředí Sourcery G++ zjistíme, že pro uvedený mikrokontrolér musíme volat kompilátor takto:
$ arm-none-linux-gnueabi-gcc -march=armv5te -mtune=arm926ej-s -mabi=aapcs-linux
kde:
  • volba -march=armv5te nastavuje architekturu procesoru na ARMv5TE. Tato architektura je výchozí pro kompilátor z prostředí SourceryG++, ale přesto je vhodné uvádět tuto volbu kvůli přehlednosti.
    Poznámka: Volbu -march=armv5te musíme také předat kompilátoru v případě, že pomocí něj voláme linker. Kompilátor si totiž na základě této hodnoty nastaví tzv. sysroot prefix. Více viz další text.
  • volba -mtune=arm926ej-s informuje kompilátor o použitém procesorovém jádru. Tato volba je podobná jako -mcpu, ale údajně může v některých případech přinést lepší výkon.
  • volba -mabi=aapcs-linux informuje kompilátor o použitém aplikačním binárním rozhraní (ABI). Pro spustitelné soubory pro OS Linux vyhovující specifikaci rozhraní ARM EABI se používá uvedená hodnotaInformationhttp://wiki.debian.org/ArmEabiPort#GCC_view.
Poznámka: Křížové nástroje z balíku Sourcery G++generují aplikace s aplikačním binárním rozhraním, tzv. ABIInformation Aplikační binární rozhraní (ABI) popisuje nízkoúrovňové rozhraní mezi aplikacemi a operačním systémem, aplikacemi a jejich knihovnami nebo součástmi aplikací. Rozhraní ARM EABI je pak vylepšením původního rozhraní ABI.
Pomocí ABI se tak například definuje jakým způsobem bude aplikace volat systémové služby jádra, funkce knihoven nebo jiných odděleně kompilovaných částí aplikace.
, které odpovídá standardu ARM EABI. Abychom mohli takto sestavené aplikace v cílovém zařízení spustit, musí v cílovém zařízení běžet linuxové jádro, které rozhraní ARM EABI podporuje. Tento požadavek splňuje jádro verze 2.6.16 a vyšší, které bylo explicitně nakonfigurováno a zkompilováno s podporou rozhraní ARM EABI. Ve výchozí konfiguraci totiž jádro podporuje pouze původní rozhraní ABI, někdy také označované jako OABI (old ABI).

Hlavičkové soubory

Stejně jako u nativního kompilátoru, tak i v případě křížového kompilátoru, bychom měli znát, jaké výchozí adresáře kompilátor prohledává, když hledá hlavičkové soubory.
Už víme, že dotaz musíme položit preprocesoru:
$ arm-none-linux-gnueabi-cpp -v
Z výstupu na obrázku 1.4 vidíme, že preprocesor hledá hlavičkové soubory ve výchozím adresáři /usr/local/SourceryG++/arm-none-linux-gnueabi/, resp. v jeho podadresářích, a to v tomto pořadí:
  1. lib/gcc/arm-none-linux-gnueabi/4.5.1/include/
  2. lib/gcc/arm-none-linux-gnueabi/4.5.1/include-fixed/
  3. arm-none-linux-gnueabi/include/
  4. arm-none-linux-gnueabi/libc/usr/include/
Poznámka: Na výpisu jsou adresáře uváděny nejprve jako cesta k preprocesoru (ta je pak aktuálním adresářem) a pak relativně k jeho umístění.
obrázek vystup-prikazu-cpp-v-arm
Obrázek 1.4 Výstup příkazu cpp -v, platforma ARM

Knihovny

Nyní jsou na řadě knihovny. Naše pátrání po seznamu adresářů, které jsou prohledávány, když se mají k naší aplikaci linkovat nějaké knihovny bude o něco složitější, než v případě nativního kompilátoru. Nicméně nepředbíhejme.
Kde hledá linker
S využitím znalostí získaných při pátrání po vlastnostech nativního kompilátoru, zkusíme nejprve zjistit, kde křížový linker dle výchozího linkovacího skriptu hledá knihovny:
$ arm-none-linux-gnueabi-ld --verbose | grep SEARCH_DIR | tr -s ’ :’ \\012
Výstup na obrázku 1.5 říká, že jsou to staré známé adresáře /usr/local/lib, /lib a /usr/lib. Ovšem nyní jsou cesty k nim uvozeny rovnítkem. Co to znamená? Návod k linkeru říká, že tato rovnítka jsou nahrazena tzv. sysroot prefixem.
Sysroot prefix lze chápat jako cestu, která se doplní (místo rovnítka) do cesty k výchozím adresářům, kde linker dle linkovacího scriptu hledá knihovny. Jinými slovy lze říci, že uvedené adresáře jsou vlastně podadresáři nějakého hlavního adresáře. A cestu k tomuto hlavnímu adresáři definuje sysroot prefix.
K čemu je to dobré? Vývojové prostředí SourceryG++ totiž podporuje více architektur procesorů ARM a pro každou z nich má adekvátní sadu knihoven umístěnou v uvedených adresářích, resp. podadresářích. Struktura těchto podadresářů je vždy stejná. To, která sada knihoven bude použita při linkování, určuje právě sysroot prefix.
Výchozí sysroot prefix je definován při kompilaci vývojového prostředí a použije se, pokud je linker volaný přímo. Zjistíme jej voláním kompilátoru s volbou -v.
V případě, že voláme linker prostřednictvím kompilátoru, tak se sysroot prefix nastavuje dle zvolené architektury. Typ architektury volíme parametrem kompilátoru -march=architektura. Dodejme, že výchozí hodnotou (pokud není zadána volba -march) je hodnota armv5te.
obrázek vystup-prikazu-ld-verbose-arm
Obrázek 1.5 Výstup příkazu ld --verbose, platforma x86
Jak se ale tedy dozvíme, který sysroot prefix bude nastaven např. pro výchozí architekturu? Stačí se zeptat kompilátoru:
$ arm-none-linux-gnueabi-gcc -print-sysroot
Zjistíme, že sysroot prefix je pro architekturu ARMv5TE nastaven na cestu /usr/local/SourceryG++/arm-none-linux-gnueabi/arm-none-linux-gnueabi/libc.
Pokud by nás zajímala například starší architektura ARMv4T, tak stačí zadat příkaz:
$ arm-none-linux-gnueabi-gcc -march=armv4t -print-sysroot
Sysroot prefix se pak změní na cestu /usr/local/SourceryG++/arm-none-linux-gnueabi/arm-none-linux-gnueabi/libc/armv4t.
Kde hledá kompilátor
Teď už známe výchozí adresáře pro hledání knihoven, které určuje výchozí linkovací skript. My ale také víme, že situace je složitější a že před těmito adresáři linker prohledává adresáře, které automaticky přidává kompilátor. Dotazem na kompilátor zjistíme, o které adresáře se vlastně jedná:
$ arm-none-linux-gnueabi-gcc -print-search-dirs | tr -s ’:’ \\012
Vidíme (viz obrázek 1.6), že kompilátor přidává pro výchozí architekturu ARMv5TE jako výchozí podadresáře lib/ a usr/lib/, které se nachází v hlavním adresáři /usr/local/SourceryG++/arm-none-linux-gnueabi/arm-none-linux-gnueabi/libc/. Ostatní adresáře patrné z výpisu nebyly pro stručnost zmíněny.
obrázek vystup-prikazu-gcc-print-search-dirs-arm
Obrázek 1.6 Výstup prikazu gcc -print-search-dirs, platforma ARM
Závěr
Závěr je podobný jako u nativního kompilátoru - knihovny pro výchozí architekturu ARMv5TE hledá linker v podadresářích lib/ a usr/lib/. Ty najdeme v adresáři /usr/local/SourceryG++/arm-none-linux-gnueabi/arm-none-linux-gnueabi/libc/. Výchozí linkovací skript pak přidá ještě podadresář usr/local/lib/. Ostatní adresáře z výpisu na obrázku 1.6 nebyly pro stručnost uvedeny.
Knihovny z uvedených výchozích adresářů pro architekturu ARMv5TE musíme přidat (byť ne nezbytně všechny) do linuxové distribuce pro cílové zařízení, protože tvoří standardní C/C++ běhové prostředí v cílovém zařízení.