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

Alan Modra amodra@gmail.com
Tue Mar 3 23:22:13 GMT 2026


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.

I made it a separate define because you might want to control this
independently of asan options being used.

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-rl78.c b/gas/config/tc-rl78.c
index 62c7e422dad..a6fe5f24c01 100644
--- a/gas/config/tc-rl78.c
+++ b/gas/config/tc-rl78.c
@@ -403,11 +403,6 @@ md_begin (void)
   rl78_abs_sym = symbol_make ("__rl78_abs__");
 }
 
-void
-rl78_md_end (void)
-{
-}
-
 /* Set the ELF specific flags.  */
 void
 rl78_elf_final_processing (void)
diff --git a/gas/config/tc-rl78.h b/gas/config/tc-rl78.h
index 8147f55ebdd..9761d509aa1 100644
--- a/gas/config/tc-rl78.h
+++ b/gas/config/tc-rl78.h
@@ -46,9 +46,6 @@ extern int target_little_endian;
 /* .-foo gets turned into PC relative relocs.  */
 #define DIFF_EXPR_OK
 
-#define md_end rl78_md_end
-extern void rl78_md_end (void);
-
 #define md_relax_frag rl78_relax_frag
 extern int rl78_relax_frag (segT, fragS *, long);
 
diff --git a/gas/config/tc-rx.c b/gas/config/tc-rx.c
index fe260d8b758..794f64a54f4 100644
--- a/gas/config/tc-rx.c
+++ b/gas/config/tc-rx.c
@@ -1218,11 +1218,6 @@ md_assemble (char * str)
   dwarf2_emit_insn (idx);
 }
 
-void
-rx_md_end (void)
-{
-}
-
 /* Write a value out to the object file, using the appropriate endianness.  */
 
 void
diff --git a/gas/config/tc-rx.h b/gas/config/tc-rx.h
index b7d84f68cf5..8a37b36275d 100644
--- a/gas/config/tc-rx.h
+++ b/gas/config/tc-rx.h
@@ -51,9 +51,6 @@ extern int target_big_endian;
 /* .-foo gets turned into PC relative relocs.  */
 #define DIFF_EXPR_OK
 
-#define md_end rx_md_end
-extern void rx_md_end (void);
-
 /* Note - the definition of MD_RELAX_FRAG here includes a reference to the
    MAX_ITERATIONS variable which is defined locally in write.c:relax_segment()
    but which is not normally passed to target specific relaxing code.  This
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

-- 
Alan Modra


More information about the Binutils mailing list