Here's a program for doing experiments:
/* on_expt_scope_expt.c
(C) Michael Kerrisk, 2019, Licensed GNU GPLv2+
*/
char *tos;
static void
exitFunc(int status, void *p)
{
int efloc;
int *xp = (int *) p;
printf("====== Entered exit handler\n");
printf("&efloc = %p (0x%llx)\n",
(void *) &efloc, (long long) (tos - (char *) &efloc));
printf("xp = %p (value: %d)\n", (void *) xp, *xp);
if (*xp != INIT_VALUE)
printf("It looks like the variable passed to the exit handler "
"has gone out of scope\n");
/* Produce a core dump, which we can examine with GDB to look at the
frames on the stack, if desired */
printf("===\n");
printf("About to abort\n");
abort();
}
static void
recur(int lev, int *xp)
{
int rloc;
int big[65536-12]; /* 12*4 == 48 other bytes allocated on
this stack frame */
tos = (char *) &rloc;
big[0] = lev;
big[0]++;
printf("&rloc = %p (%d) (%d)\n", (void *) &rloc, lev, *xp);
if (lev > 1)
recur(lev - 1, xp);
else {
printf("exit() from recur()\n");
exit(EXIT_SUCCESS);
}
}
int
main(int argc, char *argv[])
{
int lev;
int *xp;
int xx;
if (argc < 2) {
fprintf(stderr, "Usage: %s {s|h} [how]\n", argv[0]);
fprintf(stderr, "\ts => exitFunc() arg is in main() stack\n");
fprintf(stderr, "\th => exitFunc() arg is allocated on heapn");
fprintf(stderr, "\tIf 'how' is not present, then return from main()\n");
fprintf(stderr, "\tIf 'how' is 0, then exit() from main()\n");
fprintf(stderr, "\tIf 'how' is > 0, then make 'how' recursive "
"function calls, and then exit()\n");
exit(EXIT_FAILURE);
}
tos = (char *) &xp;
if (argv[1][0] == 'h') {
xp = malloc(sizeof(int));
if (xp == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
printf("Argument for exitFunc() is allocated on heap\n");
} else {
xp = &xx;
printf("Argument for exitFunc() is allocated on stack in main()\n");
}
*xp = INIT_VALUE;
printf("xp = %p (value: %d)\n", (void *) xp, *xp);
printf("===\n");
on_exit(exitFunc, xp);
if (argc == 2) {
printf("return from main\n");
return 0;
}
lev = atoi(argv[2]);
if (lev < 1) {
printf("Calling exit() from main\n");
exit(EXIT_SUCCESS);
} else {
recur(lev, xp);
}
}
Reported-by: Sami Kerola <kerolasa@iki.fi>
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
Portable application should avoid this function, and use the standard
.BR atexit (3)
instead.
+.SH NOTES
+By the time
+.I function
+is executed, stack
+.RI ( auto )
+variables may already have gone out of scope.
+Therefore,
+.I arg
+should not be a pointer to a stack variable;
+it may however be a pointer to a heap variable or a global variable.
.SH SEE ALSO
.BR _exit (2),
.BR atexit (3),