Getting Started With RISC-V Assembly
This little article is for readers who want to learn a little bit about RISC-V assembly programming. You will learn how to compile and run programs on your x86 GNU/Linux system through the magic of cross-compiling. This exercise will set the stage for some more advanced explorations later on!
What is cross-compiling? In short, it provides a way for you to consume a program on your machine (aka, “the host”), compile it, and produce a binary that can execute on another platform (“the target.”)
If you’re an embedded developer, you’re no stranger to cross-compiling. If you don’t have a RISC-V desktop machine (some of us don’t, yet) to work on, cross-compiling will let you develop/work on your x86 machine, then build a binary that can run on your smaller, often resource-constrained, embedded device. Nobody wants to run an IDE and a compiler on a smartwatch!
Today we'll jump in by installing the toolchain you need, and the QEMU emulator (https://www.qemu.org/) with RISC-V support.
I've been using QEMU for several years as a free, easy-to-use, and highly configurable, emulator. It's been a reliable friend for installing different Operating Systems and virtualizing them. It works really well for letting us run programs compiled for a different architecture...like RISC-V
Let's jump right in!
Installing Dependencies
First, let’s install the RISC-V toolchain, like this:
sudo apt install gcc-riscv64-linux-gnu
Then, install QEMU with RISC-V support:
sudo apt install qemu-system-misc qemu-user
(Note: You can also build these things for yourself -- try https://github.com/riscv-collab/riscv-gnu-toolchain for starters. I'd encourage you to at least look at the source so you can explore the depth and capabilities these tools provide.)
Writing, Compiling, and Running, a Program
Finally, let’s write a little program called hello.c
Program 1. hello.c
#include <stdio.h>
int main() {
printf("Hello, RISC-V people!\n");
return 0;
}
Once that's in place, you're ready to cross-compile your 'hello' program!
Recommended by LinkedIn
riscv64-linux-gnu-gcc -o hello hello.c
Now for a digression--let's learn something about the output file ('hello'), using the 'file' command:
$ file hello
hello: ELF 64-bit LSB pie executable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1,BuildID[sha1]=9a610a50b3e8702b2a5a4b6ab8b15ab24bdf85d7, for GNU/Linux 4.15.0, not stripped
There's a lot of information on that line! Depending on your particular compiler (and whether or not you added any flags to your invocation of gcc), you might see different symbols and values.
Here are some of the fields, along with their definitions:
At this point, if you’re on an x86 system, you’re welcome to try executing the file from the command line (./hello) but you probably won't get too far, since the “target” of our compilation is the RISC-V platform.
This is where QEMU can help – by emulating the target, it can let us run our program! Let's see how we can execute this program using QEMU--it's pretty easy! Just try:
qemu-riscv64 -L /usr/riscv64-linux-gnu ./hello
Hello, RISC-V people!
(For extra credit, what does the -L flag do on that line?)
Conclusion
So, there you have it! in this little article, we setup a basic RISC-V development toolchain and the QEMU emulator.
Using cross-compilation, we were able to build a binary that's executable by a RISC-V processor, and finally, we were able to run it in an emulator.
We'll build on these tools and techniques in our next article, where we'll actually start writing some low-level code and getting to know the RISC-V a little better. Feel free to DM me with any questions, concerns, bugs (ook!), etc.
Having worked on RISC-V extensions myself, I can say beginner-friendly explanations like these are exactly what the community needs. Looking forward for further parts!