Calling Global Constructors: Difference between revisions

no edit summary
[unchecked revision][unchecked revision]
m (Changed spelling to improvr readability)
No edit summary
Line 204:
}
</pre>
 
=== CTOR/DTOR ===
 
Another way to execute the global constructors / destructors is to execute the .ctors / .dtors symbols manually (assuming you have your own ELF loader, see [[ELF_Tutorial]]). Once you have loaded each ELF file into memory, and all of the symbols have been resolved and relocated you can use .ctors / .dtors to execute the global constructors / destructors manually (apparently, the same applies to .init_array and .fini_array). To do this, you must first locate the .ctors / .dtors section headers:
 
<source lang="c">
for (i = 0; i < ef->ehdr->e_shnum; i++)
{
char name[250];
struct elf_shdr *shdr;
 
ret = elf_section_header(ef, i, &shdr);
if (ret != ELF_SUCCESS)
return ret;
 
ret = elf_section_name_string(ef, shdr, &name);
if (ret != BFELF_SUCCESS)
return ret;
 
if (strcmp(name, ".ctors") == 0)
{
ef->ctors = shdr;
continue;
}
 
if (strcmp(name, ".dtors") == 0)
{
ef->dtors = shdr;
continue;
}
}
</source>
 
Now that you have the .ctors / .dtors section headers, you can resolve each constructor using the following. Note that .ctors / .dtors is a table of pointers (32bit for ELF32 and 64bit for ELF64). Each pointer is a function that must be executed.
 
<source lang="c">
typedef void(*ctor_func)(void);
 
for(i = 0; i < ef->ctors->sh_size / sizeof(void *); i++)
{
ctor_func func;
elf64_addr sym = 0;
 
sym = ((elf64_addr *)(ef->file + ef->ctors->sh_offset))[i];
func = ef->exec + sym;
func();
 
/* elf->file is the char * that stores the ELF file that your working with. Could be binary or shared library */
/* elf->exec is the char * that stores the location in memory that the ELF file have been loaded to, and and reloacted */
}
</source>
 
Don't be surprised if you only have one entry in .ctors / .dtors. At least on x86_64, GCC appears to add a single entry to a set of functions called _GLOBAL__sub_I_XXX and _GLOBAL__sub_D_XXX which call _Z41__static_initialization_and_destruction_0ii that actually call each constructor for you. Adding more globally defined constructors / destructors will cause this function to grow, and not .ctors / .dtors.
 
=== Stability Issues ===
 
If you don't call the constructors / destructors that GCC provides, GCC will generate code that will segfault under certain conditions with x86_64. Take this for example:
 
<source lang="cpp">
class A
{
public:
A() {}
};
 
A g_a;
 
void foo(void)
{
A *p_a = &g_a;
p_a->anything(); // <---- segfault
}
</source>
 
It appears that GCC is using the constructor / destructor initialization routines to do more than simply call the constructors / destructors of each globally defined class. Executing the functions defined in .ctors / .dtors not only initializes all of the constructors / destructors, but resolves these types of segfaults (the above is only one example of many that are resolved). From what I can tell, when globally defined objects exist, GCC might also create ".data.rel.ro" which is another relocation table that GCC needs to process. It is marked as PROGBITS and not REL/RELA, which means that the ELF loader will not do the relocations for you. Instead, executing the functions defined in .ctors will execute _Z41__static_initialization_and_destruction_0ii which appears to perform the relocations for us. See the following for more info: [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68738]
 
== Clang ==
Anonymous user