arraysassemblyx86-64attstack-memory

How to declare a local array on the stack in x64 assembly?


I'm trying to declare an array of "quadwords" on the stack in my x64 assembly program. I know how to do this by declaring the array in the .data segment, but I'd like to make the array local to main if possible. I know that I could probably do this with malloc and dynamic memory allocation, as well, but I'd first like to see if it's even possible to do this on the stack. My issue is that I've declared enough memory on the stack to store the array (along with some extra space just for good measure). I store initial values into the array elements, but I don't know how to 'iterate' through the indices. I'd like to sum all the values in this array, just for practice. I tried using movq to retrieve the element, offset from the array's starting index, but I can't use negative indices in 'scaled index' mode.

     ...
     subq $128, %rsp
     movq $100, -8(%rbp) # arr[0] = 100
     movq $79, -16(%rbp) # arr[1] = 79
     movq $85, -24(%rbp) # arr[2] = 85
     movq $62, -32(%rbp) # arr[3] = 62
     movq $91, -40(%rbp) # arr[4] = 91
     movq $0, -48(%rbp) # sum = 0
     movq $5, %rcx # movq i = 5
 loop:
     cmp $1, %rcx
     jz done
     movq (%rbp, %rcx, 8), %rax # I believe this line may be wrong because the array starts at index -8(%rbp), right?
     addq %rax, -48(%rbp)
     subq $1, %rcx
     jmp loop
     ...

Solution

  • In this answer, I show several ways to change your code to do what you want. The first one is a minimal change to your original code to get it to work; the final one is the way I would write it.

    The first example only changes the starting and ending value of rcx. It leaves the array on the stack in the unusual top-down order, and iterates over the array from the end to the beginning.

        ...
        subq $128, %rsp
        movq $100, -8(%rbp) # arr[0] = 100
        movq $79, -16(%rbp) # arr[1] = 79
        movq $85, -24(%rbp) # arr[2] = 85
        movq $62, -32(%rbp) # arr[3] = 62
        movq $91, -40(%rbp) # arr[4] = 91
        movq $0, -48(%rbp) # sum = 0
        movq $-5, %rcx
    loop:
        cmp $0, %rcx
        jz done
        movq (%rbp, %rcx, 8), %rax
        addq %rax, -48(%rbp)
        addq $1, %rcx
        jmp loop
    

    The next example places the array in memory in the usual way, with index 0 at the lowest address, and iterates from index 0 to 4. Note the offset on the load instruction to cause index 0 to access rbp-40.

        ...
        subq $128, %rsp
        movq $100, -40(%rbp) # arr[0] = 100
        movq $79, -32(%rbp) # arr[1] = 79
        movq $85, -24(%rbp) # arr[2] = 85
        movq $62, -16(%rbp) # arr[3] = 62
        movq $91, -8(%rbp) # arr[4] = 91
        movq $0, -48(%rbp) # sum = 0
        movq $0, %rcx # i = 0
    loop:
        cmp $5, %rcx
        jz done
        movq -40(%rbp, %rcx, 8), %rax
        addq %rax, -48(%rbp)
        addq $1, %rcx
        jmp loop
    

    The final example changes a few other things to match the way I would write it:

        ...
        subq $128, %rsp
        movq $100, -40(%rbp) # arr[0] = 100
        movq $79, -32(%rbp)  # arr[1] = 79
        movq $85, -24(%rbp)  # arr[2] = 85
        movq $62, -16(%rbp)  # arr[3] = 62
        movq $91, -8(%rbp)   # arr[4] = 91
        xor %eax, %eax       # sum = 0
        xor %ecx, %ecx       # i = 0
    loop:
        addq -40(%rbp, %rcx, 8), %rax
        add $1, %ecx
        cmp $5, %ecx
        jb loop
    

    This version keeps the sum in a register instead of in memory. It makes use of the fact that writing to the lower half of a 64-bit register clears the upper half of the register. It uses the usual technique to load 0 into a register. And it puts the loop condition at the bottom of the loop instead of the top.