0

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
     ...
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • We don't declare things on the stack in assembly/machine code: we just grab some stack space and use it. It is up to each function to use the stack space without conflicting with other stack space usage in the same function. So, just decide (1) how much stack space you want, and (2) allocate it, and (3) decide where you want to locate your array within that allocated stack space (i.e. vs. any other stack space being used by the same function). – Erik Eidt Dec 04 '22 at 23:01
  • To iterate, obtain the lowest address of the array, then iterate using pointer/array indexing like any other array (global or heap or provided as parameter). How do you get the lowest/starting address of the array? By calculation of build-time known offset where the array is within your stack space, combined (added to) the stack pointer. – Erik Eidt Dec 04 '22 at 23:06
  • @ErikEidt Right - I meant to phrase it as moving data into parts of the stack. I forgot that we could use the stack pointer to grab values as well since it's lower than the base pointer... Is there absolutely no way to do this by offsetting, negatively, from the base pointer? Doing it from the stack pointer requires knowing exactly where it starts (the lowest address), which gets tricky if I declare variables after the array. – TheSinisterStone Dec 04 '22 at 23:28
  • Technically don't need a base pointer, though your function is the one that sets up the base pointer, so you/it should know the delta between any base pointer and stack pointer, and can offset from that instead if you set it up so. – Erik Eidt Dec 04 '22 at 23:30
  • 1
    You have organized your array from higher addresses to lower addresses. There's not anything fundamentally wrong with that, but it's not how it's usually done. If you want to iterate over the array from 4 down to 0, then just load rcx with -5 instead of +5 and add 1 on each iteration. If you want to iterate over the elements in order, then load rcx with -1 and count down to -5. – prl Dec 05 '22 at 03:38
  • 1
    If you want to organize your array in the usual way, then change the offsets in your initialization code so that array[0] is at rbp-40 and array[4] is at rbp-8. Then change the load instruction to `movq -48(%rbp, %rcx, 8), %rax`. – prl Dec 05 '22 at 03:40
  • 1
    Regardless of which way you decide to do it, you'll need to change the termination condition so it iterates over 5 elements, not just 4. – prl Dec 05 '22 at 03:42

1 Answers1

1

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.

prl
  • 11,716
  • 2
  • 13
  • 31