3
votes

I just a bit confused by this previous discussion about .init in https://groups.google.com/forum/#!topic/gnu.gcc.help/Fit5UOU9UNs Because, from all the discussion I can get from internet shows that:

using "extern int hello_init() attribute ((constructor));" will not add code into .init section but put it into a function array in .ctors, right? But If I compile the example code:

#include <stdio.h> 

extern int hello_init() __attribute__ ((constructor)); 

int hello_init() 
{ 
  printf("init hello\n"); 
  return 0; 
} 

<<<<< if I compile it with "gcc -fPIC -shared -o libender.so test.c", using objdump -x or -d I can't see .ctors section and I can't even see any calls to __do_global_ctors_aux anywhere in the file including .init section. Did recent version of GCC has change the behavior of it? Is there anyway I can see the function with attribute of constructor from the ELF files?

  1. If i don't use constructor attribute and just compile it using "Wl,-init,hello_init". When I use objdump or readelf to dump the so file, I still can't see the code of hello_init in .init section. Does this flag really put the function code into .init section? if so is there any way I can use objdump or readelf to observe it?

my .init section dump is always be like this(no matter compiled with Wl, -init or using constructor attribute):

Disassembly of section .init:

00000000000005d0 <_init>: 
 5d0:        48 83 ec 08                  sub    $0x8,%rsp 
 5d4:        48 8b 05 05 0a 20 00         mov    0x200a05(%rip),%rax        # 200fe0 <_DYNAMIC+0x1c8> 
 5db:        48 85 c0                     test   %rax,%rax 
 5de:        74 05                        je     5e5 <_init+0x15> 
 5e0:        e8 2b 00 00 00               callq  610 <__gmon_start__@plt> 
 5e5:        48 83 c4 08                  add    $0x8,%rsp 
 5e9:        c3                           retq 

If I both used the attribute in the C file and compiled with Wl,init. I do see hello_init called twice before main. However, how I can observed them in ELF files, where didn't GCC put those function pointers?

1

1 Answers

1
votes

Ok guys, After debug a bit the asm of the loader, I think I got the answer for it. Apparently I messed up the concept of .init and DT_INIT. when you using Wl,init=, It won't really put the code into .init section(at least for my testing env on Ubuntu or CentOS). GCC will just put it into the DT_INIT tag in the . dynamic section of the .so file. And it will be called by call_init function when loading. https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=elf/dl-init.c Similar to attribute 'constructor', it will create a tag DT_INIT_ARRAY instead of .init_array section in the ELF file. All function pointers in DT_INIT_ARRAY will be called later than DT_INIT. To show those informations, you can't just objdump -s or -x to show the sections, since there is nothing. you can use readelf -d <.so file>: then you will see something like:

test@ubuntu16:~/code/small$ readelf -d libender.so

Dynamic section at offset 0xe18 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x6f0
 0x000000000000000d (FINI)               0x71e
 0x0000000000000019 (INIT_ARRAY)         0x200df8
 0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x200e08
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x1f0
 0x0000000000000005 (STRTAB)             0x3b8
 0x0000000000000006 (SYMTAB)             0x238
 0x000000000000000a (STRSZ)              184 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x201000
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x588
 0x0000000000000007 (RELA)               0x4b0
 0x0000000000000008 (RELASZ)             216 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x490
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x470
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0

So you can see the INIT tag is pointed to hello_init address not the .init section anymore. Also INIT_ARRAY size is bigger because attribute constructor is used.