Compiling LLDB on the RISC-V HiFive Unmatched

Different processors and instruction set architectures have always fascinated me. As a child of the late 70s and 80s I wasn’t quite old enough to appreciate the difference between the 6502 and 6809 processors, but by the early 90s and the introduction of the PowerPC, particularly in the PowerMac 6100 it was clear that ISAs can come and go. What I didn’t appreciate at the time, and didn’t until the early 2020s, is the licensing model around ISAs. Plenty has been written on RISC-V and the opportunity for it to revolutionize the market, so we won’t repeat it here. All I can say is that I’ve had fun over the past couple of years supporting RISC-V through purchasing and tinkering with systems built using RISC-V processors.

Recently I had the good fortune of purchasing a brand new HiFive Unmatched and really wanted to stress it as a “daily driver” for building software natively on and for a RISC-V system. Why not use a cross-compiler? Indeed, my M1 MacBook Pro kicks the Unmatched’s ass, and even running benchmarks in a Docker container on the M1 is faster:

On an M1:

7.3 seconds.

On the HiFive Unmatched:

19 seconds.

QEMU RISC-V emulation vs. what was considered a top-of-the-line RISC-V processor, the SiFive Freedom U740 SoC. Indeed, it still might be.

My HiFive Unmatched, affectionately named Stormtrooper

But, this is all missing the point isn’t it? To propel RISC-V forward as an alternative we must move beyond using emulation and relying on cross-compilers, and push ourselves to “go native.” So that’s what we’ll do by compiling the LLDB debugger natively on the HiFive Unmatched. Why LLDB? Well, for whatever reason it wasn’t installed with apt-get install llvm or apt-get install clang on my machine, so I decided to take the plunge and compile it myself.

I strongly prefer to use “chroot jails” for building software. For starters, they are useful to ensure that dependencies are strongly tracked, and that “cruft” that accumulates over time doesn’t pollute your build environment.
Since our HiFive Unmatched is running Ubuntu 22.04 “Jammy”, it made sense to use debootstrap to create our jail. First, let’s install debootstrap with apt-get install debootstrap and then look how our filesystem is structured:

debootstrap will create us a nearly fully functional environment for cross-compiling LLDB. Once it’s complete there are a few bind mounts we will need to set up.

From here, we are in the jail.

Now we’re ready to review the instructions for building LLDB. They’re pretty straightforward, and consist of the installing prerequisites, configuring your building tree, and typing make. Well, in this case, ninja. Your dependencies are the typical cast of characters for a Debian-based distribution: build-essential for the compiler, libraries for text interfaces (libedit-dev, libncurses5-dev). LLDB comes with a scripting facility, and its choice for interfacing languages is SWIG.

In Ubuntu 22.04 the swig package is in the “Universe” repository, so let’s add it:

Now we can install all of our dependencies for building LLDB.

Editor’s note: I’m aware of multistrap but haven’t had time to research it.

A couple of comments on the build options. First, we’re only going to build lldb and we’re only going to support the RISCV architecture (that is, our lldb won’t be able to read any other architectures). Building lldb requires clang to be “enabled”, so LLVM_ENABLE_PROJECTS must be set to clang;lldb. Finally, to save some disk space, we’ll build the Release version.

Now, before we build, create a screen session. This is going to allow our us to get back to our build if ssh hangs up on us.

And we’re off!

This will take some time. On my HiFive Unmatched it took 10 hours.

Whew! A little over nine and a half hours to build lldb. Let’s hope it works!

Testing

Let’s look at some basics of LLDB with a toy C application:

To get this in our debugger, compile with gcc -g -o toy toy.c, or if you have clang installed the command-line arguments are the same.

Okay, here we go. /opt/loadbuild/jails/build-lldb/opt/build/bin/lldb toy. You can see I have not installed lldb anywhere and its still in its “jail” directory, but we’re no longer in the jail (where it sits at /opt/build/bin/lldb).

We’ll make this brief, as there are a lot of LLDB tutorials online. First, let’s set a breakpoint on the “most interesting” function, add_arrays. Hitting the TAB key after typing add shows the useful completion feature.

Let’s run with r (and yes I’m aware r is a GDB command that LLDB has mapped to):

Let’s dump the RISC-V registers!

I find it interesting that even the RISC-V zero register is printed out, but then again, it is a register.

A few other quick commands. Since we’re in a frame (function) we can print the local variables.

The arrays are passed as two integer pointers, and looking back at the registers we can see the RISC-V calling convention in action (a0, a1, and a2 have the function arguments). Let’s read the memory that a1 points to:

Nifty. We can even get naughty and write values into memory. Let’s write the value 42 at the second index of the array.

Hitting c for continue we can see our handiwork:

Well, it looks like the memory write command took its argument in base 16, but it worked all the same.

Closing

As I said at the start, there’s something about different instruction sets and CPU architectures that I find fascinating. History has shown that there is still a lot of room for innovation and competition, and RISC-V is proving that out each and every day. Is it ready to be your daily driver? Probably not, but that isn’t a function of the ISA but of the market and its evolution relative to other platforms. It will continue to make progress as companies like SiFive and StarFive bring processors and boards to the market. I have my sights set on the HiFive Pro P550 “Horse Creek” board, and hopefully we can cut some time off compiling LLDB!

Leave a Reply

Your email address will not be published. Required fields are marked *