Системски софтвер/Јун 2022
Испит у јунском испитном року 2022. године одржан је 16. јуна. Трајао је 2 сата и радио се у вежбанкама.
1. задатак
Поставка
Посматра се процес асемблирања датог ивзорног асемблерског кода за amd64 архитектуру. Резултат асемблирања је предметни програм по ELF формату. Приказати садржај (1) табеле симбола и (2) релокационих записа строго поштујући школски формат и обавезно у складу са задатом нумерацијом симбола.
.intel_syntax noprefix
.section .text
.global min, la1
min: enter 0, 0 # 0x00
mov rbx, la1 # 0x04
cmp rbx, [rbp]+0x12 # 0x0c
call var[rip] # 0x10
jle la1 # 0x16
mov rax, [rbp]+0x12 # 0x18
jmp la2 # 0x1c
la1: mov rax, rbx # 0x1e
la2: leave # 0x21
ret # 0x22
.data
.global var
.type var, @object
var:
.quad la1, la2-4
.end
Напомена: хексадецимални бројеви наведени у коментару изворног асемблерског кода представљају померај до почетка инструкције у том реду гледано од почетка секције којој инструкција припада. Такође, усвојити претпоставку да је величина операционог кода:
- 2B за инструкцију
callза PC релативно адресирање, - 4B за инструкцију
movза апсолутно адресирање и - 1B за инструкције
jleиjmpза PC релативно адресирање.
Решење
| Num | Value | Size | Type | Bind | Ndx | Name |
|---|---|---|---|---|---|---|
| 0: | 0 | 0 | NOTYP | LOC | UND | |
| 1: | 0 | 0 | SCTN | LOC | 1 | .text |
| 2: | 0 | 0 | SCTN | LOC | 2 | .data |
| 3: | 0 | 0 | SCTN | LOC | 3 | .bss |
| 4: | 21 | 0 | NOTYP | LOC | 1 | la2 |
| 5: | 0 | 0 | NOTYP | GLOB | 1 | min |
| 6: | 1E | 0 | NOTYP | GLOB | 1 | la1 |
| 7: | 0 | 0 | OBJ | GLOB | 2 | var |
Иако није приказано у школском формату, симболи означени преко .type simbol, @object имају тип OBJECT у GNU асемблеру.
| Offset | Type | Symbol | Addend |
|---|---|---|---|
| 8 | R_X86_64_32 | 6 (la1) | 0 |
| 12 | R_X86_64_PC32 | 7 (var) | -4 |
| 17 | R_X86_64_PC8 | 6 (la1) | -1 |
| 1D | R_X86_64_PC8 | 1 (.text) | 20 |
Релокациони записи на 17 и 1D не морају да постоје, пошто се померај може уградити и током асемблирања.
| Offset | Type | Symbol | Addend |
|---|---|---|---|
| 0 | R_X86_64_64 | 6 (la1) | 0 |
| 8 | R_X86_64_64 | 1 (.text) | 1D |
2. задатак
Поставка
У C претпроцесору треба написати макри дефиницију REPEAT(str, n) која као свој резултат даје стринг str надовезан n пута на самог себе за вредности параметра n између 0 и 5. На пример, макро позив REPEAT("ha ", 3) резултује стрингом "ha ha ha ". Треба имати на уму да C претпроцесор не дозвољава рекурзију (макро не може рекурзивно позвати самог себе) нити дозвољава коришћење #if у телу макроа.
Решење
#define REP0(X)
#define REP1(X) X
#define REP2(X) REP1(X) X
#define REP3(X) REP2(X) X
#define REP4(X) REP3(X) X
#define REP5(X) REP4(X) X
#define REPEAT(str, n) REP##n(str)
При позиву REPEAT("ha ", 3) макропроцесор ће генерисати "ha " "ha " "ha " што је еквивалентно стрингу "ha ha ha " (тако функционише конкатенација стринг литерала у C). Ово понашање је било напоменуто на испиту.
3. задатак
Поставка
Задати модули main.c и other.c одвојено се преводе и потом статички повезују у јединствену извршну датотеку помоћу GNU ланца алата. Приликом повезивања не користе се други модули нити библиотеке. Треба попунити сваки од коментара /* ??? */ у датотеци other.c једном од наведених ознака које дефинишу какав ефекат изазива декларација која претходи коментару на датој линији:
OK-GLB: ово је исправна декларација променљиве која је глобална и за main.c и за other.c (глобална у смислу да ће се промена коју направиfoo()функција коректно рефлектовати и уmain()функцији као и обрнуто),OK-LOC: ово је исправна декларација променљиве која је локална само заfoo()(локална у смислу да се било која промена коју направиfoo()функција неће рефлектовати наmain()функцију),CMPL_E: ова декларација ће изазвати пријаву грешке од стране преводиоца (линкер неће бити ни покренут),LINK_E: ова декларација ће изазвати пријаву грешке од стране линкера (обе датотеке биће преведене без грешке, али линкер ће пријавити грешку приликом повезивања на симболу на датој линији),SMNT_E: ова декларација неће изазвати пријаву никакве грешке (ни преводиоца нити линкера), али ће програм имати семантичку грешку односно логичку неисправност која се може манифестовати током његовог извршавања иOTHR_E: дешава се нека друга грешка која није покривена претходним случајевима.
// file main.c
extern int a;
static double b;
int help; int *c = &help;
struct
{
double d;
} e;
int f = 0;
double g = 0.1;
#define h 99
int i;
unsigned j;
int main() {
// ...
}
// file other.c
double a; /* ??? */
double b; /* ??? */
int *c; /* ??? */
double d; /* ??? */
int e = 0; /* ??? */
int f; /* ??? */
double g = 0.1; /* ??? */
int h = 99; /* ??? */
unsigned *i; /* ??? */
static int j; /* ??? */
int foo() {
// ...
}
Решење
main.c
|
other.c
|
Класификација |
|---|---|---|
|
|
|
SMNT_E |
|
|
|
OK-LOC |
|
|
|
OK-GLB |
struct
{
double d;
|
double d;
|
OK-LOC |
|
|
|
SMNT_E |
|
|
|
OK-GLB |
|
|
|
LINK_E |
|
|
|
OK-LOC |
|
|
|
SMNT_E |
|
|
|
OK-LOC |
4. задатак
Поставка
Посматра се JIT емулатор и емулирање следећег програма.
- Означити све блокове који ће у току рада емулатора бити преведени. За сваки преведени блок навести јединствену идентификацију у облику
B<n>(<x>-<y>)(<n>је редни број блока у погледу тренутка његовог превођења при чему нумерација почиње од броја један, <x> је редни број прве а <y> редни број последње линије изворног кода блока). - Навести секвенцу извршавања. У секвенци извршавања могу се нађи само
GP(сваки пут када се емулатор врати у главну петљу),BH(сваки пут када се позове функција која помаже у разрешавању скокова) иB<n>(сваки пут када се изврши дати блок у складу са ознакама из претходне тачке).
BEG ; line 1
LDI 2 ; line 2 ( PZ) A <= <V>
L1 SUB B ; line 3 (CPZ) A <= A - mem[<V>]
L2 ADD C ; line 4 (CPZ) A <= A + mem[<V>]
BNG L2 ; line 5 if (!flags[P]) PC <= <V>
BZE L3 ; line 6 if ( flags[Z]) PC <= <V>
SBI 2 ; line 7 (CPZ) A <= A - <V>
BNZ L1 ; line 8 if (!flags[Z]) PC <= <V>
L3 HLT ; line 9 stop execution
B DC 9 ; line 10
C DC 2 ; line 11
END ; line 12
Решење
- Блокови по редоследу додавања у табелу (B3 је додат у табелу док још није био преведен):
- B0(1-5)
- B1(4-5)
- B2(6-6)
- B3(9-9)
- B4(7-8)
- B5(3-3)
- Редослед извршавања:
- GP
- B0
- BH
- GP
- B1
- BH (уколико се приликом превођења B1 није извршила оптимизација да се уместо branch helper угради скок на B1)
- B1
- B1
- BH
- GP
- B2
- BH
- GP
- B4
- BH
- GP
- B5
- B1
- B1
- B1
- B1
- B1
- BH
- B2
- BH
- GP
- B3
- BH
- GP