Reputation: 4862
Consider the following code:
#include <stdio.h>
void __attribute__ ((constructor)) a_constructor()
{
printf("%s\n", __func__);
}
void __attribute__ ((constructor)) b_constructor()
{
printf("%s\n", __func__);
}
int main()
{
printf("%s\n",__func__);
}
I compile the above code as : gcc -ggdb prog2.c -o prog2
. The code runs as expected.
a_constructor
b_constructor
main
But when I see its dump using objdump -d prog2 > f
. There is neither a call to __do_global_ctors_aux
anywhere in _init
or anywhere else, nor a definition of __do_global_ctors_aux
. So, how do the constructors get called? Where is the definition of __do_global_ctors_aux
? Is this some optimization?
I also tried compiling it with no optimization like this: gcc -ggdb -O0 prog2.c -o prog2
. Please Clarify.
The compilation is being done on 32 bit linux machine.
EDIT
My output from gdb bt is:
Breakpoint 1, a_constructor () at prog2.c:5
5 printf("%s\n", __func__);
(gdb) bt
#0 a_constructor () at prog2.c:5
#1 0x080484b2 in __libc_csu_init ()
#2 0xb7e31a1a in __libc_start_main (main=0x8048445 <main>, argc=1, argv=0xbffff014, init=0x8048460 <__libc_csu_init>,
fini=0x80484d0 <__libc_csu_fini>, rtld_fini=0xb7fed180 <_dl_fini>, stack_end=0xbffff00c) at libc-start.c:246
#3 0x08048341 in _start ()
Upvotes: 0
Views: 739
Reputation: 136415
So, how do the constructors get called?
If you look at the disassembly produced with gcc -g -O0 -S -fverbose-asm prog2.c -o prog2.s
, there is the following:
.text
.Ltext0:
.globl a_constructor
.type a_constructor, @function
a_constructor:
.LFB0:
.file 1 "test.c"
.loc 1 4 0
.cfi_startproc
pushq %rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp #,
.cfi_def_cfa_register 6
.loc 1 5 0
movl $__func__.2199, %edi #,
call puts #
.loc 1 6 0
popq %rbp #
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size a_constructor, .-a_constructor
.section .init_array,"aw"
.align 8
.quad a_constructor
In the above, function a_constructor
is put into .text
section. And a pointer to the function is also appended to .init_array
section. Before calling main
glibc iterates over this array and invokes all constructor functions found there.
Upvotes: 1
Reputation:
The details are implementation-specific and you don't mention your implementation.
A perfectly valid strategy used by some implementations is to create a run-time library that contains the real entry point for your program. That real entry point first calls all constructors, and then calls main
. If your program is dynamically linked and the code behind that real entry point resides in a shared library (like, say, libc
), then clearly disassembling your program cannot possibly show you where the constructor gets called.
A simple approach for figuring where precisely the call is coming from is by loading your program in a debugger, setting a breakpoint on one of the constructors, and asking for the call stack when the breakpoint is hit. For example, on Cygwin:
$ gdb ./test GNU gdb (GDB) 7.8 Copyright (C) 2014 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-pc-cygwin". Type "show configuration" for configuration details. For bug reporting instructions, please see: . Find the GDB manual and other documentation resources online at: . For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./test...done. (gdb) b a_constructor Breakpoint 1 at 0x4011c6: file test.cc, line 5. (gdb) run Starting program: /home/Harald van Dijk/test [New Thread 4440.0x1734] [New Thread 4440.0xa8c] b_constructor Breakpoint 1, a_constructor () at test.cc:5 5 printf("%s\n", __func__); (gdb) bt #0 a_constructor () at test.cc:5 #1 0x61006986 in __main () from /usr/bin/cygwin1.dll #2 0x004011f6 in main () at test.cc:14 (gdb)
This shows that on Cygwin, a variant of the strategy I mentioned is used: the real entry point is the main
function, but the compiler inserts a call to a Cygwin-specific __main
function right at the start, and it's that __main
function that searches for all constructors and calls them directly.
(Incidentally, clearly this breaks if main
is called recursively: the constructors would run a second time. This is why C++ does not allow main
to be called recursively. C does allow it, but then, standard C doesn't have constructor functions.)
And you can get a hint of how that __main
function searches for them, by not disassembling the executable program, but asking the compiler for the generated assembly:
$ gcc -S test.c -o -
I won't copy the whole assembly listing here, but it shows that on this particular implementation, constructor functions get emitted in a .ctors
segment, so it would be easy for a __main
function to simply call all functions in that segment, without the compiler having to enumerate each such function one by one.
Upvotes: 0