I'm a beginner in assembly and I'm encountering unexpected behavior in my code. The code works as expected, printing numbers from contador
variable to 1, but when I removed the unused variable loops
(which is not used anywhere in the code), it started printing random numbers infinitely. I don't understand why this happens.
Here’s the relevant part of the code:
extern print
extern input
extern string_to_int
extern int_to_string
extern print_num
section .data
contador dq 30
loops dq 10
v4 db " ", 10
section .bss
atual resb 1
section .text
global main
main:
push rbp
mov rbp, rsp
print_loop:
mov rax, [contador] ; pass the address
call int_to_string
push rsi
push rdi
mov rdx, [rsi]
mov [atual], rdx
mov rdi, atual
call print_num
mov rdi, v4
call print
dec qword [contador]
cmp qword [contador], 0
jne print_loop
pop rdi
pop rsi
mov rsp, rbp
pop rbp
mov rax, 60
mov rdi, 0
syscall
Why does the code start printing random numbers infinitely when I remove the loops
variable? Also, I would appreciate any tips or references to help improve my understanding and skills in assembly, particularly regarding variable manipulation and control flow.
This is the code of my extern functions, that I did before:
section .text
global print
global print_num
global input
global int_to_string
global string_to_int
print:
call print_str
jmp exit_success
str_len:
push rbp
mov rbp, rsp
mov rax, 0
.str_len_loop:
cmp [rdi], byte 0
je .str_len_end
inc rdi
inc rax
jmp .str_len_loop
.str_len_end:
mov rsp, rbp
pop rbp
ret
print_str:
push rbp
mov rbp, rsp
push rdi
call str_len
pop rsi
mov rdx, rax
mov rax, 1
mov rdi, 1
syscall
mov rsp, rbp
pop rbp
ret
exit_success:
xor rdi, rdi
mov rax, 0
ret
print_num:
call print_str_num
jmp exit_success_num
str_len_num:
push rbp
mov rbp, rsp
mov rax, 0
.str_len_loop_num:
cmp [rdi], byte 0
je .str_len_end_num
cmp [rdi], byte 48
jl .str_len_end_num
cmp [rdi], byte 57
jg .str_len_end_num
inc rdi
inc rax
jmp .str_len_loop_num
.str_len_end_num:
mov rsp, rbp
pop rbp
ret
print_str_num:
push rbp
mov rbp, rsp
push rdi
call str_len_num
pop rsi
mov rdx, rax
mov rax, 1
mov rdi, 1
syscall
mov rsp, rbp
pop rbp
ret
exit_success_num:
xor rdi, rdi
mov rax, 0
ret
input:
push rbp
mov rbp, rsp
sub rsp, 8
mov rdi, 0
lea rsi, [rsp]
mov rdx, 8
mov rax, 0
syscall
mov r8, rax
mov rsp, rbp
pop rbp
ret
string_to_int:
mov rax, 0
.next_digit:
mov dl, byte [rsi]
inc rsi
cmp dl, 48
jl .fim
cmp dl, 57
jg .fim
sub dl, 48
imul rax, 10
add rax, rdx
jmp .next_digit
.fim:
mov rdx, 0
ret
int_to_string:
mov rbx, 10
.prox_digit:
mov rdx, 0
div rbx
add dl, '0'
dec rsi
mov byte [rsi], dl
cmp rax, 0
jnz .prox_digit
ret
I just tried to remove the variable, because I was using it in a wrong logic and was trying just to clean the code after I got the right output. Push/pop rdi
and rsi
doesn't fix the problem.
Why does the code start printing random numbers infinitely when I remove the loops variable?
Because your int_to_string was working from a non-initialized pointer RSI, the program was overwriting random memory, but not to a degree that it would crash the program. However, once the loops variable was removed, memory layout changed and this time the memory clobber was less forgiving.
Once you have converted the number into a string, you should no longer be using those special case versions print_str_num and str_len_num. You already have text, so simply use the normal versions print_str and str_len.
First reserve some room for a small general-purpose buffer:
section .data
contador dq 30
section .bss
buffer resb 32
Modify the conversion routine so it takes in RDI the address beyond where the rightmost digit must go (and where you have prepared a space character, newline, and zero-terminator). Upon return RDI will conveniently point at the first leftmost digit in the string. Document any registers that get clobbered and/or preserve those that need preserving:
; IN (rax,rdi) OUT (rdi) MOD (rax,rbx,rdx)
int_to_string:
mov rbx, 10
.prox_digit:
xor edx, edx
div rbx
add dl, '0'
dec rdi
mov [rdi], dl
test rax, rax
jnz .prox_digit
ret
The main loop now becomes:
print_loop:
mov rdi, Buffer+28
mov dword [rdi], 00000A20h ; " ", 10, 0, 0
mov rax, [contador]
call int_to_string ; -> RDI (RAX RBX RDX)
call print
dec qword [contador]
jnz print_loop
Push/pop rdi and rsi doesn't fix the problem.
Indeed. Additionally you were pushing RSI/RDI multiple times inside the loop, and popping RDI/RSI just a single time outside the loop. That will have left a lot of RDIs and RSIs on the stack. You will always want to keep the stack balanced, what goes up must come down!