assemblygdbqemuriscv32

How to run and debug a simple riscv32 bare metal assembly (compiled into elf) using qemu-system-riscv32 and gdb-multiarch? (linker definition issue?)


I'm trying to create a simple assembly program, compile it into an .elf executable, and run+debug it using qemu-system-riscv32 and gdb-multiarch.

There are the two problems that I get, whenever I try running my .elf:

  1. qemu complains that memory sections overlap: The following two regions overlap (in the memory address space): out/hello_world.elf ELF program header segment 1 (addresses 0x000000007ffff000 - 0x0000000080000050) /usr/local/bin/../share/qemu/opensbi-riscv32-generic-fw_dynamic.bin (addresses 0x0000000080000000 - 0x000000008001e0c0)

  2. And when I try to fix the overlap problem, my main is never entered (and qemu exists). GDB shows the Program Counter / PC at 0x1004 at the beginning. When I continue, my b main is never hit. When I CTRL+C gdb indicates that the qemu procesds has exited, but no further errors except that the remote gdb session connection has been lost.

As I mentioned, I tried changing the memory ranges / entry point in my linker definition file to not overlap with the opensbi from risc-v.

To illustrate my setup, this is my hello_world.s:

.globl main
.type  main, @function
main:
  li a1, 1337
  mv a0, zero
  ret

  .size  main, .-main

qemu.s:

.section .init, "ax"
.global _start
_start:
    .cfi_startproc
    .cfi_undefined ra
    .option push
    .option norelax
    la gp, __global_pointer$
    .option pop
    la sp, __stack_top
    add s0, sp, zero
    #jal zero, _start_continue
    call main
    .cfi_endproc
    .end

Makefile:

CROSS_PREFIX=riscv32-unknown-elf-
INC=-I./src/

