Search code examples
cfunctionatexit

Why can't I pass a dynamic function pointer to atexit()?


I can pass regular function pointer to atexit():

void f1(void)
{
      printf("f1\n");
}

int main() {
  void (*fun_ptr)(void) = &f1;
  atexit(fun_ptr);
}

But when I pass function pointer returned from another function:

typedef void (*f22_t) (void);

f22_t f2(void) {
  void f22(void) {
  printf("f22\n");
  }

  return f22;
}


int main() {
  void (*fun_ptr2)(void) = f2();
  atexit(fun_ptr2);
}

It results in:

Illegal instruction (core dumped)

Why does the first option wors and second one does not?

Also, I might be misreading the documentation on atexit() https://man7.org/linux/man-pages/man3/atexit.3.html or https://pubs.opengroup.org/onlinepubs/007904875/functions/atexit.html but it does not look to me anything there mentions the information why the second scenario would not work.


Solution

  • Defining a function inside another function is a GCC extension. Per the GCC documentation, the lifetime of the nested function ends when execution of the containing function ends:

    … If you try to call the nested function through its address after the containing function exits, all hell breaks loose…

    Thus, after f2 exits, the pointer to f22 it provides is not supported for any further use, and passing it toatexit to be called later results in all hell breaking loose.

    (In practice, GCC implements nested functions by creating some executable code on the program stack, even if the bulk of the function code is in the “text” area as usual. The pointer to the nested function points to this code on the stack. After the containing function exits, its stack space is typically reused for other purposes, destroying the code that was created on the stack. So attempting to execute that code fails because the bytes have been changed.)