Sistemski softver/K1 2022

Izvor: SI Wiki
Pređi na navigaciju Pređi na pretragu

Prvi kolokvijum 2022. godine na SI odseku održan je 28. marta. Na kolokvijumu su od literature bili dostupni SystemV ABI i amd64 Architecture Programmer's Manual, i bilo je dozvoljeno pokrenuti Ubuntu virtuelnu mašinu koja se koristila i na vežbama, sa svim istim alatima. Prvih 40 minuta se radio teorijski deo, narednih 50 minuta zadatak, dok je posle kolokvijuma bio moguć uvid u radove (razlog iz kog nije bio moguć obrnut redosled rada teorijskog dela i zadatka je zbog toga što prethodnih godina neki studenti ne bi čuli napomenu da ne treba da pokrenu oba testa istovremeno, pa bi imali maksimalno 50 minuta za izradu oba).

  • Za pitanja sa više odgovora, tačni odgovori su podebljani i uokvireni
  • Za pitanja za koje se odgovori unose, tačni odgovori su podvučeni i sakriveni, tako da se prikažu kada izaberete taj tekst (primer: ovako)
  • Pritisnite levo dugme ispod za sakrivanje i otkrivanje svih odgovora, ili desno dugme za uključivanje i isključivanje interaktivnog režima:

Teorija

1. zadatak

U nastavku je dat izvorni asemblerski kod funkcije foo:

.intel_syntax noprefix
.global foo
.type foo, @function
foo:
    push rsp
    mov rax, rsp
    and rax, 0x0F
    cmp rax, 0
    je equal
    mov rax, 0
    jmp return
equal:
    mov rax, 1
return:
    pop rsp
    ret
.end

Šta je tačno za povratnu vrednost funkcije foo ukoliko se posmatra amd64 arhitektura i System V ABI konvencija?

  1. Povratnu vrednost funkcije nije moguće deterministički odrediti sve do trenutka izvršavanja.
  2. Povratna vrednost funkcije je 0 (nula) zbog postojanja "crvene zone".
  3. Povratna vrednost funkcije je 0 (nula) zato što je stek podrazumevano smešten u viši deo adresnog prostora.
  4. Povratna vrednost funkcije je 1 (jedan) usled garantovanog poravnanja oblasti za argumente.
  5. Povratna vrednost funkcije je 1 (jedan) jer posmatrana funkcija ne poziva druge funkcije.

2. zadatak

U nastavku je data deklaracija funkcije foo i definicije pratećih tipova podataka:

typedef struct
{
    long fA1;
    long fA2;
} StructTypeA;

typedef struct
{
    long fB1;
    double fB2;
} StructTypeB;

extern StructTypeB foo(
    StructTypeA param0,
    StructTypeA param1,
    StructTypeB param2,
    StructTypeA param3,
    void * param4,
    void * param5);

Dopuniti sledeće konstatacije ukoliko se posmatra amd64 arhitektura i System V ABI konvencija:

  1. Povratna vrednost funkcije foo prosleđuje se kao kombinacija GP/SSE nazad do pozivaoca.
  2. Parametar param0 prosleđuje se kao kombinacija GP u funkciju foo prilikom njenog poziva.
  3. Parametar param1 prosleđuje se kao kombinacija GP u funkciju foo prilikom njenog poziva.
  4. Parametar param2 prosleđuje se kao kombinacija GP/SSE u funkciju foo prilikom njenog poziva.
  5. Parametar param3 prosleđuje se kao stackPushII u funkciju foo prilikom njenog poziva.
  6. Parametar param4 prosleđuje se kao r9 u funkciju foo prilikom njenog poziva.
  7. Parametar param5 prosleđuje se kao stackPushI u funkciju foo prilikom njenog poziva.

Napomene:

  • Odgovor kombinacija GP predstavlja određenu kombinaciju (više od jednog) isključivo General Purpose registara.
  • Odgovor kombinacija SSE predstavlja određenu kombinaciju (više od jednog) isključivo Streaming SIMD Extension registara.
  • Odgovor kombinacija GP/SSE predstavlja određenu kombinaciju (više od jednog) General Purpose i Streaming SIMD Extension registara.
  • Odgovor stackPush<roman-numeral>, gde je <roman-numeral> rimski broj, predstavlja argument koji se prosleđuje preko steka pri čemu rimski broj označava međusobni poredak push operacija odnosno redosled stavljanja datog argumenta na vrh steka u odnosu na druge argumente koji se takođe prosleđuju preko steka.

