You are on page 1of 20

Оперативни системи 1

Вежбе 2
Наставак архитектуре RISC-V и
адресирање меморије 1. део (линкер)

Листа предмета:
ir2os1@lists.etf.rs
Оперативни системи 1, ЕТФ 2022. 1
Архитектура RISC-V - наставак

Оперативни системи 1, ЕТФ 2022. 2


Задатак 1
• Написати програм на програмском језику C који ће помоћу једне
функције бесконачно исписивати неки текст. Решење може да
буде некоректно у смислу да ће оперативни систем у коначном
времену пријавити грешку у извршавању програма и прекинути
његово извршавање. Није дозвољено коришћење петљи.
s0 Стек: (у једном реду једна реч – 8B)
#include "../lib/console.h"
sp xx Шта је проблем?
ret_main
void f() { s0_main
__putc('f');
__putc('\n');
f(); ret_a
s0_a
}

ret_a
s0_a

Оперативни системи...1, ЕТФ 2022. 3


Задатак 2
• Написати програм на програмском језику C
који ће бесконачно понављати исписивање
неког текста. Није дозвољено користити
петље и програм мора бити исправан, тј. да
оперативни систем никада не пријави грешку
за тај програм.

Оперативни системи 1, ЕТФ 2022. 4


Решење
#include "../lib/hw.h"
#include "../lib/console.h"

uint64 pc;

