Search code examples
arraysassemblyatt

How can you create an array of size x on stack and scanf values into it


Suppose the size of an integer array is stored in eax. I suppose you can you can allocate memory for the array like so:

subl (%eax), %esp

However, the size in eax is provided by the user and will be a different size with each program execution. Given this, how could initialize each 4 byte memory address with an integer supplied by the user with scanf? How can we ensure that if more integers are supplied than the size of the array, we don't overwrite any memory?


Solution

  • subl (%eax), %esp reads the content of the address denoted by the value of eax.
    This is most likely something you don't want to do in this context.

    If eax is the size in bytes of the array then subl %eax, %esp will allocate enough memory for it.
    If eax is the number of 32-bit integers of the array then leal (,%eax,4), %esp is the correct instruction.

    None of the instructions above account for the alignment of the stack.
    In order to correctly return from your procedure you must be able to correctly undo the steps above - keep track of the quantities that you need to perform this.

    You can access the items of the array through the frame pointer, if any, or directly as positive offset from esp.


    Regarding your other questions - Those are really ordinary C questions, any C tutorial covers such topics.

    If you are at a dead end when switching from C to assembly, you can let a compiler inspire you.
    Below is the code generated by GCC for a function that resemble the one you want to implement (as I've understood it):

    #include <stdio.h>
    
    void foo(int n)
    {
        int arr[n];
    
    
        for (int i = 0; i < n; i++)
          scanf("%d", &arr[i]);
    
    }
    

     

    .LC0:
            .string "%d"
    foo(int):
            pushl   %ebp
            movl    %esp, %ebp                  ;Prologue
    
            pushl   %edi
            pushl   %esi
    
            movl    8(%ebp), %edi               ;EDI = n
    
            pushl   %ebx
    
            leal    4(,%edi,4), %eax            ;EAX = n*4+4  (For alignment purpose)
    
            subl    %eax, %esp                  ;Allocate space
    
            ;Pre loop condition
            testl   %edi, %edi
            jle     .L1
    
            ;Loop init
    
            movl    %esp, %esi                  ;ESI = ptr to first element
            xorl    %ebx, %ebx                  ;EBX = Counter
    .L5:
            ;scanf("%d", &arr[i]);
            pushl   %esi
            pushl   $.LC0
            addl    $1, %ebx                    ;Inc counter 
            addl    $4, %esi                    ;Move pointer
            call    scanf
    
            ;Loop condition 
            cmpl    %ebx, %edi
            popl    %eax
            popl    %edx
            jne     .L5
    .L1:
            leal    -12(%ebp), %esp             ;Deallocate
    
            popl    %ebx
            popl    %esi
            popl    %edi
            popl    %ebp
            ret
    

    For the purpose of this answer I've assumed the existence of VLAs which is not mandatory in C11 and doesn't really map one-to-one to the capability of the assembly programmer to grow and shrink the stack.