3. zadatak

Posmatra se program opisan sadržajem dela memorije i segmentom izvornog C koda. Inicijalni sadržaj dela memorije (dat u "školskom" formatu prikazanom na auditornim vežbama):

   +7     +6     +5     +4     +3     +2     +1     +0
| 0x00 | 0x00 | 0xFF | 0xFF | 0xEE | 0xEE | 0xDD | 0xDD | <--- 0x405060708090 + 0x20
| 0xCC | 0xCC | 0xBB | 0xBB | 0xAA | 0xAA | 0x99 | 0x99 | <--- 0x405060708090 + 0x18
| 0x00 | 0x00 | 0x40 | 0x50 | 0x60 | 0x70 | 0x80 | 0x9C | <--- 0x405060708090 + 0x10
| 0x88 | 0x88 | 0x77 | 0x77 | 0x66 | 0x66 | 0x55 | 0x55 | <--- 0x405060708090 + 0x08
| 0x44 | 0x44 | 0x33 | 0x33 | 0x22 | 0x22 | 0x11 | 0x11 | <--- 0x405060708090 + 0x00

Segment izvornog C koda:

#include <stdio.h>
#include <stdint.h>

extern uint32_t *identifierPtr;
extern uint32_t identifierArr[];

void main()
{
    printf("%p", identifierPtr);
    printf("%p", &identifierPtr);
    printf("%#x", identifierPtr[0]);
    printf("%#x", identifierPtr[1]);
    printf("%p", identifierArr);
    printf("%p", &identifierArr);
    printf("%#x", identifierArr[0]);
    printf("%#x", identifierArr[1]);
}

Dopuniti sledeće konstatacije ukoliko se posmatra amd64 arhitektura i System V ABI konvencija, a simboli identifierPtr i identifierArr imaju istu vrednost 0x4050607080A0:[1]

  1. Naredba printf("%p", identifierPtr); ispisuje 0x40506070809C.
  2. Naredba printf("%p", &identifierPtr); ispisuje 0x4050607080A0.
  3. Naredba printf("%#x", identifierPtr[0]); ispisuje 0x88887777.
  4. Naredba printf("%#x", identifierPtr[1]); ispisuje 0x6070809C.
  5. Naredba printf("%p", identifierArr); ispisuje 0x4050607080A0.
  6. Naredba printf("%p", &identifierArr); ispisuje 0x4050607080A0.
  7. Naredba printf("%#x", identifierArr[0]); ispisuje 0x6070809C.
  8. Naredba printf("%#x", identifierArr[1]); ispisuje 0x4050.

Napomene:

  • Ukoliko neku vrednost nije moguće odrediti na osnovu datog sadržaja dela memorije kao odgovor treba uneti znak pitanja (?)
  • Sve odgovore za uspešno određene vrednosti treba uneti u istom formatu kakvom ih program ispisuje.
  • Format "%p" služi za ispis vrednosti pokazivača u heksadecimalnom formatu sa prefiksom 0x (nakon prefiksa ne ispisuju se vodeće nule).
  • Format "%#x" služi za ispis neoznačenog celog broja u heksadecimalnom formatu sa prefiksom 0x (nakon prefiksa ne ispisuju se vodeće nule).

4. zadatak

U nastavku je data definicija nekog tipa podataka StructType:

typedef struct
{
    char f1;
    int f2;
    short f3;
    float f4;
    char f5;
} StructType;

Označiti u nastavku šta predstavlja svaki bajt dela memorije (dat u "školskom" formatu prikazanom na auditornim vežbama) pod sledećim pretpostavkama:

  • struktura StructType smeštena je na prvo odgovarajuće slobodno mesto u datom delu memorije,
  • slobodan prostor u datom delu memorije počinje na adresi 0x8000004502 i
  • posmatra se amd64 arhitektura i System V ABI konvencija.
+7 +6 +5 +4 +3 +2 +1 +0
out out out out out out out out <--- 0x8000004500 + 0x18
padd padd padd f5 f4 f4 f4 f4 <--- 0x8000004500 + 0x10
padd padd f3 f3 f2 f2 f2 f2 <--- 0x8000004500 + 0x08
padd padd padd f1 out out out out <--- 0x8000004500 + 0x00

Napomene:

  • Odgovor out predstavlja bajt izvan posmatrane strukture.
  • Odgovor padd predstavlja neiskorišćeni odnosno padding bajt unutar posmatrane strukture.

5. zadatak

