0

I'm following the book Programming from the Ground Up which uses 32-bit GNU Assembler for its programs. My machine, however is 64 bit. I got the first two examples fine by typing them in verbatim. No errors whatsoever. However, the third example didn't compile. I made a few changes (for example, changing the l suffix to q for the operations and using the r prefix instead of the e prefix) to the third example so that it compiles fine now. Now the problem is that it ends up in an infinite loop.

Here's the code:

power.s

# PURPOSE:
# Program to illustrate how functions work.
# This program will compute the value of
# 2^3 + 5^2 (= 8 + 25 = 33)

# Everything in the main program is stored in
# registers, so the data section doesn't have
# anything.

    .section .data

    .section .text
    .globl _start

_start:
    pushq   $3          # Push the second argument (the power)
    pushq   $2          # Push the first argument (the base)
    call    power       # Call the function power
    addq    $8, %rsp    # Move the stack pointer back

    pushq   %rax        # Save the first answer before calling again

    pushq   $2
    pushq   $5
    call    power
    addq    $8, %rsp    # Move the stack pointer back

    # The first answer is already in %rax, pop the second answer to %rbx
    popq    %rbx

    # Add the answers together and store the result in %rbx
    addq    %rax, %rbx

    movq    $1, %rax
    int     $0x80

    # FUNCTION: power
    # PURPOSE:  To exponentiate a number to a given power
    #
    # INPUT:
    # 1. The base number.
    # 2. The power to raise it to.
    #
    # OUPTUT:   Will give the result as a return value.
    #
    # NOTES:    The power must be positiive.
    #
    # VARIABLES:
    # 1. %rbx       - holds the base number
    # 2. %rcx       - holds the power
    # 3. -4(%rbp)   - holds the current result
    # 4. %rax       - used for temporary storage
    .type power, @function
power:
    pushq   %rbp            # Save old base pointer
    movq    %rsp, %rbp      # Make the stack pointer the base pointer
    subq    $4, %rsp        # Get room for our local storage

    movq    8(%rbp), %rbx   # The first argument in %rbx
    movq    12(%rbp), %rcx  # The first argument in %rcx

    movq    %rbx, -4(%rbp)  # The current result

power_loop_start:
    cmpq    $1, %rcx
    je      end_power
    movq    -4(%rbp), %rax  # Fetch the last rersult into %rax
    imulq   %rbx, %rax      # Multiply the base with the result and store in %rax
    movq    %rax, -4(%rbp)  # Update the result

    decq    %rcx            # Decrement the power
    jmp     power_loop_start

end_power:
    movq    -4(%rbp), %rax  # Store return value in %rax
    movq    %rbp, %rsp      # Restore the Stack pointer
    popq    %rbp            # Restore the Base pointer
    ret

Any idea as to where it's wrong?


The previous examples which worked just fine:

exit.s

# PURPOSE:  Simple program that exits and returns a status code
#           back to the Linux kernel.
#
# INPUT:    None
#
# OUTPUT:   Returns a status code that can be viewed by typing:
#               echo $?
#           in the terminal after running the program.
#
# VARIABLES:
#           %eax holds the system call number
#           %ebx holds the return status
.section .data

.section .text
.globl _start

_start:

# This is the Linux kernel command number
# (system call) for exiting a program.
movl $1, %eax

# This is the sattus number we will return
# to the Operating System.
movl $0, %ebx

# This wakes up the kernel to run the exit command
int $0x80

maximum.s

# PURPOSE:      This program finds the maximum of a set of
#               data items.
#
# VARIABLES:    The registers have the following uses.
#
# %edi - Holds the index of the data item being examined.
# %ebx - Largest data item found.
# %eax - The current data item.
#
# The following memory locations are used:
#
# data_items - Contains the data. A '0' terminates the data.
#

    .section .data

# The data items go here
data_items:
    .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0

    .section .text

    .globl _start

_start:
    movl    $0, %edi                                # Initialize the index register
    movl    data_items(, %edi, 4), %eax             # Load the first (0th) bye of
                                                    # data into %eax
    movl    %eax, %ebx                              # The first is the max so far

start_loop:
    cmpl    $0, %eax                                # See if the current item is 0
    je      loop_exit

    incl    %edi                                    # Increment the idex by 1
    movl    data_items(, %edi, 4), %eax             # Load the next item
    cmpl    %ebx, %eax                              # Compare the values
    jle     start_loop

    movl    %eax, %ebx                              # The new item is bigger, so
                                                    # move it to %ebx
    jmp     start_loop

loop_exit:
    # %ebx is the status code and it has the maximum element
    # We'll just return this as the maximum number
    movl    $1, %eax
    int     $0x80
Hungry Blue Dev
  • 1,313
  • 16
  • 30
  • 1
    While my suggestion doesn't resolve your problem with your code, as you are just learning Assembly, you should rather follow the book in 32b realm to get some basics first, before trying 32->64b conversions on your own. That stuff is not that trivial, as it may seems. Your previous examples, which "worked" are partially illegal too, calling `int 0x80` in 64b mode is not a valid operation, and it works for you only because your kernel is mercifully keeping working 32b services handler registered there even for 64b code. 64b API has it's own `syscall` services, with different arguments. – Ped7g Mar 03 '17 at 13:55
  • 1
    And finally, if you are still curious... the first thing I see, you didn't convert the stack handling to the new sizes, so all those -4/+8 etc are invalid now, as you put two QWORD arguments on stack, and the return address plus old frame pointer are another two QWORDs, plus the local variable is now QWORD, not DWORD. Didn't check it further. – Ped7g Mar 03 '17 at 14:00
  • 1
    Your problem is located here: `subq $4, %rsp`. Registers are now 8 bytes wide, not four. However as ped7g correctly pointed out, simply compile for 32-bit or use a book for 64-bit assembly. Things are done differently in the 64-bit world, we learned from our errors. – Margaret Bloom Mar 03 '17 at 14:00
  • 1
    And please open the book index, and verify there's some "debugging" chapter soon ... if not, close the book, and search for different one, either as complete replacement, or at least as debugging guide extension. It takes much more time and effort to learn assembly without being able to watch the CPU state per-instruction in debugger. Much more is not like 2x or 3x, but from minutes/hours of bug hunting/fixing you go into land of days/weeks/months instead (when I was learning programming and assembly, I didn't have computer, so I had to debug on paper - took literally WEEKS to find some bugs). – Ped7g Mar 03 '17 at 14:06
  • Thank you @Ped7g @Margret. I used `gcc` with the `-m32` option. It works. :-) – Hungry Blue Dev Mar 03 '17 at 14:08

0 Answers0