Zoso
Zoso

Reputation: 3465

How are function sizes calculated by readelf

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

Answers (2)

JimmyTheSnout
JimmyTheSnout

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

ensc
ensc

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.

update:

.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

Related Questions