[binutils-gdb] gas: only free on exit when --enable-leak-check

Alan Modra via Binutils-cvs <[email protected]>
Newsgroups gmane.comp.gnu.binutils.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=eced22e46896200bd76d6ec95eb35a7a1396eb01

commit eced22e46896200bd76d6ec95eb35a7a1396eb01
Author: Alan Modra <[email protected]>
Date:   Thu Mar 5 09:15:04 2026 +1030

    gas: only free on exit when --enable-leak-check
    
    Adds a new --enable-leak-check option, controlling whether memory is
    freed before exit in order to find memory leaks.  The default is to
    free memory if BFD_ASAN is non-zero.
    
            * configure.ac: Add new --enable-leak-check..
            (ENABLE_LEAK_CHECK): ..defining this.
            * as.c (gas_early_init): free_notes on exit only if ENABLE_LEAK_CHECK.
            * expr.c (expr_end): Nothing to do when !ENABLE_LEAK_CHECK.
            * macro.c (macro_end): Likewise.
            * output-file.c (output_file_close): Don't stash frchain obstacks
            when !ENABLE_LEAK_CHECK.
            * read.c (read_end): Nothing to do when !ENABLE_LEAK_CHECK.
            (poend): Likewise.
            * stabs.c (stabs_end): Likewise.
            * subsegs.c (subsegs_end): Likewise.
            * symbols.c (symbol_end): Likewise.
            * config/obj-elf-attr.c (oav1_attr_info_exit): Likewise.
            * config/obj-elf.c (elf_end): Likewise.
            * config/tc-arc.c (arc_md_end): Likewise.
            * config/tc-i386.c (i386_md_end): Likewise.
            * config/tc-loongarch.c (loongarch_md_end): Likewise.
            * config/tc-ppc.c (ppc_md_end): Likewise.
            * config/tc-pru.c (pru_md_end): Likewise.
            * config/tc-riscv.c (riscv_md_end): Likewise.
            * config/tc-tic54x.c (tic54x_md_end): Likewise.
            * configure: Regenerate.
            * config.in: Regenerate.

Diff:
---
 gas/as.c                  |  3 ++-
 gas/config.in             |  3 +++
 gas/config/obj-elf-attr.c |  3 ++-
 gas/config/obj-elf.c      |  2 ++
 gas/config/tc-arc.c       |  2 ++
 gas/config/tc-i386.c      |  2 ++
 gas/config/tc-loongarch.c |  3 ++-
 gas/config/tc-ppc.c       |  2 ++
 gas/config/tc-pru.c       |  2 ++
 gas/config/tc-riscv.c     |  3 ++-
 gas/config/tc-tic54x.c    |  2 ++
 gas/configure             | 20 ++++++++++++++++++--
 gas/configure.ac          |  9 +++++++++
 gas/expr.c                |  5 +++--
 gas/macro.c               |  3 ++-
 gas/output-file.c         | 29 +++++++++++++++++------------
 gas/read.c                |  5 ++++-
 gas/stabs.c               |  2 ++
 gas/subsegs.c             |  2 ++
 gas/symbols.c             |  3 ++-
 20 files changed, 82 insertions(+), 23 deletions(-)

diff --git a/gas/as.c b/gas/as.c
index f08c7c71d73..f33f6ec85cd 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -1330,7 +1330,8 @@ gas_early_init (int *argcp, char ***argvp)
     as_fatal (_("libbfd ABI mismatch"));
 
   obstack_begin (&notes, chunksize);
-  xatexit (free_notes);
+  if (ENABLE_LEAK_CHECK)
+    xatexit (free_notes);
 
   myname = **argvp;
   xmalloc_set_program_name (myname);
diff --git a/gas/config.in b/gas/config.in
index 1bfbee9a348..ae5f41f7058 100644
--- a/gas/config.in
+++ b/gas/config.in
@@ -73,6 +73,9 @@
 /* Define if you want run-time sanity checks. */
 #undef ENABLE_CHECKING
 
+/* Define if you want memory to be freed before exit. */
+#undef ENABLE_LEAK_CHECK
+
 /* Define to 1 if translation of program messages to the user's native
    language is requested. */
 #undef ENABLE_NLS
