0

So I've just started writing x86-64 assembly (AT&T syntax) and this is the code for one of my first assignments. It is a very simple program: it asks for a base and an exponents and it makes the calculation. Unfortunately though, after hours of experimentation, I haven't been able to make it work. It yields correct results if you input 0 as an exponent, but every other time it yields the base value... I used a few breaks in the debugger and it seems that the problem lies at the cmpq %rdx, %rsi line. No matter what, it yields a TRUE flag and so the execution jumps to the end subroutine immediately... I have no idea what I'm doing wrong, so I would really appreciate some help! I would prefer if you just explained what the problem is, no need to suggest a solution :)

.text

prompt1: .asciz "\n Please enter a non - negative base value. \n"
prompt2: .asciz "\n Please enter a non - negative exponent value. \n"

input: .asciz "%ld"
output: .asciz "\n The result is: %ld \n"

.global main

main:

pushq %rbp                  #push the value of the old subroutine base pointer to the stack (subroutine prologue)
movq %rsp, %rbp             #move the value of the RSP register to the RBP register (both of them now point to the same memory adress, the base of the current subroutine's stack)

movq $0, %rax               #necessary to call the printf function
movq $prompt1, %rdi         #move the memory address of the prompt1 message to the RDI register (first argument)
call printf                 #calls printf function - prints the prompt1 message

movq $0, %rax               #necessary to call the printf function
movq $prompt2, %rdi         #move the memory address of the prompt2 message to the RDI register (first argument)
call printf                 #calls printf function - prints the prompt2 message

subq $16, %rsp              #align the stack for scanf function
movq $0, %rax               #necessary to call the scanf function
movq $input, %rdi           #move the memory address of the "input" string to the RDI register (first argument)
leaq -16(%rbp), %rsi        #move memory address pointed to by the RSP reg to the RSI reg (second argument)
call scanf                  #calls scanf function - reads base from user and stores it to the memory address on the stack pointed to by the RSI reg

movq -16(%rbp), %rsi        #copy the value from the memory address (user input) the RSP reg points towards to the RSI reg
movq %rsi, -8(%rbp)         #copy the base value to another memory address in the stack (we will need it in the pow subroutine so we can calculate the final result)

subq $16, %rsp              #align the stack for scanf function
movq $0, %rax               #necessary to call the scanf function             
movq $input, %rdi           #move the memory address of the "input" string" to the RDI register (first argument)
leaq -32(%rbp), %rsi        #move memory address pointed to by the RSP reg to the RSI reg (second argument)
call scanf                  #calls scanf function - reads exponent from user and stores it to the memory address on the stack pointed to by the RSI reg

subq $16, %rsp              #create more free space in the stack for the counter value while also keeping it 16 - byte aligned  
movq $1, -48(%rbp)          #move the initial counter to the top of the stack

movq -8(%rbp), %rcx         #fourth parameter (copy of base)
movq -48(%rbp), %rdx        #third parameter (counter)
movq -32(%rbp), %rsi        #second parameter (exponent)
movq -16(%rbp), %rdi        #first parameter (base)
call ipow                    #call ipow subroutine

movq %rcx, %rsi          
movq $0, %rax               #necessary to call the scanf function 
movq $output, %rdi          #move the memory address of the "output" string" to the RDI register (first argument)
call printf                 #calls printf function - prints the output message, but instead of %ld, the final value stored 

movq %rbp, %rsp             #move the value of the RBP register to the RSP register (both of them now point to the same memory adress, the base of the current subroutine's stack) (subroutine prologue)
popq %rbp                   #pop the value of the old subroutine base pointer from the stack
            
call exit                   #call exit function

ipow:

pushq %rbp                  #push the value of the old subroutine base pointer to the stack (subroutine prologue)
movq %rsp, %rbp             #move the value of the RSP register to the RBP register (both of them now point to the same memory adress, the base of the current subroutine's stack)  

cmpq $0, %rsi               #compare the exponent in RDX with the literal value 0
jne calc                    #if not equal (exponent isn't zero) jump to the beginning of the calc subroutine

movq $1, %rcx               #if equal, move the literal value 1 (final result) to register RAX

jmp end                     #jump to the end subroutine

calc:

cmpq %rdx, %rsi             #compare the value of the counter in RDX with the value of the exponent in RSI
jge end                     #if greater or equal end the loop / jump to the beginning of the end subroutine

movq %rcx, %rax
mul %rdi                    #multiply the value inside the RAX reg with the copy of the base stored in RDI reg --> store low - order bits of the result to RAX reg
movq %rax, %rcx

incq %rdx                   #increment the counter by 1

jmp calc                    #jump to beginning of calc subroutine / repeat loop

end:

movq %rbp, %rsp             #move the value of the RBP register to the RSP register (both of them now point to the same memory adress, the base of the current subroutine's stack) (subroutine prologue)
popq %rbp                   #pop the value of the old subroutine base pointer from the stack

ret                         #when calculations are over, return to the caller subroutine main
chiken
  • 1
  • 1
  • 1
    Have you used a debugger to see what values are in registers at that point? It looks like the comment on `mul %rdi` is incomplete; it doesn't mention that the full 128-bit result is written to RDX:RAX. If you don't want the high half of the multiply, use `imul %rdi, %rcx`, not widening `mul`. (Also, `pow` is a C standard library function name; usually best to pick a different name for a custom function like `ipow`, to avoid confusing readers.) – Peter Cordes Sep 16 '22 at 21:55
  • @PeterCordes thank you for your response. I edited the `pow` subroutine to avoid confusion. Also, I thought the syntax of `mul` was like right and that, specifically, `mul %rdi` would multiply the value in RDI with that in RAX and store the low - order bits to RAX... Regarding `imul`, where will the results of this multiplication be stored, exactly? – chiken Sep 16 '22 at 22:08
  • Read the linked duplicate: [Why is imul used for multiplying unsigned numbers?](https://stackoverflow.com/q/42587607) explains how `mul reg` works vs. `imul reg,reg` working like add, without touching registers that aren't explicitly mentioned. You're right that `mul %rdi` does what you said, but it *also* writes RDX with the high half of the 128-bit result. It does `RDX:RAX = RAX * src`. Found another more exact duplicate ([Segmentation fault after using mul second time](https://stackoverflow.com/q/42559739)) where MUL writing RDX was the problem. – Peter Cordes Sep 16 '22 at 22:09
  • @PeterCordes thank you again for the follow - up. I read the resources you kindly attached, and tried out imul, but unfortunately the problem persists. I still think it's a problem with the `cmp` instruction. Every time I try to run the program with a break right after the `cmp` instruction, it executes fully and it won't show me the register contents with `info reg`. Doesn't this mean that the execution never even reaches the instructions inside the `ipow` subroutine that are after `cmp`? – chiken Sep 16 '22 at 22:24
  • Either your debugger is broken or the `cmp` instruction itself is never reached. Single-step from earlier, with `ni` to step over calls, or `si` to truly single-step by instructions. The code in your question is not very minimal in terms of a [mcve] of your problem with `cmp`, so there might be other bugs that cause a problem before even getting to the one I noticed with `mul`. – Peter Cordes Sep 16 '22 at 22:26
  • 1
    If you're using GDB, `layout reg` / `layout next` should let you watch register values change as you single step, highlighting the one(s) changed on the last step. IDK if LLDB has similar functionality; I'd hope so, it's very valuable for seeing how your code executes. – Peter Cordes Sep 16 '22 at 22:28

0 Answers0