0

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 ()
tapananand
  • 4,392
  • 2
  • 19
  • 35

2 Answers2

1

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.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
0

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.

  • My output for bt is different than yours. I have updated in the question. – tapananand Jan 12 '15 at 12:37
  • @TapanAnand So your implementation does what I described in my second paragraph. That works too. –  Jan 12 '15 at 12:44
  • Then why can't we see who calls the constructor in gdb bt output? – tapananand Jan 12 '15 at 12:50
  • @TapanAnand You can, that's the point of my answer. In your implementation, `__libc_csu_init` is calling the constructors. In my implementation, `__main` is calling the constructors. Actually, it's possible that those functions are calling something that got inlined, but either way, it's not any part of your own code that's calling the constructors. –  Jan 12 '15 at 13:12
  • But I can't see `__libc_csu_init` calling the function `__do_global_ctors_aux`. Should I post code for `__libc_csu_init` also? – tapananand Jan 12 '15 at 13:16
  • @TapanAnand There *is* no `__do_global_ctors_aux` function. Anywhere. On either your implementation or mine. –  Jan 12 '15 at 13:28
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/68653/discussion-between-tapan-anand-and-hvd). – tapananand Jan 12 '15 at 13:29