20

If I have a program in C++/C that (language doesn't matter much, just needed to illustrate a concept):

#include <iostream>    

void foo() {
    printf("in foo");
}

int main() {
    foo();
    return 0;
}

What happens in the assembly? I'm not actually looking for assembly code as I haven't gotten that far in it yet, but what's the basic principle?

Gordon Gustafson
  • 40,133
  • 25
  • 115
  • 157
  • 3
    Note that you don't call "methods" in c, just "functions". That is why is makes no sense to act as if c and c++ are the same language. – dmckee --- ex-moderator kitten Oct 18 '09 at 23:12
  • 'language doesn't matter much': the concepts are pretty similar for compiled languages. Interpreted languages (like php, javascript) can be quite different. I believe you're looking for the behavior of compiled languages? – Adriaan Oct 19 '09 at 07:39
  • 2
    C++ has no concept of *methods*, only *member functions*. – Didier Trosset Aug 18 '10 at 06:49

11 Answers11

45

In general, this is what happens:

  1. Arguments to the function are stored on the stack. In platform specific order.
  2. Location for return value is "allocated" on the stack
  3. The return address for the function is also stored in the stack or in a special purpose CPU register.
  4. The function (or actually, the address of the function) is called, either through a CPU specific call instruction or through a normal jmp or br instruction (jump/branch)
  5. The function reads the arguments (if any) from the stack and the runs the function code
  6. Return value from function is stored in the specified location (stack or special purpose CPU register)
  7. Execution jumps back to the caller and the stack is cleared (by restoring the stack pointer to its initial value).

The details of the above vary from platform to platform and even from compiler to compiler (see e.g. STDCALL vs CDECL calling conventions). For instance, in some cases, CPU registers are used instead of storing stuff on the stack. The general idea is the same though

Isak Savo
  • 34,957
  • 11
  • 60
  • 92
  • Is the function's stack area really cleared in step 7, or is it just that the stack pointer in restored to its previous state? It seems like zeroing out the function's leftover stack memory every time would be rather inefficient... – Jeremy Friesner Oct 20 '09 at 04:42
  • No, typically the stack pointer (which is usually just a CPU register) is simply restored to its initial value (so this is a single CPU instruction). I've updated my answer to clarify this – Isak Savo Oct 20 '09 at 14:26
  • remembering: at x64 platform have single calling convention only. – lsalamon Oct 20 '09 at 20:33
  • Nope, x64 calling convention is different between most OSes (which use convention specified by AMD), and Windows (which uses its own different one). – Pavel Minaev Oct 20 '09 at 23:14
  • What about the return value, at what point is the return value assigned to the variable "waiting" for it? If the return value is on the functions stack, does that mean that the variable "waiting" for the return value, in the calling code, is assigned before the functions stack is cleared? – shadow_map Sep 02 '14 at 15:29
  • Try **Computer Systems: A programmer perspective** and its coding problems, you will get a great deal of knowledge on how to break assembly code. – saurabheights Jul 16 '15 at 11:36
13

You can see it for yourself:

Under Linux 'compile' your program with:

gcc -S myprogram.c

And you'll get a listing of the programm in assembler (myprogram.s).

Of course you should know a little bit about assembler to understand it (but it's worth learning because it helps to understand how your computer works). Calling a function (on x86 architecture) is basically:

  • put variable a on stack
  • put variable b on stack
  • put variable n on stack
  • jump to address of the function
  • load variables from stack
  • do stuff in function
  • clean stack
  • jump back to main
  • I found this option quite interesting, however is there a way to produce the assembly code in intel flavor instead of at&t flavor? – Alex Aug 31 '15 at 18:12
  • 3
    @Alex Comment comes a "little" bit too late (ca. 6 years but who cares :-P), but you can just add `-masm=intel` to the gcc command. – andreee Dec 15 '15 at 15:37
3

What happens in the assembly?

A brief explanation: The current stack state is saved, a new stack is created and the code for the function to be executed is loaded and run. This involves inconveniencing a few registers of your microprocessor, some frantic to and fro read/writes to the memory and once done, the calling function's stack state is restored.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
2

What happens? In x86, the first line of your main function might look something like:

call foo

The call instruction will push the return address on the stack and then jmp to the location of foo.

mrduclaw
  • 3,965
  • 4
  • 36
  • 37
  • Good job. If the question is what does the call instruction do, is one of the few answers that actually deals with that really. – Beeeaaar Jun 30 '18 at 19:37
1

Arguments are pushed in stack and "call" instruction is made

Call is a simple "jmp" with pushing an address of instruction into stack ("ret" in the end of a method popping it and jumping on it)

Valentin Golev
  • 9,965
  • 10
  • 60
  • 84
  • Arguments are not necessarily pushed onto the stack. This depends a lot on architecture, calling convention, compiler, etc... – mrduclaw Oct 18 '09 at 15:27
1

I think you want to take a look at call stack to get a better idea what happens during a function call: http://en.wikipedia.org/wiki/Call_stack

vehomzzz
  • 42,832
  • 72
  • 186
  • 216
1

A very good illustration: http://www.cs.uleth.ca/~holzmann/C/system/memorylayout.pdf

Tanuj
  • 545
  • 3
  • 12
1

What happens?

C mimics what will occur in assembly...

It is so close to machine that you can realize what will occur

void foo() {
    printf("in foo");

/*

db mystring 'in foo'
mov eax, dword ptr mystring
mov edx , dword ptr _printf
push eax
call edx
add esp, 8
ret
//thats it
*/

}

int main() {
    foo();
    return 0;
}
sth
  • 222,467
  • 53
  • 283
  • 367
Mandrake
  • 363
  • 1
  • 3
  • 11
0

The common idea is that the registers that are used in the calling method are pushed on the stack (stack pointer is in ESP register), this process is called "push the registers". Sometimes they're also zeroed, but that depends. Assembly programmers tend to free more registers then the common 4 (EAX, EBX, ECX and EDX on x86) to have more possibilities within the function.

When the function ends, the same happens in the reverse: the stack is restored to the state from before calling. This is called "popping the registers".

Update: this process does not necessarily have to happen. Compilers can optimize it away and inline your functions.

Update: normally parameters of the function are pushed on the stack in reverse order, when they are retrieved from the stack, they appear as if in normal order. This order is not guaranteed by C. (ref: Inner Loops by Rick Booth)

Abel
  • 56,041
  • 24
  • 146
  • 247
0

1- a calling context is established on the stack

2- parameters are pushed on the stack

3- a "call" is performed to the method

jldupont
  • 93,734
  • 56
  • 203
  • 318
0

The general idea is that you need to

  1. Save the current local state
  2. Pass the arguments to a function
  3. Call the actual function. This involves putting the return address somewhere so the RET instruction knows where to continue.

The specifics vary from architecture to architecture. And the even more specific specifics might vary between various languages. Although there usually are ways of controlling this to some extent to allow for interoperability between different languages.

A pretty useful starting point is the Wikipedia article on calling conventions. On x86 for example the stack is almost always used for passing arguments to functions. On many RISC architectures, however, registers are mainly used while the stack is only needed in exceptional cases.

Joey
  • 344,408
  • 85
  • 689
  • 683