Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

executing init and fini

Tags:

I just read about init and fini sections in ELF files and gave it a try:

#include <stdio.h> int main(){   puts("main");   return 0; }  void init(){   puts("init"); } void fini(){   puts("fini"); } 

If I do gcc -Wl,-init,init -Wl,-fini,fini foo.c and run the result the "init" part is not printed:

$ ./a.out main fini 

Did the init part not run, or was it not able to print somehow?

Is there a any "official" documentation about the init/fini stuff?

man ld says:

 -init=name      When creating an ELF executable or shared object, call      NAME when the executable or shared object is loaded, by      setting DT_INIT to the address of the function.  By      default, the linker uses "_init" as the function to call. 

Shouldn't that mean, that it would be enough to name the init function _init? (If I do gcc complains about multiple definition.)

like image 716
michas Avatar asked Sep 21 '15 16:09

michas


People also ask

What is init function in CPP?

The INIT function initializes the data structures required by the rest of the computation of the aggregate. For example, if you write a C function, the INIT function can set up large objects or temporary files for storing intermediate results.

What is Init_array?

.init_array. This section holds an array of function pointers that contributes to a single initialization array for the executable or shared object containing the section.

What is init section?

The . init sections contain codes that are to be executed before the the main program is executed.

What is fini_ array?

.fini_array. This section holds an array of function pointers that contributes to a single termination array for the executable or shared object containing the section. .hash. This section holds a symbol hash table.


1 Answers

Don't do that; let your compiler and linker fill in the sections as they see fit.

Instead, mark your functions with the appropriate function attributes, so that the compiler and linker will put them in the correct sections.

For example,

static void before_main(void) __attribute__((constructor)); static void after_main(void) __attribute__((destructor));  static void before_main(void) {     /* This is run before main() */ }  static void after_main(void) {     /* This is run after main() returns (or exit() is called) */ } 

You can also assign a priority (say, __attribute__((constructor (300)))), an integer between 101 and 65535, inclusive, with functions having a smaller priority number run first.

Note that for illustration, I marked the functions static. That is, the functions won't be visible outside the file scope. The functions do not need to be exported symbols to be automatically called.


For testing, I suggest saving the following in a separate file, say tructor.c:

#include <unistd.h> #include <string.h> #include <errno.h>  static int outfd = -1;  static void wrout(const char *const string) {     if (string && *string && outfd != -1) {         const char       *p = string;         const char *const q = string + strlen(string);          while (p < q) {             ssize_t n = write(outfd, p, (size_t)(q - p));             if (n > (ssize_t)0)                 p += n;             else             if (n != (ssize_t)-1 || errno != EINTR)                 break;         }     } }  void before_main(void) __attribute__((constructor (101))); void before_main(void) {     int saved_errno = errno;      /* This is run before main() */     outfd = dup(STDERR_FILENO);     wrout("Before main()\n");      errno = saved_errno; }  static void after_main(void) __attribute__((destructor (65535))); static void after_main(void) {     int saved_errno = errno;      /* This is run after main() returns (or exit() is called) */     wrout("After main()\n");      errno = saved_errno; } 

so you can compile and link it as part of any program or library. To compile it as a shared library, use e.g.

gcc -Wall -Wextra -fPIC -shared tructor.c -Wl,-soname,libtructor.so -o libtructor.so 

and you can interpose it into any dynamically linked command or binary using

LD_PRELOAD=./libtructor.so some-command-or-binary 

The functions keep errno unchanged, although it should not matter in practice, and use the low-level write() syscall to output the messages to standard error. The initial standard error is duplicated to a new descriptor, because in many instances, the standard error itself gets closed before the last global destructor -- our destructor here -- gets run.

(Some paranoid binaries, typically security sensitive ones, close all descriptors they don't know about, so you might not see the After main() message in all cases.)

like image 123
Nominal Animal Avatar answered Sep 24 '22 17:09

Nominal Animal