Test is on x86, 32-bit Linux. I am using g++ 4.6.3 and objdump 2.22

Here is a simple C++ code I am working on:

#include <iostream>

using namespace std;

    cout << "Hello World!" << endl;
    return 0;

When I compile it into assembly code using :

gcc -S hello.cc

I can find out a ctors section in the hello.s below:

.section    .ctors,"aw",@progbits
.align 4
.long   _GLOBAL__sub_I_main
.weakref    _ZL20__gthrw_pthread_oncePiPFvvE,pthread_once
.weakref    _ZL27__gthrw_pthread_getspecificj,pthread_getspecific
.weakref    _ZL27__gthrw_pthread_setspecificjPKv,pthread_setspecific
.weakref    _ZL22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create
.weakref    _ZL20__gthrw_pthread_joinmPPv,pthread_join
.weakref    _ZL21__gthrw_pthread_equalmm,pthread_equal
.weakref    _ZL20__gthrw_pthread_selfv,pthread_self
.weakref    _ZL22__gthrw_pthread_detachm,pthread_detach
.weakref    _ZL22__gthrw_pthread_cancelm,pthread_cancel
.weakref    _ZL19__gthrw_sched_yieldv,sched_yield
.weakref    _ZL26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
.weakref    _ZL29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
.weakref    _ZL31__gthrw_pthread_mutex_timedlockP15pthread_mutex_tPK8timespec,pthread_mutex_timedlock
.weakref    _ZL28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
.weakref    _ZL26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
.weakref    _ZL29__gthrw_pthread_mutex_destroyP15pthread_mutex_t,pthread_mutex_destroy
.weakref    _ZL30__gthrw_pthread_cond_broadcastP14pthread_cond_t,pthread_cond_broadcast
.weakref    _ZL27__gthrw_pthread_cond_signalP14pthread_cond_t,pthread_cond_signal
.weakref    _ZL25__gthrw_pthread_cond_waitP14pthread_cond_tP15pthread_mutex_t,pthread_cond_wait
.weakref    _ZL30__gthrw_pthread_cond_timedwaitP14pthread_cond_tP15pthread_mutex_tPK8timespec,pthread_cond_timedwait
.weakref    _ZL28__gthrw_pthread_cond_destroyP14pthread_cond_t,pthread_cond_destroy
.weakref    _ZL26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
.weakref    _ZL26__gthrw_pthread_key_deletej,pthread_key_delete
.weakref    _ZL30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
.weakref    _ZL33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
.weakref    _ZL33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy

However, when I assembly the asm code, producing an exe file and use the objdump produce the ctors section's contain like this:

objdump -Dr -j .ctors hellocpp

All I can get is like this:

hellocpp:     file format elf32-i386

Disassembly of section .ctors:

08049efc <__CTOR_LIST__>:
 8049efc:   ff                      (bad)  
 8049efd:   ff                      (bad)  
 8049efe:   ff                      (bad)  
 8049eff:   ff 00                   incl   (%eax)

08049f00 <__CTOR_END__>:
 8049f00:   00 00                   add    %al,(%eax)

Currently I am trying to recover the content of some ELF binaries compiled from c++ program..

So I am wondering if there is a way to get the content of ctors which equals to what g++ produced?


Thanks a lot for @Igor's help. But I am still trapped in looking for class's constructor and destructor info from ELF binary.

When evolving class definition, g++ would produce these info in the .ctors section:

    .globl  _ZN8ComputerC1Ev
    .set    _ZN8ComputerC1Ev,_ZN8ComputerC2Ev
    .globl  _ZN8ComputerD1Ev
    .set    _ZN8ComputerD1Ev,_ZN8ComputerD2Ev

Generally _ZN8ComputerC2Ev is the name of a class's constructor while _ZN8ComputerD2Ev is its destructor.

However, I just can not find corresponding info in the objdump dumped .ctors or .init_array sections.. I also tried .eh_frame and gcc_except_table, but the information dumped is massive.. I can not figure out the meaning of those information..

Could anyone give me guide?

2 Answers 2


