-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add hello world example for RISC-V 32 and 64 bit
- Loading branch information
Showing
2 changed files
with
102 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Hello World in RISC-V | ||
|
||
## 64-bit | ||
|
||
Assemble: | ||
|
||
```sh | ||
riscv64-elf-as -march rv64i -mabi lp64 -o hello.o hello.s | ||
``` | ||
|
||
Link: | ||
|
||
```sh | ||
riscv64-elf-ld -o exe --verbose hello.o | ||
``` | ||
|
||
Execute the RISC-V ELF in QEMU: | ||
|
||
```sh | ||
qemu-riscv64 exe | ||
``` | ||
|
||
Run a query on the RISC-V ELF: | ||
|
||
```sh | ||
poetry run sqlelf examples/hello-riscv/exe \ | ||
--sql "SELECT * FROM elf_instructions LIMIT 5;" | ||
``` | ||
|
||
Or run a query and attach a debugger: | ||
|
||
```sh | ||
poetry run sqlelf-debug examples/hello-riscv/exe \ | ||
--sql "SELECT * FROM elf_instructions LIMIT 5;" | ||
``` | ||
|
||
Double check the disassembly: | ||
|
||
```sh | ||
riscv64-elf-objdump --disassemble examples/hello-riscv/exe | ||
``` | ||
|
||
## 32-bit | ||
|
||
Assemble `hello.s` into an object file for the RISC-V 32-bit base integer instruction set (`-march rv32i`), little-endian (`-mlittle-endian`), with an ABI that follows the convention where `int`, `long` and `pointer` types are all 32-bit, with debug symbols included in the object file (`-g`): | ||
|
||
```sh | ||
riscv64-elf-as -march rv32i -mabi ilp32 -mlittle-endian -o hello.o hello.s -g | ||
``` | ||
|
||
Link the object file into a RISC-V 32-bit little-endian executable (`-m elf32lriscv`), with the symbol `_start` as its entry point: | ||
|
||
```sh | ||
riscv64-elf-ld -e _start -m elf32lriscv -o exe --verbose hello.o | ||
``` | ||
|
||
Execute the RISC-V ELF in QEMU: | ||
|
||
```sh | ||
qemu-riscv32 exe | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
.section .text | ||
.globl _start | ||
.equ STDOUT, 1 # File descriptor 1 is standard output (stdout) | ||
.equ WRITE, 64 # Linux write syscall | ||
.equ EXIT, 93 # Linux exit syscall | ||
.equ EXIT_CODE_SUCCESS, 0 | ||
|
||
_start: | ||
# In C, a list of parameters is passed to the kernel in a certain sequence. | ||
# For the write system call, the parameters are structured as follows: | ||
# ssize_t write(int fd, const void *buf, size_t count) | ||
# The three parameters passed are: | ||
# 1. a file descriptor (e.g. 1 for stdout) | ||
# 2. a pointer to a character buffer (i.e. a string) | ||
# 3. the number of characters in that string to be written. | ||
li a0, STDOUT | ||
la a1, buf_begin | ||
# Load a byte from memory, zero-pad it (to a 64-bit value in RV64), and store | ||
# the unsigned value in the destination register a2. | ||
lbu a2, buf_size | ||
|
||
# Store the system call number in register a7. | ||
li a7, WRITE | ||
# Switch to RISC-V supervisor mode (the Linux kernel runs in this mode) and | ||
# make a request using the value stored in a7 as the system call number. | ||
ecall | ||
|
||
li a0, EXIT_CODE_SUCCESS | ||
li a7, EXIT | ||
ecall | ||
|
||
# The .rodata section of an ELF binary contains constant values. The .rodata | ||
# section is marked as read-only, so these values cannot change at runtime. | ||
.section .rodata | ||
|
||
buf_begin: | ||
.string "Hello World!\n" | ||
buf_size: | ||
# Current address (the .) minus address of buf_begin = length of buffer. | ||
# We store the result in a 8-bit word using the .byte directive. | ||
.byte .-buf_begin |