stdinfgetsriscvforthnewlib

Calling fgets in RISC-V assembly on Spike/PK


Update: Writing this out allowed me to spot where I was going wrong, but not why. I am obviously calling fgets in the wrong way, because after five calls I get to the address 0x221000 which is where the mmapped memory is - I am writing at higher addresses - but I don't know why that that is happening. Could someone explain?

This is a bit complex and I'm at a loss to see why this behaviour is seen: I don't know if I have got the basics wrong or if it's a feature of Spike/PK.

To note: the libc here is provided by newlib and the code is compiled as riscv64-unknown-elf.

Short version I have input code written in RISC-V assembly that previously ran smoothly, but since I introduced a system call to mmap it crashes the fifth time it is executed. Is the problem because I have got the wrong sequence of calls or possibly an issue with the Spike emulator and PK proxy kernel?

Long explanation

I am writing a Forth-like threaded interpreted language. It is currently targeted at the PK proxy kernel on the Spike emulator, but hopefully soon to run on 'real' hardware. The code is at https://github.com/mcmenaminadrian/riscyforth

The TIL implements an endless loop to pick up input calling, in sequence, a routine to get the filepointer for standard input and then a routine to get the input.

To get the standard input filepointer (which is stored on the stack):

mv a0, zero
la a1, stdinopen
call fdopen
addi sp, sp, -8
sw a0 0(sp)

(stdinopen is in the .data section as .asciz "r")

Then to get the input - pop that off the stack...

lw a2, 0(sp)
addi sp, sp, 8
la a0, INPUT_BUFFER
li a1, BUFFERLEN #0x200
call fgets

INPUT_BUFFER is defined in .bss section as .common INPUT_BUFFER, BUFFERLEN, 8

This all worked fine until I added new code that mapped in some memory as R/W/X - if you know about Forth you will know that I need memory with this unusual set of permissions because I have to allow users to define new keywords.

Now the attempt to get input fails on the fourth time it is called. (Though if I define out the mmap the problem goes away.)

Here is a failure example:





WELCOME TO RISCYFORTH - Copyright Adrian McMenamin 2020 - 2021


RISCYFORTH is a threaded interpreted language closely based on FORTH. Currently we are running in EXECUTE mode and anything you type will be executed as each line is entered. RISCYFORTH is licenced under version 2 of the GNU General Public Licence. See https://github.com/mcmenaminadrian/riscyforth.git



346 5622 * dup OK

dup OK

dup OK

. 1945212 OK

z 0000000000000000 ra 0000000000010442 sp 0000003ffffffaf8 gp 000000000001fe30 tp 0000000000000000 t0 000000000009fa60 t1 000000000000000a t2 0000000000000003 s0 0000000000000000 s1 0000000000000000 a0 000000000001e530 a1 000000000009fa60 a2 0000000000000200 a3 0000000000000000 a4 0000000000221000 a5 0000000000000001 a6 000000000001ec88 a7 00000000000000d6 s2 00000000000001ff s3 000000000009fa60 s4 000000000001e530 s5 000000000009fa60 s6 0000000000000000 s7 0000000000011b64 s8 0000000000010400 s9 000000000011fc58 sA 000000000009f950 sB 0000000000000000 t3 0000000000000064 t4 0000003ffffffb08 t5 0000000000000054 t6 0000000000000054 pc 0000000000011ca4 va/inst 0000000000000008 sr 8000000200006020 User load segfault @ 0x0000000000000008

And here is where the code is failing:

0000000000011c64 <_fgets_r>:
11c64:       4785                    li      a5,1
11c66:       0ac7de63                bge     a5,a2,11d22 <_fgets_r+0xbe>
11c6a:       7139                    addi    sp,sp,-64
11c6c:       f822                    sd      s0,48(sp)
11c6e:       f04a                    sd      s2,32(sp)
11c70:       ec4e                    sd      s3,24(sp)
11c72:       e852                    sd      s4,16(sp)
11c74:       fc06                    sd      ra,56(sp)
11c76:       f426                    sd      s1,40(sp)
11c78:       e456                    sd      s5,8(sp)
11c7a:       e05a                    sd      s6,0(sp)
11c7c:       8932                    mv      s2,a2
11c7e:       8a2a                    mv      s4,a0
11c80:       89ae                    mv      s3,a1
11c82:       8436                    mv      s0,a3
11c84:       c119                    beqz    a0,11c8a <_fgets_r+0x26>
11c86:       493c                    lw      a5,80(a0)
11c88:       cbc1                    beqz    a5,11d18 <_fgets_r+0xb4>
11c8a:       397d                    addiw   s2,s2,-1
11c8c:       8ace                    mv      s5,s3
11c8e:       a819                    j       11ca4 <_fgets_r+0x40>
11c90:       601c                    ld      a5,0(s0)
11c92:       9f05                    subw    a4,a4,s1
11c94:       9aa6                    add     s5,s5,s1
11c96:       94be                    add     s1,s1,a5
11c98:       c418                    sw      a4,8(s0)
11c9a:       e004                    sd      s1,0(s0)
11c9c:       3cb000ef                jal     ra,12866 <memcpy>
11ca0:       04090f63                beqz    s2,11cfe <_fgets_r+0x9a>
11ca4:       441c                    lw      a5,8(s0)

(_fgets_r is called by fgets)

0000000000011d26 <fgets>:
11d26:       87aa                    mv      a5,a0
11d28:       8401b503                ld      a0,-1984(gp) # 1f670 <_impure_ptr>
11d2c:       86b2                    mv      a3,a2
11d2e:       862e                    mv      a2,a1
11d30:       85be                    mv      a1,a5
11d32:       bf0d                    j       11c64 <_fgets_r>

Something is going wrong in memcpy, leaving me with the equivalent of a null pointer, but I'm not clear what - but my more fundamental question is this: have I got the basic procedure to call fgets correct?

I believe I have and haven't had any previous issue, but documentation on this is few and far between and so a confirmation would be helpful and would at least suggest I look more closely at the mmap code.

Of course if anyone can spot anything else that's wrong that would be gratefully received.


Solution

  • By repeatedly opening the file my code was eating up more and more memory and eventually overwrote part of the memory range allocated via mmap. I solved this by storing the value of the file pointer in the .bss (inputfileptr) and only opening it once:

                la t0, inputfileptr
                lw a0, 0(t0)
                bne a0, zero, getstdin_getin
                la a1, stdinopen
                call fdopen                
                la t0, inputfileptr
                sw a0, 0(t0)
                getstdin_getin:
                PUSH a0                          #store value on stack