(QEMU-RISCV64) unknown error when running - qemu

I'm making a program that takes the first character of the second argument passed as a parameter and identifies whether it's an uppercase or lowercase character.
The problem is that an error occurs that is not reported when I call the function that is implemented in another file in riscv64 assembly.
The riscv64 assembly code returns 0, 1, 2 which is captured in the 'int result' variable in the .c file, which does not happen. The riscv64 assembly code has already been tested in isolation from the .c file and it worked perfectly. I have no idea where the error could be.
The code compiles without errors, but at the time of execution it freezes as if it was waiting for some kind of input
ARQUIVO.C:
#include <stdio.h>
extern int verificando(char*);
int main(int argc, char *argv[]){
argc = 3;
argv[1] = "maracuja";
argv[2] = "Graviola";
int result;
result = verificando(&(argv[2][0]));
if (result == 0){
printf("\n%s: Primeiro caracter da segunda string eh minusculo.\n", argv[2]);
}else if (result == 1){
printf("\n%s: Primeiro caracter da segunda string eh maiusculo.\n", argv[2]);
}else if (result == 2){
printf("\nNao existe uma segunda string.\n");
}else {
printf("\nErro\n");
}
return 0;
}
ARQUIVO.S:
.global verificando
verificando:
#ascii
li t5, 65
li t6, 90
li t3, 97
li t4, 122
# carrega o primeiro caracter do segundo argumento em t1 e verifica
lb t1, 0(a0)
jal verifica
verifica:
bge t6, t1, maiuscula
bge t1, t3, minuscula
bge t3, t1, erro
maiuscula:
bge t1, t5, maiuscula_2
bge t1, t3, minuscula
bge t5, t1, erro
minuscula:
bge t4, t1, minuscula_2
li a0, 2
ret
maiuscula_2: # returns 1
li a0, 1
ret
minuscula_2: # returns 0
li a0, 0
ret
erro: # returns 2
li a0, 2
ret
MAKEFILE:
default:
riscv64-linux-gnu-gcc -static arquivo.s arquivo.c -o arquivo.x
run: default
qemu-riscv64-static arquivo.x
clean:
rm -rf *.x

I found the error, it's in " jal verifica ".
I had only tested the code from the .s file in the riscv-programming.org debugger and this jal command worked perfectly, but when compiling with a .c file the correct thing is to use " j verifica ".

Related

MIPS assembly understanding READ syscall

I have a piece of code in MIPS I am failing to understand. Namely the register convention using the "read" linux syscall:
...
move a0,zero #a0 = 0
move a1,s0 #a1 = some adress in memory
li v0,4003 #v0 = 4003 = SYSCALL READ
syscall
bnez a3, label #What is in a3??
li v1,1
bne v0,v1
...
Now I get understand it as
char buffer;
int v0 = read(0, &buffer, 1);
????
What I do not get at all is what is the meaning of reg a3 in this context? v0 is the return value of read, but a3 should be parameter and not ret value. Is it errno?
It's a boolean indicating whether or not there was an error:
On a few architectures, a register is used as a boolean (0
indicating no error, and -1 indicating an error) to signal
that the system call failed.
Arch/ABI Instruction System Ret Ret Error
call # val val2
-------------------------------------------------------------
mips syscall v0 v0 v1 a3
source

Translating the following high level code into MIPS Assembly code

