====== Systémová volání a inline assembler ======
Cílem tohoto cvičení je detailně se seznámit s tím, jak aplikace volá
služby operačního systému a jak lze systémová volání volat přímo, bez
použití knihoven jako //libc//. Tato znalost se vám bude hodit v dalších
cvičeních.
===== Domácí příprava =====
Seznamte se se základními instrukcemi architektury x86 a způsobem,
jakým vkládat instrukce assembleru přímo do zdrojového kódu v jazyce
C/C+ +. Projděte si
{{:courses:b4b35osy:cviceni:inline_assembler.pdf|tuto prezentaci}} a
připomeňte si {{:courses:b4b35osy:lekce01.pdf|první}} a
{{:courses:b4b35osy:lekce02.pdf|druhou přednášku}}.
Dále se seznamte s nástroji
[[https://linux.die.net/man/1/strace|strace]] a
[[https://linux.die.net/man/1/ltrace|ltrace]]. Doporučujeme si oba
nástroje vyzkoušet na následujícím programu:
#include
int main(int argc, char *argv[])
{
printf("Hello world\n");
return 0;
}
===== Zadání =====
Vytvořte program, který čte ze standardního vstupu celá nezáporná
dekadická čísla oddělená mezerami nebo konci řádků a vypíše je na
standardní výstup v hexadecimální podobě oddělená koncem řádku.
Program nesmí používat žádnou standardní knihovnu jako např. libc.
Předpokládejte, že maximální hodnota čísla bude 2^32-1.
Jinými slovy vytvořte program fungující podobně jako program níže, ale tak,
aby šel přeložit s přepínači kompilátoru ''-nostdlib -nostdinc -static -m32
-Wall -g -O2''.
#include
int main()
{
unsigned num;
while (scanf("%u", &num) == 1)
printf("0x%x\n", num);
return 0;
}
Do BRUTE nahrávejte soubor ''hexconv.c'' se svou implementací.
===== Nápověda =====
* Pro vyvolání služeb jádra potřebujete znát ABI (application binary interface) jádra OS. To se dočtete v [[http://man7.org/linux/man-pages/man2/syscall.2.html|man syscall]] v řádcích architektury i386.
* Čísla jednotlivých systémových volání najdete v souboru ''/usr/include/asm/unistd_32.h''.
* Pozor! Jak ABI, tak čísla systémových volání se liší mezi 32- a 64-bitových jádrem. 64-bitové jádro je možné volat pomocí obou ABI, ale v této úloze používejte 32-bitové ABI, protože program je kompilován s přepínačem ''-m32''.
* Můžete vyjít z kódu níže, který již obsahuje náhradu funkce ''scanf''.
* Pro tisk i načítání můžete použít pole pevné délky např. 20 (maximální výstup má 8 hexadecimálních znaků).
#include /* TODO: write your own system call wrappers for read, write, exit */
#include /* TODO: sprintf -- convert number to hex string */
#include /* TODO: strlen -- length of output for write */
int isnum(char ch)
{
return ch >= '0' && ch <= '9';
}
int isspc(char ch)
{
return ch == ' ' || ch == '\n';
}
static void print(unsigned num)
{
char buf[20];
/* TODO: Get rid of sprintf() and strlen() */
sprintf(buf, "0x%x\n", num);
int ret = write(STDOUT_FILENO, buf, strlen(buf));
if (ret == -1)
_exit(1); // TODO your new exit
}
/* TODO: main() is called by libc. Real entry point is _start(). */
int main()
{
char buf[20];
unsigned num = 0;
int i;
int num_digits = 0;
unsigned chars_in_buffer = 0;
for (/* no init */; /* no end condition */; i++, chars_in_buffer--) {
if (chars_in_buffer == 0) {
int ret = read(STDIN_FILENO, buf, sizeof(buf));
if (ret < 0)
return 1; // TODO replace by exit
i = 0;
chars_in_buffer = ret;
}
if (num_digits > 0 && (chars_in_buffer == 0 /* EOF */ || !isnum(buf[i]))) {
print(num);
num_digits = 0;
num = 0;
}
if (chars_in_buffer == 0 /* EOF */ || (!isspc(buf[i]) && !isnum(buf[i])))
return 0; // TODO: replace by exit
if (isnum(buf[i])) {
num = num * 10 + buf[i] - '0';
num_digits++;
}
}
}