Paweł Hajdan
Paweł Hajdan

Reputation: 18542

How to count static initializer in an ELF file?

I'm trying to count static initializers in a C++ file.

Solution I already have (which used to work with gcc-4.4) is looking at size of the .ctors ELF section.

After an upgrade to gcc-4.6, this seems to no longer return valid results (calculated number of static initializers is 0, which doesn't match reality, e.g. as returned by nm).

Now the issue is I'd like the solution to work even in absence of symbols (otherwise I'd have used nm).

Below is the output of readelf -SW of an example executable:

There are 35 section headers, starting at offset 0x4f39820:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        00000174 000174 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            00000188 000188 000020 00   A  0   0  4
  [ 3] .note.gnu.build-id NOTE            000001a8 0001a8 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        000001cc 0001cc 000918 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          00000ae4 000ae4 00a5e0 10   A  6   1  4
  [ 6] .dynstr           STRTAB          0000b0c4 00b0c4 00ef72 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0001a036 01a036 0014bc 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         0001b4f4 01b4f4 000450 00   A  6  13  4
  [ 9] .rel.dyn          REL             0001b944 01b944 268480 08   A  5   0  4
  [10] .rel.plt          REL             00283dc4 283dc4 0048c8 08   A  5  12  4
  [11] .init             PROGBITS        0028868c 28868c 00002e 00  AX  0   0  4
  [12] .plt              PROGBITS        002886c0 2886c0 0091a0 04  AX  0   0 16
  [13] .text             PROGBITS        00291860 291860 3ac5638 00  AX  0   0 16
  [14] malloc_hook       PROGBITS        03d56ea0 3d56ea0 00075a 00  AX  0   0 16
  [15] google_malloc     PROGBITS        03d57600 3d57600 008997 00  AX  0   0 16
  [16] .fini             PROGBITS        03d5ff98 3d5ff98 00001a 00  AX  0   0  4
  [17] .rodata           PROGBITS        03d5ffc0 3d5ffc0 ffa640 00   A  0   0 64
  [18] .eh_frame_hdr     PROGBITS        04d5a600 4d5a600 0004b4 00   A  0   0  4
  [19] .eh_frame         PROGBITS        04d5aab4 4d5aab4 001cb8 00   A  0   0  4
  [20] .gcc_except_table PROGBITS        04d5c76c 4d5c76c 0003ab 00   A  0   0  4
  [21] .tbss             NOBITS          04d5df0c 4d5cf0c 000014 00 WAT  0   0  4
  [22] .init_array       INIT_ARRAY      04d5df0c 4d5cf0c 000090 00  WA  0   0  4
  [23] .ctors            PROGBITS        04d5df9c 4d5cf9c 000008 00  WA  0   0  4
  [24] .dtors            PROGBITS        04d5dfa4 4d5cfa4 000008 00  WA  0   0  4
  [25] .jcr              PROGBITS        04d5dfac 4d5cfac 000004 00  WA  0   0  4
  [26] .data.rel.ro      PROGBITS        04d5dfc0 4d5cfc0 1b160c 00  WA  0   0 32
  [27] .dynamic          DYNAMIC         04f0f5cc 4f0e5cc 000220 08  WA  6   0  4
  [28] .got              PROGBITS        04f0f7ec 4f0e7ec 00a800 04  WA  0   0  4
  [29] .data             PROGBITS        04f1a000 4f19000 0206b8 00  WA  0   0 32
  [30] .bss              NOBITS          04f3a6c0 4f396b8 04c800 00  WA  0   0 32
  [31] .comment          PROGBITS        00000000 4f396b8 00002a 01  MS  0   0  1
  [32] .shstrtab         STRTAB          00000000 4f396e2 00013e 00      0   0  1
  [33] .symtab           SYMTAB          00000000 4f39d98 4ff960 10     34 140163  4
  [34] .strtab           STRTAB          00000000 54396f8 144992a 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

Should I be looking at .init or .init_array instead? Could you point me to corresponding documentation that explains the change between gcc or linker versions?

Upvotes: 3

Views: 1624

Answers (2)

zwol
zwol

Reputation: 140569

Static constructors can be triggered by any of the three sections .init, .ctors, or .init_array (oldest to newest in that order). .init contains a fragment of code, .ctors and .init_array contain pointers to code. The difference between .ctors and .init_array has to do with the overall order in which constructors are executed. As far as I know, none of this is documented anywhere other than code comments and mailing list posts, but it's probably worth checking the ELF ABI documents (g- and ps- both).

You cannot deduce the number of static constructors in a file from the size of any of these sections. It is permitted, and common, for compilers to generate a single special function which invokes all of the constructors in a file, and reference only that one function in whichever of the sections it uses. All you can know for sure (without examining the contents of the sections, applying relocations, and chasing pointers / call instructions into the .text segment and reverse engineering whatever gets called) is: in an object file, if at least one of these sections has nonzero size, then there is at least one file- or global-scope constructor in the file; if all three sections are empty, then there are none. (In an executable, all three sections are always nonempty, because the data structures that they define need headers and trailers, which are automatically added at link time.)

Note also that constructors for block-scoped static objects are not invoked from any of these sections; they're invoked the first time control reaches their declaration.

Upvotes: 8

I am assuming you have access to all the source code of your applications (and perhaps all the libraries it is called). This obviously is true for free software.

Then, you might measure that more precisely at compilation time, when compiling (with a recent version of GCC, e.g. 4.7 or 4.8) your application. You could extend it with MELT (that is a high level domain specific language to extend GCC), or with painful GCC plugins coded in C++, to measure such things.

And I am not entirely sure that your question makes a precise sense. If your application is e.g. linked to some shared library which use visibility tricks to hide its static constructors, understanding how much static constructors that library calls is not really defined.

Upvotes: -1

Related Questions