diff --git a/gas/config/obj-elf-attr.c b/gas/config/obj-elf-attr.c
index a731c1f1300..02e474a2c2c 100644
--- a/gas/config/obj-elf-attr.c
+++ b/gas/config/obj-elf-attr.c
@@ -129,7 +129,8 @@ oav1_attr_info_init (void)
 void
 oav1_attr_info_exit (void)
 {
-  oav1_attr_info_free (recorded_attributes);
+  if (ENABLE_LEAK_CHECK)
+    oav1_attr_info_free (recorded_attributes);
 }
 
 /* Record that we have seen an explicit specification of attribute TAG
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index e09be292cd1..5c44aea0914 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -3005,6 +3005,8 @@ elf_begin (void)
 void
 elf_end (void)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   while (section_stack)
     {
       struct section_stack *top = section_stack;
diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c
index d1a5f7e40c6..4e08b563298 100644
--- a/gas/config/tc-arc.c
+++ b/gas/config/tc-arc.c
@@ -2722,6 +2722,8 @@ md_begin (void)
 void
 arc_md_end (void)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   htab_delete (arc_opcode_hash);
   htab_delete (arc_reg_hash);
   htab_delete (arc_aux_hash);
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index ca4523fe2cd..edc44a37345 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -3773,6 +3773,8 @@ i386_print_statistics (FILE *file)
 void
 i386_md_end (void)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   htab_delete (op_hash);
   htab_delete (reg_hash);
   GOT_symbol = NULL;
diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c
index cce8725485a..716b2f15eea 100644
--- a/gas/config/tc-loongarch.c
+++ b/gas/config/tc-loongarch.c
@@ -529,7 +529,8 @@ md_begin ()
 void
 loongarch_md_end (void)
 {
-  htab_delete (align_hash);
+  if (ENABLE_LEAK_CHECK)
+    htab_delete (align_hash);
 }
 
 unsigned long
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index a5dbc73386a..78e5941484c 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -1892,6 +1892,8 @@ md_begin (void)
 void
 ppc_md_end (void)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   if (ppc_hash)
     {
       htab_delete (ppc_hash);
diff --git a/gas/config/tc-pru.c b/gas/config/tc-pru.c
index e55ce4790ba..ee86511fafc 100644
--- a/gas/config/tc-pru.c
+++ b/gas/config/tc-pru.c
@@ -1796,6 +1796,8 @@ md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED)
 void
 pru_md_end (void)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   htab_delete (pru_opcode_hash);
   htab_delete (pru_reg_hash);
 }
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 6bcf53832a0..ef7c7e3a9a6 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -5782,7 +5782,8 @@ riscv_md_finish (void)
 void
 riscv_md_end (void)
 {
-  htab_delete (riscv_pcrel_hi_fixup_hash);
+  if (ENABLE_LEAK_CHECK)
+    htab_delete (riscv_pcrel_hi_fixup_hash);
 }
 
 /* Adjust the symbol table.  */
diff --git a/gas/config/tc-tic54x.c b/gas/config/tc-tic54x.c
index c6af9bc1803..7b3b0738a1f 100644
--- a/gas/config/tc-tic54x.c
+++ b/gas/config/tc-tic54x.c
@@ -3061,6 +3061,8 @@ md_begin (void)
 void
 tic54x_md_end (void)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   htab_delete (stag_hash);
   htab_delete (subsym_recurse_hash);
   while (macro_level != -1)
diff --git a/gas/configure b/gas/configure
index e77c440d880..eeace90f375 100755
--- a/gas/configure
+++ b/gas/configure
@@ -816,6 +816,7 @@ enable_plugins
 enable_largefile
 enable_targets
 enable_checking
+enable_leak_check
 enable_compressed_debug_sections
 enable_default_compressed_debug_sections_algorithm
 enable_x86_tls_check
@@ -1488,6 +1489,7 @@ Optional Features:
   --disable-largefile     omit support for large files
   --enable-targets        alternative target configurations besides the primary
   --enable-checking       enable run-time checks
+  --enable-leak-check     enable freeing memory before exit
   --enable-compressed-debug-sections={all,gas,none}
                           compress debug sections by default
   --enable-default-compressed-debug-sections-algorithm={zlib,zstd}
@@ -11150,7 +11152,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11153 "configure"
+#line 11155 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11256,7 +11258,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11259 "configure"
+#line 11261 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11917,6 +11919,20 @@ $as_echo "#define ENABLE_CHECKING 1" >>confdefs.h
 
 fi
 
+ac_leak_check=BFD_ASAN
+# Check whether --enable-leak_check was given.
+if test "${enable_leak_check+set}" = set; then :
+  enableval=$enable_leak_check; case "${enableval}" in
+  no)	ac_leak_check=0 ;;
+  *)	ac_leak_check=1 ;;
+esac
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define ENABLE_LEAK_CHECK ${ac_leak_check}
+_ACEOF
+
+
 # PR gas/19109
 # Decide the default method for compressing debug sections.
 ac_default_compressed_debug_sections=unset
diff --git a/gas/configure.ac b/gas/configure.ac
index 7fb573f8b21..69547f5ffb9 100644
--- a/gas/configure.ac
+++ b/gas/configure.ac
@@ -62,6 +62,15 @@ if test x$ac_checking != x ; then
   AC_DEFINE(ENABLE_CHECKING, 1, [Define if you want run-time sanity checks.])
 fi
 
+ac_leak_check=BFD_ASAN
+AC_ARG_ENABLE(leak_check,
+[  --enable-leak-check     enable freeing memory before exit],
+[case "${enableval}" in
+  no)	ac_leak_check=0 ;;
+  *)	ac_leak_check=1 ;;
+esac])dnl
+AC_DEFINE_UNQUOTED(ENABLE_LEAK_CHECK, ${ac_leak_check}, [Define if you want memory to be freed before exit.])
+
 # PR gas/19109
 # Decide the default method for compressing debug sections.
 ac_default_compressed_debug_sections=unset
