This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 840791e927746bddd77957675b6f1e742d7398df Author: Marco Casaroli <[email protected]> AuthorDate: Tue Oct 20 00:00:07 2026 +0200 libs/libbuiltin/gcov: Forget the coverage data of an unloaded module. With CONFIG_COVERAGE_MINI, __gcov_init() adds the gcov_info of each object file to a list, and __gcov_dump() and __gcov_reset() walk this list. A loadable module that is built with the coverage options adds its own gcov_info, which is in the memory of the module. After the module is unloaded, the list still points to this memory, and the next dump or reset reads freed memory. Add __gcov_unload(), which removes from the list each gcov_info in an address range. libelf_uninit() calls it for the memory of a module before it frees the memory. This is the path of dlclose(), rmmod() and the exit of a program that exec() loaded. struct module_s now always keeps the sizes of the text and the data, and binfmt saves them too. A mutex protects the list, because a module can load or unload while a dump runs. The unload does not dump the data of the module. The dump writes the data of all object files, and it can run in the context of a task that exits. To keep the data of a module, dump it while the module is loaded: with "gcov -d", or with a call to __gcov_dump() in the module. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- binfmt/elf.c | 2 ++ include/gcov.h | 18 ++++++++++++++++++ include/nuttx/lib/elf.h | 2 -- libs/libbuiltin/libgcc/gcov.c | 42 ++++++++++++++++++++++++++++++++++++++++++ libs/libc/elf/elf_insert.c | 4 ---- libs/libc/elf/elf_remove.c | 10 ++++++++-- 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/binfmt/elf.c b/binfmt/elf.c index ca24544a08b..e59a066e317 100644 --- a/binfmt/elf.c +++ b/binfmt/elf.c @@ -264,6 +264,8 @@ static int elf_loadbinary(FAR struct binary_s *binp, binp->mod.textalloc = (FAR void *)loadinfo.textalloc; binp->mod.dataalloc = (FAR void *)loadinfo.datastart; + binp->mod.textsize = loadinfo.textsize; + binp->mod.datasize = loadinfo.datasize; binp->mod.gotbase = loadinfo.fdpic ? loadinfo.gotbase : 0; # ifdef CONFIG_BINFMT_CONSTRUCTORS binp->mod.initarr = loadinfo.initarr; diff --git a/include/gcov.h b/include/gcov.h index 5dc38d07dda..46c9ed34400 100644 --- a/include/gcov.h +++ b/include/gcov.h @@ -27,6 +27,8 @@ * Included Files ****************************************************************************/ +#include <nuttx/config.h> + #include <sys/types.h> /**************************************************************************** @@ -61,6 +63,22 @@ extern void __gcov_reset(void); extern void __gcov_dump(void); +#if defined(CONFIG_COVERAGE_MINI) && defined(CONFIG_ARCH_TOOLCHAIN_GCC) + +/**************************************************************************** + * Name: __gcov_unload + * + * Description: + * Forget the coverage data in the memory from addr to addr + size. The + * loader calls it before it frees a module. + * + ****************************************************************************/ + +void __gcov_unload(FAR const void *addr, size_t size); +#else +# define __gcov_unload(addr, size) +#endif + #undef EXTERN #ifdef __cplusplus } diff --git a/include/nuttx/lib/elf.h b/include/nuttx/lib/elf.h index 63f78867a2a..c847e09f4f0 100644 --- a/include/nuttx/lib/elf.h +++ b/include/nuttx/lib/elf.h @@ -204,10 +204,8 @@ struct module_s * the module is unloaded */ #endif -#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) size_t textsize; /* Size of the kernel .text memory allocation */ size_t datasize; /* Size of the kernel .bss/.data memory allocation */ -#endif uint8_t nopen; /* Outstanding references: insmod() * and dlopen() each take one, rmmod() diff --git a/libs/libbuiltin/libgcc/gcov.c b/libs/libbuiltin/libgcc/gcov.c index 2ff033bd111..8e8c2b97e45 100644 --- a/libs/libbuiltin/libgcc/gcov.c +++ b/libs/libbuiltin/libgcc/gcov.c @@ -30,6 +30,7 @@ #include <sys/stat.h> #include <sys/types.h> +#include <nuttx/mutex.h> #include <nuttx/streams.h> #include <nuttx/lib/lib.h> #include <nuttx/reboot_notifier.h> @@ -156,6 +157,14 @@ typedef CODE int (*dump_fn_t)(FAR struct dump_args *, FAR struct gcov_info *__gcov_info_start; FAR struct gcov_info *__gcov_info_end; +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Protects the list: a module can add to it or leave it at any time */ + +static rmutex_t g_gcov_lock = NXRMUTEX_INITIALIZER; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -341,6 +350,7 @@ static int gcov_dump_foreach(dump_fn_t handler, FAR struct dump_args *args) int size; int i; + nxrmutex_lock(&g_gcov_lock); for (info = __gcov_info_start; info; info = info->next) { memset(args->path_buffer, 0x00, PATH_MAX); @@ -350,6 +360,7 @@ static int gcov_dump_foreach(dump_fn_t handler, FAR struct dump_args *args) if (data == NULL) { syslog(LOG_ERR, "gcov alloc failed!"); + nxrmutex_unlock(&g_gcov_lock); return -ERROR; } @@ -360,6 +371,7 @@ static int gcov_dump_foreach(dump_fn_t handler, FAR struct dump_args *args) { syslog(LOG_ERR, "gcov alloc failed! skip %s", info->filename); lib_free(data); + nxrmutex_unlock(&g_gcov_lock); return -ERROR; } @@ -397,12 +409,14 @@ static int gcov_dump_foreach(dump_fn_t handler, FAR struct dump_args *args) lib_free(data); } + nxrmutex_unlock(&g_gcov_lock); return OK; exit: lib_free(filename); lib_free(data); + nxrmutex_unlock(&g_gcov_lock); return -ERROR; } @@ -441,8 +455,10 @@ void __gcov_init(FAR struct gcov_info *info) inited++; } + nxrmutex_lock(&g_gcov_lock); info->next = __gcov_info_start; __gcov_info_start = info; + nxrmutex_unlock(&g_gcov_lock); } void __gcov_merge_add(FAR gcov_type *counters, unsigned int n_counters) @@ -558,6 +574,7 @@ void __gcov_reset(void) uint32_t functions; uint32_t counts; + nxrmutex_lock(&g_gcov_lock); for (info = __gcov_info_start; info; info = info->next) { for (functions = 0; functions < info->n_functions; functions++) @@ -576,4 +593,29 @@ void __gcov_reset(void) } } } + + nxrmutex_unlock(&g_gcov_lock); +} + +void __gcov_unload(FAR const void *addr, size_t size) +{ + FAR const char *start = addr; + FAR struct gcov_info **prev; + FAR const char *info; + + nxrmutex_lock(&g_gcov_lock); + for (prev = &__gcov_info_start; *prev != NULL; ) + { + info = (FAR const char *)*prev; + if (info >= start && info < start + size) + { + *prev = (*prev)->next; + } + else + { + prev = &(*prev)->next; + } + } + + nxrmutex_unlock(&g_gcov_lock); } diff --git a/libs/libc/elf/elf_insert.c b/libs/libc/elf/elf_insert.c index 0c5c0185e03..f6a54f9b4b4 100644 --- a/libs/libc/elf/elf_insert.c +++ b/libs/libc/elf/elf_insert.c @@ -121,10 +121,8 @@ void libelf_dumpmodule(FAR struct module_s *modp) binfo(" modname: %s\n", modp->modname); #endif binfo(" textalloc: %08lx\n", (long)modp->textalloc); -#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) binfo(" dataalloc: %08lx\n", (long)modp->dataalloc); binfo(" textsize: %ld\n", (long)modp->textsize); -#endif #ifdef CONFIG_ARCH_USE_SEPARATED_SECTION binfo(" sectalloc: %p\n", modp->sectalloc); @@ -418,10 +416,8 @@ FAR void *libelf_insert(FAR struct list_node *registry, modp->nsect = loadinfo.ehdr.e_shnum; #endif -#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) modp->textsize = loadinfo.textsize; modp->datasize = loadinfo.datasize; -#endif /* Call the module initializer. An FDPIC object's constructors reach its * globals through its own data base, which the loading thread does not diff --git a/libs/libc/elf/elf_remove.c b/libs/libc/elf/elf_remove.c index 5623096182d..d9be2e288f4 100644 --- a/libs/libc/elf/elf_remove.c +++ b/libs/libc/elf/elf_remove.c @@ -26,6 +26,7 @@ #include <assert.h> #include <nuttx/debug.h> #include <errno.h> +#include <gcov.h> #include <nuttx/arch.h> #include <nuttx/fdpic.h> @@ -115,6 +116,11 @@ int libelf_uninit(FAR struct module_s *modp) if (modp->textalloc != NULL || modp->dataalloc != NULL) { + /* The coverage data of the module must not outlive it */ + + __gcov_unload(modp->textalloc, modp->textsize); + __gcov_unload(modp->dataalloc, modp->datasize); + /* Free the module memory and nullify so that the memory cannot * be freed again * @@ -143,6 +149,8 @@ int libelf_uninit(FAR struct module_s *modp) } # endif + __gcov_unload(modp->sectalloc[i], + lib_malloc_size(modp->sectalloc[i])); lib_free(modp->sectalloc[i]); } @@ -195,10 +203,8 @@ int libelf_uninit(FAR struct module_s *modp) modp->textalloc = NULL; modp->dataalloc = NULL; -#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) modp->textsize = 0; modp->datasize = 0; -#endif } #if CONFIG_LIBC_ELF_MAXDEPEND > 0
