Proces překladu a externí assembler

Pozor! Toto cvičení bude probíhat v GNU/Linuxu.

Proces překladu

Program v jazyce C je překládán v následujících krocích:

  • Preprocesor provede expanzi maker a provede inkluzi hlavičkových souborů. Jak vypadá výsledek tohoto kroku zjistíte pomocí gcc -E foo.c
  • Překladač jazyka C zdrojový kód přeloží a vygeneruje kód v assembleru. Výsledek tohoto kroku zjistíte pomocí gcc -S foo.c a v aktuálním adresáři se vytvoří soubor foo.S. Všimněte si, že kód je zapsaný v AT&T syntaxi.
  • Assembler vygeneruje objektový kód. Výsledek tohoto kroku získáte pomocí gcc -c foo.c. V aktuálním adresáři se vytvoří soubor foo.o představující zkompilovaný strojový kód daných funkcí. Obsah tohoto souboru můžete prozkoumat pomocí
    • objdump -d foo.o
    • objdump -d -M intel foo.o
  • Linker sloučí objektové soubory a provede provázání s knihovnami. Pokud překladač nemá specifikovaný přepínač -o, generuje se výstup překladu do souboru a.out. Obsah výsledného souboru lze opět prozkoumat pomocí příkazu objdump.

Překladač běžně tyto kroky dělá současně a k překladu často stačí použít gcc -o foo foo.c.

Úkol č. 1

Přeložte následující kód a zjistěte, jak vypadá výstup jednotlivých kroků. U překladu je možné pomocí přepínačů -O0, -O1, -O2 a -O3 nastavit úroveň optimalizací. Zjistěte jaké úpravy kódu (toho v assembleru) překladač udělal při zapnutí jednotlivých optimalizací. Vyzkoušejte i pro jiný kód.

#include <stdio.h>

int fact(int x)
{
	if (x == 1) return 1;
	return x * fact(x - 1);
}

int main()
{
	printf("fact(5) = %i\n", fact(5));
}

Externí assembler

Doposud jsme v rámci cvičení uvažovali pouze inline assembler, což mělo své výhody i nevýhody související zejména s provázaností s jazykem C. Abychom se těmto problémům vyhli, můžeme použít externí assembler. Ve Windows se nejčastěji používá Microsoft MASM v GNU/Linuxu pak GAS (AT&T syntaxe) nebo NASM (Intel syntaxe). V následujícím cvičení budeme používat právě NASM.

Nejkratší program

Následující program definuje jeden globální symbol _start, místo odkud program bude spuštěn. Nastaví parametry systémového volání sys_exit a provede jej.

; tato sekce slouží k uložení kódu programu
section .text
	global _start

_start:
	mov eax,1            ; systemove volani (sys_exit)
	mov ebx,0            ; navratovy kod 0 (ok)
	int 80h

Program přeložíte a slinkujete pomocí příkazů: (pokud se zdrojový soubor jmenuje ex0.s)

nasm -felf ex0.s  
ld -o ex0 ex0.o

# v pripade 64bitoveho OS pouzijeme pro vygenerovani 32bitoveho objektoveho souboru
ld -melf_i386 -o ex0 ex0.o 

Funkci programu můžete ověřit vyzvednutím návratového kódu programu příkazem:

echo $?

Složitější program

; tato sekce slouží k uložení konstantních dat
section .data
	hello:     db 'Hello world!',10,0    ; "Hello world!" + '\n' + '\0'

section .text
	global _start
	extern printf    ; deklaruje, že bude volat externí symbol (i.e., funkci z jiného souboru/knihovny)
_start:
	mov eax, hello
	push eax
	call printf
	add esp, 4

	mov eax,1        ; systemove volani (sys_exit)
	mov ebx,0        ; navratovy kod 0 (ok)
	int 80h

Jelikož program vyžaduje funkci z knihovny libc, je potřeba ji připojit při linkování. Tzn. kompilaci provést následovně.

nasm -felf ex1.s
ld -o ex1 --dynamic-linker /lib/ld-linux.so.2 ex1.o -lc

# v pripade 64bitoveho OS pouzijeme pro vygenerovani 32bitoveho objektoveho souboru
ld -melf_i386 -o ex1 --dynamic-linker /lib/ld-linux.so.2 ex1.o -lc

Propojení s programy v C

Díky tomu, že externí assembler generuje stejný objektový kód jako C a k linkování dochází až na konci překladu, je možné propojit programy v assembleru i v C.

Uvažujme násleudující kód v assembleru, který představuje funkci s konstantní hodnotou:

section .text
        global foo
foo:
	mov eax, 42
	ret
Po přeložení pomocí nasm -felf ex2-asm.s získáme objektový soubor obsahující jednu funkci void foo(); V C pak můžeme deklarovat její prototyp a funkci zavolat.
#include <stdio.h>

int foo(); /* prototyp funkce */

int main()
{
	printf("FOO:%i\n", foo());
}

Při překladu je ovšem potřeba přilinkovat i objektový soubor s funkcí foo. tj. gcc -o ex2 ex2-c.c ex2-asm.o. V případě 64bitového OS je potřeba ještě přidat přepínač -m32 pro vygenerování 32bitového binárního souboru.

Úkol č.2

  1. V externím assembleru napište program, který vypíše faktoriál čísel 1 až 6.
  2. Upravte tento program, aby fungoval jako funkce jednoho parametru (počet prvních čísel faktoriálu) a tuto funkci zavolejte z C.

Pro získání bodového hodnocení je potřeba splnit oba úkoly č. 2.

Další užitečné zdroje

  1. P. Tišnovský: Použití assembleru v Linuxu.

Last update on 25. 4. 2017 18:28
Powered by Schemik.

© Petr Krajča, 2010, 2012
petr.krajca (at) upol.cz