admin管理员组

文章数量:1122832

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.

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.

Share Improve this question edited yesterday Sep Roland 38.8k9 gold badges48 silver badges88 bronze badges asked Jan 3 at 10:43 Felipe TeixeiraFelipe Teixeira 233 bronze badges New contributor Felipe Teixeira is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 7
  • 1 Your int_to_string does not have rsi set so it's overwriting random memory. You are lucky it even does something instead of crashing. Also atual resb 1 is just 1 byte but you use it as 8. – Jester Commented Jan 3 at 11:16
  • 3 That is not using 1 byte, that is allocating 1 byte. The mov [atual], rdx is using 8 bytes. Yes, you should allocate a buffer but since you already have atual which is where I guess you want the result anyway you could use that after making it large enough. Also note that int_to_string writes backwards so you should pass the pointer to the end of the buffer. Furthermore, the push rsi; push rdi are useless and the matching pops are outside the loop so you unbalance the stack (you are again lucky, because you terminate the program before that causes any problems). – Jester Commented Jan 3 at 17:45
  • 2 Tip: install a debugger and learn to use it. By single stepping this code, you could have found the bug for yourself, or at least narrowed it down to ask a more specific question. A debugger is an absolutely essential tool for assembly programming, unlike other languages where it might just be an optional extra, and trying to work without one will just waste your time. Ideally you would learn to use it before you start writing any code. – Nate Eldredge Commented Jan 3 at 21:02
  • 1 @NateEldredge I started to use gdb, I think its a little bit confusing, but Im getting used to it. Thank for your tip – Felipe Teixeira Commented Jan 4 at 10:55
  • 1 The bottom of stackoverflow.com/tags/x86/info has a couple GDB asm tips. There are some GUI front-ends for it, and at least one mod that use its scriptability to build a text UI that's good for reverse-engineering at least, and maybe general asm debugging. I agree with Nate, asm without a debugger is like trying to build a robot while blindfolded. Moreso than most other languages, in asm every instruction mutates the machine state in some small consistent way, and is a building block for larger things, and the register state is small enough to show in a debugger window. – Peter Cordes Commented yesterday
 |  Show 2 more comments

1 Answer 1

Reset to default 4

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.

Your number-printing solution is convoluted!

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!

本文标签: x86 64Why does the assembly code stop functioning right when I remove an unused variableStack Overflow