I am playing around with ARM assembly on a 64bit install of Raspbian on a Pi3-B+. The code determines if a value is a prime or not.
1
2 .data
3 testvalue:
4 .word 0 // The number we are going to test
5 factor:
6 .word 0 // The number we are going to test as a factor
7 limit:
8 .word 0 // The top of the loop. testvalue - 1
9 isPrimeMsg:
10 .asciz "%d is a prime number.\n"
11 isNotPrimeMsg:
12 .asciz "%d is not a prime number.\n"
13 scanpattern:
14 .asciz "%d"
15 prompt:
16 .asciz "Input a test value: "
17 tooSmallMsg:
18 .asciz "%d is too small. Value must be > 5.\n"
19 diagnostic:
20 .asciz "%d, %d, %d.\n"
21
22 .text
23 .global main
24
25 main:
26 LDR X0, =prompt // Load X0 with the input prompt
27 BL printf // Print the input prompt
28
29 LDR X0, =scanpattern // Load X0 with the scan pattern
30 LDR X1, =testvalue // Load X1 with the address to store the user input
31 BL scanf // get the user input and store it at testvalue address
32
33 LDR X0, =testvalue // Load X0 with the address of testvalue
34 LDR X0, [X0] // Load X0 with the actual test value, currently 0
35 MOV X1, #5 // Load 5 into X1
36 CMP X0, X1 // Perform X0 - X1
37 BMI toosmall // If the result is negative then test value < 5. Jump to inform user
38
39 LDR X0, =testvalue // Load X1 with the address of the test value
40 LDR X0, [X0] // Load X1 with actual test value
41 SUB X0, X0, #1 // Subtract 1 from testvalue
42 LDR X1, =limit
43 STR X0, [X1] // Store the result in address of upper limit
44
45 LDR X0, =factor // Load X0 with the address of factor
46 MOV X1, #2 // Load X1 with the immediate value 2
47 STR X1, [X0] // Store 2 at the address of factor
48
49 LDR X0, =diagnostic // Load X0 with diagnostic string
50 LDR X1, =testvalue // Load X1 with address of testvalue
51 LDR X1, [X1] // Load X1 with value of testvalue
52 LDR X2, =limit // Load X2 with address of limit
53 LDR X2, [X2] // Load X2 with value of limit
54 LDR X3, =factor // Load X3 with address of factor
55 LDR X3, [X3] // Load X3 with value of factor
56 BL printf // Print diagnostic string
57
58
59 checkfactor:
60 LDR X0, =testvalue // Load X0 with address of testvalue
61 LDR X0, [X0] // Load X0 with the test value
62 LDR X1, =factor // Load X1 with address of factor
63 LDR X1, [X1] // Load X1 with the current factor candidate
64 LDR X2, =limit // Load X2 with address of limit
65 LDR X2, [X2] // Load X2 with the value for upper limit
66 CMP X2, X1 // Check if the factor value has reached the upper limit
67 BEQ isPrime // If it has and we have not failed yet then we have a prime number
68
69 subtractloop:
70 CMP X0, #0 // Compare the current testvalue with zero
71 BEQ notPrime // If it is then we have a factor and the test value is not prime
72 BMI nextfactor // If it's gone negative then it's time to test the next factor
73 SUB X0, X0, X1 // Otherwise keep doing division by subraction
74
75 nextfactor:
76 LDR X0, =factor // Load X0 with addresss of factor
77 LDR X1, [X0] // Load X0 with value of factor
78 ADD X1, X1, #1 // Increment the value of factor by 1
79 STR X1, [X0] // Store new value of factor in address of factor
80 B checkfactor // Jump back and start testing again
81
82 toosmall:
83 LDR X0, =tooSmallMsg
84 LDR X1, = testvalue
85 LDR X1, [X1]
86 BL printf
87 B end
88
89 notPrime:
90 LDR X0, =isNotPrimeMsg
91 LDR X1, =testvalue
92 LDR X1, [X1]
93 BL printf
94 B end
95
96 isPrime:
97 LDR X0, =isPrimeMsg
98 LDR X1, =testvalue
99 LDR X1, [X1]
100 BL printf
101 B end
102
103 end:
104 B exit
The problem occurs between lines 39 and 49. Specifically when line 47 is executed. The output of GDB looks like:
39 LDR X0, =testvalue // Load X1 with the address of the test value
(gdb) stepi
40 LDR X0, [X0] // Load X1 with actual test value
(gdb) stepi
41 SUB X0, X0, #1 // Subtract 1 from testvalue
(gdb) i r x0
x0 0x13 19
(gdb) p (int)testvalue
$2 = 19
(gdb) stepi
42 LDR X1, =limit
(gdb) stepi
43 STR X0, [X1] // Store the result in address of upper limit
(gdb) i r x0
x0 0x12 18
(gdb) p (int)limit
$3 = 0
(gdb) stepi
45 LDR X0, =factor // Load X0 with the address of factor
(gdb) p (int)limit
$4 = 18
(gdb) stepi
46 MOV X1, #2 // Load X1 with the immediate value 2
(gdb) p (int)limit
$5 = 18
(gdb) stepi
47 STR X1, [X0] // Store 2 at the address of factor
(gdb) i r x1
x1 0x2 2
(gdb) p (int)limit
$6 = 18
(gdb) stepi
49 LDR X0, =diagnostic // Load X0 with diagnostic string
(gdb) p (int)limit
$7 = 0
(gdb) p (int)testvalue
$8 = 19
(gdb) p (int)limit
$9 = 0
(gdb) p (int)factor
$10 = 2
(gdb) q
At line 42, X1 is loaded with the address for the limit variable. At line 43 the value at X0 is written to that variable address. An inspection of the value in X0 (i r x0) shows the value is 0x12 (18). If the value of limit is checked before line 43 is executed the result is 0 (p (int)limit -> $3=0). When line 43 is executed and the value at limit is checked again it's 18 (p (int)limit -> $4=18). Lines 45 and 46 are executed and when limit is checked again it's still 18 (p (int)limit -> $6=18).
When line 47 is executed (the command is STR X1, [X0]) which should store the value 2 into the variable factor, the value stored in limit is reset to zero (p (int)limit -> $7=0).
How does line 47 disturb the value at =limit when X0 has been loaded with the address for =factor?
.word
is 32 bits but you write 64 bits to it. limit
is directly after factor
in memory so writing 64 bits to factor
will overwrite limit
with the top 32 bits which are zero. Use .quad
to define 64 bit variables or switch to 32 bit registers.