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]

Reply via email to