U nastavku je dat sadržaj datoteke main.c sa izvornim C kodom:

#include <stdio.h>

int main()
{
    printf("Hello World!\n");
    return 0;
}

Posmatra se amd64 arhitektura i System V ABI konvencija, a pokrenute su sledeće komande:

gcc -S -o main.s main.c
as -o main.o main.s
ld -o executable \
    --entry=_start \
    /lib/x86_64-linux-gnu/crt1.o \
    /lib/x86_64-linux-gnu/crti.o \
    /lib/x86_64-linux-gnu/crtn.o \
    -lc \
    main.o
./executable
  1. Program se regularno izvršava i ispisuje poruku Hello World! na standardni izlaz.
  2. Nije moguće dobiti izvršnu datoteku executable zato što simbol koji predstavlja ulaznu tačku nije definisan.
  3. Nije moguće dobiti izvršnu datoteku executable zato što u proces linkovanja nije uključena standardna C biblioteka.
  4. Izvršavanje programa se zaustavlja prilikom punjenja u operativnu memoriju zato što nije poznato ko učitava dinamičke biblioteke.
  5. Nije moguće dobiti izvršnu datoteku executable zato što u proces linkovanja nisu uključene C runtime objektne datoteke.

Zadatak

Postavka

U nastavku je dat segment izvornog asemblerskog koda napisan za amd64 arhitekturu prema System V ABI konvenciji:

.intel_syntax noprefix
.text
.global foo
.type foo, @function
foo:
    endbr64
    mov rcx, QWORD PTR 24[rsp]
    movsx rdx, si
    movsx esi, si
    mov rax, rdi
    imul esi, DWORD PTR 16[rsp]
    imul rcx, rdx
    imul rdx, QWORD PTR 8[rsp]
    mov DWORD PTR 8[rdi], esi
    mov QWORD PTR 16[rdi], rcx
    mov QWORD PTR [rdi], rdx
    ret

Napisati segment izvornog C koda, koji odgovara navedenom segmentu izvornog asemblerskog koda, tako da sadrži samo sledeće elemente:

  • definicije potencijalno potrebnih tipova podataka i
  • definiciju funkcije foo.

Napomene:

  • Ukoliko se tekstualni editor Ace ne učita na ispravan način treba osvežiti internet stranicu odnosno izvršiti refresh (prethodno uneti odgovori na druga pitanja čuvaju se na serveru i neće biti izgubljeni usled osvežavanja internet stranice).
  • Klikom na dugme za proveru rešenja moguće je odmah, čak i pre predaje, ispitati ispravnost rešenja. Penalty regime definiše iznos kazne odnosno penala za svaku proveru rešenja izraženo u procentima maksimalnog mogućeg broja poena za ovo pitanje. U skladu sa vrednošću Penalty regime penali za proveru rešenja redom iznose 0%, 0%, 0%, 30%, 40%, 50% i 60%. Dakle, samo prve tri provere rešenja ne uzrokuju nikakav penal, četvrta provera rešenja uzrokuje penal od 30%, peta penal od 40%, šesta penal od 50%, dok svaka provera rešenja počev od sedme uzrokuje penal od 60% (potpuno tačno rešenje koje je proveravano sedam ili više puta biće ocenjeno sa (100 - 60)% poena).

Napomene

Sledeće napomene su bile pomenute tokom kolokvijuma:

  • Originalni C program je kompajliran koristeći GCC opciju -O3 za optimizaciju.
  • Postojala su četiri test primera od 10, 30, 30 i 30 bodova.
  • Moodle ocenjivač nije koristio opciju za optimizaciju, i očekivao je od studenata da ne alociraju objekte na steku. Zbog ovoga, može da se desi da studenti dobiju identičan ispis objdump nad objektnim fajlom svog kompajliranog koda kao što je asemblerski kod dat u zadatku ali da poslednja dva test primera ne prođu.
  • Način kastovanja strukture je objašnjen na kolokvijumu.

Rešenje

typedef struct {
    long a;
    int b;
    long c;
} StructType;

StructType foo(short arg, StructType p) {
    return (StructType) {
        p.a * arg,
        p.b * arg,
        p.c * arg
    };
}

Napomene

  1. Na kolokvijumu je na ovom mestu slovo A pisalo ćirilicom, pa se studentima koji su kopirali adresu odavde nisu priznali bodovi na mestima gde se ta vrednost tražila. Tokom uvida, asistent je primetio grešku i rekao da će ispraviti bodove.