XV6 Series: MIT Lab4 2024
RISC-V assembly
- Which registers contain arguments to functions? For example, which register holds 13 in main’s call to printf?
In RISC, a0-a7 registers are used to store function’s arguments. Specifically, for the printf
, register a2 holds the value 13.
void main(void) {
1c: 1141 add sp,sp,-16
1e: e406 sd ra,8(sp)
20: e022 sd s0,0(sp)
22: 0800 add s0,sp,16
("%d %d\n", f(8)+1, 13);
printf24: 4635 li a2,13
26: 45b1 li a1,12
28: 00001517 auipc a0,0x1
2c: 84850513 add a0,a0,-1976 # 870 <malloc+0x100>
30: 68c000ef jal 6bc <printf>
(0);
exit34: 4501 li a0,0
36: 26e000ef jal 2a4 <exit>
int f(int x) {
: 1141 add sp,sp,-16
e10: e422 sd s0,8(sp)
12: 0800 add s0,sp,16
return g(x);
}
- Where is the call to function f in the assembly code for main? Where is the call to g? (Hint: the compiler may inline functions.)
From the code above, we can see that the calls to function f
and g
are optimized in main. That means the output of the function calls are computed in compiled time. You can see that the register a1
holds the result of the f(8) + 1
.
- At what address is the function printf located?
From the code above, we can see that the printf
function is located at 0x6bc
.
- What value is in the register ra just after the jalr to printf in main?
Notes: jal
(jump and link): jump to the address and save the address of the next instruction in the ra
register.
By this definition, then the register ra
will store the address of the next instruction, which is 0x34
in this case.
- Run the following code.
unsigned int i = 0x00646c72;
("H%x Wo%s", 57616, (char *) &i); printf
What is the output? Here’s an ASCII table that maps bytes to characters.
The output depends on that fact that the RISC-V is little-endian. If the RISC-V were instead big-endian what would you set i to in order to yield the same output? Would you need to change 57616 to a different value?
In little-endian system, the least significant byte is stored at the lowest address. That means the value i = 0x00646c72
is stored as 72 6c 64 00
in memory. In which,
0x72
isr
0x6c
isl
0x64
isd
0x00
is\0
Also, in printf
, %x
is used to print the hexadecimal format.
Therefore, the output is: He110 World
If RISC-V is big-endian, then the value of i
should be 0x726c6400
and the value of 57616
.
In the following code, what is going to be printed after ‘y=’? (note: the answer is not a specific value.) Why does this happen?
("x=%d y=%d", 3); printf
Since the printf
function requires two arguments for 2 format specifiers, but only one argument is provided, the output will be undefined or the program will be crashed.
Backtrace
General Knowledge
Compiler generates machine code to maintain a stack frame on the stack corresponding to each function call in the current call chain. Each stack frame contains the return address, and the “frame pointer” to the caller’s stack frame.
Register s0
contains the pointer to the current stack frame. The address of the caller’s stack frame is stored at s0 - 8
, and the address of the previous frame pointer is stored at s0 - 16
.
Implementation
void
(void) {
backtrace= r_fp();
uint64 fp ("backtrace:\n");
printfwhile (fp != PGROUNDDOWN(fp)) {
("%p\n", (void*) (*(uint64 *)(fp - 8)));
printf= *(uint64 *)(fp - 16);
fp }
}
General Knowledge (Optional)
There are three types of events that cause a user process to “trap” into the kernel:
- System call (requests by user for OS services)
- Interrupts (external device(s) want attention)
- Program fault (illegal action by program)
In case of the system call, the ecall
trigger the trap process by:
- Switch to supervisor mode (S mode)
- Store
PC
tosepc
- Set
PC
tostvec
Thestvec
holds the address of the trampoline code a.k.a the trap handler code.