The .ctors section is a list of pointers terminated with -1 (0xFFFFFFFF), so it does not make sense to disassemble it. If you rearrange the bytes as data, you get:

__CTOR_LIST__: .long 0xffffffff
__CTOR_END__:  .long 0x00000000

So, for whatever reason, the resulting exe does not actually use the .ctors section. I suspect the linker instead placed the pointers into the new-style .init_array section. Note that it is, again, a list of pointers, and not code.


The .ctors or .init_array sections only contain so-called constructor functions - functions that need to be executed at startup, before the main() itself. These are usually compiler-generated functions that perform construction of global objects (such as cin, cout etc.), or other startup-related tasks. You can, in fact, add your own functions to that list using __attribute__((constructor)).

What does not go there are general C++ class constructors - there is no need to execute those on startup. They will be called when and if you construct an object of a specific class - e.g. by declaring a variable or calling operator new.

  • Groovy! I dump the content of .init_array section and it contains the address of _GLOBAL__sub_I_main function! Sep 23, 2014 at 1:40

As Igor stated, the .ctors section is a list of function pointers, ending with a sentinel value of 0xffffffff. To see its contents, just do

$ objdump -s -j.ctors bar.so

But your assembly file only contains weak symbols. Those are foreign functions in other libraries, and are invoked when their libraries are loaded at runtime.

For example, put this in a file bar.cpp:

class Foo {
  int i;

  Foo(int n) : i(n) {

Foo global_foo(123);

Compile with

$ g++ -shared -fPIC bar.cpp -obar.so

The contents of the .init_array section is

$ objdump -s -j.init_array bar.so

bar.so:     file format elf64-x86-64

Contents of section .init_array:
 200820 ad060000 00000000                    ........        

There's a function pointer there, 0xad060000 00000000. But you have to change its endianness, e.g. with Python:

>>> import struct
>>> import binascii
>>> binascii.hexlify(struct.pack("<Q", 0xad06000000000000))

Now list all symbols and grep for that address:

$ objdump -C --syms bar.so | grep 00000000000006ad
00000000000006ad l     F .text  0000000000000015
  [... on above line ...] global constructors keyed to bar.cpp

The disassembly for it,

$ objdump -C -d bar.so


00000000000006ad <global constructors keyed to bar.cpp>:
 6ad:   55                      push   %rbp
 6ae:   48 89 e5                mov    %rsp,%rbp
 6b1:   be ff ff 00 00          mov    $0xffff,%esi
 6b6:   bf 01 00 00 00          mov    $0x1,%edi
 6bb:   e8 ba ff ff ff          callq  67a <__static_initialization_and_destruction_0(int, int)>
 6c0:   c9                      leaveq 
 6c1:   c3                      retq   

which jumps to __static_initialization_and_destruction_0(int, int):

000000000000067a <__static_initialization_and_destruction_0(int, int)>:
 67a:   55                      push   %rbp
 67b:   48 89 e5                mov    %rsp,%rbp
 67e:   48 83 ec 10             sub    $0x10,%rsp
 682:   89 7d fc                mov    %edi,-0x4(%rbp)
 685:   89 75 f8                mov    %esi,-0x8(%rbp)
 688:   83 7d fc 01             cmpl   $0x1,-0x4(%rbp)
 68c:   75 1d                   jne    6ab <__static_initialization_and_destruction_0(int, int)+0x31>
 68e:   81 7d f8 ff ff 00 00    cmpl   $0xffff,-0x8(%rbp)
 695:   75 14                   jne    6ab <__static_initialization_and_destruction_0(int, int)+0x31>
 697:   be 7b 00 00 00          mov    $0x7b,%esi
 69c:   48 8b 05 9d 03 20 00    mov    0x20039d(%rip),%rax        # 200a40 <_DYNAMIC+0x1e8>
 6a3:   48 89 c7                mov    %rax,%rdi
 6a6:   e8 f5 fe ff ff          callq  5a0 <Foo::Foo(int)@plt>
 6ab:   c9                      leaveq 
 6ac:   c3                      retq   

which puts 123 (0x7b) on the stack and calls Foo::Foo(int).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.