I have translated this C program into MIPS assembly code.
So, here I want to know:
Have I done something wrong?
If my assembly code is correct, can I do it more precisely by reducing the
number of instructions. I have used 14 instructions here
Here is my approach for converting this C program
This is my MIPS assembly code
#s0 = res
main:
addi $a0 , $0 , 27
addi $a1 , $0 , 3
jal division # call function
add $s0 , $v0 , $0 # res = returned value
division:
addi $t0 , $0 , 0 # $t0 = 0
addi $s0 , $0 , 0 # $s0 = val and val = 0
add $s1 , $0 , $a0 # $s1 = i and i = 27
loop:
slt $t1 , $s1 , $t0 # checking if i>0
bne $t1 , $0 , break # if $t1 = 0 then break
addi $s0 , $s0 , 1 # else val = val + 1
sub $s1 , $s1 , $a1 # i = i - y
add $v0 , $s0 , $0 # put return value in $v0
j loop
break:
jr $ra # return to caller
This is the high level C program
int main(){
int res;
res = division (27, 3);
}
int division(int x, int y)
{
int i;
int val = 0;
for (i = x; i>0; i = i-y){
val = val + 1;
}
return val;
}
Errors:
The relation is negated incorrectly.  You have translated it as
for ( i = x; i >= 0; i -= y )
So, it will loop one time more than you want (when the integer division is exact).  You are aware that loop condition test requires negation for the if-goto-label style of assembly:
if ( ! (i > 0) ) goto break;
However ! (i > 0) is i <= 0 where you have i < 0.
You don't properly terminate the program when the main is finished, so it will accidentally fall into the divide subroutine.  The solution is to add an exit syscall to the main.
Suggestions:
The register usage is excessive, and using fewer registers will also decrease the number of instructions required.  Further, using the $s registers without preserving their original values is a violation of the calling convention.  However, the best solution is to simply avoid using them in that function — just use $a0 and $a1 directly instead of copying them elsewhere.
(It is ok to use s registers in main without preserving their original values, b/c main is at the top of the call chain — it has no caller — by contrast we might consider division as a general purpose function that could be called from any caller, and so should follow the calling convention.)
We not only want to minimize the instructions, but also the number of instructions in the loop.  You're doing the work of "return val" by copying $s0 into $v0, but every time in the loop whereas only once is necessary, so that is better left for after the loop.  Even better still, simply use $v0 for the counter (val) in the first place.
The slt instruction has an equivalent slti that is handy when comparing to a constant.  With slti, no need to load a constant 0 into $t0 register.  If you did need a constant 0 in a register, there is always the $0 register as well.
The MIPS instruction set has relational branch instructions that compare with zero, so you can branch on register <= 0 (or < 0) directly without slt/slti.

MIPS difference between two integers in array

I need to build a program (hard-code) in MIPS that gets an array of 10 integers and finds the difference between two nearby numbers in the array. This is what I have build:
.data
array: .word 23,-2,45,67,89,12,-100,0,120,6
arrend:
comma: .asciiz ", "
# array = {23,-2,45,67,89,12,-100,0,120,6}
# Algorithm being implemented to find the difference between nearby elements of the array
# difference = 0 (use $t0 for difference)
# loop i = 0 to length-1 do (use $t1 for i)
# difference = array[i]-array[i+1]
# end loop (use $t3 for base addr. of array)
# registers:
# t0 -- difference
#
# t3 -- pointer to current array element (e.g. arrptr)
# t2 -- pointer to end of array
#
# t4 -- current value fetched from array (i)
# t5 -- value fetched from array (i+1)
.text
main:
li $t0,0 # difference = 0
la $t3,array # load base addr. of array
la $t2,arrend # load address of array end
j test
loop:
lw $t4,0($t3) # load array[i]
addi $t3,$t3,4 # increment array pointer
lw $t5,0($t3) # load array[i+1]
sub $t0, $t4, $t5 # the difference of two nearby elements
# print value of difference
li $v0,1
addi $a0,$t0,0
syscall
# print comma
li $v0,4
la $a0,comma
syscall
test:
blt $t3,$t2,loop # more to do? if yes, loop
the output should be :
25, -47, -22, -22, 77, 112, -100, -120, 114,
but I get the output 25, -47, -22, -22, 77, 112, -100, -120, 114, -8230,
I found out if I change la $t2,arrend to la $t2,0x10010024, it will work, but I don't know how to write it in the code.
furthermore, how can I improve my code?
Ok... So, you are iterating one more time then you need... Just sub 1 from t2 right after you assign the end pointer of the array.
It's also called bufferoverflow.

