8. Systémová volání

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 prezentaci o inline assembleru a připomeňte si první a druhou přednášku.

Dále se seznamte s nástroji strace a ltrace. Doporučujeme si oba nástroje vyzkoušet na následujícím programu:

#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("Hello world\n");
    return 0;
}

Pokud vás zajímají podrobnosti o kombinovaní assembleru a C, podívejte se do dokumentace překladače GCC. Mimo jiné tam najdete popisy obecných omezení a omezení závislých na architektuře CPU (hledejte sekci “x86 family”).

Zadání úlohy

Vytvořte program, který čte ze standardního vstupu celá nezáporná dekadická čísla oddělená mezerami nebo konci řádků (případně jinými nečíselnými znaky) 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

-ffreestanding -fno-stack-protector -nostdlib -nostdinc -static -m32 -Wall -g -O2
#include <stdio.h>

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 man syscall v řádcích architektury i386.
  • V Ubuntu čísla jednotlivých systémových volání najdete v souboru /usr/include/x86_64-linux-gnu/asm/unistd_32.h.
    Pozor! Jak ABI, tak čísla systémových volání se liší mezi 32- a 64-bitovým 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 načítání vstupu místo 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 <unistd.h> /* TODO: replace this by writing your own system call */
                    /* wrappers for read(), write(), exit() */
#include <stdio.h>  /* TODO: replace this by your own implementation of */
                    /* sprintf() (for conversion of a number to hex string) */
#include <string.h> /* TODO: replace this with your implementation of strlen() */

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. Without libc, the entry point is called _start(). */
int main()
{
    char buf[20];
    unsigned num = 0;
    int i = 0;
    int num_digits = 0;
    unsigned chars_to_process = 0;

    for (/* no init */; /* no end condition */; i++, chars_to_process--) {
        if (chars_to_process == 0) {
            int ret = read(STDIN_FILENO, buf, sizeof(buf));
            if (ret < 0)
                return 1; // TODO: replace by exit
            i = 0;
            chars_to_process = ret;
        }
        if (
            num_digits > 0
            && (chars_to_process == 0 /* EOF */ || !isnum(buf[i]))
        ) {
            print(num);
            num_digits = 0;
            num = 0;
        }
        if (
            chars_to_process == 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++;
        }
    }
}

Materiály

Domácí příprava na další cvičení

viz stránka https://osy.pages.fel.cvut.cz/docs/cviceni/lab9/