void a() {
asm volatile("mv %0, ra" : "=r“ (pc));
}
void b() {
asm volatile("mv ra, %0" : :"r“ (pc));
}

void main() {
a();
__putc('a');
__putc('\n');
b();
}
5
Оперативни системи 1, ЕТФ 2022.
Задатак 3.
• Написати програм за процесор RISC-V који треба да
изврши неки потпрограм, али тако да се нигдe у коду
не види позив тог потпрограма.

Оперативни системи 1, ЕТФ 2022. 6


Решење
#include "../lib/hw.h" void main() {
#include "../lib/console.h" dispatch();
}
uint64 pc;

void f() {
__putc('a');
__putc('\n');

asm volatile("mv ra, %0" : :"r" (pc));


asm volatile("sd ra, 8(sp)");
}

void dispatch() {
asm volatile("mv %0, ra" : "=r" (pc));
asm volatile("mv ra, %0" : :"r" (f));
}

Оперативни системи 1, ЕТФ 2022. 7


Прекидна рутина (1/2)
• Прекидна рутина се позива приликом системског позива, изузетка или прекида
• Системски позив ради се инструкцијом ecall
• Прекидна рутина се извршава у системском режиму
• Приликом позива ради се следеће:
– Вредност регистра pc се уписује у регистар sepc
– У регистар sstatus уписују се следеће вредности
• У бит SPP (бит 8) вредност која показује из ког режима се догодио скок (
0 – кориснички, 1 – системски)
• У бит SIE (бит 1) нулу, чиме се маскирају спољашњи прекиди
• У бит SPIE (бит 5) претходна вредност бита
– У регистар scause разлог скока у прекидну рутину
– Скаче се на прекидну рутину чија је адреса (мора бити поравната на 4 бајта) у
регистру stvec
Оперативни системи 1, ЕТФ 2022. 8
Прекидна рутина (2/2)
• Повратак из прекидне рутине ради се инструкцијом
• Приликом повратка ради се следеће:
– Режим у који се прелази дефинисан је битом SPP (након повратка поставља се на
нулу)
– Бит SIE добија вредност бита SPIE
– Вредност регистра sepc се уписује у регистар pc
• Обрада спољашњих прекида конзоле дата је у библиотеци console.lib (заглавље
console.h)
– console_handler()
– Не обухвата никакво чување регистара
– Омогућава рад функција __putc и __ getc

Оперативни системи 1, ЕТФ 2022. 9


Разлог прекида
БНТ Вредност Опис
1 1 Софтверски прекид од тајмера
1 9 Спољашњи хардверски прекид
0 2 Илегална инструкција
0 5 Недозвољена адреса читања
0 7 Недозвољена адреса уписа
0 8 Системски позив из корисничког режима
0 9 Системски позив из системског режима

Оперативни системи 1, ЕТФ 2022. 10


Задатак 4.
Написати програм на програмском језику C који на сваких 5
секунди испише нешто. Тајмер је подешен да десет пута у
секунди генерише прекид. Тајмер генерише софтверски прекид
и крај обраде софтверског прекида се означава уписом нуле на
позицију бита 1 у регистру sip.

Оперативни системи 1, ЕТФ 2022. 11


Решење
Прекидна рутина:
.globl interrupt ld ra, 0(sp)
.globl interruptvec ld sp, 8(sp)
.align 4 ld gp, 16(sp)
interruptvec: ld tp, 24(sp)
addi sp, sp, -256 ...
sd ra, 0(sp)
sd sp, 8(sp) addi sp, sp, 256
sd gp, 16(sp)
sd tp, 24(sp) sret
...

call interrupt

Оперативни системи 1, ЕТФ 2022. 12


#include "../lib/hw.h"
#include "../lib/console.h"

uint64 timer = 0;
extern "C" void interruptvec();
extern "C" void interrupt() {
uint64 scause;
asm volatile("csrr %0,scause":"=r"(scause));
if (scause == 0x8000000000000001UL) {
timer++;
if (timer >= 50) {
__putc('a');
__putc('\n');
timer = 0;
}
uint64 sip;
asm volatile("csrr %0, sip" : "=r" (sip));
sip &= ~2;
asm volatile("csrw sip, %0" : : "r" (sip));
}
console_handler(); Оперативни системи 1, ЕТФ 2022. 13
}
void main() {
asm volatile("csrw stvec, %0" : : "r" (interruptvec));
uint64 sstatus;
asm volatile("csrr %0, sstatus" : "=r" (sstatus));
sstatus |= 1 << 1;
asm volatile("csrw sstatus, %0" : : "r" (sstatus));
while(1);
}

Оперативни системи 1, ЕТФ 2022. 14


Адресирање меморије 1. део

Оперативни системи 1, ЕТФ 2022. 15


К2 септембар 2014. задатак 2.
Код за први пролаз неког линкера дат је у наставку. (У првом пролазу линкер само
прикупља извезене симболе, а не проверава и да ли су сви увезени симболи и
дефинисани; ту проверу ради приликом обраде увезених симбола у другом пролазу.)
Попунити изостављене делове кода означене коментарима /***1***/ до /***5***/.

Оперативни системи 1, ЕТФ 2022. 16


void Linker::firstPass () { if (status==-1) {
this.status = OK; Output::errorMsg(“Symbol
/***4***/
this.binarySize = /***1***/;
0;//BinarySize
//BinarySize
of processed
of processed
output output %d this.status = ERROR;
for (FileReader::reset(); !FileReader::isDone(); FileReader::next()) { } already defined”,
ObjectFile* objFile = FileReader::currentObjectFile(); } sym->getName());
if (objFile==0) { }
Output::error(“Fatal internal error: null pointer exception.“); int size = objFile->getBinarySize();
exit(-1); /***5***/
} }
for (objFile.reset(); !objFile.isDone(); objFile.nextSymbol()) { } this.binarySize += size;
Symbol* sym = objFile->getCurrentSymbol();
if (sym==0) {
Output::error(“Fatal internal error: null pointer exception.“);
/***2***/
} exit(-1);
if (sym->getKind() == Symbol::export) {
int offset = sym->getOffset(); // offset in objFile
offset += /***3***/;
offset += this.binarySize;
int status =
SymbolTable::addSymDef(sym->getName(),offset,
objFile->getName());
Оперативни системи 1, ЕТФ 2022. 17
К2 септембар 2016. задатак 2.
Потребно је имплементирати процедуру resolveSymbols која се користи у другом
пролазу једног линкера. Ова процедура обрађује један улазни .obj фајл и треба да разреши
адресна поља машинских инструкција која користе симболе које тај фајл увози. Улазни и
излазни фајл су меморијски пресликани, техником виртуелне меморије, тако да се њихов
садржај може једноставно посматрати као садржај меморије процеса. На почетак меморијски
пресликаног садржаја улазног .obj фајла указује први, а на почетак тог преписаног садржаја у
излазном (.exe) фајлу указује други аргумент ове процедуре; пре позива ове процедуре,
линкер је већ преписао садржај бинарног преведеног кода (без заглавља) свих улазних .obj
фајлова у садржај излазног фајла.
На самом почетку улазног фајла налази се заглавље. Сви помераји (offset, односно релативне
адресе) у њему изражени су у јединицама sizeof(char)==1, а величине су unsigned
long (скраћено ulong). Садржај почетка заглавља је, редом, следећи:
• један ulong који садржи померај почетка бинарног преведеног кода у .obj фајлу у односу
на почетак целог садржаја тог фајла (заправо садржи величину целог заглавља иза кога
следи бинарни преведени код);
• један ulong који садржи укупан број симбола који се увозе (n);
Оперативни системи 1, ЕТФ 2022. 18
• n редом поређаних парова: име симбола који се увози (низ знакова произвољне дужине,
завршен знаком '\0'), иза кога следи један улонг који представља померај првог
неразрешеног адресног поља у машинској инструкцији које треба да садржи вредност
разрешене адресе тог симбола; таква поља су даље уланчана у једноструку листу, тако да
свако поље садржи померај наредног таквог поља за исти симбол, с тим да вредност
помераја 0 означава крај листе (последње такво поље за тај симбол); ови помераји су
релативни у односу на почетак преведеног бинарног кода унутар садржаја .obj фајла.

Линкер поседује табелу симбола чија операција:


ulong SymbolTable::resolveSymbol(char* symbol);
враћа померај (релативну адресу) у односу на почетак излазног фајла (.exe) у који се дати
симбол преводи, уколико он постоји у табели, а 0 ако га нема. Грешку недефинисаног
симбола треба обрадити позивом функције:
int errorSymbolUndefined(char* symbol);
Ова функција исписује кориснику поруку о недефинисаном датом симболу и враћа -1, што у
том случају треба да врати и функција resolveSymbols. У случају успеха, функција
resolveSymbols треба да врати 0.
int resolveSymbols (char* inputObj, char* output);
Оперативни системи 1, ЕТФ 2022. 19
typedef unsigned long ulong;
const ulong OffsBinaryStartOffset = 0,
OffsNumOfImportedSymbols = OffsBinaryStartOffset + sizeof(ulong),
OffsImportedSymbols = OffsNumOfImportedSymbols + sizeof(ulong);

int resolveSymbols (char* inputObj, char* output) {


ulong binaryStartOffs = *(ulong*)(inputObj + OffsBinaryStartOffset);
char* binaryStart = inputObj + binaryStartOffs;
ulong numOfSymbols = *(ulong*)(inputObj + OffsNumOfImportedSymbols);
char* symbol = inputObj + OffsImportedSymbols;
for (ulong i=0; i<numOfSymbols; i++) {
ulong addr = SymbolTable::resolveSymbol(symbol);
if (addr==0) return errorSymbolUndefined(symbol);
ulong symbolLen = strlen(symbol)+1;
ulong fieldOffs = *(ulong*)(symbol+symbolLen);
for (; fieldOffs>0; fieldOffs=*(ulong*)(binaryStart+fieldOffs))
*(ulong*)(output+fieldOffs) = addr;
symbol = symbol+symbolLen+sizeof(fieldOffs);
}
return 0;
}

Оперативни системи 1, ЕТФ 2022. 20

You might also like