You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2026/10/22 10:39:34 UTC

(nuttx) branch master updated (77521cf4ad1 -> 8b6495a68bc)

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


    from 77521cf4ad1 arch/arm/src/stm32h7: remove non-existent TIM10 and TIM11 timers
     new 840791e9277 libs/libbuiltin/gcov: Forget the coverage data of an unloaded module.
     new 344540bb619 libs/libc: Export the gcov functions to loadable modules.
     new 8b6495a68bc Documentation: Describe the coverage of loadable modules.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Documentation/os/libs/libbuiltin.rst |  9 ++++++++
 binfmt/elf.c                         |  2 ++
 include/gcov.h                       | 41 +++++++++++++++++++++++++++++++++++
 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 +++++++--
 libs/libc/libc.csv                   |  6 ++++++
 8 files changed, 108 insertions(+), 8 deletions(-)


(nuttx) 03/03: Documentation: Describe the coverage of loadable modules.

Posted by ac...@apache.org.
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 8b6495a68bcf1457689c15bd118e75caf3fc6a39
Author: Marco Casaroli <ma...@gmail.com>
AuthorDate: Tue Oct 20 00:03:29 2026 +0200

    Documentation: Describe the coverage of loadable modules.
    
    A loadable module of a flat or protected build can use the coverage of
    CONFIG_COVERAGE_ALL with CONFIG_COVERAGE_MINI and GCC, and its data
    goes away when the module is unloaded.  Say so, say how to keep the
    data, and say that the other runtimes do not export their functions.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <ma...@gmail.com>
---
 Documentation/os/libs/libbuiltin.rst | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/os/libs/libbuiltin.rst b/Documentation/os/libs/libbuiltin.rst
index 256e2dfdc51..0b46e574eba 100644
--- a/Documentation/os/libs/libbuiltin.rst
+++ b/Documentation/os/libs/libbuiltin.rst
@@ -44,6 +44,15 @@ to the one module you care about.  Data is written under
 ``CONFIG_COVERAGE_GCOV_DUMP_REBOOT`` dumps it on reboot.  From a shell it is
 ``gcov dump -d <path>``.
 
+A loadable module of a flat or protected build (an ELF program or a shared
+library) can use the coverage of ``CONFIG_COVERAGE_ALL`` with
+``CONFIG_COVERAGE_MINI`` and GCC.  The system symbol table exports the
+coverage functions to the module, and the loader removes the data of the
+module when it unloads the module.  The unload does not dump this data: dump
+it while the module is loaded, with ``gcov -d <path>`` or with a call to
+``__gcov_dump()`` in the module.  The other runtimes do not export their
+coverage functions, so build a module for them without the coverage options.
+
 Profiling
 =========
 


(nuttx) 02/03: libs/libc: Export the gcov functions to loadable modules.

Posted by ac...@apache.org.
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 344540bb619a2c98b8e9861a93ea2acdc646ff0b
Author: Marco Casaroli <ma...@gmail.com>
AuthorDate: Tue Oct 20 00:00:17 2026 +0200

    libs/libc: Export the gcov functions to loadable modules.
    
    Code that is built with the GCC coverage options calls __gcov_init()
    from a constructor and __gcov_exit() from a destructor, refers to
    __gcov_merge_add(), and calls __gcov_fork() in place of fork().  With
    CONFIG_COVERAGE_MINI the image has these functions, but the system
    symbol table does not export them, so an instrumented module does not
    load: "Exported symbol "__gcov_init" not found".
    
    Declare them in gcov.h and add them to libc.csv, together with
    __gcov_dump() and __gcov_reset(), so that a module can also dump or
    reset the coverage data.  The other coverage runtimes do not change:
    libgcov of CONFIG_COVERAGE_TOOLCHAIN keeps its functions hidden, and
    the clang runtimes need other functions.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <ma...@gmail.com>
---
 include/gcov.h     | 23 +++++++++++++++++++++++
 libs/libc/libc.csv |  6 ++++++
 2 files changed, 29 insertions(+)

diff --git a/include/gcov.h b/include/gcov.h
index 46c9ed34400..e225acc5d41 100644
--- a/include/gcov.h
+++ b/include/gcov.h
@@ -29,8 +29,15 @@
 
 #include <nuttx/config.h>
 
+#include <stdint.h>
 #include <sys/types.h>
 
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct gcov_info;
+
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/
@@ -65,6 +72,22 @@ extern void __gcov_dump(void);
 
 #if defined(CONFIG_COVERAGE_MINI) && defined(CONFIG_ARCH_TOOLCHAIN_GCC)
 
+/****************************************************************************
+ * Name: __gcov_init, __gcov_merge_add, __gcov_exit, __gcov_fork
+ *
+ * Description:
+ *   The functions that instrumented code calls.  The symbol table of the
+ *   loadable modules takes them from here.
+ *
+ ****************************************************************************/
+
+void __gcov_init(FAR struct gcov_info *info);
+void __gcov_merge_add(FAR uint64_t *counters, unsigned int n_counters);
+void __gcov_exit(void);
+#ifdef CONFIG_ARCH_HAVE_FORK
+pid_t __gcov_fork(void);
+#endif
+
 /****************************************************************************
  * Name: __gcov_unload
  *
diff --git a/libs/libc/libc.csv b/libs/libc/libc.csv
index 451835fdbba..17080b6a087 100644
--- a/libs/libc/libc.csv
+++ b/libs/libc/libc.csv
@@ -1,6 +1,12 @@
 "__assert","assert.h","","void","FAR const char *","int","FAR const char *"
 "__cxa_atexit","stdlib.h","","int","FAR CODE void (*)(FAR void *)|FAR void *","FAR void *","FAR void *"
 "__errno","errno.h","defined(CONFIG_BUILD_FLAT)","FAR int *"
+"__gcov_dump","gcov.h","defined(CONFIG_COVERAGE_MINI) && defined(CONFIG_ARCH_TOOLCHAIN_GCC)","void"
+"__gcov_exit","gcov.h","defined(CONFIG_COVERAGE_MINI) && defined(CONFIG_ARCH_TOOLCHAIN_GCC)","void"
+"__gcov_fork","gcov.h","defined(CONFIG_COVERAGE_MINI) && defined(CONFIG_ARCH_TOOLCHAIN_GCC) && defined(CONFIG_ARCH_HAVE_FORK)","pid_t"
+"__gcov_init","gcov.h","defined(CONFIG_COVERAGE_MINI) && defined(CONFIG_ARCH_TOOLCHAIN_GCC)","void","FAR struct gcov_info *"
+"__gcov_merge_add","gcov.h","defined(CONFIG_COVERAGE_MINI) && defined(CONFIG_ARCH_TOOLCHAIN_GCC)","void","FAR uint64_t *","unsigned int"
+"__gcov_reset","gcov.h","defined(CONFIG_COVERAGE_MINI) && defined(CONFIG_ARCH_TOOLCHAIN_GCC)","void"
 "__stack_chk_fail","ssp/ssp.h","defined(CONFIG_STACK_CANARIES)","void","void"
 "_alert","debug.h","!defined(CONFIG_CPP_HAVE_VARARGS) && defined(CONFIG_DEBUG_ERROR)","void","FAR const char *","..."
 "_err","debug.h","!defined(CONFIG_CPP_HAVE_VARARGS) && defined(CONFIG_DEBUG_ERROR)","void","FAR const char *","..."


(nuttx) 01/03: libs/libbuiltin/gcov: Forget the coverage data of an unloaded module.

Posted by ac...@apache.org.
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 <ma...@gmail.com>
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 <ma...@gmail.com>
---
 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