[PATCH v1 1/4] aarch64 gas: free hash tables in md_begin() to avoid memory leaks
Matthieu Longo
matthieu.longo@arm.com
Tue Feb 24 16:26:14 GMT 2026
The AArch64 GAS backend allocates several hash tables in md_begin(). All of
these hash tables use strings as keys; some of those strings are dynamically
allocated, while others are statically allocated. None of these hash tables
have ever been deallocated in aarch64_md_end(), resulting in memory leaks
that spam LeakSanitizer reports.
The first part of the fix deallocates the hash tables in aarch64_md_end().
However, a hash table cannot determine whether a key was dynamically
allocated unless it is provided with a custom entry deallocator. This also
means that dynamically and statically allocated string keys cannot be mixed
in a same hash table. The second part of the fix therefore passes a custom
deallocator to htab_create_alloc() to correctly free, on one hand, dynamically
-allocated string keys, and on the other hand, clean up the list of opcode
nodes associated with each key. Finally, it also ensures that only one type
of string key is used per hash table.
---
gas/config/tc-aarch64.c | 104 +++++++++++++++++++++++++++++++++++-----
gas/config/tc-aarch64.h | 3 ++
2 files changed, 94 insertions(+), 13 deletions(-)
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index ca9387dcfd8..56d97a91f5b 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -10492,6 +10492,33 @@ fill_instruction_hash_table (void)
}
}
+static void
+free_opcode_template (void *entry)
+{
+ string_tuple_t *tuple = entry;
+ /* Note: Only the value is dynamically allocated.
+ See allocation code in fill_instruction_hash_table().
+
+ It is important to note that the value can be the first node of a list.
+ If a value with the same key is found, then the new value is chained to
+ first value. So let's not forget to free the whole list. */
+ templates *templ = (templates *) tuple->value;
+ while (templ != NULL)
+ {
+ templates *templ_next = templ->next;
+ free (templ);
+ templ = templ_next;
+ }
+ /* Don't free ENTRY that was allocated on obstack. */
+}
+
+static htab_t
+instruction_htab_create (void)
+{
+ return htab_create_alloc (16, hash_string_tuple, eq_string_tuple,
+ free_opcode_template, notes_calloc, NULL);
+}
+
static inline void
convert_to_upper (char *dst, const char *src, size_t num)
{
@@ -10513,6 +10540,22 @@ get_upper_str (const char *str)
return ret;
}
+static void
+free_dyn_string_key_tuple (void *entry)
+{
+ string_tuple_t *tuple = entry;
+ /* Note: only the key is dynamically allocated, not the value. */
+ free ((char *) tuple->key);
+ /* Don't free ENTRY that was allocated on obstack. */
+}
+
+static htab_t
+dyn_str_htab_create (void)
+{
+ return htab_create_alloc (16, hash_string_tuple, eq_string_tuple,
+ free_dyn_string_key_tuple, notes_calloc, NULL);
+}
+
/* MD interface: Initialization. */
void
@@ -10521,9 +10564,12 @@ md_begin (void)
unsigned mach;
unsigned int i;
- aarch64_ops_hsh = str_htab_create ();
- aarch64_cond_hsh = str_htab_create ();
- aarch64_shift_hsh = str_htab_create ();
+ /* Each hash table value is a dynamically allocated node pointing to the
+ opcode associated with its key. Nodes with the same key are chained. */
+ aarch64_ops_hsh = instruction_htab_create ();
+
+ /* Neither the keys nor the values in these hash tables use dynamically
+ allocated strings. */
aarch64_sys_regs_hsh = str_htab_create ();
aarch64_pstatefield_hsh = str_htab_create ();
aarch64_sys_regs_ic_hsh = str_htab_create ();
@@ -10534,14 +10580,18 @@ md_begin (void)
aarch64_sys_regs_mlbi_hsh = str_htab_create ();
aarch64_sys_regs_sr_hsh = str_htab_create ();
aarch64_reg_hsh = str_htab_create ();
- aarch64_barrier_opt_hsh = str_htab_create ();
aarch64_nzcv_hsh = str_htab_create ();
- aarch64_pldop_hsh = str_htab_create ();
- aarch64_hint_opt_hsh = str_htab_create ();
aarch64_sys_ins_gic_hsh = str_htab_create ();
aarch64_sys_ins_gicr_hsh = str_htab_create ();
aarch64_sys_ins_gsb_hsh = str_htab_create ();
+ /* These hash tables use dynamically allocated strings as keys. */
+ aarch64_cond_hsh = dyn_str_htab_create ();
+ aarch64_shift_hsh = dyn_str_htab_create ();
+ aarch64_barrier_opt_hsh = dyn_str_htab_create ();
+ aarch64_pldop_hsh = dyn_str_htab_create ();
+ aarch64_hint_opt_hsh = dyn_str_htab_create ();
+
fill_instruction_hash_table ();
for (i = 0; aarch64_sys_regs[i].name != NULL; ++i)
@@ -10599,7 +10649,7 @@ md_begin (void)
for (i = 0; aarch64_operand_modifiers[i].name != NULL; i++)
{
const char *name = aarch64_operand_modifiers[i].name;
- checked_hash_insert (aarch64_shift_hsh, name,
+ checked_hash_insert (aarch64_shift_hsh, xstrdup (name),
aarch64_operand_modifiers + i);
/* Also hash the name in the upper case. */
checked_hash_insert (aarch64_shift_hsh, get_upper_str (name),
@@ -10616,7 +10666,7 @@ md_begin (void)
const char *name = aarch64_conds[i].names[j];
if (name == NULL)
break;
- checked_hash_insert (aarch64_cond_hsh, name,
+ checked_hash_insert (aarch64_cond_hsh, xstrdup (name),
aarch64_conds + i);
/* Also hash the name in the upper case. */
checked_hash_insert (aarch64_cond_hsh, get_upper_str (name),
@@ -10630,7 +10680,7 @@ md_begin (void)
/* Skip xx00 - the unallocated values of option. */
if ((i & 0x3) == 0)
continue;
- checked_hash_insert (aarch64_barrier_opt_hsh, name,
+ checked_hash_insert (aarch64_barrier_opt_hsh, xstrdup (name),
aarch64_barrier_options + i);
/* Also hash the name in the upper case. */
checked_hash_insert (aarch64_barrier_opt_hsh, get_upper_str (name),
@@ -10640,7 +10690,7 @@ md_begin (void)
for (i = 0; i < ARRAY_SIZE (aarch64_barrier_dsb_nxs_options); i++)
{
const char *name = aarch64_barrier_dsb_nxs_options[i].name;
- checked_hash_insert (aarch64_barrier_opt_hsh, name,
+ checked_hash_insert (aarch64_barrier_opt_hsh, xstrdup (name),
aarch64_barrier_dsb_nxs_options + i);
/* Also hash the name in the upper case. */
checked_hash_insert (aarch64_barrier_opt_hsh, get_upper_str (name),
@@ -10653,7 +10703,7 @@ md_begin (void)
/* Skip the unallocated hint encodings. */
if (name == NULL)
continue;
- checked_hash_insert (aarch64_pldop_hsh, name,
+ checked_hash_insert (aarch64_pldop_hsh, xstrdup (name),
aarch64_prfops + i);
/* Also hash the name in the upper case. */
checked_hash_insert (aarch64_pldop_hsh, get_upper_str (name),
@@ -10663,15 +10713,17 @@ md_begin (void)
for (i = 0; aarch64_hint_options[i].name != NULL; i++)
{
const char* name = aarch64_hint_options[i].name;
- const char* upper_name = get_upper_str(name);
+ const char* upper_name = get_upper_str (name);
- checked_hash_insert (aarch64_hint_opt_hsh, name,
+ checked_hash_insert (aarch64_hint_opt_hsh, xstrdup (name),
aarch64_hint_options + i);
/* Also hash the name in the upper case if not the same. */
if (strcmp (name, upper_name) != 0)
checked_hash_insert (aarch64_hint_opt_hsh, upper_name,
aarch64_hint_options + i);
+ else
+ free ((char *) upper_name);
}
for (i = 0; aarch64_sys_ins_gic[i].name != NULL; i++)
@@ -10712,6 +10764,32 @@ md_begin (void)
#endif
}
+void
+aarch64_md_end (void)
+{
+ /* Deallocate all the hash tables. */
+ htab_delete (aarch64_ops_hsh);
+ htab_delete (aarch64_sys_regs_hsh);
+ htab_delete (aarch64_pstatefield_hsh);
+ htab_delete (aarch64_sys_regs_ic_hsh);
+ htab_delete (aarch64_sys_regs_dc_hsh);
+ htab_delete (aarch64_sys_regs_at_hsh);
+ htab_delete (aarch64_sys_regs_tlbi_hsh);
+ htab_delete (aarch64_sys_regs_plbi_hsh);
+ htab_delete (aarch64_sys_regs_mlbi_hsh);
+ htab_delete (aarch64_sys_regs_sr_hsh);
+ htab_delete (aarch64_reg_hsh);
+ htab_delete (aarch64_nzcv_hsh);
+ htab_delete (aarch64_sys_ins_gic_hsh);
+ htab_delete (aarch64_sys_ins_gicr_hsh);
+ htab_delete (aarch64_sys_ins_gsb_hsh);
+ htab_delete (aarch64_cond_hsh);
+ htab_delete (aarch64_shift_hsh);
+ htab_delete (aarch64_barrier_opt_hsh);
+ htab_delete (aarch64_pldop_hsh);
+ htab_delete (aarch64_hint_opt_hsh);
+}
+
/* Command line processing. */
const char md_shortopts[] = "m:";
diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h
index d1fb4c9058b..b89df2bc425 100644
--- a/gas/config/tc-aarch64.h
+++ b/gas/config/tc-aarch64.h
@@ -73,6 +73,9 @@ struct aarch64_fix
#define md_cleanup() aarch64_cleanup ()
+extern void aarch64_md_end (void);
+#define md_end aarch64_md_end
+
#define md_start_line_hook() aarch64_start_line_hook ()
#define tc_frob_label(S) aarch64_frob_label (S)
--
2.53.0
More information about the Binutils
mailing list