clean:
    rm -f out/*.o out/*.elf

%:
    # create output dir
    mkdir -p out
    # compile the assembly specified in the name
    $(CROSS_PREFIX)as $(INC) -g -c src/qemu.s -o out/qemu.o
    $(CROSS_PREFIX)as $(INC) -g -c src/$@.s -o out/$@.o
    # link the assembly
    $(CROSS_PREFIX)ld -Triscv32-virt.ld out/qemu.o out/$@.o -o out/$@.elf

My debug script:

#!/usr/bin/env bash
SEQUENCE=$1
make ${SEQUENCE}
qemu-system-riscv32 -kernel out/${SEQUENCE}.elf -nographic -s -S -m 512M &
CHILD_PID=$!
gdb-multiarch -tui -q -ex "file out/${SEQUENCE}.elf" -ex "target remote 127.0.0.1:1234" -ex "layout asm" -ex "b main" -ex "ni"
kill -9 ${CHILD_PID}

(due to the ni command GDB shows PC at 0x1004 instead of 0x1000, but why does qemu enter my application at 0x1000 and not at the ELF entry address?)

I created the ld file with the help of the following answer: How to debug cross-compiled QEMU program with GDB?

Which has resulted in such a file:

riscv32-virt.ld:

/* Script for -z combreloc */
/* Copyright (C) 2014-2023 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
          "elf32-littleriscv")
OUTPUT_ARCH(riscv)
MEMORY
{
   RAM (rwx) : ORIGIN = 0x80020000, LENGTH = 511M
   RAM2 (rwx) : ORIGIN = 0x80000000, LENGTH = 128K
}
ENTRY(_start)
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", ORIGIN(RAM))); . = SEGMENT_START("text-segment", ORIGIN(RAM)) + SIZEOF_HEADERS;
  PROVIDE(__stack_top = ORIGIN(RAM) + LENGTH(RAM));
  .interp         : { *(.interp) }
  .note.gnu.build-id : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rela.dyn       :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
      *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
      *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
      *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
      *(.rela.ifunc)
    }
  .rela.plt       :
    {
      *(.rela.plt)
      PROVIDE_HIDDEN (__rela_iplt_start = .);
      *(.rela.iplt)
      PROVIDE_HIDDEN (__rela_iplt_end = .);
    }
  .init           :
  {
    KEEP (*(SORT_NONE(.init)))
  }
  .plt            : { *(.plt) *(.iplt) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(SORT(.text.sorted.*))
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .sdata2         :
  {
    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
  }
  .sbss2          : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
  .eh_frame_hdr   : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .sframe         : ONLY_IF_RO { *(.sframe) *(.sframe.*) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
  /* These sections are generated by the Sun/Oracle C++ compiler.  */
  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .sframe         : ONLY_IF_RW { *(.sframe) *(.sframe.*) }
  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges*) }
  /* Thread Local Storage sections  */
  .tdata      :
   {
     PROVIDE_HIDDEN (__tdata_start = .);
     *(.tdata .tdata.* .gnu.linkonce.td.*)
   }
  .tbss       : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array    :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array    :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
    PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array    :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  . = DATA_SEGMENT_RELRO_END (0, .);
  .data           :
  {
    __DATA_BEGIN__ = .;
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  .got            : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
  /* We want the small data sections together, so single-instruction offsets
     can access them all, and initialized data all before uninitialized, so
     we can shorten the on-disk segment size.  */
  .sdata          :
  {
    __SDATA_BEGIN__ = .;
    *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
    *(.sdata .sdata.* .gnu.linkonce.s.*)
  }
  _edata = .; PROVIDE (edata = .);
  . = .;
  __bss_start = .;
  .sbss           :
  {
    *(.dynsbss)
    *(.sbss .sbss.* .gnu.linkonce.sb.*)
    *(.scommon)
  }
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we do not
      pad the .data section.  */
   . = ALIGN(. != 0 ? 32 / 8 : 1);
  }
  . = ALIGN(32 / 8);
  . = SEGMENT_START("ldata-segment", .);
  . = ALIGN(32 / 8);
  __BSS_END__ = .;
    __global_pointer$ = MIN(__SDATA_BEGIN__ + 0x800,
                    MAX(__DATA_BEGIN__ + 0x800, __BSS_END__ - 0x800));
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1.  */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions.  */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2.  */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2.  */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions.  */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3.  */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  /* DWARF 5.  */
  .debug_addr     0 : { *(.debug_addr) }
  .debug_line_str 0 : { *(.debug_line_str) }
  .debug_loclists 0 : { *(.debug_loclists) }
  .debug_macro    0 : { *(.debug_macro) }
  .debug_names    0 : { *(.debug_names) }
  .debug_rnglists 0 : { *(.debug_rnglists) }
  .debug_str_offsets 0 : { *(.debug_str_offsets) }
  .debug_sup      0 : { *(.debug_sup) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

As can be noted I expanded the memory to 512M (because I need that much for my tests). I did this by assigning -m 512M to qemu, and changing it here in the linker definition file.

At first I had this:

MEMORY
{
   RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 512M
}

But this caused the overlapping error. So I tried movinf the ORIGIN to, for example, 0x80020000, but that made it so that my code is never entered during a gdb session. The same applies for the current code (because maybe qemu expects the 0x80000000 location to be mapped):

MEMORY
{
   RAM (rwx) : ORIGIN = 0x80020000, LENGTH = 511M
   RAM2 (rwx) : ORIGIN = 0x80000000, LENGTH = 128K
}

I did double check with readelf to check if the entry address is correct, and it is!:


Elf file type is EXEC (Executable file)
Entry point 0x80020000
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  RISCV_ATTRIBUT 0x001050 0x00000000 0x00000000 0x0004c 0x00000 R   0x1
  LOAD           0x000000 0x8001f000 0x8001f000 0x01050 0x01050 R E 0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .riscv.attributes
   01     .init .text .eh_frame

The versions of the relevant software:

# make --version
GNU Make 4.3
Built for x86_64-pc-linux-gnu

# readelf --version
GNU readelf (GNU Binutils for Ubuntu) 2.38

# qemu-system-riscv32 --version
QEMU emulator version 8.0.2 (v8.0.2)

# gdb-multiarch --version
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1

# riscv32-unknown-elf-as --version
GNU assembler (GNU Binutils) 2.40.0.20230214

# riscv32-unknown-elf-ld --version
GNU ld (GNU Binutils) 2.40.0.20230214

# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.2 LTS
Release:        22.04
Codename:       jammy

This is running on WSL2 on Windows 10 21H2 19044.2604.

What am I doing wrong that qemu won't enter my entry point and won't execute my code?

When diving into GDB I did stumble upon an interesting piece of information:

(gdb) maintenance info sections
Exec file: `/mnt/c/Users/nxf75132/Desktop/qrv32/out/hello_world.elf', file type elf32-littleriscv.
 [0]      0x80020000->0x80020018 at 0x00001000: .init ALLOC LOAD READONLY CODE HAS_CONTENTS
 [1]      0x80020018->0x80020024 at 0x00001018: .text ALLOC LOAD READONLY CODE HAS_CONTENTS
 [2]      0x80020024->0x80020050 at 0x00001024: .eh_frame ALLOC LOAD READONLY DATA HAS_CONTENTS
 [3]      0x0000->0x004c at 0x00001050: .riscv.attributes READONLY HAS_CONTENTS
 [4]      0x0000->0x0040 at 0x000010a0: .debug_aranges READONLY HAS_CONTENTS
 [5]      0x0000->0x005b at 0x000010e0: .debug_info READONLY HAS_CONTENTS
 [6]      0x0000->0x0035 at 0x0000113b: .debug_abbrev READONLY HAS_CONTENTS
 [7]      0x0000->0x0097 at 0x00001170: .debug_line READONLY HAS_CONTENTS
 [8]      0x0000->0x0054 at 0x00001207: .debug_str READONLY HAS_CONTENTS

The at part seems to indicate 0x00001000 for the .init section - I suspect this might be the problem, but how do I fix this?

When I do gdb cat and this command, all starting addresses seem to overlap with the at addresses (only in some cases it's mismatched). But the .init and .text sections are in the expected format where the at is the start address (second column 0x...->).

Edit: When running additional debugging with:

# qemu-system-riscv32 -D ./log.txt -kernel "out/hello_world.elf" -m 512M -monitor stdio -d cpu,exec,in_asm,nochain -singlestep
QEMU 8.0.2 monitor - type 'help' for more information
(qemu) VNC server running on 127.0.0.1:5900
quit # after some time (e.g. one minute)

I get extra debugging in the log.txt, the first entry is:

----------------
IN: 
Priv: 3; Virt: 0
0x00001000:  00000297          auipc                   t0,0                    # 0x1000

Trace 0: 0x7f5940000100 [00000000/00001000/00109003/ff000201] 
 V      =   0
 pc       00001000
 mhartid  00000000
 mstatus  00000000
 mstatush 00000000
 hstatus  00000000
 vsstatus 00000000
 mip      00000080
 mie      00000000
 mideleg  00000000
 hideleg  00000000
 medeleg  00000000
 hedeleg  00000000
 mtvec    00000000
 stvec    00000000
 vstvec   00000000
 mepc     00000000
 sepc     00000000
 vsepc    00000000
 mcause   00000000
 scause   00000000
 vscause  00000000
 mtval    00000000
 stval    00000000
 htval    00000000
 mtval2   00000000
 mscratch 00000000
 sscratch 00000000
 satp     00000000
 x0/zero  00000000 x1/ra    00000000 x2/sp    00000000 x3/gp    00000000
 x4/tp    00000000 x5/t0    00000000 x6/t1    00000000 x7/t2    00000000
 x8/s0    00000000 x9/s1    00000000 x10/a0   00000000 x11/a1   00000000
 x12/a2   00000000 x13/a3   00000000 x14/a4   00000000 x15/a5   00000000
 x16/a6   00000000 x17/a7   00000000 x18/s2   00000000 x19/s3   00000000
 x20/s4   00000000 x21/s5   00000000 x22/s6   00000000 x23/s7   00000000
 x24/s8   00000000 x25/s9   00000000 x26/s10  00000000 x27/s11  00000000
 x28/t3   00000000 x29/t4   00000000 x30/t5   00000000 x31/t6   00000000

After this the PC goes up to PC 0x1014, where it does a jr, t0 to 0x80000000. The instructions are sometimes not aligned (e.g. PC=0x8000004e) which could indicate that 'garbage' is being executed?

Or for example 0x8000005e twice in a row.

I let it run for a while and it seems to get stuck in some kind of loop around 0x8000d4ca/0x8000d4cc/0x8000d4ce (and also jumping around in this region), never reaching my main / startup code. When I look at the executed instructions, to me they don't make logical sense to me:

0x00001000:  00000297          auipc                   t0,0                    # 0x1000
0x00001004:  02828613          addi                    a2,t0,40
0x00001008:  f1402573          csrrs                   a0,mhartid,zero
0x0000100c:  0202a583          lw                      a1,32(t0)
0x00001010:  0182a283          lw                      t0,24(t0)
0x00001014:  00028067          jr                      t0
0x80000000:  00050433          add                     s0,a0,zero
0x80000004:  000584b3          add                     s1,a1,zero
0x80000008:  00060933          add                     s2,a2,zero
0x8000000c:  2571              jal                     ra,1676                 # 0x80000698
0x80000698:  494255b7          lui                     a1,1229082624
0x8000069c:  34f58593          addi                    a1,a1,847
0x800006a0:  4208              lw                      a0,0(a2)
0x800006a2:  feb517e3          bne                     a0,a1,-18               # 0x80000690
0x800006a6:  4589              addi                    a1,zero,2
0x800006a8:  4248              lw                      a0,4(a2)
0x800006aa:  fea5c3e3          bgt                     a0,a1,-26               # 0x80000690
0x800006ae:  4589              addi                    a1,zero,2
0x800006b0:  00b54463          bgt                     a1,a0,8                 # 0x800006b8
0x800006b4:  4a48              lw                      a0,20(a2)
0x800006b6:  8082              ret                     
0x8000000e:  00050833          add                     a6,a0,zero
0x80000012:  00040533          add                     a0,s0,zero
0x80000016:  000485b3          add                     a1,s1,zero
0x8000001a:  00090633          add                     a2,s2,zero
0x8000001e:  58fd              addi                    a7,zero,-1
0x80000020:  01180463          beq                     a6,a7,8                 # 0x80000028
0x80000024:  0b051d63          bne                     a0,a6,186               # 0x800000de
0x80000028:  0001c817          auipc                   a6,114688               # 0x8001c028
0x8000002c:  fdc80813          addi                    a6,a6,-36
0x80000030:  4885              addi                    a7,zero,1
0x80000032:  0118282f          amoadd.w                a6,a7,(a6)
0x80000036:  0a081463          bnez                    a6,168                  # 0x800000de
0x8000003a:  0001c297          auipc                   t0,114688               # 0x8001c03a
0x8000003e:  fd228293          addi                    t0,t0,-46
0x80000042:  00000317          auipc                   t1,0                    # 0x80000042
0x80000046:  fbe30313          addi                    t1,t1,-66
0x8000004a:  0062a023          sw                      t1,0(t0)
0x8000004e:  0001c297          auipc                   t0,114688               # 0x8001c04e
0x80000052:  fc228293          addi                    t0,t0,-62
0x80000056:  0002a283          lw                      t0,0(t0)
0x8000005a:  405303b3          sub                     t2,t1,t0
0x8000005e:  0001ce17          auipc                   t3,114688               # 0x8001c05e
0x80000062:  fa2e0e13          addi                    t3,t3,-94
0x80000066:  007e2023          sw                      t2,0(t3)
0x8000006a:  0001d297          auipc                   t0,118784               # 0x8001d06a
0x8000006e:  06e28293          addi                    t0,t0,110
0x80000072:  0001e317          auipc                   t1,122880               # 0x8001e072
0x80000076:  04e30313          addi                    t1,t1,78
0x8000007a:  06628363          beq                     t0,t1,102               # 0x800000e0
0x8000007e:  a8a1              j                       88                      # 0x800000d6
0x800000d6:  02b1              addi                    t0,t0,12
0x800000d8:  fa5354e3          ble                     t0,t1,-88               # 0x80000080
0x80000080:  ff82af03          lw                      t5,-8(t0)
0x80000084:  4e0d              addi                    t3,zero,3
0x80000086:  01cf1b63          bne                     t5,t3,22                # 0x8000009c
0x8000008a:  ff42ae03          lw                      t3,-12(t0)
0x8000008e:  ffc2af03          lw                      t5,-4(t0)
0x80000092:  9f1e              add                     t5,t5,t2
0x80000094:  9e1e              add                     t3,t3,t2
0x80000096:  01ee2023          sw                      t5,0(t3)
0x8000009a:  a835              j                       60                      # 0x800000d6
0x800000dc:  a011              j                       4                       # 0x800000e0
0x800000e0:  0001c297          auipc                   t0,114688               # 0x8001c0e0
0x800000e4:  f2828293          addi                    t0,t0,-216
0x800000e8:  4305              addi                    t1,zero,1
0x800000ea:  0062a023          sw                      t1,0(t0)
0x800000ee:  0330000f          fence                   rw,rw
0x800000f2:  4081              mv                      ra,zero
0x800000f4:  2bb1              jal                     ra,1372                 # 0x80000650
0x80000650:  0000100f          fence.i                 
0x80000654:  4101              mv                      sp,zero
0x80000656:  4181              mv                      gp,zero
0x80000658:  4201              mv                      tp,zero
0x8000065a:  4281              mv                      t0,zero
0x8000065c:  4301              mv                      t1,zero
0x8000065e:  4381              mv                      t2,zero
0x80000660:  4401              mv                      s0,zero
0x80000662:  4481              mv                      s1,zero
0x80000664:  4681              mv                      a3,zero
0x80000666:  4701              mv                      a4,zero
0x80000668:  4781              mv                      a5,zero
0x8000066a:  4801              mv                      a6,zero
0x8000066c:  4881              mv                      a7,zero
0x8000066e:  4901              mv                      s2,zero
0x80000670:  4981              mv                      s3,zero
0x80000672:  4a01              mv                      s4,zero
0x80000674:  4a81              mv                      s5,zero
0x80000676:  4b01              mv                      s6,zero
0x80000678:  4b81              mv                      s7,zero
0x8000067a:  4c01              mv                      s8,zero
0x8000067c:  4c81              mv                      s9,zero
0x8000067e:  4d01              mv                      s10,zero
0x80000680:  4d81              mv                      s11,zero
0x80000682:  4e01              mv                      t3,zero
0x80000684:  4e81              mv                      t4,zero
0x80000686:  4f01              mv                      t5,zero
0x80000688:  4f81              mv                      t6,zero
(...)                     

Solution

  • The "solution" (or a workaround?) was to use another qemu "board" -- machine as it is called. Default seems ot be Spike, I tried VirtIO:

    change in debug.sh:

    # (...)
    
    qemu-system-riscv32 -cpu rv32 -device loader,file="out/${SEQUENCE}.elf",cpu-num=0 -nographic -s -S -m 512M -machine virt &
    CHILD_PID=$!
    
    gdb-multiarch -tui -q \
        -ex "file out/${SEQUENCE}.elf" \
        -ex "target remote 127.0.0.1:1234" \
        -ex "layout src" \
        -ex "layout regs" \
        -ex "b main" \
        -ex "focus cmd"
    
    # (...)
    

    and the linker definition memory section definition:

    MEMORY
    {
       RAM (rwx)  : ORIGIN = 0x80000000, LENGTH = 510M
       RAM2 (rwx) : ORIGIN = 0xffff0000, LENGTH = 0xffff
    }
    

    I don't know why it works, and if this is a real solution, but for now it seems to be a good work-around. If anyone can chime in and correct these mistakes with more certainty than me, I will accept such an answer.

    My idea was to bypass the firmware (not have any firmware at all). I don't know the impact of the lack of firmware..