i am trying to learn RISCV assembly. I am trying to learn the ISA and implement some R type, S type instruction. However whenever i try to run the sw and lw instruction. It always gives me error address out of range and i don't understand why. This is an example:
lw a0,40(zero)
addi a1,zero,1
addi a2,zero,1
beq a0,a1,SAVE
LOOP:
addi a1,a1,1
addi t1,a1,0
addi t2,a2,0
jal ra,MUL
add a2,zero,t0
bne a1,a0,LOOP
SAVE:
sw a2,44(zero)
jal x0,DONE
MUL:
andi t0,t0,0
LOOP_MUL:
add t0,t0,t2
addi t1,t1,-1
bne t1,zero,LOOP_MUL
jalr zero,ra,0
DONE:
add zero,zero,zero
I get some error like this : Error in D:\ctmt\BTL_RISCV\riscv1.asm line 2: Runtime exception at 0x00400004: address out of range 0x00000004.
I am really grateful if someone can explain to me why this bug happens
Address out of range applies to load and store instructions. It tells you the address of the instruction (because that's what the hardware sees/knows), and the address you are attempting to access. Usually such is because of a bad pointer value in the base register, but sometimes, the pointer value is close to the end of a something and the immediate value puts it past the end.
You should look at the memory map for the simulator or environment that you're using, it may tell you what areas of memory are legal — it may also support reconfiguring the memory map. Low memory (values from 0-2048 or so) are not guaranteed to be legal memory locations on many systems, but can sometimes be configured as legal for small memory models, e.g. on embedded systems. One reason low memory is configured as illegal is to catch null pointer dereferencing, which is a common software error (using a null pointer).
Related
I have an assignment in my Computer Architecture class where we are supposed to complete a set of tasks. The last task is to create a subroutine that takes a string and reverses it. We're not allowed to use the original string in order to create a "new memory string". Instead, we are supposed to gradually replace the content of the original string.
My first thought was to load the leftmost and rightmost character in temporary registers and then using these to "swap" their positions. After this is done, the addresses should increment and decrease (there's two addresses, one pointing to the start of the string and one pointing to the end). Then this is going to loop until both of the addresses point to the same point, where the loop then will end.
This is the code I have for the reverse_string subroutine. $a0 is the address for the NULL-ended string. I'm not sure if the algorithm I was thinking of in my head translated into MIPS properly, I'm very new to this type of language.
reverse_string:
#### Write your solution here ####
beqz $a0, rs_exit # Check if string is NULL, exit if it is
li $t0, 0
li $t1, 0
add $t0, $t0, $a0 # Save leftmost adress of string
add $t1, $a0, $v0 # Save rightmost adress of string
rs_loop:
beq $t0, $t1, rs_exit
lbu $t2, 0($t0) # Save leftmost character in temporary register
lbu $t3, 0($t1) # Save rightmost character in temporary register
sb $a0, 0($t2) # Replace rightmost character with leftmost character
sb $a0, 0($t3) # Replace leftmost character with rightmost character
addi $t0, $t0, 1 # Increment leftmost adress by 1
subi $t1, $t1, 1 # Decrement rightmost by 1
j rs_loop
rs_exit:
jr $ra
The code in main for executing reverse_string is the following:
##
### reverse_string
##
li $v0, 4
la $a0, STR_reverse_string
syscall
la $a0, STR_str
la $a1, reverse_string
jal reverse_string
la $a0, STR_str
jal print_test_string
So, as mentioned previously. The expected result is that the program should print out the reversed string. Currently, I'm having errors at the following line:
sb $a0, 0($t2) # Replace rightmost character with leftmost character
The error:
Runtime exception at 0x004000c4: address out of range 0x0000004a
I've tried for several hours. There's several people who have successfully received help with similar problems, however they were a bit different (input from users and also they created new strings instead of replacing the contents of the original one)
I appreciate any help! Thank you.
My first thought was to ... this is going to loop until both of the addresses point to the same point...
Until the "end" pointer is equal or less than "start" pointer. For even length like "abcd" the pointers pointing at "b" and "c" are valid, but after swap and incrementing + decrementing they are still not equal, but you should end the loop. Anyway, except this detail, your idea is good.
Runtime exception at 0x004000c4: address out of range 0x0000004a
This means the sb (store byte) instruction did try to write at memory address 0x0000004a, which is not accessible (no memory there, or not enough rights for your process to write there). Which means the 0($t2) did evaluate to that address, which means the value in t2 is equal to 0x4a. You should be able to see that in debugger, when single stepping over instructions.
From there you have to back-track whole operation, how it become this value and why.
la $a0, STR_str
la $a1, reverse_string
jal reverse_string
why a1 is set? The contract in task says that string address is passed in a0, nothing else. But anyway, that's not your code, just curious... so let's get onto your code.
beqz $a0, rs_exit # Check if string is NULL, exit if it is
li $t0, 0
li $t1, 0
add $t0, $t0, $a0 # Save leftmost adress of string
add $t1, $a0, $v0 # Save rightmost adress of string
null test is ok, you can use also addi or $zero = $0 for zero value, i.e.:
beqz $a0, rs_exit # Check if string is NULL, exit if it is
add $t0, $a0, $zero # t0 = left pointer (start of string)
add $t1, $a0, $v0 # t1 = start of string plus unknown value in v0
And as you can read in the second comment, you have one bug right there at beginning.
Which does imply you didn't debug your code at all, or with very limited inputs.
If the only input to routine is address of string, you have to find char-by-char where the terminating zero is stored, and use that to figure out "end" pointer.
(you can for example copy a0 to a1, load byte from a1, check for zero, increment a1 and fetch again, ... until that terminating zero is found ... the address just ahead (-1) of that first zero is your "end" pointer)
Let's pretend you have correct pointers... then another part:
lbu $t2, 0($t0) # Save leftmost character in temporary register
lbu $t3, 0($t1) # Save rightmost character in temporary register
sb $a0, 0($t2) # Replace rightmost character with leftmost character
sb $a0, 0($t3) # Replace leftmost character with rightmost character
The first two are correct (with correct pointers). But the other two are completely wrong. the "sb" store byte has arguments "value, memory address", so you are trying to store bottom byte of string address to memory address represented by character... 0x4a is in ASCII encoding character 'J', which, as the error message points out, is not valid memory address.
You may consider yourself kinda lucky, because when programming in assembly, sometimes similar bugs actually happen to have in register wrong value, which is accessible, and some memory is overwritten which should have not been, but without crash or any sign of problem. Then much later some completely other part of code may reach for that memory, expecting something else to be stored there, and it will produce some bug. These "memory overwrite" bugs are extremely difficult to decipher and fix, as any oldschool assembly/C/C++ programmer can tell you.
So your code is very weak try to implement the idea you described.
Try to run through it with debugger (if you are using SPIM/MARS simulators, all of them have built-in debugger, not state of art one, but usable for these tiny tutorial tasks), and try to fully understand what is happening, and why those instructions do not represent your idea, and what they are actually doing in reality.
You have to learn this skill, if you want to code in assembly, assembly allows no room for mistakes or some vague interpretations, or to get working code just by "trying" things, changing source randomly. Always figure out what the code actually does, and how precisely it differs from what you want. Then fix it.
Generally assembly questions which show "no debugging" gets downvoted really quickly, because debugging is time consuming process, and just outsourcing it to SO crowd is rude ... but you have bonus point from me for stating clearly your idea, and overall providing almost complete reproducible example (you did forgot to show the string definition ... and in assembly, the way how you define data, is quite often even more important than code, so it may be you have some kind of bug also there, like not adding zero terminator to string, etc...).
Also never try to guess how the instruction works by it's name. Always study the reference manual properly, and make sure you understand everything what it says about the instruction.
You can to some weak results by guessing and trying random things in higher level languages, but it's just waste of time in assembly, even this short routine of ~20 lines already allows for millions of variations (somewhat meaningful at first sight), and only few hundreds of them are correct solution.
Now try again, and focus to stay in control all the time. If you are not sure about something, how to understand it, reread it few more times, or build short code exercising the part you are not sure about, and check in debugger what the CPU does... eventually ask on SO with explanation what you expected, and what surprised you in debugger.
I am currently working on a small program on a ci20 machine that prompt the user for a integer value then print the value to the screen.
My current code
.data
prompt:
.asciiz "Please enter an integer: "
message:
.asciiz "\nValue entered: "
.text
.global main
main:
addiu $sp, $sp, -4 # push stack
sw $ra, ($sp) # save return address
addi $v0, $0, 4
la $a0, prompt
syscall # printing prompt
addi $v0, $0, 5
syscall # get user input
move $t0, $v0 # save input in $t0
move $a0, $v0
addi $v0, $0, 1 # Not sure if this is right to print message
la $a0, message # Not sure if this is right to print message
syscall
lw $ra, ($sp) # restoring $sp
addiu $sp, $sp, +4 # release the stack space used for $sp
When I try to run the program I get a seg fault and not sure why. Any help or suggestion would be greatly appreciated.
edit: for some reason I completely ignored this code was tested on ci20 machine.
So is this linux? Then you can't use MARS syscalls, you have to find linux syscalls instead. It is then probably segfaulting on the very first syscall instruction, as the arguments are invalid for Linux.
To display "prompt" you use syscall with arguments set as v0 = 4, a0 = prompt ... to display "message" you set arguments for syscall as v0 = 1, a0 = message.
If this is in MARS, then v0=1 is "print integer", so a0 should be integer, not address of "message" string. .. you probably want to call syscall twice, with v0=4 and v0=1 (argument a0 being "message" and users integer for particular call).
Anyway, none of this should segfault. The segfault happens probably at the end, where your code ends with addiu $sp, $sp, +4, not returning to the ra, or calling syscall "exit" function (from the saving of ra at the start of your code it looks like you want rather to return than exit, but it's up to you). So the execution continues over some random instructions (uninitialized memory content).
Anyway 2, you should figure out how to load this code in debugger and step over it instruction by instruction, then you will be capable to say where exactly it segfaults, and what was the content of registers before segfaulting instruction. If your code segfaults and you don't even know where, it shows lack of effort on your side.
(disclaimer: I never did MIPS assembly, so I'm mostly guessing how it works and may have overlooked something)
edit about syscall, maybe this hint will help too?
syscall isn't some magic instruction doing all that nifty stuff on the CPU. It just jumps to some handler routine.
That handler code is set up by the OS. Most of the MIPS assembly listings on SO are targetted at MARS or SPIM, which have completely different handler than Linux.
So you should study linux ABI for MIPS, and how syscall is used there. And then find linux system calls table, you will probably find ton of x86 docs, so you have to convert that into v0/a0/... ABI.
You can still follow MARS examples, but any OS interaction has to be adjusted, and don't expect to find alternative for everything. For example outputting the number is not available in linux. You have to convert the number value into ASCII string by yourself (for single digit numbers adding '0' is enough, for numbers above 9 you have to calculate digit for each power of 10 and convert it into ASCII character and store it into some buffer), and output then the string with sys_write/etc. (or link with some libc and call sprintf-like function from C library).
Related assembly codes are located in boot/setup.s and I paste them below:
mov ax,#0x0001 ! protected mode (PE) bit
lmsw ax ! This is bit!
jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
The first two lines have made the corresponding bit changes in CR0 control register.
So,my problem is : When instruction lmsw ax is being executed,
the ip register points to next instruction jmpi 0,8 .
More exactly , at this point , cs:ip points to the memory location of instruction
jmpi 0,8 .But after execution of instruction lmsw ax, the PE mechanism is enabled.
The cs value now represents segment selector, but the corresponding GDT description entry is not
prepared for it. the GDT only contains two valid entries located in 1 and 2 respectively.So, I
think the next instruction specified by cs:ip is not the instruction jmpi 0,8.cs:ip
now points to an invalid memory address. The above last instruction jmpi 0,8 which is used
to place the right values into cs and eip registers cannot be reached. I know I was wrong because the
Linux 0.11 is verifying by long term practice. Please help me point the mistakes that I make.Thanks very much.
The CPU doesn't look up selectors in the GDT (or LDT) every time segment register is used. It only reads the descriptor table in memory when the segment register is loaded. It then stores the information in the segment descriptor cache.The same thing happens in real mode, when a segment register is loaded with a value, that value is used to create an entry in the descriptor cache. Then whenever that segment is used, both in real and protected mode, the processor uses the values stored in the cache.
When you switch from real mode to protected mode none of the segment registers change and none of the entries in the descriptor cache change. The cache entry for the CS register remains the same as it was before, and so the CPU executes following instruction as expected. It's not until the following far jump instruction is executed that the value of the CS register changes, which then replaces the old real mode descriptor entry with a new protected mode entry.
I'm reading about PIC implementation on MIPS on Linux here. It says:
The global pointer which is stored in the $gp register (aka $28) is a callee saved register.
The Wikipedia article about MIPS says the same.
However, according to them, when a .cpload directive is being used in function prologue, it clobbers the previous value of $gp without saving it first. When a .cprestore is used, it saves the current $gp to the stack frame, as opposed to the value of $gp that was there on function entrance. Same goes for the effect .cprestore has on jal/jalr: it restores $gp once the callee returns - assuming the callee might've clobbered it.
And finally, there's nothing in the function epilogue about $gp.
All in all, doesn't sound like a callee-saved register to me. Sounds like a caller-saved register. What am I misunderstanding here?
Linux programs on MIPS can be compiled as pic or not. If compiled as pic, then they must use "abicalls", and its behaviour is a little different from that of the no-abicalls convention.
From the "section Position-Independent Function Prologue" of the "SYSTEM V APPLICATION BINARY INTERFACE - MIPS Processor Supplement 3rd Edition" we can cite:
After calculating the gp, a function allocates the local stack space and saves the gp on the stack, so it can be restored after subsequent function calls. In other words, the gp is a caller saved register.
The code in the following figure illustrates a position-independent function prologue. _gp_disp represents the offset between the beginning of the function and the global offset table.
name:
la gp, _gp_disp
addu gp, gp, t9
addiu sp, sp, –64
sw gp, 32(sp)
So in summary, if you're using -mabicalls then gp is calculated at the beginning of all the functions needing global symbols (with some exceptions), and additionally any code (abi or not) that calls abi code will ensure that the called function address is stored in t9.
This is code snipper from header.S file in kernel code. I could not understand what the lretw instruction does. I've checked out so many online sources for the instruction.
# We will have entered with %cs = %ds+0x20, normalize %cs so
# it is on par with the other segments.
pushw %ds
pushw $6f
lretw
Can any one help me in understanding this instruction?
ret is the instruction to return from a procedure. So basically it pops the return address from the stack into the EIP register.
the l prefix is here to tell that it is a far return from procedure. In this case, the instruction first pops a value from the stack into the EIP register and then pops a second value into the CS register.
the w suffix is here because at this step we are running in real mode, and operands are 16 bits wide.
The exact code is:
pushw %ds
pushw $6f
lretw
6:
The 6: is very important here. So what this does is: push the value of ds into the stack, push the adress of the 6 label into the stack, and then trigger this lretw instruction. So basically, it will load the address of label 6 into the instruction pointer register, and load the cs register with the value of the ds register. So this is just a trick to continue the execution at label 6 with a change of the cs register value.
You should download http://www.intel.com/design/intarch/manuals/243191.htm which gives precise details for all instructions, including a pseudo-code that details what each instruction is doing.