Label moving after optimization is turned on in gcc - c

I have a strange problem after turning on level 1 optimization in gcc. What I do is save the label and jmp back to it from a different function later.
void
UMS__suspend_procr( VirtProcr *animatingPr )
{
animatingPr->nextInstrPt = &&ResumePt;
[Some Code and inline volatile asm]
ResumePt:
return;
}
I do some of these jumps and they all work fine.
The problem is that when I turn on O1 it does not save the right label address. Instead it does this:
804b14e: 8b 45 08 mov 0x8(%ebp),%eax
804b151: c7 40 14 4e b1 04 08 movl $0x804b14e,0x14(%eax)
804b158: 8b 55 08 mov 0x8(%ebp),%edx
So the program is jumping back even before the assignment.

This code is not valid GNU C. To begin with, computed gotos (&&label) are a feature specific to GNU C, not part of the C language, but that's ok if you're using GNU C. However, the only place they're valid in GNU C is with a goto statement. You cannot use the pointer with inline asm as an indirect jump/call destination, because adjusting the stack frame is up to the compiler, and the current logical view of the stack frame from the point of the inline asm and the label destination might not match. With an explicit goto statement, the compiler can patch this up, but with asm it can't even tell it's happening.
As for the bigger picture, if you're writing code like this, you should really rethink some of your assumptions. There's certainly a better way to accomplish what you want.

Related

Locate the machine instructions that access the memory in executable

