casaroli opened a new pull request, #21368: URL: https://github.com/apache/nuttx/pull/21368
## Summary `atexit()`, `on_exit()`, `at_quick_exit()` and `__cxa_atexit()` share one table of `CONFIG_LIBC_MAX_EXITFUNS` entries in the task information. When the table was full, the registration failed, so a C++ program with more static objects than entries lost their destructors without notice. With `CONFIG_LIBC_MAX_EXITFUNS=0` (the default), `atexit()` returned 0 and never called the function. POSIX requires at least 32 functions. - **libs/libc/stdlib: Grow the exit function list on the heap.** The first `CONFIG_LIBC_MAX_EXITFUNS` entries stay in the task information (they need no heap). More entries come from the heap, 8 at a time. The number of exit functions is limited only by memory: `ATEXIT_MAX` (in `<nuttx/atexit.h>`) and `sysconf(_SC_ATEXIT_MAX)` are `INT_MAX`. A registration fails only when the heap is full, and then returns non-zero. The heap part is freed after the exit functions ran, or in `task_uninit_info()` when the process ends without `exit()`. The functions still run newest first, also the ones that an exit function registers, and an `exit()` from an exit function runs the remaining ones once. crt0 now always registers the C++ destructors with `atexit()`: with `CONFIG_LIBC_MAX_EXITFUNS=0` it called them when `main()` returned, before the exit functions that `main()` registered. - **Documentation/reference/user: Describe the exit function list.** The page named options that no longer exist (`CONFIG_SCHED_ATEXIT`, `CONFIG_SCHED_ATEXIT_MAX`, `CONFIG_SCHED_ONEXIT`) and said that only one function can be registered. ## Impact - With the default `CONFIG_LIBC_MAX_EXITFUNS=0`, `atexit()`, `on_exit()`, `at_quick_exit()` and `__cxa_atexit()` now register the function and it runs at `exit()` (before: the call returned 0 and the function never ran). The first registration takes 8 entries from the heap (96 bytes on 32-bit targets, 192 bytes on 64-bit targets). - `CONFIG_LIBC_MAX_EXITFUNS` stays, with the same default and the same name in the defconfigs. It is now the number of entries that need no heap, not a limit. - `ATEXIT_MAX` and `sysconf(_SC_ATEXIT_MAX)` are `INT_MAX` (before: `CONFIG_LIBC_MAX_EXITFUNS`). - `struct atexit_list_s` in `struct task_info_s` gets a count and a pointer for the heap part, and is always present: +8 bytes per process, or +12 bytes (32-bit) and +16 bytes (64-bit) with `CONFIG_LIBC_MAX_EXITFUNS=0`, where the list was not present. `struct task_info_s` is internal (`<nuttx/tls.h>`): the public API does not change. In protected and kernel builds the kernel and the user libc of the same build use the same layout. - Size (text/data/bss, `CONFIG_LIBC_MAX_EXITFUNS=0`): lm3s6965-ek:qemu-protected kernel +16/0/0, user +360/0/0; lm3s6965-ek:nsh (flat) +208/0/+8; mps2-an500:knsh (protected) kernel +24/0/0, user +632/0/0. All build modes. A process that registers no function uses no heap for the list. With `CONFIG_HAVE_CXXINITIALIZE`, crt0 registers one function in each program. ## Testing Test program `atexitmany.c`: each case runs in a new process (a new task in the flat and protected builds). The first function that a case registers checks what ran. | Case | master | this PR | |---|---|---| | `many`: 100 `atexit()` + 100 `__cxa_atexit()`, all run, newest first | knsh: 184 registrations fail, 15 of 199 run, 8 out of order; flat and protected: all return 0, none runs | PASS | | `onexit`: 40 `on_exit()`, each gets the status of `exit()` and its argument | FAIL | PASS | | `nested`: a function registers another one during `exit()`; it runs next | FAIL | PASS | | `reexit`: a function calls `exit(5)`; the rest run once, with status 5 | FAIL | PASS | | `quick`: `quick_exit()` runs the 40 `at_quick_exit()` functions, no `atexit()` function | FAIL | PASS | | `fork` (kernel build): 40 functions, then `fork()`; the child runs them once at `exit()`, the parent too | FAIL | PASS | | `sysconf(_SC_ATEXIT_MAX)` | knsh 16, flat and protected 0 | 2147483647 | Runs on master 1b0691e870d and on this PR (rebased on it): - qemu-armv8a:knsh (kernel, arm64, QEMU with HVF, `CONFIG_LIBC_MAX_EXITFUNS=16`): master 0 of 7, this PR 7 of 7. Also with `CONFIG_LIBC_MAX_EXITFUNS=0` and `CONFIG_HAVE_CXXINITIALIZE=y` (crt0 registers the C++ destructors in each program): 7 of 7. - qemu-armv8a:nsh (flat, arm64, `CONFIG_LIBC_MAX_EXITFUNS=0`): master 0 of 6, this PR 6 of 6. - mps2-an500:knsh (protected, armv7-m, QEMU TCG, `CONFIG_LIBC_MAX_EXITFUNS=0`, `CONFIG_HAVE_CXXINITIALIZE=y`): master 0 of 6, this PR 6 of 6. - ostest exits with status 0 in each of these runs (also on qemu-armv8a:nsh after the knsh runs). Builds with `EXTRAFLAGS="-Wno-cpp -Werror"`, each OK (crt0 built in each but sim, with `CONFIG_HAVE_CXXINITIALIZE=y` where marked C++ init): qemu-armv7a:knsh (kernel, armv7-a, C++ init; `CONFIG_LIBC_MAX_EXITFUNS` 0 and 4), mps2-an500:knsh (protected, armv7-m, C++ init), lm3s6965-ek:qemu-protected (protected, armv7-m; 0 and 16), lm3s6965-ek:nsh (flat, armv7-m), qemu-armv8a:knsh (kernel, arm64, C++ init; 16 and 0), qemu-armv8a:nsh (flat, arm64), rv-virt:knsh64 (kernel, rv64, C++ init; 0 and 8), rv-virt:pnsh64 (protected, rv64), rv-virt:nsh64 (flat, rv64), qemu-intel64:knsh_romfs (kernel, x86_64, C++ init), esp32s3-devkit:kernel_oct (kernel, xtensa, C++ init), esp32s3-devkit:knsh (protected, xtensa, C++ init), sim:nsh (flat, macOS host). qemu-intel64 needs `-Wno-error=shadow` with x86_64-elf-gcc 16.2: `libm/lib_gamma.c` declares `sinpi()`, which shadows a built-in of that compiler. This PR does not change that file. No nuttx-apps test covers more than a few exit functions: testing/ltp skips `pthread_exit` 4-1 and 5-1 when `CONFIG_LIBC_MAX_EXITFUNS` is below 32. A nuttx-apps PR after this one removes that condition and the `CONFIG_LIBC_MAX_EXITFUNS == 0` warning of graphics/nxwm. `tools/checkpatch.sh -c -u -m -g` passes. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
