Reputation: 18542
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
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
Reputation: 1
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