Nativní nebo křížové nástroje?
Podobně jako si Hamlet v Shakespearově dramatu pokládal kardinální otázku: ”Být či nebýt?”, tak si i programátor aplikací pro embedded zařízení musí položit a (zodpovědět!) podobně klíčovou otázku: ”Nativní či křížové nástroje?”. Filozofování o životě a jeho smyslu necháme povolanějším. Onu programátorskou otázku, zda nativní či křížové nástroje, se však pokusíme zodpovědět v následujícím textu.
Začneme hned další otázkou. Co vlastně jako programátoři ke své práci potřebujeme? Řada z vás bezmyšlenkovitě odpoví, že pravidelný přísun pizzy, kafe (včetně vody - žijeme přeci zdravě, že) a nějaký ten počítač. Ale já měl na mysli programové vybavení počítače, resp. vývojové nástroje. Pojďme si ve stručnosti připomenout, co všechno pro vývoj aplikace potřebujeme.
Nástroje vývojáře
Pro jednoduchost budeme uvažovat vývoj aplikace v jazyce C/C++, která poběží na našem počítači s OS Linux. Nejdůležitějším nástrojem je bezpochyby kompilátor (gcc). Kromě něj budeme určitě ještě potřebovat několik pomocných utilit tzv. binárních utilit (binutils) jako je linker (ld), preprocesor (cpp), readelf a další. A protože budeme chtít aplikaci sestavovat opakovaně a hlavně pohodlně, přidáme do seznamu ještě program make a soubor makefile. No a pro ladění aplikace budeme potřebovat debugger (gdb) a případně i trasovací utility (strace apod.).
Vedle vývojových nástrojů musí náš seznam doplňovat i sada standardních systémových knihoven a jejich hlavičkových souborů, které vytváří standardní běhové (run-time) prostředí pro naší čerstvě sestavenou aplikaci. V sadě knihoven rozhodně nesmí chybět knihovna libc, která implementuje standardní knihovní funkce jazyka C. Většinou však budeme potřebovat i další knihovny, které doplňují funkcionalitu knihovny libc o funkce vyžadované standardem POSIX.
Velmi často se nám budou hodit i hlavičkové soubory jádra v podobě tzv. sanitized kernel headers (očištěná sada hlavičkových souborů). Tyto hlavičkové soubory tvoří kompletní popis rozhraní systému Linux (API) a vůči nim se aplikace kompiluje v případě, že využívá nějaké systémové služby jádra volané přímo bez použití funkcí standardních systémových knihoven.
Uvedli jsme si seznam nástrojů, knihoven a hlavičkových souborů jádra, které potřebujeme pro sestavení a běh aplikace na našem počítači. Zmíněné nástroje se označují jako nativní (native), protože umí vytvořit spustitelný soubor aplikace pouze pro stejné prostředí, v kterém sami běží.
Nástroje vývojáře embedďáka
Zkusme si ale teď představit jaké nástroje, knihovny a hlavičkové soubory jádra budeme potřebovat, když dostaneme za úkol napsat a sestavit aplikaci pro embedded zařízení vybavené např. mikrokontrolérem AT91SAM9260 a operačním systémem Linux. Mikrokontrolér AT91SAM9260 se skládá z procesoru typu ARM926EJ-S, který běží až na 200MHz, a z řady periferií. Zařízení se standardně ovládá přes sériový port či telnet.
První myšlenku, totiž že budeme aplikaci kompilovat přímo v zařízení, ihned zavrhneme. Slabý procesor, málo paměti, upravená a minimalizovaná linuxová distribuce a žádný monitor a klávesnice.
Pak nám ovšem zbývá zase jen náš pracovní počítač, kde je výkonu přehršel, linuxová distribuce je standardní (např. Debian), atd. Zkrátka máme k dispozici veškerý komfort. Problém ovšem je, že naše vývojové nástroje umí vygenerovat spustitelné soubory pouze pro náš počítač. Jinými slovy jsou nám v tuhle chvíli k ničemu.
Intuitivně tušíme, že potřebujeme nástroje, které půjdou spustit na našem počítači, ale vygenerují spustitelný soubor pro cílové embedded zařízení. To znamená, že kompilátor poběží na procesoru našeho počítače (nejčastěji architektura x86/amd64), ale přitom dokáže zdrojový kód aplikace přeložit do strojového kódu procesoru v embedded zařízení (architektura ARM). Anebo, to že debugger spustíme sice na našem počítači, ale přesto s ním budeme schopni ladit aplikaci v cílovém zařízení.
Vývojové nástroje, které použijeme pro vývoj, sestavení a ladění aplikace pro jiný typ procesoru, než na kterém běží, se nazývají křížové nástroje (cross-tools).
Výše uvedené můžeme dokonce zobecnit - křížové nástroje jsou ty, které použijeme pro vývoj aplikace, která poběží v jiném prostředí než samotné nástroje. Jiným prostředím máme na mysli jiný procesor, jiný operační systém nebo třeba jen jiné verze systémových knihoven (např. 64-bitových). O počítači, na kterém křížové nástroje běží, mluvíme jako o hostujícím počítači (host). Zařízení, kde běží výsledný spustitelný soubor aplikace, označujeme jako cílové (target) zařízení.
Když mluvíme o křížových nástrojích, tak máme na mysli:
- cross-compiler - kompilátor, který zkompiluje zdrojové kódy pro prostředí cílového zařízení, ale sám přitom běží v prostředí hostujícího počítače.
- cross-debugger - debugger, který umožňuje z pohodlí hostujícího počítače ladit aplikaci v cílovém zařízení.
- cross-binutils - utility jako linker, assembler a další, které běží na hostujícím počítači, ale umí pracovat s objektovými a spustitelnými soubory vytvořenými pro cílové zařízení.
Samozřejmě kromě křížových nástrojů musíme mít v našem počítači k dispozici i kompletní běhové prostředí cílového zařízení. Tím máme na mysli sadu standardních systémových knihoven zkompilovaných pro cílové zařízení včetně jejich hlavičkových souborů a hlavičkové soubory jádra, které je nainstalováno v cílovém zařízení. Aplikace pro cílové zařízení se totiž musí kompilovat vůči nim a nikoliv vůči hlavičkovým souborům jádra na hostujícím počítači.
Na příkladu aplikace hello si ukážeme rozdíl v použití nativních a křížových nástrojů. Na obrázku 1.1 je výpis hlavičky spustitelného souboru aplikace hello zkompilované nativním kompilátorem (tedy pro platformu x86), zatímco na obrázku 1.2 je výpis hlavičky stejné aplikace ovšem zkompilované křížovým kompilátorem pro platformu ARM. Všimněte si, že pokaždé spouštíme jinou utilitu readelf. V prvním případě nativní verzi, v druhém pak křížovou. Svoji pozornost věnujte řádku Machine v obou výpisech hlavičky spustitelného souboru.