Reputation: 3465
I am trying to understand how readelf utility calculates function size. I wrote a simple program
#include <stdio.h>
int main() {
printf("Test!\n");
}
Now to check function size I used this (is this OK ? ):
readelf -sw a.out|sort -n -k 3,3|grep FUNC
which yielded:
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
29: 0000000000400470 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
30: 00000000004004a0 0 FUNC LOCAL DEFAULT 13 register_tm_clones
31: 00000000004004e0 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
34: 0000000000400500 0 FUNC LOCAL DEFAULT 13 frame_dummy
48: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
50: 00000000004005b4 0 FUNC GLOBAL DEFAULT 14 _fini
51: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
58: 0000000000400440 0 FUNC GLOBAL DEFAULT 13 _start
64: 00000000004003e0 0 FUNC GLOBAL DEFAULT 11 _init
45: 00000000004005b0 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
60: 000000000040052d 16 FUNC GLOBAL DEFAULT 13 main
56: 0000000000400540 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init
Now if I check the main function's size, it shows 16. How did it arrive at that? Is that the stack size ?
Compiler used gcc version 4.8.5 (Ubuntu 4.8.5-2ubuntu1~14.04.1)
GNU readelf (GNU Binutils for Ubuntu) 2.24
Upvotes: 1
Views: 1312
Reputation: 16
In the 'cat test.s' output provided by ensc, the "secret sauce" is the "..." between 'main:' and '.LFE0:'; those are the assembly instructions generated by the compiler that implement the call to printf(). The corresponding machine code for each assembler instruction occupies some number of bytes; "." is incremented by the number of bytes used by each instruction, so at the end of main, ". - main" is the total number of bytes occupied by the machine instructions for main().
It's the compiler that determines which sequences of assembly instructions are needed to execute the printf() call, and that varies from target to target, and from optimization level to optimization level. In your case, your compiler generated machine code occupying 16 bytes. The '.size main, . - main' caused the assembler to create the ELF symbol for main() with its st_size field set to 16.
readelf read the ELF symbol for main, saw that its st_size field was 16, and dutifully reported 16 as the size for main(). readelf doesn't 'calculate' a function's size -- it just reports the st_size field for the function's ELF symbol. The calculation is done by the assembler, when it interprets the '.size main, . - main' directive.
Upvotes: 0
Reputation: 6984
ELF symbols have an attribute st_size
which specifies their size (see <elf.h>
):
typedef struct
{
...
Elf32_Word st_size; /* Symbol size */
...
} Elf32_Sym;
This attribute is generated by the toolchain generating the binary; e.g. when looking at the assembly code generated by the C compiler:
gcc -c -S test.c
cat test.s
you will see something like
.globl main
.type main, @function
main:
...
.LFE0:
.size main, .-main
where .size
is a special as pseudo op.
.size
is the size of the code.
Here, .size
gets assigned the result of . - main
, where ".
" is the actual address and main
the address where main()
starts.
Upvotes: 2