0

Here is some simple C++ code. I define a variable-length array in test_func. I want to know the relationship between the array arr and the rbp register.

void test_func(int m, int n) {
    int arr[m];
    for(int i = 0; i < m; ++i) {
        arr[i] = m;
    }
    cout << "ok";
}
int main() {
   
    int m = 30, n = 12;
    test_func(m, n);
    return 0;
}

I use gdb to check out their addresses, but I find out nothing. enter image description here

Then I use g++ -E test.cpp -o test.i , g++ -S test.i -o test.S. I find some strange Assembly code.

_Z9test_funcii:
.LFB1559:
    pushq   %rbp
    .seh_pushreg    %rbp
    pushq   %r12
    .seh_pushreg    %r12
    subq    $40, %rsp
    .seh_stackalloc 40
    leaq    128(%rsp), %rbp
    .seh_setframe   %rbp, 128
    .seh_endprologue
    movl    %ecx, -64(%rbp)
    movl    %edx, -56(%rbp)
    movq    %rsp, %rax
    movq    %rax, %r8
    movl    -64(%rbp), %eax
    cltq
    subq    $1, %rax
    movq    %rax, -112(%rbp)
    movq    %rax, %rdx
    addq    $1, %rdx
    movq    %rdx, %r11
    movl    $0, %r12d
    movq    %rax, %rdx
    addq    $1, %rdx
    movq    %rdx, %r9
    movl    $0, %r10d
    addq    $1, %rax
    salq    $2, %rax
    addq    $15, %rax
    shrq    $4, %rax
    salq    $4, %rax
    call    ___chkstk_ms
    subq    %rax, %rsp
    movq    %rsp, %rax
    addq    $3, %rax
    shrq    $2, %rax
    salq    $2, %rax
    movq    %rax, -120(%rbp)
    movl    $0, -100(%rbp)
.L3:
    movl    -100(%rbp), %eax
    cmpl    -64(%rbp), %eax
    jge .L2
    movq    -120(%rbp), %rdx
    movl    -100(%rbp), %eax
    cltq
    movl    -64(%rbp), %ecx
    movl    %ecx, (%rdx,%rax,4)
    addl    $1, -100(%rbp)
    jmp .L3
.L2:
    movq    %r8, %rsp
    nop
    leaq    -88(%rbp), %rsp
    popq    %r12
    popq    %rbp
    ret
    .seh_endproc
    .def    __main; .scl    2;  .type   32; .endef
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main

I want to know the position of arr on the stack and its relationship with the rbp register.

BoP
  • 2,310
  • 1
  • 15
  • 24
  • 3
    The array is not required to be on the stack at all, nor even exist. `test_func` has no side-effects except for `cout<<"ok";` The compiler does not need to allocate the array or execute the loop. It might or might not, depending on compiler optimization settings. – paddy Mar 29 '23 at 01:59
  • 5
    Please note that variable-length arrays are [**not** in C++](https://stackoverflow.com/questions/1887097/why-arent-variable-length-arrays-part-of-the-c-standard). It's a GCC-specific non-standard and ***non**-portable* extension to its C++ compiler. Don't use it when programming in C++, use `std::vector` if you need a "dynamic array" of any kind. – Some programmer dude Mar 29 '23 at 02:00
  • 2
    IMO : You should not worry about the inner workings of variable length arrays. Just use std::vector and just go by the promise that it uses contiguous memory. – Pepijn Kramer Mar 29 '23 at 02:54

1 Answers1

0

pretty much the same way it handles fixed size arrays. The stack pointer is decremented by the size of the object. Usually the stack size of all locals is calculated and a single stack adjust is done at the start. For dynamically requested stack (legal c VLA, gcc 'illegal' c++ VLA, alloca,...) a further decrement is done when the size is known

Here is a c version - compiled with -O2 to reduce clutter, but with side effects to force the compiler to write real code

#include <stdio.h>

int main(){

    int sz;
    scanf("%d", &sz);
    int j[sz];
    for(int i = 0; i < sz; i++){
        j[i] = sz+22;
    }
    printf("%d", j[sz/2]);
}

godbolt shows

.LC0:
        .string "%d"
main:
        push    rbp
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        mov     rbp, rsp
        sub     rsp, 16
        lea     rsi, [rbp-4]
        call    __isoc99_scanf
        movsx   rdx, DWORD PTR [rbp-4]
        mov     rsi, rdx
        sal     rdx, 2
        lea     rax, [rdx+15]
        and     rax, -16
        sub     rsp, rax   <<<<<==== dynamically adjusted top of stack
        mov     rdi, rsp
        add     rdx, rdi
        test    esi, esi
        jle     .L4
        lea     ecx, [rsi+22]
        mov     rax, rdi
.L3:
        mov     DWORD PTR [rax], ecx
        add     rax, 4
        cmp     rax, rdx
        jne     .L3
.L4:
        mov     eax, esi
        shr     eax, 31
        add     eax, esi
        sar     eax
        cdqe
        mov     esi, DWORD PTR [rdi+rax*4]
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        call    printf
        xor     eax, eax
        leave
        ret

reminder, on most current system the stack grows down, ie newer things are a t lower addresses, hence the subtract to allocate stack space

pm100
  • 48,078
  • 23
  • 82
  • 145
  • 1
    ’Thank you for your explanation. I understand what happened by executing the assembly step by step. – Frank Mar 29 '23 at 08:08