auipc rd, imm20
(add upper immediate to pc
):
add imm20 << 12
to the address of this instruction and write
the result in register rd
jalr rd, imm12(rs)
(jump and link register):
set pc
to rs + imm12
(sign-extended, LSB cleared);
write pc + 4
to register rd
jalr rs
:
pseudoinstruction for jalr ra, 0(rs)
ret
: set pc
to ra
,
pseudoinstruction for jalr zero, 0(ra)
call imm32
(usually with gcc -S
):
PC-relative call with 32-bit offset,
pseudoinstruction for auipc ra, imm32_hi
; jalr ra, imm32_lo(ra)
long bar(long x)
{
return x;
}
long foo(long x)
{
return bar(x) + 1;
}
long main(void)
{
return foo(0) + foo(1);
}
$ riscv64-unknown-elf-gcc -O -fno-omit-frame-pointer -mcmodel=medany -mno-relax -fno-pie -no-pie -fno-inline -o foo foo.c
$ riscv64-unknown-elf-objdump -d foo
...
0000000000010188 <bar>:
10188: 1141 addi sp,sp,-16
1018a: e422 sd s0,8(sp)
1018c: 0800 addi s0,sp,16
1018e: 6422 ld s0,8(sp)
10190: 0141 addi sp,sp,16
10192: 8082 ret
0000000000010194 <foo>:
10194: 1141 addi sp,sp,-16
10196: e406 sd ra,8(sp)
10198: e022 sd s0,0(sp)
1019a: 0800 addi s0,sp,16
1019c: 00000097 auipc ra,0x0
101a0: fec080e7 jalr -20(ra) # 10188 <bar>
101a4: 0505 addi a0,a0,1
101a6: 60a2 ld ra,8(sp)
101a8: 6402 ld s0,0(sp)
101aa: 0141 addi sp,sp,16
101ac: 8082 ret
00000000000101ae <main>:
101ae: 1101 addi sp,sp,-32
101b0: ec06 sd ra,24(sp)
101b2: e822 sd s0,16(sp)
101b4: e426 sd s1,8(sp)
101b6: 1000 addi s0,sp,32
101b8: 4501 li a0,0
101ba: 00000097 auipc ra,0x0
101be: fda080e7 jalr -38(ra) # 10194 <foo>
101c2: 84aa mv s1,a0
101c4: 4505 li a0,1
101c6: 00000097 auipc ra,0x0
101ca: fce080e7 jalr -50(ra) # 10194 <foo>
101ce: 9526 add a0,a0,s1
101d0: 60e2 ld ra,24(sp)
101d2: 6442 ld s0,16(sp)
101d4: 64a2 ld s1,8(sp)
101d6: 6105 addi sp,sp,32
101d8: 8082 ret
...
.
.
.
+-> +----------------+ |
| | main's ra | |
| | previous s0/fp ----+
| | saved s1 |
| | ... |
| +----------------+ <-+
| | foo's ra @main | |
+---- previous s0/fp | |
+----------------+ |
.
.
.
backtrace()
(kernel/printf.c
)stvec
(supervisor trap vector base address register); search for “w_stvec
” in kernel/trap.c
sepc
stvec
sret
instruction: the cpu returns to the address specified in sepc
and switches to the orignal (user/kernel) modeprintf("%d\n", *(volatile char *)0xdeadbeef);
to main()
in user/echo.c
scause
: 13 (0xd), “load access fault”sepc
: pc of the faulted instructionstval
: the virtual memory address$ echo
usertrap(): unexpected scause 0x000000000000000d (load page fault) pid=3
sepc=0x000000000000001a stval=0x00000000deadbeef
lbu
instruction at the address from sepc
)lbu
instruction at address 0x1a
uservec
(kernel/trampoline.S
) at address 0x3ffffff000
TRAMPOLINE
defined in kernel/memlayout.h
usertrap()
in kernel/trap.c
)p->trapframe->epc = r_sepc()
do? save the user program counter (i.e., 0x1a
)exit()
kills the current processp->killed = 1
(usertrap()
in kernel/trap.c
)?
usertrapret()
w_sepc(p->trapframe->epc)
do?
write the saved user program counter (i.e., 0x1a
) to sepc
, where cpu will resume execution upon sret
((void (*)(uint64,uint64))fn)(TRAPFRAME, satp)
do? jump to trampoline code userret
(kernel/trampoline.S
)sret
, which returns to user: the same lbu
instruction at address 0x1a
p->trapframe->epc += 4
p->trapframe->a1 = 42
)#include <sys/syscall.h>
#include <unistd.h>
/*
* syscall number & return value: %rax
* syscall arguments: %rdi, %rsi, %rdx, %r10, %r8, %r9
* %rcx and %r11 are not preserved
*
* To compile, use `gcc -nostdlib`.
*/
void _start(void)
{
int fd = 1;
char buf[] = "hello world!\n";
size_t n = sizeof(buf) - 1;
asm volatile ("syscall"
: /* ignore output */
: "a"(__NR_write), "D"(fd), "S"(buf), "d"(n)
: "rcx", "r11", "memory"
);
asm volatile ("syscall"
: /* no output */
: "a"(__NR_exit), "D"(0)
);
}
ecall
instruction - see the priv speca7
a0
–a5
a0
usertrap()
(kernel/trap.c
): calls syscall()
if scause
is 8 (i.e., ecall
from user space)syscall()
(kernel/syscall.c
): dispatches based on a7
& sets a0
to return valuewrite(fd, buf, n)
in xv6
sys_write
(kernel/sysfile.c
): how to retrieve the values of fd
, buf
, and n
?
from what registers? where & when are these registers stored?buf
in kernel?
buf
is a user-space pointer; we cannot simply dereference it in kernelcopyin()
(kernel/vm.c
)stvec
, sepc
, scause
, stval
, sstatus
, sscratch
ecall
, sret
kernel/trampoline.S
, kernel/trap.c
handle_exception
, handle_syscall
),
kernel/syscall_table.c (sys_call_table
)