MIPS, function call goes to infinite loop

C code
int GCD(int m, int n)
{ //Call MACRO PRINTARGS as the first line of code in GCD
//PRINTARGS($a0, $a1)
int result = 0;
// Base Case:
if(m == n)
result = m;
else if (m > n)
// Recursive Case:
result = GCD(m-n, n);
else
// Recursive Case:
result = GCD(m, n-m);
//Call MACRO PRINTARGS as the last line of code in GCD before jr $ra
//PRINTARGS($a0, $a1)
return result;
}
GCD:
my function will go infinite loop if the first argument is large than second argument
(Except the case m=2, n=1)
$a2 is m
$a3 is n
$v1 is where i store the result
The Greatest Common Denominator (GCD) is the largest positive integer that divides the two
numbers without a remainder.
(PRINTARGS is just a function i wrote to print the value of m and n every time it runs the loop)
GCD:
PRINTARGS($a2,$a3)
addi $sp $sp,-4
sw $ra 0($sp)
lb $a0,newline
li $v0,11
syscall #print new line
li $v1,0 # result = 0 result = v1
#base case
bne $a2,$a3,else2 #if m!=n, go to else2
move $v1,$a2 #move value m to v1
j GCD_DONE #jump to GCD_DONE
#Recursive Case: else if (m > n), result = GCD(m-n, n);
else2:
blt $a2,$a3,else3 #if m >n jump to else 3
sub $a2,$a2,$a3 #m-n
jal GCD
else3:
#Recursive Case: result = GCD(m, n-m);
sub $a3,$a3,$a2 #n-m
jal GCD
PRINTARGS($a2,$a3)
GCD_DONE:
addi $sp $sp,4
lw $ra 0($sp)
jr $ra
Two things:
Firstly, in the else2 clause, the function continues execution into else3 after the recursive call. Change to this:
else2:
blt $a2,$a3,else3 #if m >n jump to else 3
sub $a2,$a2,$a3 #m-n
jal GCD
j GCD_DONE
Also, you should be incrementing $sp after restoring from the stack.

R0 is violated after function returns

