Monday 28 March 2022

RISC-V Programs

I  bought my Nezha back in June 2021.  Of course the reason for buying it was the RISC-V (RV) instruction set, so to make it worthwhile I needed to write some assembly programs.  I learned assembly mainly from Anthony J Dos Reis's book which I started last October.  I wrote a few I/O routines then, and have been slow to follow up but I have gradually made progress.

Part of the problem is that C is an easier language for "low-level" program and the use of Assembly is somewhat artificial.  One solution is to use assembly to look at our programming environment provided by Linux, we can more easily look at registers and memory in Assembly.  Another solution is to look closely at how the C and assembly environments work together, which I shall cover in a subsequent post.

I am unsure how much to use clib subroutines in my program.  They provide me with all the functionality of the C standard functions.  At best it saves me time writing lots of low-level subroutines, at worst it means that I might as well be writing C.

Display registers and addresses

My first program (memdisp1) simply displayed the contents of some particular registers and label addresses within the program itself.  I used clib printf to display register values but found that it only prints 32-bit values.  This meant that addresses were incomplete, I could shift right to get the remaining hex digits.  It turns out that there are 6 more bits to be read.



From the output we can see that the program entry point (main) is at 2A,DA2E,66D4 and the stack pointer is at 3F,FFDA,E490.  A 32-bit address gives a potential range of 4GB and 6 extra bits multiply that by 64 to 256GB, although of course we dont have that much memory.  In practice linux will assign an appropriate set of memory pages to the process.  It is more important for us to know where in the stack our data is and what offset in the program our instructions are.

Display register values properly


As printf doesn't do a good job of printing my 64-bit registers (there may be a compilation setting I am missing which limits results to 32-bit) I wrote an assembly program (memdisp2) to print a register value by selecting the rightmost 4 bits and printing the corresponding hex digit.  The program loops round shift right 4 bits each time until all the digits have been printed.
As there are no C lib calls in this program we don't need the overhead of C initialisation and the program is a lot smaller1408B compared tih 8584B



Display a range of memory


We now have the tools to print out a decent memory dump within our program.  The code to display a 64 bit register goes in a routine disp64.  We put the code to display a range of addresses in another subroutine disprange, so that it can be used generally.  Memdisp4 simply calls disprange to display some values.


The result looks pretty good to me.  We could also use the debugger (GDB) to get this type of information but it is handy to be able to dump it within our program. The program is loaded at a different location each time it is run and to understand more about the linking and loading process I would be delving further into Linux rather than RV assembler.


No comments:

Post a Comment