Edit: I want to test the system by inserting a breakpoint and comparing memory before and after the breakpoint.
I used static analysis to get a list of C source code locations and debugging information (ie, a dwarf) provides a mapping between C source code and machine instructions in executable.
But the problem is that there are many machine instructions that mapped to one line of C source code and I need to test all of them.
The machine instruction to be tested is to modify the memory state.
So I want to reduce the number of instruction by eliminating the instruction that doesn't modify the memory.
For example, I have the following source code test.c and I have the line number 5.
2 int var1 = 10;
3 void foo() {
4 int *var2 = (int*)malloc(sizeof(int));
5 for(*var2=var1;;) {
6 /* ... */
7 }
8 }
To be clear, line number 5 accesses the global memory var1 and the heap memory *var2.
I compiled the above program with the command gcc -g test.c and the result is
(a.out)
00000000004004d6 <foo>:
4004d6: 55 push %rbp
4004d7: 48 89 e5 mov %rsp,%rbp
4004da: 48 83 ec 10 sub $0x10,%rsp
4004de: bf 04 00 00 00 mov $0x4,%edi
4004e3: e8 d8 fe ff ff callq 4003c0 <malloc#plt>
4004e8: 48 89 45 f8 mov %rax,-0x8(%rbp)
4004ec: 8b 15 1e 04 20 00 mov 0x20041e(%rip),%edx # 600910 <var2>
4004f2: 48 8b 45 f8 mov -0x8(%rbp),%rax
4004f6: 89 10 mov %edx,(%rax)
4004f8: eb fe jmp 4004f8 <foo+0x22>
and dwarfdump -l a.out give me the following result.
0x004004d6 [ 3, 0] NS uri: "/home/workspace/test.c"
0x004004de [ 4, 0] NS
0x004004ec [ 5, 0] NS
0x004004f8 [ 5, 0] DI=0x1
Now I know that, in the a.out, the location 0x4004ec, 0x4004f2, 0x4004f6 and 0xf004f8 are mapped to the line number 5 in C source code.
But I want to exclude the 0x4004f8 (jmp) which doesn't access the (heap, global or local) memory.
Does anyone know how to get only instructions that access memory?
This is only answering the question about finding asm instructions with explicit memory operands. The part about associating them with C statements is pretty bogus outside of -O0 compiler output (where each statement is compiled to a separate block of instructions to support GDB's jump to another line in the same function, or modifying variables in memory while stopped at breakpoint). See Basile's answer which tries to make some sense of the C statement stuff in the question.
Intel-syntax disassembly might be handy, because all explicit memory operands will have ptr in them, like mov rax, qword ptr [rbp - 0x8], so you can text search.
In asm source, the <size> ptr syntax isn't required when a register operand implies the operand size, but disassemblers like objdump -drwC -Mintel always put it in.
In AT&T syntax, you could also just look for () or a bare symbol name as an operand.
Don't forget to filter out lea instructions. lea is like the & operator in C. It's a shift-and-add instruction that uses memory-operand syntax and machine encoding.
Also don't forget to filter out various long-nop instructions that use addressing modes to get the right amount of padding in one instruction. For example:
66 2e 0f 1f 84 00 00 00 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
So if the mnemonic is lea or nop, ignore the instruction. (32-bit code sometimes uses other instructions as NOPs, but usually it's actually an lea that sets a register to itself in machine code generated by gas / ld from compiler .p2align directives.)
objdump disassembles rep stos with explicit operands, like rep stos QWORD PTR es:[rdi],rax. So you will actually get rep movs and rep stos operands. (Note that rep movs and rep cmps have two memory operands, unlike normal instructions. They're implicit in the machine code, but objdump makes them explicit.) This will also miss implicit memory operands like the stack for push / pop and call / ret.
A given C statement is compiled into several machine instructions, and several of them may access memory. Think of something like ptr->fld = arr[i++] * arr[j]--; .... BTW, in some cases, arr[j] might have been used earlier, could already sit in some register, so might not need another memory load (but only a store, which could be defered later).
I want to know the location, in executable, of the machine instruction that accesses (heap, global or local) memory generated by the given code
So your question might not make sense in general. Several machine instructions (or none of them) might access memory (related to a single C statement in your source code). And register allocation and register spilling may happen, so a given machine instruction might be related to a C variable quite far from the "current" C instruction (which has no sense).
An optimizing compiler is allowed to mix the several C statements and might output intermixed machine code. Read also about sequence points. There is no obvious mapping between machine code instruction and C statement (notably with optimizations enabled), that is why you often debug with less optimizations enabled (so gcc -g prefers to be used with -O0 or -Og, not more).
With GCC compile your src.c source file using
gcc -O -S -Wall -fverbose-asm src.c
and you'll get a slightly more readable src.s assembler file. You could use some editor or pager to look into that generated file.
Does anyone know how to get only instructions that access memory?
That does not make much sense. An optimizing compiler would sometimes share some common machine code related to several different C statements.
BTW, you might also ask GCC to dump various internal representations, for example using gcc -O -fdump-tree-all ; then you get hundreds of (textual) internal dump files (partially dumping various internal representations). Remember that GCC has hundreds of optimization passes.
Notice you might be more interested to work on GCC internal representations (e.g. GENERIC or GIMPLE or even RTL) by adding your own GCC plugin (or GCC MELT extensions). That could require months of work (notably to undestand details of GCC internal architecture and representations).
Without understanding your high-level goals and motivations, we cannot help you more.
You should read much more about semantics and about undefined behavior, which is (indirectly) more relevant to your question than what you believe.
Notice that C statements do not correspond (one to many) to machine instructions. An optimizing compiler don't compile C statements one by one, it compiles an entire translation unit at once (and may for example do inline expansions, loop unrolling, stack unwinding, constant folding, register allocation and spilling, interprocedural optimizations and dead code elimination). This is why C compilers are so complex beasts of many millions of source code lines. BTW, most C compilers (e.g. GCC or Clang) are free software, so you can spend several months or years studying their source code.
Read also some good book on compilers (e.g. the latest Dragon Book), some books on semantics, and on programming languages pragmatics.
If you are interested by GCC internals specifically, my documentation page (also available here) of GCC MELT contains lots of slides and references.
If you only care about machine instructions, you might entirely forget about C and work, with the help of some dissassembler library like libopcode (see this), only on machine code in object files.
Look also into other static source code analyers, including Coccinelle & Frama-C and libclang.
If you are interested only by GCC emitted code and can afford recompiling your C source code, you might instead work inside the GCC compiler (thru your GCC plugin or GCC MELT extension) at the GIMPLE level and detect (and perhaps transform) those GIMPLE instructions accessing memory. Detecting (and perhaps transforming) GIMPLE statements modifying memory could be simpler and might be enough.
I want to test the system by inserting a breakpoint and comparing memory before and after the breakpoint.
This is a bit similar to e.g. address sanitizers and other instrumentation features of GCC. You could spend several years working on something similar (and transforming some GIMPLE), then you probably want to add several additional passes in GCC (and you might need some extra runtime support).
Notice however that recent GDB is scriptable (in Guile or Python) and has watchpoints. If you just want to debug one particular program, that might be enough (and you might not need to dive into compiler internals, which would take many months or years of work). You should also use valgrind and address sanitizers.

data reserved in functions in C

In C programming language, a variable can have a memory address and a value.
And as I understood every function as well have an address and also data which allocated at that address. My question is what is the meaning of the data which these functions point to?
You already got (good) answers, but I think some (obscure?) fact about C should be pointed out, regarding your question:
In C programming language, a variable can have a memory address and a value.
Actually the defining property of a variable is that is always has a value – if it's uninitialized, semantically it still has a value, only that this value is the "undefined value" and reading the "undefined value" invokes undefined behaviour.
But, and this is important, not every variable in C does have an address! There is this little storage classifier register, which exact meaning most people do not fully comprehend. The most widespread – and wrong – interpretation is, that register means that the variable is to be placed in registers only. The problem is: There are instruction architectures in which registers do not exist, but C has been designed to be still viable for them.
The true meaning of the register classifier is, that you can not take the address of a variable that is register, which means you can not create pointers toward it.
The upshot of this is, that a variable that is register the only important thing is its value. And it is perfectly legal for the C compiler to generate code that completely discards the "place" (be it register, memory location or something entirely different) where its value came to be, as long as it able to faithfully recreate the value in a way, that it is semantically conforming to the program text. This also implies that it is perfectly legal to perform a whole re-computation of whatever had to be executed to obtain the final value. Which is why applying the register storage qualifier to variable may result in sudden increase of code size and drop of performance.
As such the register storage qualifier is not a mechanism for optimizing code, but should be treated as a special purpose tool for writing code that's neither time nor size critical but has to operate under very specific, tight constraints. One example would be for example bootloaders or system initialization code, which task it is to initialize memory access in the first place and have to operate with just a few bytes – or even none – of usable memory storage, but can re-compute values required for each step.
The C programming language is (like every programming language) a specification (in some report). It is not a software. You probably should read the n1570 (draft specification of C11) report.
Conceptually, a function does not have any data in C (but its code may refer to static addresses, contain literal constants - including pointers- etc...). It has some behavior, practically implemented by some code. What is code is not defined by the C standard.
Practically speaking, and this depends upon the particular implementation (look into the difference between Harvard machine & computer architectures and Von Neumann ones), a function pointer is some address of machine code (often, the target of the CALL machine instruction translating the C calls to it).
On desktops & laptops & tablets with some usual operating system (like Linux, Windows, MacOSX, iOS, Android...) -all are Von Neumann architectures: x86-64 or ARM-, your process has a single virtual address space containing code segments and data segments and heap data. Then function pointers and data pointers are of the same kind, and it is practically meaningful to cast between them. A canonical example is the usage of POSIX dlsym: you often cast its result to some function pointer (e.g. inside some plugin which is dynamically loaded with dlopen). The address of a function is practically speaking the address of its first machine code instruction (sitting in some code segment in the common address space). Read this & that for creative examples. Another useful example is JIT compilation libraries like asmjit, GNU lightning, libgccjit, LLVM: they enable you to generate machine code at runtime, and to get a (fresh) function pointer from these.
Neither dlsym nor JIT libraries are stricto sensu conforming to the C standard, because in a purely standard conforming C program the set of functions is statically known and any function pointer should point to some existing function of the same signature (read about calling conventions & ABIs), otherwise it is undefined behavior.
On some embedded computers with a Harvard architecture (e.g. some Arduino), code and data sit in different spaces, and a code address might not have the same number of bits than a data address. On such systems, a cast between function and data pointers is meaningless (unless you dive into deep implementation details). The C standard was specified to be general enough to take such weird computers into account.
Read also a lot more about closures and continuations. The C standard don't have them (hence callbacks conventionally take some client data argument). You probably will learn a lot by reading SICP. Read also about homoiconicity.
Read also about Operating Systems: If you use Linux (which I recommend, because it is mostly made of free software whose source code you can study), read Advanced Linux Programming. Read also Operating Systems: Three Easy Pieces.
In other words: your question (on function pointers and addresses) has different approaches. A dogmatic programming language lawyer approach (and the issue is to understand deeply the semantics of function pointers in the C standards; look also into CompCert & Frama-C); a pragmatic operating system and implementation specific approach (and then it depends upon your computer, its instruction set, and its OS and even your particular C compiler -and version- and optimization flags; and you may even have some "magic mechanisms" -like dlsym & dlopen or JIT compilation libraries- to create functions at runtime; which is magic because the C standards don't think of that).
You can find your answer here.
The C language supports two kinds of memory allocation through the variables in C programs:
Static allocation: is what happens when you declare a static or global variable. Each static or global variable defines one block of space, of a fixed size. The space is allocated once, when your program is started (part of the exec operation), and is never freed.
Automatic allocation: happens when you declare an automatic variable, such as a function argument or a local variable. The space for an automatic variable is allocated when the compound statement containing the declaration is entered, and is freed when that compound statement is exited.
In GNU C, the size of the automatic storage can be an expression that varies. In other C implementations, it must be a constant.
Function pointers point to blocks of machine instructions that get executed when you call the function.
Say you have this:
#include <stdio.h>
int plus_42(int x)
{
int res=x+42;
printf("%d + 42 = %d\n", x,res);
return res;
}
int main()
{
return plus_42(1);
}
If you compile it, link it, and run objdump -d on the result:
gcc plus_42.c && objdump -d a.out
you'll get (depending on your architecture, something like):
0000000000400536 <plus_42>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 20 sub $0x20,%rsp
40053e: 89 7d ec mov %edi,-0x14(%rbp)
400541: 8b 45 ec mov -0x14(%rbp),%eax
400544: 83 c0 2a add $0x2a,%eax
400547: 89 45 fc mov %eax,-0x4(%rbp)
40054a: 8b 55 fc mov -0x4(%rbp),%edx
40054d: 8b 45 ec mov -0x14(%rbp),%eax
400550: 89 c6 mov %eax,%esi
400552: bf 04 06 40 00 mov $0x400604,%edi
400557: b8 00 00 00 00 mov $0x0,%eax
40055c: e8 af fe ff ff callq 400410 <printf#plt>
400561: 8b 45 fc mov -0x4(%rbp),%eax
400564: c9 leaveq
400565: c3 retq
0000000000400566 <main>:
400566: 55 push %rbp
400567: 48 89 e5 mov %rsp,%rbp
40056a: bf 01 00 00 00 mov $0x1,%edi
40056f: e8 c2 ff ff ff callq 400536 <plus_42>
400574: 5d pop %rbp
400575: c3 retq
400576: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40057d: 00 00 00
plus some boilerplate.
Here, 0000000000400536 and 0000000000400566 are the addresses of main and plus_42 (= the pointers that main and plus_42 point to) respectively, and the hex numbers you see in the 2nd column is the data, which is decoded in the 3d column into human readable names of the machine instructions that the data represents.

How to find the "exit" of a C program

The test is on 32-bit x86 Linux.
So basically I am trying to log the information of executed basic blocks by insert instrumentation instructions in assembly code.
My strategy is like this: Write the index of a executed basic block in a globl array, and flush the array from memory to the disk when the array is full (16M).
Here is my problem. I need the flush the array to the disk when the execution of instrumented binary is over, even if it does not reach 16M boundary. However, I just don't know where to find the exit of a assembly program.
I tried this:
grep exit from the target assembly program, and flush the memory right before the call exit instruction. But according to some debugging experience, the target C program, say, a md5sum binary, does not call exit when it finishes the execution.
Flush the memory at the end of main function. However, in the assembly code, I just don't know where is the exact end of main function. I can do a conservative approach, say, looking for all the ret instruction, but it seems to me that not all the main function ends with a ret instruction.
So here is my question, how to identify the exact execution end of a assembly code , and insert some instrumentation instructions there? Hooking some library code is fine to me. I understand with different input, binary could exit at different position, so I guess I need some conservative estimation. Am I clear? thanks!
I believe you cannot do that in the general case. First, if main is returning some code, it is an exit code (if main has no explicit return the recent C standards require that the compiler adds an implicit return 0;). Then a function could store the address of exit in some data (e.g. a global function, a field in a struct, ...), and some other function could indrectly call that thru a function pointer. Practically, a program can load some plugins using dlopen and use dlsym for "exit" name, or simply call exit inside the plugin, etc... AFAIU solving that problem (of finding actual exit calls, in the dynamic sense) in full generality can be proved equivalent to the halting problem. See also Rice's theorem.
Without claiming an exhaustive approach, I would suggest something else (assuming you are interested in instrumenting programs coded in C or C++, etc... whose source code is available to you). You could customize the GCC compiler with MELT to change the basic blocks processed inside GCC to call some of your instrumentation functions. It is not trivial, but it is doable... Of course you'll need to recompile some C code with such a customized GCC to instrument it.
(Disclaimer, I am the main author of MELT; feel free to contact me for more...)
BTW, do you know about atexit(3)? It could be helpful for your flushing issue... And you might also use LD_PRELOAD tricks (read about dynamic linkers, see ld-linux(8)).
atexit() will properly handle 95+% of programs. You can either modify its chain of registered handlers, or instrument it as you are other blocks. However, some programs may terminate by use of _exit() which does not invoke atexit handlers. Probably instrumenting _exit to invoke data flushing and installing an atexit (or on_exit() on BSD-like programs) handler should cover nearly 100% of programs.
Addendum: Note that the Linux Base Specification says that the C library startup shall:
call the initializer function (*init)().
call main() with appropriate arguments.
call exit() with the return value from main().
A method that should be working everytime would be to create a shared memory section for storing your data there.
You also create a child process which is waiting for the process being debugged to finish.
As soon as the process being debugged has finished the child process will finalize the write operations using the data that is in the shared memory.
This should work on all forms of exit, process interruptions (e.g. Ctrl+C, closing the terminal window, ...) or even if the process has been killed using "kill".
But according to some debugging experience, the target C program, say, a md5sum binary, does not call exit when it finishes the execution.
Let's take a look at a md5sum binary on an i686 GNU/Linux system:
In the disassembly (objdump -d /usr/bin/md5sum) we have this:
Disassembly of section .text:
08048f50 <.text>:
8048f50: 55 push %ebp
8048f51: 89 e5 mov %esp,%ebp
8048f53: 57 push %edi
8048f54: 56 push %esi
8048f55: 53 push %ebx
8048f56: 83 e4 f0 and $0xfffffff0,%esp
8048f59: 81 ec c0 00 00 00 sub $0xc0,%esp
8048f5f: 8b 7d 0c mov 0xc(%ebp),%edi
[ ... ]
8049e8f: 68 b0 d6 04 08 push $0x804d6b0
8049e94: 68 40 d6 04 08 push $0x804d640
8049e99: 51 push %ecx
8049e9a: 56 push %esi
8049e9b: 68 50 8f 04 08 push $0x8048f50
8049ea0: e8 4b ef ff ff call 8048df0 <__libc_start_main#plt>
8049ea5: f4 hlt
This is all startup boilerplate code. The actual program's main call is invoked inside the call __libc_start_main. If the program returns from that, then, hey look, there is a hlt instruction. That's your target. Look for that hlt instruction and instrument that as the end of the program.
You could try this:
int main()
bool keepGoing = true;
{
while(keepGoing) {
string x;
cin >> x;
if(x == "stop") {
keepGoing = false;
}
}
}
even though it is primitive... I probably butchered the coding but it's just a concept.

What is the meaning of the data32 data32 nopw %cs:0x0(%rax,%rax,1) instruction in disassembly of gcc's output?

While running some tests for the -O2 optimization of the gcc compilers, I observed the following instruction in the disassembled code for a function:
data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
What does this instruction do?
To be more detailed I was trying to understand how does the compiler optimize useless recursions like the below with O2 optimization:
int foo(void)
{
return foo();
}
int main (void)
{
return foo();
}
The above code causes stack overflow when compiled without optimization, but works for O2 optimized code.
I think with O2 it completely removed the pushing the stack of the function foo, but why is the data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1) needed?
0000000000400480 <foo>:
foo():
400480: eb fe jmp 400480 <foo>
400482: 66 66 66 66 66 2e 0f data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
400489: 1f 84 00 00 00 00 00
0000000000400490 <main>:
main():
400490: eb fe jmp 400490 <main>
You see an operand forwarding optimization of the cpu pipeline.
Although it is an empty loop, gcc tries to optimize this as well :-).
The cpu you are running has a superscalar architecture. It means, that it has a pipeline in it, and different phases of the executions of the consecuting instructions happen parallel. For example, if there is a
mov eax, ebx ;(#1)
mov ecx, edx ;(#2)
then the loading & decoding of instruction #2 can happen already while #1 is executed.
The pipelining has major problems to solve in the case of the branches, even if they are unconditional.
For example, while the jmp is decoding, the next instruction is already prefetched into the pipeline. But the jmp changes the location of the next instruction. In such cases, the pipeline needs to by emptied and refilled, and a lot of worthy cpu cycles will be lost.
Looks this empty loop will run faster if the pipeline is filled with a no-op in this case, despite that it won't be ever executed. It is actually an optimization of some uncommon feature of the x86 pipeline.
Earlier dec alphas could even segfault from such things, and empty loops had to have a lot of no-ops in them. x86 would be only slower. This is because they must be compatible with the intel 8086.
Here you can read a lot from the handling of branching instructions in pipelines.
To answer the question in the title, the instruction
data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
is a 14-byte NOP (no operation) instruction that is used to pad the gap between the foo function and the main function to maintain 16-byte alignment.
The x86 architecture has a large number of different NOP instructions of different sizes that can be used to insert padding into an executable segment such that they'll have no effect if the CPU ends up executing over them. Then Intel optimization manual contains information on recommended NOP encoding for different lengths that can be used as padding.
In this specific case, it is completely irrelevant, as the NOP will never be executed (or even decoded as it is after an unconditional jump), so the compiler could pad with any random garbage it wanted to.
The functions foo() is an infinite recursion without termination. Without optimization, gcc generates normal subroutine calls, which include stacking the return address at least. As the stack is limited, this will create an stack overflow which is _undefined_behaviour_.
If optimizing, gcc detects foo() does not require a stack frame at all (there are no arguments or local variables). It also detects, foo() instantly returns to the caller (which would also be foo()). This is called tail-chaining: a function call right at the end of a function (i.e. explicit/implicit return) is converted to a jump to that function, so there is no need for a stack.
This is still undefined behaviour, but this time, nothing "bad" is observed.
Just remenber: undefined includes fatal behaviour as well as expected behaviour (but that just by chance). Code which behaves differently with different optimization levels should always be regarder errorneous.
There is one exception: Timing. This is not subject to the C language standard (neither of most other languages).
As others stated, the data32 ... is very certain padding to get an 16 byte alignment which might be the size of the internal instruction bus and/or cache lines.

Do functions occupy memory space?

void demo()
{
printf("demo");
}
int main()
{
printf("%p",(void*)demo);
return 0;
}
The above code prints the address of function demo.
So if we can print the address of a function, that means that this function is present in the memory and is occupying some space in it.
So how much space it is occupying in the memory?
You can see for yourself using objdump -r -d:
0000000000000000 <demo>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: bf 00 00 00 00 mov $0x0,%edi
5: R_X86_64_32 .rodata
9: b8 00 00 00 00 mov $0x0,%eax
e: e8 00 00 00 00 callq 13 <demo+0x13>
f: R_X86_64_PC32 printf-0x4
13: 5d pop %rbp
14: c3 retq
0000000000000015 <main>:
EDIT
I took your code and compiled (but not linked!) it. Using objdump you can see the actual way the compiler lays out the code to be run. At the end of the day there is no such thing as a function: for the CPU it's just a jump to some location (that in this listing happens to be labeled). So the size of the "function" is the size of the code that comprises it.
There seems to be some confusion that this is somehow not "real code". Here is what GDB says:
Dump of assembler code for function demo:
0x000000000040052d <+0>: push %rbp
0x000000000040052e <+1>: mov %rsp,%rbp
0x0000000000400531 <+4>: mov $0x400614,%edi
0x0000000000400536 <+9>: mov $0x0,%eax
0x000000000040053b <+14>: callq 0x400410 <printf#plt>
0x0000000000400540 <+19>: pop %rbp
0x0000000000400541 <+20>: retq
This is exactly the same code, with exactly the same size, patched by the linker to use real addresses. gdb prints offsets in decimal while objdump uses the more favourable hex. As you can see, in both cases the size is 21 bytes.
So if we can print the address of a function, that means that this
function is present in the memory and is occupying some space in it.
Yes, the functions you write are compiled into code that's stored in memory. (In the case of an interpreted language, the code itself is kept in memory and executed by an interpreter.)
So how much space it is occupying in the memory?
The amount of memory depends entirely on the function. You can write a very long function or a very short one. The long one will require more memory. Space used for code generally isn't something you need to worry about, though, unless you're working in an environment with severe memory constraints, such as on a very small embedded system. On desktop computer (or even mobile device) with a modern operating system, the virtual memory system will take care of moving pages of code into or out of physical memory as they're needed, so there's very little chance that your code will consume too much memory.
Of course it's occupying space in memory, the entire program is loaded in memory once you execute it. Typically, the program instructions are stored in the lowest bytes of the memory space, known as the text section. You can read more about that here: http://www.geeksforgeeks.org/memory-layout-of-c-program/
Yes, all functions that you use in your code do occupy memory space. However, the memory space does not necessarily belong exclusively to your function. For example, an inline function would occupy space inside each function from where it is called.
The standard does not provide a way to tell how much space a function occupies in memory, as pointer arithmetic, the trick that lets you compute sizes of contiguous memory regions in the data memory, is not defined for function pointers. Moreover, ISO C forbids conversion of function pointer to object pointer type, so you cannot get around this restriction by casting your function pointer to, say, a char*.
printf("%p",demo);
The above code prints the address of function demo().
That is undefined behavior: %p expects a void*, while you are passing it a void (*)(). You should see a compiler warning, telling that what you are doing is not valid (demo).
As for determining the amount of memory it is occupying, this is not possible at run-time. However, there are other ways you can determine it:
How to get the length of a function in bytes?
The functions are compiled into machine code that will run only on a specific ISA (x86, probably ARM if it's going to run on your phone, etc.) Since different processors may need more or fewer instructions to run the same function, and the length of instructions can also vary, there is no way to know in advance exactly how big the function will be until you compile it.
Even if you know what processor and operating system it will be compiled for, different compilers will create different, equivalent representations of the function depending on which instructions they use and how they optimize the code.
Also, keep in mind a function occupies memory in different ways. I think you are talking about the code itself, which is its own section. During execution, the function can also occupy space on the stack - every time the function is called, more memory is taken up in the form of a stack frame. The amount depends on the number and type of local variables and arguments declared by the function.
Yes however you can declare it as being inline, so the compiler will take the source code and move it where ever you call that function. Or you can also use preprocessor macros. Though do keep in mind using inline will generate larger code but it will execute faster, and the compiler can decide to ignore your inline request if it feels that it will become to large.

Resources