I am implementing a uart queue in s3c44b0x (ARM7TDMI), the uart0 ISR will enqueue the char while the main loop will dequeue the char. however, while dequeuing, the value (in R0) returned may be not the one dequeued from the queue, and I found R0 is violated after returning from the dequeue function (input 'v' cont., and test() is in the main loop):
wish for your help.
CHAR cliDequeue(void)
{
CHAR bTmpCh;
if (gwCliQSize == 0)
{
return 0;
}
bTmpCh = gabCliQ[gwCliQTail]; /* char is enqueued in the Q in ISR */
gwCliQTail++;
gwCliQTail %= MAX_CLI_QUEUE_LEN;
ASSERT(gwCliQSize > 0);
gwCliQSize--;
ASSERT(bTmpCh == 'v'); /* will not assert */
//uartPutChar(bTmpCh);
return bTmpCh;
}
void test(void)
{
CHAR bTestCh;
bTestCh = cliDequeue();
if (bTestCh != 0)
{
ASSERT(bTestCh == 'v'); /* assert here ! */
uartPutChar(bTestCh);
}
}
We don't have enough information / context to answer definitively. It would also be helpful if you posted the corresponding assembly code so that we could see how/when things are moved in & out of R0. REgardless, a few things spring to mind immediately from your posted C code.
(0) Are the variables shared between interrupts & the main loop declared as volatile?
(1)
In CliDequeue, you're accessing an array which is shared with an ISR. It appears to be a single reader / single writer construct, so that isn't automatically bad, but your housekeeping isn't airtight.
For example, one invariant you must be sure to satisfy is that the queue size & tail pointer are in sync. Yet, unless this routine is called with interrupts disabled, your tail pointer & queue size aren't adjusted as a single transaction.
(2)
Furthermore, I'd guess that gwCliQSize is also adjusted in the interrupt (incremented in ISR, decremented in the application). Another race condition. To perform gwCliQSize--, behind the scenes you are probably reading from memory to a register, decrementing the register, then writing it back. What happens if you read 5 from memory into R1, then an interrupt fires and increments it to 6, then you exit the ISR, and the register decrement and writeback (with a value of 4).
(3)
Lastly, it's possible (although not too likely) that bTmpCh or bTestCh are stored on the stack, and that your stack is getting corrupted / slammed by another task / interrupt / etc. So when your assert fails, you're thinking it's R0 that is corrupted, but really it could be that the value moved into R0 before return, or the value moved out of R0 into a stack variable, is getting clobbered.
I've nattered on enough. There are other possibilities but from what you've posted (and not posted) it's impossible to say for sure.
P.S. If you've used a debugger and it's really & literally R0's value that is getting corrupted, not just the value of the character in the queue, that points to a problem in your scheduler / context switcher / ISR pre- or post-amble etc...
here is the assembly code:
for the test():
0x00001308 E92D4010 STMDB R13!,{R4,R14}
37: bTestCh = cliDequeue();
38:
0x0000130C EB000207 BL cliDequeue(0x00001B30)
0x00001310 E1A04000 MOV R4,R0
39: if (bTestCh != 0)
40: {
0x00001314 E3540000 CMP R4,#pTest(0x00000000)
0x00001318 0A000007 BEQ 0x0000133C
41: ASSERT(bTestCh == 'v');
0x0000131C E1A00000 NOP
0x00001320 E3540076 CMP R4,#0x00000076
0x00001324 0A000001 BEQ 0x00001330
0x00001328 E1A00000 NOP
0x0000132C EAFFFFFE B 0x0000132C
0x00001330 E1A00000 NOP
42: uartPutChar(bTestCh);
43: }
0x00001334 E1A00004 MOV R0,R4
0x00001338 EB00014A BL uartPutChar(0x00001868)
44: }
45:
46: int main(void)
0x0000133C E8BD4010 LDMIA R13!,{R4,R14}
0x00001340 E12FFF1E BX R14
for the cliDequeu(), BTW, gwCliQSize is defined as
UINT32 volatile gwCliQSize;
0x00001B30 E59F00D4 LDR R0,[PC,#0x00D4]
0x00001B34 E5900000 LDR R0,[R0]
0x00001B38 E3500000 CMP R0,#pTest(0x00000000)
0x00001B3C 1A000001 BNE 0x00001B48
78: return 0;
79: }
80:
81: bTmpCh = gabCliQ[gwCliQTail];
82: gwCliQTail++;
83: gwCliQTail %= MAX_CLI_QUEUE_LEN;
84: ASSERT(gwCliQSize > 0);
85: gwCliQSize--;
86:
87: //chCheck(bTmpCh);
88: ASSERT(bTmpCh == 'v'); /* will not assert */
89: //uartPutChar(bTmpCh);
90:
91: return bTmpCh;
0x00001B40 E3A00000 MOV R0,#pTest(0x00000000)
92: }
93:
94:
95: void cliQInit(void)
0x00001B44 E12FFF1E BX R14
81: bTmpCh = gabCliQ[gwCliQTail];
0x00001B48 E59F00C0 LDR R0,[PC,#0x00C0]
0x00001B4C E59F20C4 LDR R2,[PC,#0x00C4]
0x00001B50 E5922000 LDR R2,[R2]
0x00001B54 E7D01002 LDRB R1,[R0,R2]
82: gwCliQTail++;
0x00001B58 E59F00B8 LDR R0,[PC,#0x00B8]
0x00001B5C E5900000 LDR R0,[R0]
0x00001B60 E2800001 ADD R0,R0,#0x00000001
0x00001B64 E59F20AC LDR R2,[PC,#0x00AC]
0x00001B68 E5820000 STR R0,[R2]
83: gwCliQTail %= MAX_CLI_QUEUE_LEN;
0x00001B6C E2820000 ADD R0,R2,#pTest(0x00000000)
0x00001B70 E5900000 LDR R0,[R0]
0x00001B74 E20000FF AND R0,R0,#0x000000FF
0x00001B78 E5820000 STR R0,[R2]
84: ASSERT(gwCliQSize > 0);
0x00001B7C E1A00000 NOP
0x00001B80 E59F0084 LDR R0,[PC,#0x0084]
0x00001B84 E5900000 LDR R0,[R0]
0x00001B88 E3500000 CMP R0,#pTest(0x00000000)
0x00001B8C 1A000001 BNE 0x00001B98
0x00001B90 E1A00000 NOP
0x00001B94 EAFFFFFE B 0x00001B94
0x00001B98 E1A00000 NOP
85: gwCliQSize--;
86:
87: //chCheck(bTmpCh);
0x00001B9C E59F0068 LDR R0,[PC,#0x0068]
0x00001BA0 E5900000 LDR R0,[R0]
0x00001BA4 E2400001 SUB R0,R0,#0x00000001
0x00001BA8 E59F205C LDR R2,[PC,#0x005C]
0x00001BAC E5820000 STR R0,[R2]
88: ASSERT(bTmpCh == 'v'); /* will not assert */
89: //uartPutChar(bTmpCh);
90:
0x00001BB0 E1A00000 NOP
0x00001BB4 E3510076 CMP R1,#0x00000076
0x00001BB8 0A000001 BEQ 0x00001BC4
0x00001BBC E1A00000 NOP
0x00001BC0 EAFFFFFE B 0x00001BC0
0x00001BC4 E1A00000 NOP
91: return bTmpCh;
92: }
93:
94:
95: void cliQInit(void)
0x00001BC8 E1A00001 MOV R0,R1
0x00001BCC EAFFFFDC B 0x00001B44
for cliEnqueue:
void cliEnqueue(CHAR bC)
{
if (gwCliQSize == MAX_CLI_QUEUE_LEN)
{
ASSERT(0);
}
gabCliQ[gwCliQHeader] = bC;
gwCliQHeader++;
gwCliQHeader %= MAX_CLI_QUEUE_LEN;
gwCliQSize++;
}
assembly:
0x00001A5C E59F11AC LDR R1,[PC,#0x01AC]
0x00001A60 E59F21AC LDR R2,[PC,#0x01AC]
0x00001A64 E5922000 LDR R2,[R2]
0x00001A68 E7C10002 STRB R0,[R1,R2]
25: gwCliQHeader++;
0x00001A6C E59F11A0 LDR R1,[PC,#0x01A0]
0x00001A70 E5911000 LDR R1,[R1]
0x00001A74 E2811001 ADD R1,R1,#0x00000001
0x00001A78 E59F2194 LDR R2,[PC,#0x0194]
0x00001A7C E5821000 STR R1,[R2]
26: gwCliQHeader %= MAX_CLI_QUEUE_LEN;
0x00001A80 E2821000 ADD R1,R2,#pTest(0x00000000)
0x00001A84 E5911000 LDR R1,[R1]
0x00001A88 E20110FF AND R1,R1,#0x000000FF
0x00001A8C E5821000 STR R1,[R2]
27: gwCliQSize++;
0x00001A90 E59F1174 LDR R1,[PC,#0x0174]
0x00001A94 E5911000 LDR R1,[R1]
0x00001A98 E2811001 ADD R1,R1,#0x00000001
0x00001A9C E59F2168 LDR R2,[PC,#0x0168]
0x00001AA0 E5821000 STR R1,[R2]
28: }
29:
30:
31: static void chCheck(CHAR cTmpChar)
32: {
0x00001AA4 E12FFF1E BX R14
0),1): that gwCliQSize and the array are shared between ISR and main loop.
2) gwCliQSize is defined as volatile
3) from the assembly, bTestCh is R4 (moved from R0) and bTmpCh is R1 (moved to R0 before B)
4) I am using the J-LINK, but without J-LINK (run from flash), it still exists.