diff --git a/gas/expr.c b/gas/expr.c
index c965486bf04..5b6828d391a 100644
--- a/gas/expr.c
+++ b/gas/expr.c
@@ -1640,8 +1640,9 @@ expr_begin (void)
 void
 expr_end (void)
 {
-  for (size_t i = 0; i < ARRAY_SIZE (seen); i++)
-    free (seen[i]);
+  if (ENABLE_LEAK_CHECK)
+    for (size_t i = 0; i < ARRAY_SIZE (seen); i++)
+      free (seen[i]);
 }
 
 /* Return the encoding for the operator at INPUT_LINE_POINTER, and
diff --git a/gas/macro.c b/gas/macro.c
index 76989259feb..65992a7cbbc 100644
--- a/gas/macro.c
+++ b/gas/macro.c
@@ -67,7 +67,8 @@ macro_init (void)
 void
 macro_end (void)
 {
-  htab_delete (macro_hash);
+  if (ENABLE_LEAK_CHECK)
+    htab_delete (macro_hash);
 }
 
 /* Read input lines till we get to a TO string.
diff --git a/gas/output-file.c b/gas/output-file.c
index 5d5634b1462..6b04e77a6a7 100644
--- a/gas/output-file.c
+++ b/gas/output-file.c
@@ -81,18 +81,23 @@ output_file_close (void)
      which will call xexit() which may call this function again...  */
   stdoutput = NULL;
 
-  /* We can't free obstacks attached to the output bfd sections before
-     closing the output bfd since data in those obstacks may need to
-     be accessed, but we can't access anything in the output bfd after
-     it is closed..  */
-  for (sec = obfd->sections; sec; sec = sec->next)
-    stash_frchain_obs (sec);
-  stash_frchain_obs (reg_section);
-  stash_frchain_obs (expr_section);
-  stash_frchain_obs (bfd_abs_section_ptr);
-  stash_frchain_obs (bfd_und_section_ptr);
-  obstack_ptr_grow (&notes, NULL);
-  obs = obstack_finish (&notes);
+  if (ENABLE_LEAK_CHECK)
+    {
+      /* We can't free obstacks attached to the output bfd sections before
+	 closing the output bfd since data in those obstacks may need to
+	 be accessed, but we can't access anything in the output bfd after
+	 it is closed..  */
+      for (sec = obfd->sections; sec; sec = sec->next)
+	stash_frchain_obs (sec);
+      stash_frchain_obs (reg_section);
+      stash_frchain_obs (expr_section);
+      stash_frchain_obs (bfd_abs_section_ptr);
+      stash_frchain_obs (bfd_und_section_ptr);
+      obstack_ptr_grow (&notes, NULL);
+      obs = obstack_finish (&notes);
+    }
+  else
+    obs = NULL;
 
   /* Close the bfd.  */
   if (!flag_always_generate_output && had_errors ())
diff --git a/gas/read.c b/gas/read.c
index bac5bea544c..7a0c73abc5c 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -304,6 +304,8 @@ read_begin (void)
 void
 read_end (void)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   stabs_end ();
   poend ();
   _obstack_free (&cond_obstack, NULL);
@@ -621,7 +623,8 @@ pobegin (void)
 static void
 poend (void)
 {
-  htab_delete (po_hash);
+  if (ENABLE_LEAK_CHECK)
+    htab_delete (po_hash);
 }
 
 #define HANDLE_CONDITIONAL_ASSEMBLY(num_read)				\
diff --git a/gas/stabs.c b/gas/stabs.c
index c7797bc01e4..0233cc5e018 100644
--- a/gas/stabs.c
+++ b/gas/stabs.c
@@ -673,6 +673,8 @@ stabs_begin (void)
 void
 stabs_end (void)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   free ((char *) current_function_label);
   free (last_asm_file);
   free (prev_line_file);
diff --git a/gas/subsegs.c b/gas/subsegs.c
index 199fd869f21..eebd1113c07 100644
--- a/gas/subsegs.c
+++ b/gas/subsegs.c
@@ -47,6 +47,8 @@ subsegs_begin (void)
 void
 subsegs_end (struct obstack **obs)
 {
+  if (!ENABLE_LEAK_CHECK)
+    return;
   for (; *obs; obs++)
     _obstack_free (*obs, NULL);
   _obstack_free (&frchains, NULL);
diff --git a/gas/symbols.c b/gas/symbols.c
index 5ad5dbb2dd9..5844439fd34 100644
--- a/gas/symbols.c
+++ b/gas/symbols.c
@@ -3150,7 +3150,8 @@ symbol_begin (void)
 void
 symbol_end (void)
 {
-  htab_delete (sy_hash);
+  if (ENABLE_LEAK_CHECK)
+    htab_delete (sy_hash);
 }
 
 void
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.