7

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; main() { 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?

Update:

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

9

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.

Edit:

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.

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

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 { public: 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)) '00000000000006ad' 

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 

shows

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).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.