-2

I am novice in assembly (NASM). I know that RBP points to any parameters and local variable in function. It’s implemented by simple offset. If we wanna get first variable we rbp-4, if we wanna get fist parameter we add to rbp 4. But how can we do this if any function can have any number of local variable or parameters? If we want we can have 100 variable in one function and how does we can points to any variable by simple constant offset?

Thank you. Sorry for my English. I am not native speaker.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Ruslan
  • 51
  • 5

2 Answers2

3

You can't have a variable number of variables in languages like C, so the compiler always knows where it put them. Thus it knows the right constant offset for every variable.

If you're talking about variadic functions like printf(char *, ...), then you have rules from the calling convention for how they're laid out, and you would normally increment a pointer. To index by arg-number, you need to know the width of all previous args. They're at least 8, but depending on the calling convention can be wider instead of using a hidden pointer in x86-64 System V. printf has conversions for long double among other things (which is wider than 8 bytes), so it does have to support keeping track of which arg is where in case a conversion references an arg by number


If you're talking about using the stack as a stack data structure, with push / pop potentially inside loops, then you need to keep a pointer to know when your stack data structure is "empty", and further pops would eat into other local vars.

Having some of your local variables be variable-length arrays makes things trickier, like C int foo[n] where n is another variable. Many C compilers handle it by inventing a pointer to each VLA. The pointers have known fixed width so the compiler knows where to find them, and they can get initialized as space is reserved for the VLAs with sub rsp, rax / mov [rbp-16], rsp for example.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
2

Addressing of function parameters and local variables depends on the chosen calling convention. Your to get first parameter we add to rbp 4 is certainly wrong, because in 64bit mode (implied by using RBP or RSP for addressing) can items be pushed on stack with 64bit granularity only. Perhaps you had 32bit StandardCall convention on your mind, where typical prologue of a function looks like this:

Function: PUSH EBP 
          MOV EBP,ESP 
          SUB ESP,LocalsSize

Register EBP can be used inside the Function for addressing of unlimited number of parameters and Local variables (I used three parameters and four DWORD locals in this example):

Invokation      Stack   Address

PUSH Param3     Param3  EBP+4*4
PUSH Param2     Param2  EBP+3*4
PUSH Param1     Param1  EBP+2*4
CALL Function   return
                 EBP  
                Local1  EBP-1*4
                Local2  EBP-2*4
                Local3  EBP-3*4
                Local4  EBP-4*4

See also this 32bit and 64bit StdCall convention examples.

vitsoft
  • 5,515
  • 1
  • 18
  • 31
  • 2
    *are all items stored on stack with 64bit granularity.* not for locals they're not; compilers don't leave 4 bytes of unused space between `int a, b;` local variables. It's totally normal to see a char at `[rbp-1]` or an `int` at `[rbp-4]`. Maybe you're thinking about stack args, where the calling convention does dictate everything having its own 8-byte stack slot. – Peter Cordes Jun 05 '21 at 12:44
  • 3
    `[rbp+4]` for the first parameter is what you should be picking on; that's 100% wrong. (That's the middle of the saved RBP value, and x86-64 calling conventions pass the first few args in registers. Unless they're structs that can't fit in a reg, or some C++ reason why an object needs to have an address.) – Peter Cordes Jun 05 '21 at 12:54
  • @PeterCordes Ad `[rbp-1]` etc. sure, items on the stack are **addressable** with byte granularity. Perhaps I should replace **stored** with **pushed**. For instance, `PUSH FS` zeroextens 16bit register in 64bit mode before pushing it, AFAIK. – vitsoft Jun 05 '21 at 14:28
  • If that's what you mean, then absolutely yes you should change your answer to say that instead of something that's wrong! Of course, local vars aren't allocated with a separate `push` for each one, so it's still not *relevant* here. The question is not very clear, but it does distinguish local variables from parameters. – Peter Cordes Jun 05 '21 at 21:36
  • 2
    You're still incorrectly claiming that `[rbp-4]` is incorrect for "*the first local variable*". If you want to pick on anything in the question, complain about the `[rbp+4]` for "first parameter", because that's the only clear sign of confusion based on 32-bit stack-args. (Although `[ebp+4]` is of course not right either; `[esp+4]` before any pushes is.) – Peter Cordes Jun 06 '21 at 00:22