[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 (¬es, 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 (¬es, NULL);
- obs = obstack_finish (¬es);
+ 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 (¬es, NULL);
+ obs = obstack_finish (¬es);
+ }
+ 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