Table of Contents
BeagleV-Ahead - RISC-V Assembly on Linux
The BeagleV-Ahead board is an excellent choice for diving into assembly programming with the RISC-V architecture. In this quick tutorial, I'll walk you through the steps to kickstart your RISC-V assembly programming journey on the Ubuntu image we loaded in the initial getting started tutorial. Let's get those assembly gears turning!
Linux SYSCALLs
We will use Linux syscall functions to display a welcome message on the console screen.
A Linux syscall, short for system call, is a fundamental interface between the operating system kernel and user-space programs. It provides a way for applications to request services or perform operations that require higher privileges and access to system resources. Syscalls serve as a bridge between the user-level applications and the kernel, allowing programs to interact with the underlying operating system.
Writing our first risc-v assembly program
Start a new ssh session
Using your preferred text editor, write the following code. In my case, I've chosen the nano editor, which is very user-friendly.
nano hello.s
Here is the RISC-V assembly code that prints "Hello to the element14 community!" to the console using Linux syscall functions:
# Risc-V Assembler program to print "Hello to the element14 community!" # to stdout. # # a0-a2 - parameters to linux function services # a7 - linux function number .global _start # Provide program starting address to linker # Setup the parameters to print hello world # and then call Linux to do it. _start: addi a0, x0, 1 # 1 = StdOut la a1, helloworld # load address of helloworld addi a2, x0, 34 # length of our string addi a7, x0, 64 # linux write system call ecall # Call linux to output the string # Setup the parameters to exit the program # and then call Linux to do it. addi a0, x0, 0 # Use 0 return code addi a7, x0, 93 # Service command code 93 terminates ecall # Call linux to terminate the program .data helloworld: .ascii "Hello to the element14 community!\n"
This code initializes parameters to use the Linux syscall for writing (write
) to standard output (stdout
). After printing the message, it sets up parameters to exit the program using the Linux syscall for program termination (exit
). The actual message "Hello to the element14 community!\n" is stored in the .data
section.
Explanation:
-
.global _start
: This declares the_start
label as the entry point for the program, making it visible to the linker. -
_start:
: The actual start of the program, where the instructions begin. -
addi a0, x0, 1
: Sets up the file descriptor for standard output (stdout) in registera0
. -
la a1, helloworld
: Loads the address of thehelloworld
string into registera1
. -
addi a2, x0, 34
: Specifies the length of the string, which is 34 characters (including the newline). -
addi a7, x0, 64
: Sets up registera7
with the system call number for the Linuxwrite
system call. -
ecall
: Triggers a system call, invoking the Linux kernel to perform the specified action (in this case, write to stdout). -
The subsequent block sets up parameters to exit the program using the Linux
exit
system call (command code 93). -
Finally, the
.data
section contains the definition of thehelloworld
string.
Next, let's save the file.
In the nano editor, save the file by pressing Ctrl + O, then confirm the file name and press Enter. To exit, press Ctrl + X.
Assembling
We need to convert the source code written in assembly language into machine code or object code.
Write
beagle@BeagleV:~$ as hello.s -o hello.o
The as command is the GNU assembler, and here it is used to assemble the RISC-V assembly source code file hello.s into an object file hello.o.
The -o flag specifies the output file.
Linking
beagle@BeagleV:~$ ld hello.o -o hello
- The ld command is the GNU linker, and it links the object file hello.o into an executable file named hello.
- The -o flag specifies the output file.
After these commands, we have an executable file named hello.
We can execute it to see the "Hello to the element14 community!" message on the console.
Console output
beagle@BeagleV:~$ nano hello.s beagle@BeagleV:~$ as hello.s -o hello.o beagle@BeagleV:~$ ld hello.o -o hello beagle@BeagleV:~$ ./hello Hello to the element14 community! beagle@BeagleV:~$ objdump -d hello hello: file format elf64-littleriscv Disassembly of section .text: 00000000000100e8 <_start>: 100e8: 00100513 li a0,1 100ec: 00001597 auipc a1,0x1 100f0: 02058593 add a1,a1,32 # 1110c <__DATA_BEGIN__> 100f4: 02200613 li a2,34 100f8: 04000893 li a7,64 100fc: 00000073 ecall 10100: 00000513 li a0,0 10104: 05d00893 li a7,93 10108: 00000073 ecall beagle@BeagleV:~$
Unlocking Binary Secrets: Getting Info with Objdump
objdump is a command-line utility that is part of the GNU Binutils suite. It is used to display information about one or more object files (binary files) including executable, shared object, and relocatable object files. objdump provides detailed information about the binary file's contents, such as disassembled machine code, section headers, symbols, and more.
Debugging without JTAG using `gdb`
GDB, short for GNU Debugger, is a powerful and versatile debugger that plays a pivotal role in software development. GDB is an essential tool to identify, analyze, and fix bugs within the code. https://en.wikipedia.org/wiki/GNU_Debugger
To Install gdb:
sudo apt install -y gdb
gdb help:
beagle@BeagleV:~$ gdb hello GNU gdb (Ubuntu 13.1-2ubuntu2.1) 13.1 Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "riscv64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from hello... (No debugging symbols found in hello) (gdb) help List of classes of commands: aliases -- User-defined aliases of other commands. breakpoints -- Making program stop at certain points. data -- Examining data. files -- Specifying and examining files. internals -- Maintenance commands. obscure -- Obscure features. running -- Running the program. stack -- Examining the stack. status -- Status inquiries. support -- Support facilities. text-user-interface -- TUI is the GDB text based interface. tracepoints -- Tracing of program execution without stopping the program. user-defined -- User-defined commands. Type "help" followed by a class name for a list of commands in that class. Type "help all" for the list of all commands. Type "help" followed by command name for full documentation. Type "apropos word" to search for commands related to "word". Type "apropos -v word" for full documentation of commands related to "word". Command name abbreviations are allowed if unambiguous. (gdb)
A typical GDB Debugging Session
In this GDB session we will debug the "hello" executable.
- We start GDB with the command: gdb hello
- Then GDB provides information about its version, configuration, and licensing. And it indicates that no debugging symbols are found in the "hello" program.
- We start the program execution with the starti command.
- The program stops at the _start function.
- With the disas command we disassemble the _start function, revealing the assembly code.
- Then we set a breakpoint at the instruction address *0x00000000000100fc.
- We use the continue command to resume execution.
- The program hits the breakpoint at address 0x00000000000100fc, corresponding to the _start function.
- We use the info registers command to display the values of various registers, providing insight into the program state.
- The program continues its execution until completion, displaying the message "Hello to the element14 community!".
- We exit gdb with the exit command.
Session log:
beagle@BeagleV:~$ gdb hello GNU gdb (Ubuntu 13.1-2ubuntu2.1) 13.1 Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "riscv64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from hello... (No debugging symbols found in hello) (gdb) starti Starting program: /home/beagle/hello Program stopped. 0x00000000000100e8 in _start () (gdb) disas Dump of assembler code for function _start: => 0x00000000000100e8 <+0>: li a0,1 0x00000000000100ec <+4>: auipc a1,0x1 0x00000000000100f0 <+8>: add a1,a1,32 # 0x1110c 0x00000000000100f4 <+12>: li a2,34 0x00000000000100f8 <+16>: li a7,64 0x00000000000100fc <+20>: ecall 0x0000000000010100 <+24>: li a0,0 0x0000000000010104 <+28>: li a7,93 0x0000000000010108 <+32>: ecall End of assembler dump. (gdb) break *0x00000000000100fc Breakpoint 1 at 0x100fc (gdb) continue Continuing. Breakpoint 1, 0x00000000000100fc in _start () (gdb) info registers ra 0x2aaaaeeb1e 0x2aaaaeeb1e sp 0x3ffffff3c0 0x3ffffff3c0 gp 0x2aaab95408 0x2aaab95408 tp 0x3ff7e65780 0x3ff7e65780 t0 0x3ff7f8f9a0 274743228832 t1 0x2aaaadc10c 183252140300 t2 0xb1c51557703218e4 -5637075893295638300 fp 0x2aaabaf8d0 0x2aaabaf8d0 s1 0x2aaabaf890 183253006480 a0 0x1 1 a1 0x1110c 69900 a2 0x22 34 a3 0x0 0 a4 0x0 0 a5 0x0 0 a6 0x48 72 a7 0x40 64 s2 0x2aaabaf8d0 183253006544 s3 0x0 0 s4 0x2aaabaf890 183253006480 s5 0x3ff7ffdd70 274743680368 s6 0x2aaabacf10 183252995856 s7 0x2aaab94e60 183252897376 s8 0x0 0 s9 0x0 0 s10 0x63 99 s11 0x2aaab9d010 183252930576 t3 0x3ff7efbf2c 274742624044 t4 0x2aaabad 44739501 --Type <RET> for more, q to quit, c to continue without paging-- t5 0x2aaabad 44739501 t6 0x2aaabadd50 183252999504 pc 0x100fc 0x100fc <_start+20> (gdb) continue Continuing. Hello to the element14 community! [Inferior 1 (process 2313) exited normally] (gdb) exit beagle@BeagleV:~$
Complete session:
References
- Creating your own RISC-V Project with BeagleBoard.org by Join Jason Kridner, founder and president of the board at the BeagleBoard.
- https://smist08.wordpress.com/2019/09/07/risc-v-assembly-language-hello-world/
- https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md
- https://jborza.com/post/2021-05-11-riscv-linux-syscalls/
- RISC-V Foundation
- RISC-V ISA Specification
- RISC-V GNU Toolchain