gcc attribute: constructor & destructor
gcc attribute: constructor & destructor
Reqruies
- Compiler:
gcc 2.7.2or later
constructor, called when a class instance is created, and destructor, called when its lifecycle ends, already exist.constructor and destructor for creation and destruction like object-oriented languages.💡 Note: Even in C, you can achieve similar effects by indirectly hooking functions usingweaksymbols and theLD_PRELOADtechnique.
constructor attribute, called before the main function executes, and destructor attribute, called after the main function finishes, as extension features to supplement this.gcc-4.7.0/Function-AttributesDocconstructor destructor constructor (priority) destructor (priority)
The constructor attribute causes the function to be called automatically before execution enters main(). Similarly, the destructor attribute causes the function to be called automatically after main() has completed or exit() has been called.
Functions with these attributes are useful for initializing data that will be used implicitly during program execution.
You can optionally provide an integer
priorityto control the execution order of constructor and destructor functions.Constructors with smaller priority numbers are executed earlier, while destructors have the opposite relationship.
Therefore, if you have a constructor that allocates a resource and a destructor that releases the same resource, they typically share the same priority.
The priorities of constructor and destructor functions are the same as those specified for C++ objects.
usage.cc
Compile & Runbash
ctor_func defined using attribute((constructor)) is called first, and dtors_func defined using attribute((destructor)) is called after the main function.Internal Process of Constructor and Destructor
constructors were managed using the .ctors section of ELF, and destructors were managed using the .dtors section..init_array and .fini_array sections of ELF..init_array as the reference.First, check the start address of the sample code created earlier.
Check start addressbash
e_entry value in the ELF header.objdump to find the function at the start address confirms that it is the _start function.The starting position of ELFbash
_start function, it has a structure that internally stores the addresses of __libc_csu_init, __libc_csu_fini, and the main function at the register level, and then calls _libc_start_main.glibc before calling main(), the program's entry point.-v option when building with GCC.Check link processbash
sysroot directory are included to perform these startup routines.check libc-start.c in glibcc
zenuml title Call init and fini sequence @Actor Kernel // 1. Kernel triggers the entry point Kernel -> _start: execve() // 2. _start prepares arguments and calls libc _start -> "__libc_start_main".init() { // 3. call_init Phase (Constructors) "__libc_start_main" -> "call_init".run() { while("for each func in .init_array") { "call_init" -> Contructor: execute() } } // 4. Actual Program Execution "__libc_start_main" -> main.run() // 5. Cleanup Phase (Destructors) "__libc_start_main" -> exit.process() { exit -> "call_fini".run() { while("for each func in .fini_array (Reverse)") { "call_fini" -> Destructor: execute() } } // 6. Final Control Return to Kernel @return Kernel: _exit() } }
INIT_ARRAY / FINI_ARRAY and INIT_ARRAYSZ / FINI_ARRAYSZ.Check ELF dynamic sectionbash
Inside INIT_ARRAYbash
x/2g command confirms that the start address of the explicitly declared ctor_func is registered in the INIT_ARRAY.Inside FINI_ARRAYbash
Priorities
Supporting priorities: ctor_pri.cc
Testing Priority valuesbash
How are the same priorities handled?c
Testing Identical Prioritiesbash
Behavior on Abnormal Termination
main function?Does it work in signal handler too?c
Testing Signal Handlerbash
while (1) and interrupted by an external SIGINT signal, explicitly calling exit(0) inside the signal handler ensures that dtors_func() is called normally with the intervention of the OS.Leveraging in Shared Libraries
By simply loading an external library equipped with constructors and destructors into memory (e.g., using LD_PRELOAD), you can easily inject desired code logic before and after the main function entry at any time.
Testing Shared Library (hooklib.c)c
Testing LD_PRELOADbash
Testing Shared Library Link (hooklib.c)c
sample.cc
Testing Shared Library Executionbash