diff mbox series

[4/7] Move libc_freeres_ptrs and libc_subfreeres to weak functions

Message ID 20221115193159.173838-5-adhemerval.zanella@linaro.org
State New
Headers show
Series Fixing remaining lld issues | expand

Commit Message

Adhemerval Zanella Nov. 15, 2022, 7:31 p.m. UTC
They are both used by __libc_freeres to free all library malloc
allocated resources to help tooling like mtrace or valgrind on
memory leak tracking.

The previous scheme use linker markers and linker script entries
to consolidate the free routines function pointers on RELRO segment
and to be freed buffers on BSS.

This patch adds specific free functions for libc_freeres_ptrs
buffers and adds a new function pointer array with all required
free functions.  The function pointer array is used instead of
a chain of call_function_static_weak because it leads to better
code generation on most ABIs.

It allows to remove both the internal macros and the linker
script sections requirement.

Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
---
 Makerules                                     |   7 -
 crypt/md5-crypt.c                             |  10 +-
 crypt/sha256-crypt.c                          |  10 +-
 crypt/sha512-crypt.c                          |  10 +-
 elf/Makefile                                  |   1 +
 elf/dl-libc.c                                 |   4 +-
 grp/fgetgrent.c                               |   8 +-
 gshadow/fgetsgent.c                           |   2 +-
 iconv/gconv_cache.c                           |   3 +-
 iconv/gconv_conf.c                            |   3 +-
 iconv/gconv_db.c                              |   6 +-
 iconv/gconv_dl.c                              |   5 +-
 include/libc-symbols.h                        |  73 ------
 inet/getnameinfo.c                            |   8 +-
 inet/getnetgrent.c                            |   8 +-
 inet/rcmd.c                                   |   8 +-
 inet/rexec.c                                  |   8 +-
 intl/dcigettext.c                             |   3 +-
 intl/finddomain.c                             |   2 +-
 intl/loadmsgcat.c                             |   1 -
 intl/localealias.c                            |  15 +-
 libio/genops.c                                |   8 +-
 locale/loadarchive.c                          |   2 +-
 locale/localeinfo.h                           |   4 +-
 locale/setlocale.c                            |   4 +-
 login/getutent.c                              |   8 +-
 login/getutid.c                               |   8 +-
 login/getutline.c                             |   8 +-
 malloc/set-freeres.c                          | 222 ++++++++++++++++--
 malloc/thread-freeres.c                       |   2 +-
 misc/efgcvt-template.c                        |   8 +-
 misc/efgcvt.c                                 |   1 +
 misc/fstab.c                                  |   3 +-
 misc/hsearch.c                                |   4 -
 misc/mntent.c                                 |   8 +-
 misc/qefgcvt.c                                |   1 +
 misc/unwind-link.c                            |   2 +-
 nscd/nscd_getgr_r.c                           |   3 +-
 nscd/nscd_gethst_r.c                          |   3 +-
 nscd/nscd_getpw_r.c                           |   3 +-
 nscd/nscd_getserv_r.c                         |   3 +-
 nscd/nscd_netgroup.c                          |   3 +-
 nss/getXXbyYY.c                               |  10 +-
 nss/getXXent.c                                |  10 +-
 nss/nss_action.c                              |   2 +-
 nss/nss_database.c                            |   2 +-
 nss/nss_module.c                              |   2 +-
 posix/regcomp.c                               |   3 +-
 posix/register-atfork.c                       |   3 +-
 pwd/fgetpwent.c                               |   8 +-
 resolv/gai_misc.c                             |   3 +-
 resolv/res-close.c                            |   2 -
 resolv/res_hconf.c                            |   9 +-
 resolv/resolv_conf.c                          |   3 +-
 resolv/tst-leaks2.c                           |   2 +
 rt/aio_misc.c                                 |   3 +-
 shadow/fgetspent.c                            |   8 +-
 stdio-common/reg-modifier.c                   |   3 +-
 stdio-common/reg-printf.c                     |   9 +-
 stdio-common/reg-type.c                       |   9 +-
 stdlib/fmtmsg.c                               |   3 +-
 stdlib/setenv.c                               |   3 +-
 sunrpc/clnt_perr.c                            |   4 +-
 sunrpc/rpc_thread.c                           |   1 -
 sunrpc/tst-svc_register.c                     |   6 +-
 sysdeps/generic/set-freeres-fp.h              |  19 ++
 sysdeps/generic/set-freeres-os.h              |  19 ++
 sysdeps/generic/set-freeres-system.h          |  27 +++
 .../ldbl-128ibm-compat/ieee128-qefgcvt.c      |   1 +
 .../ldbl-128ibm-compat/set-freeres-fp.h       |  22 ++
 sysdeps/posix/getaddrinfo.c                   |   5 +-
 sysdeps/posix/ttyname.c                       |   8 +-
 sysdeps/unix/sysv/linux/check_pf.c            |   3 +-
 sysdeps/unix/sysv/linux/set-freeres-os.h      |  24 ++
 sysdeps/unix/sysv/linux/ttyname.c             |   3 +-
 time/tzfile.c                                 |   8 +-
 time/tzset.c                                  |   3 +-
 77 files changed, 547 insertions(+), 206 deletions(-)
 create mode 100644 sysdeps/generic/set-freeres-fp.h
 create mode 100644 sysdeps/generic/set-freeres-os.h
 create mode 100644 sysdeps/generic/set-freeres-system.h
 create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/set-freeres-fp.h
 create mode 100644 sysdeps/unix/sysv/linux/set-freeres-os.h

Comments

Florian Weimer Dec. 12, 2022, 10:55 a.m. UTC | #1
* Adhemerval Zanella via Libc-alpha:

> diff --git a/crypt/md5-crypt.c b/crypt/md5-crypt.c
> index 7c4fb9fb97..9660cdd698 100644
> --- a/crypt/md5-crypt.c
> +++ b/crypt/md5-crypt.c
> @@ -299,10 +299,7 @@ __md5_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
>    return buffer;
>  }
>  
> -#ifndef _LIBC
> -# define libc_freeres_ptr(decl) decl
> -#endif
> -libc_freeres_ptr (static char *buffer);
> +static char *buffer;
>  
>  char *
>  __md5_crypt (const char *key, const char *salt)
> @@ -330,7 +327,10 @@ __md5_crypt (const char *key, const char *salt)
>  static void
>  __attribute__ ((__destructor__))
>  free_mem (void)
> +#else
> +void
> +__md5_crypt_freemem (void)
> +#endif
>  {
>    free (buffer);
>  }
> -#endif
> diff --git a/crypt/sha256-crypt.c b/crypt/sha256-crypt.c
> index a98a968a8b..75fd582429 100644
> --- a/crypt/sha256-crypt.c
> +++ b/crypt/sha256-crypt.c
> @@ -386,10 +386,7 @@ __sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
>    return buffer;
>  }
>  
> -#ifndef _LIBC
> -# define libc_freeres_ptr(decl) decl
> -#endif
> -libc_freeres_ptr (static char *buffer);
> +static char *buffer;
>  
>  /* This entry point is equivalent to the `crypt' function in Unix
>     libcs.  */
> @@ -422,7 +419,10 @@ __sha256_crypt (const char *key, const char *salt)
>  static void
>  __attribute__ ((__destructor__))
>  free_mem (void)
> +#else
> +void
> +__sha256_crypt_freemem (void)
> +#endif
>  {
>    free (buffer);
>  }
> -#endif
> diff --git a/crypt/sha512-crypt.c b/crypt/sha512-crypt.c
> index ea13527c09..ae6ecaef16 100644
> --- a/crypt/sha512-crypt.c
> +++ b/crypt/sha512-crypt.c
> @@ -408,10 +408,7 @@ __sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
>    return buffer;
>  }
>  
> -#ifndef _LIBC
> -# define libc_freeres_ptr(decl) decl
> -#endif
> -libc_freeres_ptr (static char *buffer);
> +static char *buffer;
>  
>  /* This entry point is equivalent to the `crypt' function in Unix
>     libcs.  */
> @@ -444,7 +441,10 @@ __sha512_crypt (const char *key, const char *salt)
>  static void
>  __attribute__ ((__destructor__))
>  free_mem (void)
> +#else
> +void
> +__sha512_crypt_freemem (void)
> +#endif
>  {
>    free (buffer);
>  }
> -#endif

I think you should delete the entire deallocation logic, or make use of
the ELF destructor unconditional.  This wasn't linked into libc, so it
wasn't run from __libc_freeres, and the hook registration was a no-op.
(I believe, I haven't checked.)

Thanks,
Florian
Florian Weimer Dec. 12, 2022, 10:58 a.m. UTC | #2
* Adhemerval Zanella via Libc-alpha:

> diff --git a/elf/dl-libc.c b/elf/dl-libc.c
> index 266e068da6..06bce2f6dd 100644
> --- a/elf/dl-libc.c
> +++ b/elf/dl-libc.c
> @@ -228,7 +228,7 @@ __libc_dlclose (void *map)
>  }
>  
>  
> -static bool __libc_freeres_fn_section
> +static bool
>  free_slotinfo (struct dtv_slotinfo_list **elemp)
>  {
>    size_t cnt;
> @@ -256,7 +256,7 @@ free_slotinfo (struct dtv_slotinfo_list **elemp)
>  }
>  
>  
> -libc_freeres_fn (free_mem)
> +void __libc_freemem (void)
>  {
>    struct link_map *l;
>    struct r_search_path_elem *d;

I believe this should be called __dl_libc_freemem or something like
that, to point towards dl-libc.c as the implementing file.

Thanks,
Florian
Florian Weimer Dec. 12, 2022, 11 a.m. UTC | #3
* Adhemerval Zanella via Libc-alpha:

> diff --git a/grp/fgetgrent.c b/grp/fgetgrent.c
> index fd2b4d32d6..df04530b04 100644
> --- a/grp/fgetgrent.c
> +++ b/grp/fgetgrent.c
> @@ -25,7 +25,7 @@
>  /* We need to protect the dynamic buffer handling.  */
>  __libc_lock_define_initialized (static, lock);
>  
> -libc_freeres_ptr (static char *buffer);
> +static char *buffer;
>  
>  /* Read one entry from the given stream.  */
>  struct group *
> @@ -82,3 +82,9 @@ fgetgrent (FILE *stream)
>  
>    return result;
>  }
> +
> +void
> +__libc_fgetgrent_freemem (void)
> +{
> +  free (buffer);
> +}
> diff --git a/gshadow/fgetsgent.c b/gshadow/fgetsgent.c
> index 02f9c7d643..4d9282e560 100644
> --- a/gshadow/fgetsgent.c
> +++ b/gshadow/fgetsgent.c
> @@ -28,7 +28,7 @@
>  /* We need to protect the dynamic buffer handling.  */
>  __libc_lock_define_initialized (static, lock);
>  
> -libc_freeres_ptr (static char *buffer);
> +static char *buffer;
>  
>  /* Read one shadow entry from the given stream.  */
>  struct sgrp *

Missing free function in the second patch.

I think you should consider introducing a call_free_static_weak that
does something like

  if (&ptr != NULL)
    free (ptr);

in the static case, and calls

  free (ptr);

unconditionally for the dynamic case.  And then add attribute_hidden
variable declarations to a suitable wrapper header under include/.

This avoids writing all these little helper functions.

Thanks,
Florian
Andreas Schwab Dec. 12, 2022, 11:05 a.m. UTC | #4
On Nov 15 2022, Adhemerval Zanella via Libc-alpha wrote:

> diff --git a/elf/dl-libc.c b/elf/dl-libc.c
> index 266e068da6..06bce2f6dd 100644
> --- a/elf/dl-libc.c
> +++ b/elf/dl-libc.c
> @@ -228,7 +228,7 @@ __libc_dlclose (void *map)
>  }
>  
>  
> -static bool __libc_freeres_fn_section
> +static bool
>  free_slotinfo (struct dtv_slotinfo_list **elemp)
>  {
>    size_t cnt;
> @@ -256,7 +256,7 @@ free_slotinfo (struct dtv_slotinfo_list **elemp)
>  }
>  
>  
> -libc_freeres_fn (free_mem)
> +void __libc_freemem (void)

Line break after return type.
Florian Weimer Dec. 12, 2022, 11:08 a.m. UTC | #5
* Adhemerval Zanella via Libc-alpha:

> diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
> index be8c2a35fc..ce5d010133 100644
> --- a/malloc/set-freeres.c
> +++ b/malloc/set-freeres.c
> @@ -15,32 +15,210 @@
>     License along with the GNU C Library; if not, see
>     <https://www.gnu.org/licenses/>.  */

> +/* Resource Freeing Hooks:
> +
> +   Normally a process exits and the OS cleans up any allocated
> +   memory.  However, when tooling like mtrace or valgrind is monitoring
> +   the process we need to free all resources that are part of the
> +   process in order to provide the consistency required to track
> +   memory leaks.
> +
> +   A single public API exists and is __libc_freeres(), and this is used
> +   by applications like valgrind to freee resouces.
> +
> +   Each free routines must be explicit listed below, with the care to define
> +   weak functions for external symbol if applicable.   */
> +
> +/* From libc.so.  */
> +extern void __libc_freemem (void) weak_function;
> +extern void __hdestroy (void) weak_function;
> +extern void __gconv_cache_freemem (void) weak_function;

These declarations should come from headers under include/, so that they
can be proper type-checked against the implementation.

> +static void (*__libc_freeres_funcs[])(void) attribute_relro =
> +{
> +  __libc_freemem,
> +  __hdestroy,
> +  __gconv_cache_freemem,

I think we can avoid these relocations for the dynamic case if we just
call these functions using call_function_static_weak, or directly free
the pointers using call_free_static_weak (see the other message).  The
latter has somewhat large code size, but I think that's the right
trade-off here.

Thanks,
Florian
Adhemerval Zanella Dec. 12, 2022, 1:14 p.m. UTC | #6
On 12/12/22 07:55, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> diff --git a/crypt/md5-crypt.c b/crypt/md5-crypt.c
>> index 7c4fb9fb97..9660cdd698 100644
>> --- a/crypt/md5-crypt.c
>> +++ b/crypt/md5-crypt.c
>> @@ -299,10 +299,7 @@ __md5_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
>>    return buffer;
>>  }
>>  
>> -#ifndef _LIBC
>> -# define libc_freeres_ptr(decl) decl
>> -#endif
>> -libc_freeres_ptr (static char *buffer);
>> +static char *buffer;
>>  
>>  char *
>>  __md5_crypt (const char *key, const char *salt)
>> @@ -330,7 +327,10 @@ __md5_crypt (const char *key, const char *salt)
>>  static void
>>  __attribute__ ((__destructor__))
>>  free_mem (void)
>> +#else
>> +void
>> +__md5_crypt_freemem (void)
>> +#endif
>>  {
>>    free (buffer);
>>  }
>> -#endif
>> diff --git a/crypt/sha256-crypt.c b/crypt/sha256-crypt.c
>> index a98a968a8b..75fd582429 100644
>> --- a/crypt/sha256-crypt.c
>> +++ b/crypt/sha256-crypt.c
>> @@ -386,10 +386,7 @@ __sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
>>    return buffer;
>>  }
>>  
>> -#ifndef _LIBC
>> -# define libc_freeres_ptr(decl) decl
>> -#endif
>> -libc_freeres_ptr (static char *buffer);
>> +static char *buffer;
>>  
>>  /* This entry point is equivalent to the `crypt' function in Unix
>>     libcs.  */
>> @@ -422,7 +419,10 @@ __sha256_crypt (const char *key, const char *salt)
>>  static void
>>  __attribute__ ((__destructor__))
>>  free_mem (void)
>> +#else
>> +void
>> +__sha256_crypt_freemem (void)
>> +#endif
>>  {
>>    free (buffer);
>>  }
>> -#endif
>> diff --git a/crypt/sha512-crypt.c b/crypt/sha512-crypt.c
>> index ea13527c09..ae6ecaef16 100644
>> --- a/crypt/sha512-crypt.c
>> +++ b/crypt/sha512-crypt.c
>> @@ -408,10 +408,7 @@ __sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
>>    return buffer;
>>  }
>>  
>> -#ifndef _LIBC
>> -# define libc_freeres_ptr(decl) decl
>> -#endif
>> -libc_freeres_ptr (static char *buffer);
>> +static char *buffer;
>>  
>>  /* This entry point is equivalent to the `crypt' function in Unix
>>     libcs.  */
>> @@ -444,7 +441,10 @@ __sha512_crypt (const char *key, const char *salt)
>>  static void
>>  __attribute__ ((__destructor__))
>>  free_mem (void)
>> +#else
>> +void
>> +__sha512_crypt_freemem (void)
>> +#endif
>>  {
>>    free (buffer);
>>  }
>> -#endif
> 
> I think you should delete the entire deallocation logic, or make use of
> the ELF destructor unconditional.  This wasn't linked into libc, so it
> wasn't run from __libc_freeres, and the hook registration was a no-op.
> (I believe, I haven't checked.)

Indeed you are right and for modern systems is highly unlikely that glibc
libcrypto will be used anyways (at least for system I usually check libxcrypt
is used instead). I will just remove it.
Adhemerval Zanella Dec. 12, 2022, 1:16 p.m. UTC | #7
On 12/12/22 07:58, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> diff --git a/elf/dl-libc.c b/elf/dl-libc.c
>> index 266e068da6..06bce2f6dd 100644
>> --- a/elf/dl-libc.c
>> +++ b/elf/dl-libc.c
>> @@ -228,7 +228,7 @@ __libc_dlclose (void *map)
>>  }
>>  
>>  
>> -static bool __libc_freeres_fn_section
>> +static bool
>>  free_slotinfo (struct dtv_slotinfo_list **elemp)
>>  {
>>    size_t cnt;
>> @@ -256,7 +256,7 @@ free_slotinfo (struct dtv_slotinfo_list **elemp)
>>  }
>>  
>>  
>> -libc_freeres_fn (free_mem)
>> +void __libc_freemem (void)
>>  {
>>    struct link_map *l;
>>    struct r_search_path_elem *d;
> 
> I believe this should be called __dl_libc_freemem or something like
> that, to point towards dl-libc.c as the implementing file.

Alright, I will rename it.
Adhemerval Zanella Dec. 12, 2022, 1:49 p.m. UTC | #8
On 12/12/22 08:00, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> diff --git a/grp/fgetgrent.c b/grp/fgetgrent.c
>> index fd2b4d32d6..df04530b04 100644
>> --- a/grp/fgetgrent.c
>> +++ b/grp/fgetgrent.c
>> @@ -25,7 +25,7 @@
>>  /* We need to protect the dynamic buffer handling.  */
>>  __libc_lock_define_initialized (static, lock);
>>  
>> -libc_freeres_ptr (static char *buffer);
>> +static char *buffer;
>>  
>>  /* Read one entry from the given stream.  */
>>  struct group *
>> @@ -82,3 +82,9 @@ fgetgrent (FILE *stream)
>>  
>>    return result;
>>  }
>> +
>> +void
>> +__libc_fgetgrent_freemem (void)
>> +{
>> +  free (buffer);
>> +}
>> diff --git a/gshadow/fgetsgent.c b/gshadow/fgetsgent.c
>> index 02f9c7d643..4d9282e560 100644
>> --- a/gshadow/fgetsgent.c
>> +++ b/gshadow/fgetsgent.c
>> @@ -28,7 +28,7 @@
>>  /* We need to protect the dynamic buffer handling.  */
>>  __libc_lock_define_initialized (static, lock);
>>  
>> -libc_freeres_ptr (static char *buffer);
>> +static char *buffer;
>>  
>>  /* Read one shadow entry from the given stream.  */
>>  struct sgrp *
> 
> Missing free function in the second patch.
> 
> I think you should consider introducing a call_free_static_weak that
> does something like
> 
>   if (&ptr != NULL)
>     free (ptr);
> 
> in the static case, and calls
> 
>   free (ptr);
> 
> unconditionally for the dynamic case.  And then add attribute_hidden
> variable declarations to a suitable wrapper header under include/.
> 
> This avoids writing all these little helper functions.

We already have call_free_static_weak function that does exactly that,
maybe you are proposing a something like:

# ifdef SHARED
#  define declare_libc_freeres (name, ptr) \
static void name (void) { free (ptr); }
# else 
#  define declare_libc_freeres (name, ptr) \
static void name (void) { if (ptr != NULL) free (ptr); }
# endif
Adhemerval Zanella Dec. 12, 2022, 1:49 p.m. UTC | #9
On 12/12/22 08:05, Andreas Schwab wrote:
> On Nov 15 2022, Adhemerval Zanella via Libc-alpha wrote:
> 
>> diff --git a/elf/dl-libc.c b/elf/dl-libc.c
>> index 266e068da6..06bce2f6dd 100644
>> --- a/elf/dl-libc.c
>> +++ b/elf/dl-libc.c
>> @@ -228,7 +228,7 @@ __libc_dlclose (void *map)
>>  }
>>  
>>  
>> -static bool __libc_freeres_fn_section
>> +static bool
>>  free_slotinfo (struct dtv_slotinfo_list **elemp)
>>  {
>>    size_t cnt;
>> @@ -256,7 +256,7 @@ free_slotinfo (struct dtv_slotinfo_list **elemp)
>>  }
>>  
>>  
>> -libc_freeres_fn (free_mem)
>> +void __libc_freemem (void)
> 
> Line break after return type.
> 

Ack.
Adhemerval Zanella Dec. 12, 2022, 1:51 p.m. UTC | #10
On 12/12/22 08:08, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
>> index be8c2a35fc..ce5d010133 100644
>> --- a/malloc/set-freeres.c
>> +++ b/malloc/set-freeres.c
>> @@ -15,32 +15,210 @@
>>     License along with the GNU C Library; if not, see
>>     <https://www.gnu.org/licenses/>.  */
> 
>> +/* Resource Freeing Hooks:
>> +
>> +   Normally a process exits and the OS cleans up any allocated
>> +   memory.  However, when tooling like mtrace or valgrind is monitoring
>> +   the process we need to free all resources that are part of the
>> +   process in order to provide the consistency required to track
>> +   memory leaks.
>> +
>> +   A single public API exists and is __libc_freeres(), and this is used
>> +   by applications like valgrind to freee resouces.
>> +
>> +   Each free routines must be explicit listed below, with the care to define
>> +   weak functions for external symbol if applicable.   */
>> +
>> +/* From libc.so.  */
>> +extern void __libc_freemem (void) weak_function;
>> +extern void __hdestroy (void) weak_function;
>> +extern void __gconv_cache_freemem (void) weak_function;
> 
> These declarations should come from headers under include/, so that they
> can be proper type-checked against the implementation.

Ack.

> 
>> +static void (*__libc_freeres_funcs[])(void) attribute_relro =
>> +{
>> +  __libc_freemem,
>> +  __hdestroy,
>> +  __gconv_cache_freemem,
> 
> I think we can avoid these relocations for the dynamic case if we just
> call these functions using call_function_static_weak, or directly free
> the pointers using call_free_static_weak (see the other message).  The
> latter has somewhat large code size, but I think that's the right
> trade-off here.

I used a function pointer array mainly because it tends to generate less
code on some ABIs compare to a chain of call_function_static_weak, but I
did not take in consideration the relocation time spent.  I don't have
a strong opinion in fact, I will change to direct call using
call_free_static_weak.
Florian Weimer Dec. 12, 2022, 2:05 p.m. UTC | #11
* Adhemerval Zanella Netto:

>> I think you should consider introducing a call_free_static_weak that
>> does something like
>> 
>>   if (&ptr != NULL)
>>     free (ptr);
>> 
>> in the static case, and calls
>> 
>>   free (ptr);
>> 
>> unconditionally for the dynamic case.  And then add attribute_hidden
>> variable declarations to a suitable wrapper header under include/.
>> 
>> This avoids writing all these little helper functions.
>
> We already have call_free_static_weak function that does exactly that,

I don't see call_free_static_weak?

> maybe you are proposing a something like:
>
> # ifdef SHARED
> #  define declare_libc_freeres (name, ptr) \
> static void name (void) { free (ptr); }
> # else 
> #  define declare_libc_freeres (name, ptr) \
> static void name (void) { if (ptr != NULL) free (ptr); }
> # endif

It has to be &ptr != NULL for the weak case, and you also need to create
a weak alias.

Thanks,
Florian
Adhemerval Zanella Dec. 12, 2022, 2:16 p.m. UTC | #12
On 12/12/22 11:05, Florian Weimer wrote:
> * Adhemerval Zanella Netto:
> 
>>> I think you should consider introducing a call_free_static_weak that
>>> does something like
>>>
>>>   if (&ptr != NULL)
>>>     free (ptr);
>>>
>>> in the static case, and calls
>>>
>>>   free (ptr);
>>>
>>> unconditionally for the dynamic case.  And then add attribute_hidden
>>> variable declarations to a suitable wrapper header under include/.
>>>
>>> This avoids writing all these little helper functions.
>>
>> We already have call_free_static_weak function that does exactly that,
> 
> I don't see call_free_static_weak?
> 
>> maybe you are proposing a something like:
>>
>> # ifdef SHARED
>> #  define declare_libc_freeres (name, ptr) \
>> static void name (void) { free (ptr); }
>> # else 
>> #  define declare_libc_freeres (name, ptr) \
>> static void name (void) { if (ptr != NULL) free (ptr); }
>> # endif
> 
> It has to be &ptr != NULL for the weak case, and you also need to create
> a weak alias.

Right, and do we really need a weak_alias in this fact? Wouldn't weak_function
suffice in this case for !SHARED?
Florian Weimer Dec. 12, 2022, 3:39 p.m. UTC | #13
* Adhemerval Zanella Netto:

>> It has to be &ptr != NULL for the weak case, and you also need to create
>> a weak alias.
>
> Right, and do we really need a weak_alias in this fact? Wouldn't weak_function
> suffice in this case for !SHARED?

weak_function is active for shared builds, and I don't think we want it
there because it obscures bugs that would otherwise lead to link
failures.

Thanks,
Florian
diff mbox series

Patch

diff --git a/Makerules b/Makerules
index 3226b7a12b..962b2cd90c 100644
--- a/Makerules
+++ b/Makerules
@@ -560,14 +560,7 @@  $(common-objpfx)shlib.lds: $(common-objpfx)config.make $(..)Makerules
 		  -Wl,--verbose 2>/dev/null | \
 	  sed > $@T \
 	      -e '/^=========/,/^=========/!d;/^=========/d' \
-	      -e 's/^.*\*(\.dynbss).*$$/& \
-		 PROVIDE(__start___libc_freeres_ptrs = .); \
-		 *(__libc_freeres_ptrs) \
-		 PROVIDE(__stop___libc_freeres_ptrs = .);/'\
 	      -e 's@^.*\*(\.jcr).*$$@& \
-		 PROVIDE(__start___libc_subfreeres = .);\
-		 __libc_subfreeres : { *(__libc_subfreeres) }\
-		 PROVIDE(__stop___libc_subfreeres = .);\
 		 PROVIDE(__start___libc_IO_vtables = .);\
 		 __libc_IO_vtables : { *(__libc_IO_vtables) }\
 		 PROVIDE(__stop___libc_IO_vtables = .);\
diff --git a/crypt/md5-crypt.c b/crypt/md5-crypt.c
index 7c4fb9fb97..9660cdd698 100644
--- a/crypt/md5-crypt.c
+++ b/crypt/md5-crypt.c
@@ -299,10 +299,7 @@  __md5_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
   return buffer;
 }
 
-#ifndef _LIBC
-# define libc_freeres_ptr(decl) decl
-#endif
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
 char *
 __md5_crypt (const char *key, const char *salt)
@@ -330,7 +327,10 @@  __md5_crypt (const char *key, const char *salt)
 static void
 __attribute__ ((__destructor__))
 free_mem (void)
+#else
+void
+__md5_crypt_freemem (void)
+#endif
 {
   free (buffer);
 }
-#endif
diff --git a/crypt/sha256-crypt.c b/crypt/sha256-crypt.c
index a98a968a8b..75fd582429 100644
--- a/crypt/sha256-crypt.c
+++ b/crypt/sha256-crypt.c
@@ -386,10 +386,7 @@  __sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
   return buffer;
 }
 
-#ifndef _LIBC
-# define libc_freeres_ptr(decl) decl
-#endif
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
 /* This entry point is equivalent to the `crypt' function in Unix
    libcs.  */
@@ -422,7 +419,10 @@  __sha256_crypt (const char *key, const char *salt)
 static void
 __attribute__ ((__destructor__))
 free_mem (void)
+#else
+void
+__sha256_crypt_freemem (void)
+#endif
 {
   free (buffer);
 }
-#endif
diff --git a/crypt/sha512-crypt.c b/crypt/sha512-crypt.c
index ea13527c09..ae6ecaef16 100644
--- a/crypt/sha512-crypt.c
+++ b/crypt/sha512-crypt.c
@@ -408,10 +408,7 @@  __sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
   return buffer;
 }
 
-#ifndef _LIBC
-# define libc_freeres_ptr(decl) decl
-#endif
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
 /* This entry point is equivalent to the `crypt' function in Unix
    libcs.  */
@@ -444,7 +441,10 @@  __sha512_crypt (const char *key, const char *salt)
 static void
 __attribute__ ((__destructor__))
 free_mem (void)
+#else
+void
+__sha512_crypt_freemem (void)
+#endif
 {
   free (buffer);
 }
-#endif
diff --git a/elf/Makefile b/elf/Makefile
index eca7b28ab5..6de2ca6d6c 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -612,6 +612,7 @@  $(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
 	    --required=_IO_wmem_jumps \
 	    --required=_IO_wstr_jumps \
 	    --required=_IO_wstrn_jumps \
+	    --required=__libc_freeres_funcs \
 	    --optional=_IO_old_cookie_jumps \
 	    --optional=_IO_old_file_jumps \
 	    --optional=_IO_old_proc_jumps \
diff --git a/elf/dl-libc.c b/elf/dl-libc.c
index 266e068da6..06bce2f6dd 100644
--- a/elf/dl-libc.c
+++ b/elf/dl-libc.c
@@ -228,7 +228,7 @@  __libc_dlclose (void *map)
 }
 
 
-static bool __libc_freeres_fn_section
+static bool
 free_slotinfo (struct dtv_slotinfo_list **elemp)
 {
   size_t cnt;
@@ -256,7 +256,7 @@  free_slotinfo (struct dtv_slotinfo_list **elemp)
 }
 
 
-libc_freeres_fn (free_mem)
+void __libc_freemem (void)
 {
   struct link_map *l;
   struct r_search_path_elem *d;
diff --git a/grp/fgetgrent.c b/grp/fgetgrent.c
index fd2b4d32d6..df04530b04 100644
--- a/grp/fgetgrent.c
+++ b/grp/fgetgrent.c
@@ -25,7 +25,7 @@ 
 /* We need to protect the dynamic buffer handling.  */
 __libc_lock_define_initialized (static, lock);
 
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
 /* Read one entry from the given stream.  */
 struct group *
@@ -82,3 +82,9 @@  fgetgrent (FILE *stream)
 
   return result;
 }
+
+void
+__libc_fgetgrent_freemem (void)
+{
+  free (buffer);
+}
diff --git a/gshadow/fgetsgent.c b/gshadow/fgetsgent.c
index 02f9c7d643..4d9282e560 100644
--- a/gshadow/fgetsgent.c
+++ b/gshadow/fgetsgent.c
@@ -28,7 +28,7 @@ 
 /* We need to protect the dynamic buffer handling.  */
 __libc_lock_define_initialized (static, lock);
 
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
 /* Read one shadow entry from the given stream.  */
 struct sgrp *
diff --git a/iconv/gconv_cache.c b/iconv/gconv_cache.c
index 8d47545c41..e250228561 100644
--- a/iconv/gconv_cache.c
+++ b/iconv/gconv_cache.c
@@ -449,7 +449,8 @@  __gconv_release_cache (struct __gconv_step *steps, size_t nsteps)
 
 
 /* Free all resources if necessary.  */
-libc_freeres_fn (free_mem)
+void
+__gconv_cache_freemem (void)
 {
   if (cache_malloced)
     free (gconv_cache);
diff --git a/iconv/gconv_conf.c b/iconv/gconv_conf.c
index f069e28323..608109c040 100644
--- a/iconv/gconv_conf.c
+++ b/iconv/gconv_conf.c
@@ -530,7 +530,8 @@  __gconv_load_conf (void)
 
 
 /* Free all resources if necessary.  */
-libc_freeres_fn (free_mem)
+void
+__gconv_conf_freemem (void)
 {
   if (__gconv_path_elem != NULL && __gconv_path_elem != &empty_path_elem)
     free ((void *) __gconv_path_elem);
diff --git a/iconv/gconv_db.c b/iconv/gconv_db.c
index 4943c954a3..7aeae5993c 100644
--- a/iconv/gconv_db.c
+++ b/iconv/gconv_db.c
@@ -169,7 +169,7 @@  add_derivation (const char *fromset, const char *toset,
      not all memory will be freed.  */
 }
 
-static void __libc_freeres_fn_section
+static void
 free_derivation (void *p)
 {
   struct known_derivation *deriv = (struct known_derivation *) p;
@@ -793,7 +793,6 @@  __gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
 
 /* Free the modules mentioned.  */
 static void
-__libc_freeres_fn_section
 free_modules_db (struct gconv_module *node)
 {
   if (node->left != NULL)
@@ -812,7 +811,8 @@  free_modules_db (struct gconv_module *node)
 
 
 /* Free all resources if necessary.  */
-libc_freeres_fn (free_mem)
+void
+__gconv_db_freemem (void)
 {
   /* First free locale memory.  This needs to be done before freeing
      derivations, as ctype cleanup functions dereference steps arrays which we
diff --git a/iconv/gconv_dl.c b/iconv/gconv_dl.c
index 5ed982636a..6cc05e42fc 100644
--- a/iconv/gconv_dl.c
+++ b/iconv/gconv_dl.c
@@ -184,7 +184,7 @@  __gconv_release_shlib (struct __gconv_loaded_object *handle)
 
 
 /* We run this if we debug the memory allocation.  */
-static void __libc_freeres_fn_section
+static void
 do_release_all (void *nodep)
 {
   struct __gconv_loaded_object *obj = (struct __gconv_loaded_object *) nodep;
@@ -196,7 +196,8 @@  do_release_all (void *nodep)
   free (obj);
 }
 
-libc_freeres_fn (free_mem)
+void
+__gconv_dl_freemem (void)
 {
   __tdestroy (loaded, do_release_all);
   loaded = NULL;
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index f4437ff6ad..a1d422131f 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -237,79 +237,6 @@  requires at runtime the shared libraries from the glibc version used \
 for linking")
 #endif
 
-/* Resource Freeing Hooks:
-
-   Normally a process exits and the OS cleans up any allocated
-   memory.  However, when tooling like mtrace or valgrind is monitoring
-   the process we need to free all resources that are part of the
-   process in order to provide the consistency required to track
-   memory leaks.
-
-   A single public API exists and is __libc_freeres(), and this is used
-   by applications like valgrind to freee resouces.
-
-   There are 3 cases:
-
-   (a) __libc_freeres
-
-	In this case all you need to do is define the freeing routine:
-
-	foo.c:
-	libfoo_freeres_fn (foo_freeres)
-	{
-	  complex_free (mem);
-	}
-
-	This ensures the function is called at the right point to free
-	resources.
-
-   (b) __libc_freeres_ptr
-
-	The framework for (a) iterates over the list of pointers-to-free
-	in (b) and frees them.
-
-	foo.c:
-	libc_freeres_ptr (static char *foo_buffer);
-
-	Freeing these resources alaways happens last and is equivalent
-	to registering a function that does 'free (foo_buffer)'.
-
-   (c) Explicit lists of free routines to call or objects to free.
-
-	It is the intended goal to remove (a) and (b) which have some
-	non-determinism based on link order, and instead use explicit
-	lists of functions and frees to resolve cleanup ordering issues
-	and make it easy to debug and maintain.
-
-	As of today the following subsystems use (c):
-
-	Per-thread cleanup:
-	* malloc/thread-freeres.c
-
-	libdl cleanup:
-	* dlfcn/dlfreeres.c
-
-	libpthread cleanup:
-	* nptl/nptlfreeres.c
-
-	So if you need any shutdown routines to run you should add them
-	directly to the appropriate subsystem's shutdown list.  */
-
-/* Resource pointers to free in libc.so.  */
-#define libc_freeres_ptr(decl) \
-  __make_section_unallocated ("__libc_freeres_ptrs, \"aw\", %nobits") \
-  decl __attribute__ ((section ("__libc_freeres_ptrs" __sec_comment)))
-
-/* Resource freeing functions from libc.so go in this section.  */
-#define __libc_freeres_fn_section \
-  __attribute__ ((__used__, section ("__libc_freeres_fn")))
-
-/* Resource freeing functions for libc.so.  */
-#define libc_freeres_fn(name) \
-  static void name (void) __attribute_used__ __libc_freeres_fn_section;	\
-  text_set_element (__libc_subfreeres, name);				\
-  static void name (void)
-
 /* Declare SYMBOL to be TYPE (`function' or `object') of SIZE bytes
    alias to ORIGINAL, when the assembler supports such declarations
    (such as in ELF).
diff --git a/inet/getnameinfo.c b/inet/getnameinfo.c
index 4733be6f5b..6b1cf206c4 100644
--- a/inet/getnameinfo.c
+++ b/inet/getnameinfo.c
@@ -77,7 +77,7 @@  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 # define min(x,y) (((x) > (y)) ? (y) : (x))
 #endif /* min */
 
-libc_freeres_ptr (static char *domain);
+static char *domain;
 
 /* Former NI_IDN_ALLOW_UNASSIGNED, NI_IDN_USE_STD3_ASCII_RULES flags,
    now ignored.  */
@@ -556,3 +556,9 @@  getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
   return 0;
 }
 libc_hidden_def (getnameinfo)
+
+void
+__libc_getnameinfo_freemem (void)
+{
+  free (domain);
+}
diff --git a/inet/getnetgrent.c b/inet/getnetgrent.c
index e354194b72..00549d046d 100644
--- a/inet/getnetgrent.c
+++ b/inet/getnetgrent.c
@@ -21,7 +21,7 @@ 
 #include <libc-lock.h>
 
 /* Static buffer for return value.  We allocate it when needed.  */
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 /* All three strings should fit in a block of 1kB size.  */
 #define BUFSIZE 1024
 
@@ -47,3 +47,9 @@  getnetgrent (char **hostp, char **userp, char **domainp)
 
   return __getnetgrent_r (hostp, userp, domainp, buffer, BUFSIZE);
 }
+
+void
+__libc_getnetgrent_freemem (void)
+{
+  free (buffer);
+}
diff --git a/inet/rcmd.c b/inet/rcmd.c
index 2b95fa11d8..40326ab501 100644
--- a/inet/rcmd.c
+++ b/inet/rcmd.c
@@ -98,7 +98,7 @@  int iruserok (uint32_t raddr, int superuser, const char *ruser,
 
 libc_hidden_proto (iruserok_af)
 
-libc_freeres_ptr(static char *ahostbuf);
+static char *ahostbuf;
 
 int
 rcmd_af (char **ahost, u_short rport, const char *locuser, const char *remuser,
@@ -817,3 +817,9 @@  __validuser2_sa (FILE *hostf, struct sockaddr *ra, size_t ralen,
 
     return retval;
 }
+
+void
+__libc_rcmd_freemem (void)
+{
+  free (ahostbuf);
+}
diff --git a/inet/rexec.c b/inet/rexec.c
index 064e979d68..354c73ddc6 100644
--- a/inet/rexec.c
+++ b/inet/rexec.c
@@ -42,7 +42,7 @@ 
 #include <sys/uio.h>
 
 int	rexecoptions;
-libc_freeres_ptr (static char *ahostbuf);
+static char *ahostbuf;
 
 int
 rexec_af (char **ahost, int rport, const char *name, const char *pass,
@@ -196,3 +196,9 @@  rexec (char **ahost, int rport, const char *name, const char *pass,
 {
 	return rexec_af(ahost, rport, name, pass, cmd, fd2p, AF_INET);
 }
+
+void
+__libc_rexec_freemem (void)
+{
+  free (ahostbuf);
+}
diff --git a/intl/dcigettext.c b/intl/dcigettext.c
index 1fc074a414..bfac152908 100644
--- a/intl/dcigettext.c
+++ b/intl/dcigettext.c
@@ -1671,7 +1671,8 @@  mempcpy (void *dest, const void *src, size_t n)
 #ifdef _LIBC
 /* If we want to free all resources we have to do some work at
    program's end.  */
-libc_freeres_fn (free_mem)
+void
+__intl_freemem (void)
 {
   void *old;
 
diff --git a/intl/finddomain.c b/intl/finddomain.c
index 416e28ad76..58de876e28 100644
--- a/intl/finddomain.c
+++ b/intl/finddomain.c
@@ -185,7 +185,7 @@  out:
 #ifdef _LIBC
 /* This is called from iconv/gconv_db.c's free_mem, as locales must
    be freed before freeing gconv steps arrays.  */
-void __libc_freeres_fn_section
+void
 _nl_finddomain_subfreeres (void)
 {
   struct loaded_l10nfile *runp = _nl_loaded_domains;
diff --git a/intl/loadmsgcat.c b/intl/loadmsgcat.c
index 98d5f53232..0b98f233dd 100644
--- a/intl/loadmsgcat.c
+++ b/intl/loadmsgcat.c
@@ -1284,7 +1284,6 @@  _nl_load_domain (struct loaded_l10nfile *domain_file,
 
 #ifdef _LIBC
 void
-__libc_freeres_fn_section
 _nl_unload_domain (struct loaded_domain *domain)
 {
   size_t i;
diff --git a/intl/localealias.c b/intl/localealias.c
index b36092363a..4bf068d0ea 100644
--- a/intl/localealias.c
+++ b/intl/localealias.c
@@ -126,14 +126,10 @@  struct alias_map
 };
 
 
-#ifndef _LIBC
-# define libc_freeres_ptr(decl) decl
-#endif
-
-libc_freeres_ptr (static char *string_space);
+static char *string_space;
 static size_t string_space_act;
 static size_t string_space_max;
-libc_freeres_ptr (static struct alias_map *map);
+static struct alias_map *map;
 static size_t nmap;
 static size_t maxmap;
 
@@ -439,3 +435,10 @@  alias_compare (const struct alias_map *map1, const struct alias_map *map2)
   return c1 - c2;
 #endif
 }
+
+void
+__libc_localealias_freemem (void)
+{
+  free (string_space);
+  free (map);
+}
diff --git a/libio/genops.c b/libio/genops.c
index 8a7fc4f7c5..b3a0603a0a 100644
--- a/libio/genops.c
+++ b/libio/genops.c
@@ -765,8 +765,8 @@  weak_alias (_IO_flush_all_linebuffered, _flushlbf)
    actual buffer because this will happen anyway once the program
    terminated.  If we do want to look for memory leaks we have to free
    the buffers.  Whether something is freed is determined by the
-   function sin the libc_freeres section.  Those are called as part of
-   the atexit routine, just like _IO_cleanup.  The problem is we do
+   function called by __libc_freeres (those are not called as part of
+   the atexit routine, different from  _IO_cleanup).  The problem is we do
    not know whether the freeres code is called first or _IO_cleanup.
    if the former is the case, we set the DEALLOC_BUFFER variable to
    true and _IO_unbuffer_all will take care of the rest.  If
@@ -844,8 +844,8 @@  _IO_unbuffer_all (void)
 #endif
 }
 
-
-libc_freeres_fn (buffer_free)
+void
+__libio_freemem (void)
 {
   dealloc_buffers = true;
 
diff --git a/locale/loadarchive.c b/locale/loadarchive.c
index fcc4913319..60ae1daa2c 100644
--- a/locale/loadarchive.c
+++ b/locale/loadarchive.c
@@ -498,7 +498,7 @@  _nl_load_locale_from_archive (int category, const char **namep)
   return lia->data[category];
 }
 
-void __libc_freeres_fn_section
+void
 _nl_archive_subfreeres (void)
 {
   struct locale_in_archive *lia;
diff --git a/locale/localeinfo.h b/locale/localeinfo.h
index fd43033a19..1bf4215e71 100644
--- a/locale/localeinfo.h
+++ b/locale/localeinfo.h
@@ -381,10 +381,10 @@  extern struct __locale_data *_nl_load_locale_from_archive (int category,
 							   const char **namep)
      attribute_hidden;
 
-/* Subroutine of setlocale's __libc_subfreeres hook.  */
+/* Subroutine of setlocale's free resource.  */
 extern void _nl_archive_subfreeres (void) attribute_hidden;
 
-/* Subroutine of gconv-db's __libc_subfreeres hook.  */
+/* Subroutine of gconv-db's free resource.  */
 extern void _nl_locale_subfreeres (void) attribute_hidden;
 
 /* Validate the contents of a locale file and set up the in-core
diff --git a/locale/setlocale.c b/locale/setlocale.c
index 56c14d8533..d1e773f46a 100644
--- a/locale/setlocale.c
+++ b/locale/setlocale.c
@@ -468,7 +468,7 @@  setlocale (int category, const char *locale)
 }
 libc_hidden_def (setlocale)
 
-static void __libc_freeres_fn_section
+static void
 free_category (int category,
 	       struct __locale_data *here, struct __locale_data *c_data)
 {
@@ -498,7 +498,7 @@  free_category (int category,
 
 /* This is called from iconv/gconv_db.c's free_mem, as locales must
    be freed before freeing gconv steps arrays.  */
-void __libc_freeres_fn_section
+void
 _nl_locale_subfreeres (void)
 {
 #ifdef NL_CURRENT_INDIRECT
diff --git a/login/getutent.c b/login/getutent.c
index 8d2f1207f8..4ee31eade3 100644
--- a/login/getutent.c
+++ b/login/getutent.c
@@ -20,7 +20,7 @@ 
 
 
 /* Local buffer to store the result.  */
-libc_freeres_ptr (static struct utmp *buffer);
+static struct utmp *buffer;
 
 
 struct utmp *
@@ -42,3 +42,9 @@  __getutent (void)
 }
 libc_hidden_def (__getutent)
 weak_alias (__getutent, getutent)
+
+void
+__libc_getutent_freemem (void)
+{
+  free (buffer);
+}
diff --git a/login/getutid.c b/login/getutid.c
index 303c178db1..680c4f6855 100644
--- a/login/getutid.c
+++ b/login/getutid.c
@@ -20,7 +20,7 @@ 
 
 
 /* Local buffer to store the result.  */
-libc_freeres_ptr (static struct utmp *buffer);
+static struct utmp *buffer;
 
 struct utmp *
 __getutid (const struct utmp *id)
@@ -40,3 +40,9 @@  __getutid (const struct utmp *id)
 }
 libc_hidden_def (__getutid)
 weak_alias (__getutid, getutid)
+
+void
+__libc_getutid_freemem (void)
+{
+  free (buffer);
+}
diff --git a/login/getutline.c b/login/getutline.c
index 661eff3a3c..12f7994de1 100644
--- a/login/getutline.c
+++ b/login/getutline.c
@@ -20,7 +20,7 @@ 
 
 
 /* Local buffer to store the result.  */
-libc_freeres_ptr (static struct utmp *buffer);
+static struct utmp *buffer;
 
 
 struct utmp *
@@ -41,3 +41,9 @@  __getutline (const struct utmp *line)
 }
 libc_hidden_def (__getutline)
 weak_alias (__getutline, getutline)
+
+void
+__libc_getutline_freemem (void)
+{
+  free (buffer);
+}
diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
index be8c2a35fc..ce5d010133 100644
--- a/malloc/set-freeres.c
+++ b/malloc/set-freeres.c
@@ -15,32 +15,210 @@ 
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <atomic.h>
 #include <stdlib.h>
-#include <set-hooks.h>
 #include <libc-internal.h>
 #include <unwind-link.h>
 #include <dlfcn/dlerror.h>
 #include <ldsodefs.h>
+#include <set-freeres-system.h>
 
 #include "../nss/nsswitch.h"
 #include "../libio/libioP.h"
 
-DEFINE_HOOK (__libc_subfreeres, (void));
-
-symbol_set_define (__libc_freeres_ptrs);
-
-extern void __libpthread_freeres (void)
-#if PTHREAD_IN_LIBC && defined SHARED
-/* It is possible to call __libpthread_freeres directly in shared
-   builds with an integrated libpthread.  */
-  attribute_hidden
-#else
-  __attribute__ ((weak))
-#endif
-  ;
-
-void __libc_freeres_fn_section
+/* Resource Freeing Hooks:
+
+   Normally a process exits and the OS cleans up any allocated
+   memory.  However, when tooling like mtrace or valgrind is monitoring
+   the process we need to free all resources that are part of the
+   process in order to provide the consistency required to track
+   memory leaks.
+
+   A single public API exists and is __libc_freeres(), and this is used
+   by applications like valgrind to freee resouces.
+
+   Each free routines must be explicit listed below, with the care to define
+   weak functions for external symbol if applicable.   */
+
+/* From libc.so.  */
+extern void __libc_freemem (void) weak_function;
+extern void __hdestroy (void) weak_function;
+extern void __gconv_cache_freemem (void) weak_function;
+extern void __gconv_conf_freemem (void) weak_function;
+extern void __gconv_db_freemem (void) weak_function;
+extern void __gconv_dl_freemem (void) weak_function;
+extern void __intl_freemem (void) weak_function;
+extern void __libio_freemem (void) weak_function;
+extern void __libc_fstab_freemem (void) weak_function;
+extern void __nscd_gr_map_freemem (void) weak_function;
+extern void __nscd_hst_map_freemem (void) weak_function;
+extern void __nscd_pw_map_freemem (void) weak_function;
+extern void __nscd_serv_map_freemem (void) weak_function;
+extern void __nscd_group_map_freemem (void) weak_function;
+extern void __libc_regcomp_freemem (void) weak_function;
+extern void __libc_atfork_freemem (void) weak_function;
+extern void __libc_resolv_conf_freemem (void) weak_function;
+extern void __libc_resolv_res_hconf_freemem (void) weak_function;
+extern void __res_thread_freeres (void) weak_function;
+extern void __libc_printf_freemem (void) weak_function;
+extern void __libc_fmtmsg_freemem (void) weak_function;
+extern void __libc_setenv_freemem (void) weak_function;
+extern void __rpc_freemem (void) weak_function;
+extern void __rpc_thread_destroy (void) weak_function;
+extern void __libc_getaddrinfo_freemem (void) weak_function;
+extern void __libc_tzset_freemem (void) weak_function;
+extern void __libc_fgetgrent_freemem (void) weak_function;
+extern void __libc_getnameinfo_freemem (void) weak_function;
+extern void __libc_getnetgrent_freemem (void) weak_function;
+extern void __libc_rcmd_freemem (void) weak_function;
+extern void __libc_rexec_freemem (void) weak_function;
+extern void __libc_localealias_freemem (void) weak_function;
+extern void __libc_getutent_freemem (void) weak_function;
+extern void __libc_getutid_freemem (void) weak_function;
+extern void __libc_getutline_freemem (void) weak_function;
+extern void __libc_mntent_freemem (void) weak_function;
+extern void __libc_fgetpwent_freemem (void) weak_function;
+extern void __libc_fgetspent_freemem (void) weak_function;
+extern void __libc_reg_printf_freemem (void) weak_function;
+extern void __libc_reg_type_freemem (void) weak_function;
+extern void __libc_tzfile_freemem (void) weak_function;
+/* From misc/efgcvt-template.c  */
+extern void __libc_efgcvt_freemem (void) weak_function;
+extern void __libc_qefgcvt_freemem (void) weak_function;
+/* From nss/getXXbyYY.c  */
+extern void __libc_getgrgid_freemem (void) weak_function;
+extern void __libc_getgrnam_freemem (void) weak_function;
+extern void __libc_getpwnam_freemem (void) weak_function;
+extern void __libc_getpwuid_freemem (void) weak_function;
+extern void __libc_getspnam_freemem (void) weak_function;
+extern void __libc_getaliasbyname_freemem (void) weak_function;
+extern void __libc_gethostbyaddr_freemem (void) weak_function;
+extern void __libc_gethostbyname_freemem (void) weak_function;
+extern void __libc_gethostbyname2_freemem (void) weak_function;
+extern void __libc_getnetbyaddr_freemem (void) weak_function;
+extern void __libc_getnetbyname_freemem (void) weak_function;
+extern void __libc_getprotobynumber_freemem (void) weak_function;
+extern void __libc_getprotobyname_freemem (void) weak_function;
+extern void __libc_getrpcbyname_freemem (void) weak_function;
+extern void __libc_getrpcbynumber_freemem (void) weak_function;
+extern void __libc_getservbyname_freemem (void) weak_function;
+extern void __libc_getservbyport_freemem (void) weak_function;;
+/* From nss/getXXent.c */
+extern void __libc_getgrent_freemem (void) weak_function;
+extern void __libc_getpwent_freemem (void) weak_function;
+extern void __libc_getspent_freemem (void) weak_function;
+extern void __libc_getaliasent_freemem (void) weak_function;
+extern void __libc_gethostent_freemem (void) weak_function;
+extern void __libc_getnetent_freemem (void) weak_function;
+extern void __libc_getprotoent_freemem (void) weak_function;
+extern void __libc_getrpcent_freemem (void) weak_function;
+extern void __libc_getservent_freemem (void) weak_function;
+
+/* From either libc.so or libpthread.so  */
+extern void __libpthread_freeres (void) weak_function;
+/* From either libc.so or libanl.so  */
+extern void __gai_freemem (void) weak_function;
+/* From either libc.so or librt.so  */
+extern void __aio_freemem (void) weak_function;
+
+/* From libcrypto.so.  */
+extern void __md5_crypt_freemem (void) weak_function;
+extern void __sha256_crypt_freemem (void) weak_function;
+extern void __sha512_crypt_freemem (void) weak_function;
+
+static void (*__libc_freeres_funcs[])(void) attribute_relro =
+{
+  __libc_freemem,
+  __hdestroy,
+  __gconv_cache_freemem,
+  __gconv_conf_freemem,
+  __gconv_db_freemem,
+  __gconv_dl_freemem,
+  __intl_freemem,
+  __libio_freemem,
+  __libc_fstab_freemem,
+  __nscd_gr_map_freemem,
+  __nscd_hst_map_freemem,
+  __nscd_pw_map_freemem,
+  __nscd_serv_map_freemem,
+  __nscd_group_map_freemem,
+  __libc_regcomp_freemem,
+  __libc_atfork_freemem,
+  /* __res_thread_freeres deallocates the per-thread resolv_context, which
+     in turn drop the reference count of the current global object.  So
+     it need to be before __libc_resolv_conf_freemem.  */
+  __res_thread_freeres,
+  __libc_resolv_res_hconf_freemem,
+  __libc_resolv_conf_freemem,
+  __libc_printf_freemem,
+  __libc_fmtmsg_freemem,
+  __libc_setenv_freemem,
+  __rpc_freemem,
+  __rpc_thread_destroy,
+  __libc_getaddrinfo_freemem,
+  __libc_tzset_freemem,
+  __libc_fgetgrent_freemem,
+  __libc_getnameinfo_freemem,
+  __libc_getnetgrent_freemem,
+  __libc_rcmd_freemem,
+  __libc_rexec_freemem,
+  __libc_localealias_freemem,
+  __libc_getutent_freemem,
+  __libc_getutid_freemem,
+  __libc_getutline_freemem,
+  __libc_mntent_freemem,
+  __libc_fgetpwent_freemem,
+  __libc_fgetspent_freemem,
+  __libc_reg_printf_freemem,
+  __libc_reg_type_freemem,
+  __libc_tzfile_freemem,
+
+  __libc_efgcvt_freemem,
+  __libc_qefgcvt_freemem,
+
+  __libc_getgrgid_freemem,
+  __libc_getgrnam_freemem,
+  __libc_getpwnam_freemem,
+  __libc_getpwuid_freemem,
+  __libc_getspnam_freemem,
+  __libc_getaliasbyname_freemem,
+  __libc_gethostbyaddr_freemem,
+  __libc_gethostbyname_freemem,
+  __libc_gethostbyname2_freemem,
+  __libc_getnetbyaddr_freemem,
+  __libc_getnetbyname_freemem,
+  __libc_getprotobynumber_freemem,
+  __libc_getprotobyname_freemem,
+  __libc_getrpcbyname_freemem,
+  __libc_getrpcbynumber_freemem,
+  __libc_getservbyname_freemem,
+  __libc_getservbyport_freemem,
+
+  __libc_getgrent_freemem,
+  __libc_getpwent_freemem,
+  __libc_getspent_freemem,
+  __libc_getaliasent_freemem,
+  __libc_gethostent_freemem,
+  __libc_getnetent_freemem,
+  __libc_getprotoent_freemem,
+  __libc_getrpcent_freemem,
+  __libc_getservent_freemem,
+
+  __gai_freemem,
+
+  __aio_freemem,
+
+  __libpthread_freeres,
+
+  __md5_crypt_freemem,
+  __sha256_crypt_freemem,
+  __sha512_crypt_freemem,
+
+  SET_FREERES_SYSTEM_FUNCS
+};
+
+void
 __libc_freeres (void)
 {
   /* This function might be called from different places.  So better
@@ -49,8 +227,6 @@  __libc_freeres (void)
 
   if (!atomic_compare_and_exchange_bool_acq (&already_called, 1, 0))
     {
-      void *const *p;
-
       call_function_static_weak (__nss_module_freeres);
       call_function_static_weak (__nss_action_freeres);
       call_function_static_weak (__nss_database_freeres);
@@ -58,9 +234,9 @@  __libc_freeres (void)
       _IO_cleanup ();
 
       /* We run the resource freeing after IO cleanup.  */
-      RUN_HOOK (__libc_subfreeres, ());
-
-      call_function_static_weak (__libpthread_freeres);
+      for (int i = 0; i < array_length (__libc_freeres_funcs); i++)
+	if (__libc_freeres_funcs[i] != NULL)
+	  __libc_freeres_funcs[i] ();
 
 #ifdef SHARED
       __libc_unwind_link_freeres ();
@@ -71,10 +247,6 @@  __libc_freeres (void)
 #ifdef SHARED
       GLRO (dl_libc_freeres) ();
 #endif
-
-      for (p = symbol_set_first_element (__libc_freeres_ptrs);
-           !symbol_set_end_p (__libc_freeres_ptrs, p); ++p)
-        free (*p);
     }
 }
 libc_hidden_def (__libc_freeres)
diff --git a/malloc/thread-freeres.c b/malloc/thread-freeres.c
index b22e1d789f..11bf290aa6 100644
--- a/malloc/thread-freeres.c
+++ b/malloc/thread-freeres.c
@@ -27,7 +27,7 @@ 
 
 /* Thread shutdown function.  Note that this function must be called
    for threads during shutdown for correctness reasons.  Unlike
-   __libc_subfreeres, skipping calls to it is not a valid optimization.
+   __libc_freeres, skipping calls to it is not a valid optimization.
    This is called directly from pthread_create as the thread exits.  */
 void
 __libc_thread_freeres (void)
diff --git a/misc/efgcvt-template.c b/misc/efgcvt-template.c
index 87a90e78d4..8ed4896a0c 100644
--- a/misc/efgcvt-template.c
+++ b/misc/efgcvt-template.c
@@ -38,7 +38,7 @@ 
 
 static char FCVT_BUFFER[MAXDIG];
 static char ECVT_BUFFER[MAXDIG];
-libc_freeres_ptr (static char *FCVT_BUFPTR);
+static char *FCVT_BUFPTR;
 
 char *
 __FCVT (FLOAT_TYPE value, int ndigit, int *decpt, int *sign)
@@ -73,3 +73,9 @@  __GCVT (FLOAT_TYPE value, int ndigit, char *buf)
   SPRINTF (buf, "%.*" FLOAT_FMT_FLAG "g", MIN (ndigit, NDIGIT_MAX), value);
   return buf;
 }
+
+void
+__EFGCVT_FREEMEM (void)
+{
+  free (FCVT_BUFPTR);
+}
diff --git a/misc/efgcvt.c b/misc/efgcvt.c
index 95f1443d60..33bee945d5 100644
--- a/misc/efgcvt.c
+++ b/misc/efgcvt.c
@@ -24,6 +24,7 @@ 
 #define __GCVT __gcvt
 #define __ECVT_R __ecvt_r
 #define __FCVT_R __fcvt_r
+#define __EFGCVT_FREEMEM __libc_efgcvt_freemem
 #include <efgcvt-dbl-macros.h>
 #include <efgcvt-template.c>
 
diff --git a/misc/fstab.c b/misc/fstab.c
index 952f7adc36..f55657ee17 100644
--- a/misc/fstab.c
+++ b/misc/fstab.c
@@ -177,7 +177,8 @@  fstab_convert (struct fstab_state *state)
 
 /* Make sure the memory is freed if the programs ends while in
    memory-debugging mode and something actually was allocated.  */
-libc_freeres_fn (fstab_free)
+void
+__libc_fstab_freemem (void)
 {
   char *buffer;
 
diff --git a/misc/hsearch.c b/misc/hsearch.c
index 3b7be37f81..37994b053e 100644
--- a/misc/hsearch.c
+++ b/misc/hsearch.c
@@ -46,7 +46,3 @@  __hdestroy (void)
   __hdestroy_r (&htab);
 }
 weak_alias (__hdestroy, hdestroy)
-
-/* Make sure the table is freed if we want to free everything before
-   exiting.  */
-text_set_element (__libc_subfreeres, __hdestroy);
diff --git a/misc/mntent.c b/misc/mntent.c
index 44e54df669..61d29b5f0e 100644
--- a/misc/mntent.c
+++ b/misc/mntent.c
@@ -28,7 +28,7 @@  struct mntent_buffer
 
 /* We don't want to allocate the static buffer all the time since it
    is not always used (in fact, rather infrequently).  */
-libc_freeres_ptr (static void *mntent_buffer);
+static void *mntent_buffer;
 
 static void *
 allocate (void *closure)
@@ -56,3 +56,9 @@  getmntent (FILE *stream)
   return __getmntent_r (stream, &buffer->m,
 			buffer->buffer, sizeof (buffer->buffer));
 }
+
+void
+__libc_mntent_freemem (void)
+{
+  free (mntent_buffer);
+}
diff --git a/misc/qefgcvt.c b/misc/qefgcvt.c
index f48e0b6016..b1f2833725 100644
--- a/misc/qefgcvt.c
+++ b/misc/qefgcvt.c
@@ -24,6 +24,7 @@ 
 #define __GCVT __qgcvt
 #define __ECVT_R __qecvt_r
 #define __FCVT_R __qfcvt_r
+#define __EFGCVT_FREEMEM __libc_qefgcvt_freemem
 #include <efgcvt-ldbl-macros.h>
 #include <efgcvt-template.c>
 
diff --git a/misc/unwind-link.c b/misc/unwind-link.c
index e8653bbaf1..0e01eed7fc 100644
--- a/misc/unwind-link.c
+++ b/misc/unwind-link.c
@@ -131,7 +131,7 @@  __libc_unwind_link_after_fork (void)
     }
 }
 
-void __libc_freeres_fn_section
+void
 __libc_unwind_link_freeres (void)
 {
   if (global_libgcc_handle != NULL)
diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c
index bde3b588a0..5afa258c19 100644
--- a/nscd/nscd_getgr_r.c
+++ b/nscd/nscd_getgr_r.c
@@ -68,7 +68,8 @@  libc_locked_map_ptr (,__gr_map_handle) attribute_hidden;
 /* Note that we only free the structure if necessary.  The memory
    mapping is not removed since it is not visible to the malloc
    handling.  */
-libc_freeres_fn (gr_map_free)
+void
+__nscd_gr_map_freemem (void)
 {
   if (__gr_map_handle.mapped != NO_MAPPING)
     {
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
index 31d13580a1..a537364a75 100644
--- a/nscd/nscd_gethst_r.c
+++ b/nscd/nscd_gethst_r.c
@@ -81,7 +81,8 @@  libc_locked_map_ptr (, __hst_map_handle) attribute_hidden;
 /* Note that we only free the structure if necessary.  The memory
    mapping is not removed since it is not visible to the malloc
    handling.  */
-libc_freeres_fn (hst_map_free)
+void
+__nscd_hst_map_freemem (void)
 {
   if (__hst_map_handle.mapped != NO_MAPPING)
     {
diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c
index 82fdd17d8c..52256b58c4 100644
--- a/nscd/nscd_getpw_r.c
+++ b/nscd/nscd_getpw_r.c
@@ -67,7 +67,8 @@  libc_locked_map_ptr (static, map_handle);
 /* Note that we only free the structure if necessary.  The memory
    mapping is not removed since it is not visible to the malloc
    handling.  */
-libc_freeres_fn (pw_map_free)
+void
+__nscd_pw_map_freemem (void)
 {
   if (map_handle.mapped != NO_MAPPING)
     {
diff --git a/nscd/nscd_getserv_r.c b/nscd/nscd_getserv_r.c
index de843b3363..92c80fe8fe 100644
--- a/nscd/nscd_getserv_r.c
+++ b/nscd/nscd_getserv_r.c
@@ -62,7 +62,8 @@  libc_locked_map_ptr (, __serv_map_handle) attribute_hidden;
 /* Note that we only free the structure if necessary.  The memory
    mapping is not removed since it is not visible to the malloc
    handling.  */
-libc_freeres_fn (serv_map_free)
+void
+__nscd_serv_map_freemem (void)
 {
   if (__serv_map_handle.mapped != NO_MAPPING)
     {
diff --git a/nscd/nscd_netgroup.c b/nscd/nscd_netgroup.c
index 11b7f3214c..33c0096f86 100644
--- a/nscd/nscd_netgroup.c
+++ b/nscd/nscd_netgroup.c
@@ -31,7 +31,8 @@  libc_locked_map_ptr (static, map_handle);
 /* Note that we only free the structure if necessary.  The memory
    mapping is not removed since it is not visible to the malloc
    handling.  */
-libc_freeres_fn (pw_map_free)
+void
+__nscd_group_map_freemem (void)
 {
   if (map_handle.mapped != NO_MAPPING)
     {
diff --git a/nss/getXXbyYY.c b/nss/getXXbyYY.c
index 35a3a81e2b..566a510b5a 100644
--- a/nss/getXXbyYY.c
+++ b/nss/getXXbyYY.c
@@ -58,6 +58,9 @@ 
 #define APPEND_R1(name) name##_r
 #define INTERNAL(name) INTERNAL1 (name)
 #define INTERNAL1(name) __##name
+#define APPEND_FREEMEM_NAME1(name) __libc_##name##_freemem
+#define APPEND_FREEMEM_NAME(name) APPEND_FREEMEM_NAME1(name)
+#define FREEMEM_NAME APPEND_FREEMEM_NAME (FUNCTION_NAME)
 
 /* Sometimes we need to store error codes in the `h_errno' variable.  */
 #ifdef NEED_H_ERRNO
@@ -86,8 +89,13 @@  extern int INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf,
 __libc_lock_define_initialized (static, lock);
 
 /* This points to the static buffer used.  */
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
+void
+FREEMEM_NAME (void)
+{
+  free (buffer);
+}
 
 LOOKUP_TYPE *
 FUNCTION_NAME (ADD_PARAMS)
diff --git a/nss/getXXent.c b/nss/getXXent.c
index 35720a05dd..39d15ea9c5 100644
--- a/nss/getXXent.c
+++ b/nss/getXXent.c
@@ -43,6 +43,9 @@ 
 #define APPEND_R1(name) name##_r
 #define INTERNAL(name) INTERNAL1 (name)
 #define INTERNAL1(name) __##name
+#define APPEND_FREEMEM_NAME1(name) __libc_##name##_freemem
+#define APPEND_FREEMEM_NAME(name) APPEND_FREEMEM_NAME1(name)
+#define FREEMEM_NAME APPEND_FREEMEM_NAME (GETFUNC_NAME)
 
 /* Sometimes we need to store error codes in the `h_errno' variable.  */
 #ifdef NEED_H_ERRNO
@@ -62,8 +65,13 @@  extern int INTERNAL (REENTRANT_GETNAME) (LOOKUP_TYPE *resbuf, char *buffer,
 __libc_lock_define_initialized (static, lock);
 
 /* This points to the static buffer used.  */
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
+void
+FREEMEM_NAME (void)
+{
+  free (buffer);
+}
 
 LOOKUP_TYPE *
 GETFUNC_NAME (void)
diff --git a/nss/nss_action.c b/nss/nss_action.c
index babea4e735..893880790f 100644
--- a/nss/nss_action.c
+++ b/nss/nss_action.c
@@ -102,7 +102,7 @@  __nss_action_allocate (struct nss_action *actions, size_t count)
   return result;
 }
 
-void __libc_freeres_fn_section
+void
 __nss_action_freeres (void)
 {
   struct nss_action_list_wrapper *current = nss_actions;
diff --git a/nss/nss_database.c b/nss/nss_database.c
index f2ed2f2c25..7e5b7f362b 100644
--- a/nss/nss_database.c
+++ b/nss/nss_database.c
@@ -495,7 +495,7 @@  __nss_database_get_noreload (enum nss_database db)
   return result;
 }
 
-void __libc_freeres_fn_section
+void
 __nss_database_freeres (void)
 {
   free (global_database_state);
diff --git a/nss/nss_module.c b/nss/nss_module.c
index 444facb9b8..ed224018b1 100644
--- a/nss/nss_module.c
+++ b/nss/nss_module.c
@@ -416,7 +416,7 @@  __nss_module_disable_loading (void)
   __libc_lock_unlock (nss_module_list_lock);
 }
 
-void __libc_freeres_fn_section
+void
 __nss_module_freeres (void)
 {
   struct nss_module *current = nss_module_list;
diff --git a/posix/regcomp.c b/posix/regcomp.c
index 84fc1cc82b..634dccf03f 100644
--- a/posix/regcomp.c
+++ b/posix/regcomp.c
@@ -710,7 +710,8 @@  re_comp (const char *s)
 }
 
 #ifdef _LIBC
-libc_freeres_fn (free_mem)
+void
+__libc_regcomp_freemem (void)
 {
   __regfree (&re_comp_buf);
 }
diff --git a/posix/register-atfork.c b/posix/register-atfork.c
index c039fb454f..d386731b92 100644
--- a/posix/register-atfork.c
+++ b/posix/register-atfork.c
@@ -217,7 +217,8 @@  __run_postfork_handlers (enum __run_fork_handler_type who, _Bool do_locking,
 }
 
 
-libc_freeres_fn (free_mem)
+void
+__libc_atfork_freemem (void)
 {
   lll_lock (atfork_lock, LLL_PRIVATE);
 
diff --git a/pwd/fgetpwent.c b/pwd/fgetpwent.c
index 1ddc98a0d0..f93a045bcd 100644
--- a/pwd/fgetpwent.c
+++ b/pwd/fgetpwent.c
@@ -25,7 +25,7 @@ 
 /* We need to protect the dynamic buffer handling.  */
 __libc_lock_define_initialized (static, lock);
 
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
 /* Read one entry from the given stream.  */
 struct passwd *
@@ -82,3 +82,9 @@  fgetpwent (FILE *stream)
 
   return result;
 }
+
+void
+__libc_fgetpwent_freemem (void)
+{
+  free (buffer);
+}
diff --git a/resolv/gai_misc.c b/resolv/gai_misc.c
index 8ce3c778eb..5de7470fab 100644
--- a/resolv/gai_misc.c
+++ b/resolv/gai_misc.c
@@ -434,7 +434,8 @@  handle_requests (void *arg)
 
 
 /* Free allocated resources.  */
-libc_freeres_fn (free_res)
+void
+__gai_freemem (void)
 {
   size_t row;
 
diff --git a/resolv/res-close.c b/resolv/res-close.c
index dd116ea5c0..55e49b6db0 100644
--- a/resolv/res-close.c
+++ b/resolv/res-close.c
@@ -140,5 +140,3 @@  __res_thread_freeres (void)
   /* Make sure we do a full re-initialization the next time.  */
   _res.options = 0;
 }
-/* Also must be called when the main thread exits.  */
-text_set_element (__libc_subfreeres, __res_thread_freeres);
diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c
index dd09a2a255..c6dc47ac5b 100644
--- a/resolv/res_hconf.c
+++ b/resolv/res_hconf.c
@@ -330,7 +330,6 @@  _res_hconf_init (void)
 #if IS_IN (libc)
 # if defined SIOCGIFCONF && defined SIOCGIFNETMASK
 /* List of known interfaces.  */
-libc_freeres_ptr (
 static struct netaddr
 {
   int addrtype;
@@ -342,7 +341,13 @@  static struct netaddr
       uint32_t	mask;
     } ipv4;
   } u;
-} *ifaddrs);
+} *ifaddrs;
+
+void
+__libc_resolv_res_hconf_freemem (void)
+{
+  free (ifaddrs);
+}
 # endif
 
 /* Reorder addresses returned in a hostent such that the first address
diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c
index 6dd552bb47..2fb6b36839 100644
--- a/resolv/resolv_conf.c
+++ b/resolv/resolv_conf.c
@@ -654,7 +654,8 @@  __resolv_conf_detach (struct __res_state *resp)
 }
 
 /* Deallocate the global data.  */
-libc_freeres_fn (freeres)
+void
+__libc_resolv_conf_freemem (void)
 {
   /* No locking because this function is supposed to be called when
      the process has turned single-threaded.  */
diff --git a/resolv/tst-leaks2.c b/resolv/tst-leaks2.c
index 160d4728d5..48d643dcde 100644
--- a/resolv/tst-leaks2.c
+++ b/resolv/tst-leaks2.c
@@ -23,6 +23,8 @@ 
 #include <resolv.h>
 #include <support/check.h>
 
+void __libc_freeres (void);
+
 static int
 do_test (void)
 {
diff --git a/rt/aio_misc.c b/rt/aio_misc.c
index b4304d0a6f..a96ed4f51b 100644
--- a/rt/aio_misc.c
+++ b/rt/aio_misc.c
@@ -694,7 +694,8 @@  handle_fildes_io (void *arg)
 
 
 /* Free allocated resources.  */
-libc_freeres_fn (free_res)
+void
+__aio_freemem (void)
 {
   size_t row;
 
diff --git a/shadow/fgetspent.c b/shadow/fgetspent.c
index 5f75e31c29..f354ff3bb4 100644
--- a/shadow/fgetspent.c
+++ b/shadow/fgetspent.c
@@ -28,7 +28,7 @@ 
 /* We need to protect the dynamic buffer handling.  */
 __libc_lock_define_initialized (static, lock);
 
-libc_freeres_ptr (static char *buffer);
+static char *buffer;
 
 /* Read one shadow entry from the given stream.  */
 struct spwd *
@@ -85,3 +85,9 @@  fgetspent (FILE *stream)
 
   return result;
 }
+
+void
+__libc_fgetspent_freemem (void)
+{
+  free (buffer);
+}
diff --git a/stdio-common/reg-modifier.c b/stdio-common/reg-modifier.c
index fa1cd2ac3c..b8232b5ff8 100644
--- a/stdio-common/reg-modifier.c
+++ b/stdio-common/reg-modifier.c
@@ -183,7 +183,8 @@  __handle_registered_modifier_wc (const unsigned int **format,
 }
 
 
-libc_freeres_fn (free_mem)
+void
+__libc_printf_freemem (void)
 {
   if (__printf_modifier_table != NULL)
     {
diff --git a/stdio-common/reg-printf.c b/stdio-common/reg-printf.c
index 5f4c6a24c2..f3075e8fb6 100644
--- a/stdio-common/reg-printf.c
+++ b/stdio-common/reg-printf.c
@@ -24,8 +24,7 @@ 
 
 
 /* Array of functions indexed by format character.  */
-libc_freeres_ptr (printf_arginfo_size_function **__printf_arginfo_table)
-  attribute_hidden;
+printf_arginfo_size_function **__printf_arginfo_table attribute_hidden;
 printf_function **__printf_function_table attribute_hidden;
 
 __libc_lock_define_initialized (static, lock)
@@ -79,3 +78,9 @@  __register_printf_function (int spec, printf_function converter,
 				      (printf_arginfo_size_function*) arginfo);
 }
 weak_alias (__register_printf_function, register_printf_function)
+
+void
+__libc_reg_printf_freemem (void)
+{
+  free (__printf_arginfo_table);
+}
diff --git a/stdio-common/reg-type.c b/stdio-common/reg-type.c
index f308db6323..3612f57655 100644
--- a/stdio-common/reg-type.c
+++ b/stdio-common/reg-type.c
@@ -22,8 +22,7 @@ 
 
 
 /* Array of functions indexed by format character.  */
-libc_freeres_ptr (printf_va_arg_function **__printf_va_arg_table)
-  attribute_hidden;
+printf_va_arg_function **__printf_va_arg_table attribute_hidden;
 
 __libc_lock_define_initialized (static, lock);
 
@@ -59,3 +58,9 @@  __register_printf_type (printf_va_arg_function fct)
   return result;
 }
 weak_alias (__register_printf_type, register_printf_type)
+
+void
+__libc_reg_type_freemem (void)
+{
+  free (__printf_va_arg_table);
+}
diff --git a/stdlib/fmtmsg.c b/stdlib/fmtmsg.c
index 787a263d63..7561a2936b 100644
--- a/stdlib/fmtmsg.c
+++ b/stdlib/fmtmsg.c
@@ -361,7 +361,8 @@  __addseverity (int severity, const char *string)
 weak_alias (__addseverity, addseverity)
 
 
-libc_freeres_fn (free_mem)
+void
+__libc_fmtmsg_freemem (void)
 {
   struct severity_info *runp = severity_list;
 
diff --git a/stdlib/setenv.c b/stdlib/setenv.c
index 2176cbac31..e44d41019c 100644
--- a/stdlib/setenv.c
+++ b/stdlib/setenv.c
@@ -323,7 +323,8 @@  clearenv (void)
   return 0;
 }
 #ifdef _LIBC
-libc_freeres_fn (free_mem)
+void
+__libc_setenv_freemem (void)
 {
   /* Remove all traces.  */
   clearenv ();
diff --git a/sunrpc/clnt_perr.c b/sunrpc/clnt_perr.c
index 67499fd03f..c3d13722df 100644
--- a/sunrpc/clnt_perr.c
+++ b/sunrpc/clnt_perr.c
@@ -389,8 +389,8 @@  auth_errmsg (enum auth_stat stat)
 }
 
 
-libc_freeres_fn (free_mem)
+void
+__rpc_freemem (void)
 {
-  /* Not libc_freeres_ptr, since buf is a macro.  */
   free (buf);
 }
diff --git a/sunrpc/rpc_thread.c b/sunrpc/rpc_thread.c
index 0abe6dc172..a04b7ec47f 100644
--- a/sunrpc/rpc_thread.c
+++ b/sunrpc/rpc_thread.c
@@ -37,7 +37,6 @@  __rpc_thread_destroy (void)
 		thread_rpc_vars = NULL;
 	}
 }
-text_set_element (__libc_subfreeres, __rpc_thread_destroy);
 
 /*
  * Initialize RPC multi-threaded operation
diff --git a/sunrpc/tst-svc_register.c b/sunrpc/tst-svc_register.c
index 41c5398ab6..4f3c31c504 100644
--- a/sunrpc/tst-svc_register.c
+++ b/sunrpc/tst-svc_register.c
@@ -276,9 +276,9 @@  do_test (void)
               else
                 /* This is arguably a bug: Regular process termination
                    does not unregister the service with rpcbind.  The
-                   unset rpcbind call happens from a __libc_subfreeres
-                   callback, and this only happens when running under
-                   memory debuggers such as valgrind.  */
+                   unset rpcbind call happens from a __libc_freeres,
+                   and this only happens when running under memory debuggers
+		   such as valgrind.  */
                 TEST_VERIFY (!state.unset_called);
             }
           else
diff --git a/sysdeps/generic/set-freeres-fp.h b/sysdeps/generic/set-freeres-fp.h
new file mode 100644
index 0000000000..ddd0a48a20
--- /dev/null
+++ b/sysdeps/generic/set-freeres-fp.h
@@ -0,0 +1,19 @@ 
+/* System specific resource deallocation.  Generic version.
+   Copyright (C) 2020-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define SET_FREERES_FP_FUNCS
diff --git a/sysdeps/generic/set-freeres-os.h b/sysdeps/generic/set-freeres-os.h
new file mode 100644
index 0000000000..834fa7cc57
--- /dev/null
+++ b/sysdeps/generic/set-freeres-os.h
@@ -0,0 +1,19 @@ 
+/* System specific resource deallocation.  Generic version.
+   Copyright (C) 2020-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define SET_FREERES_OS_FUNCS
diff --git a/sysdeps/generic/set-freeres-system.h b/sysdeps/generic/set-freeres-system.h
new file mode 100644
index 0000000000..cc0d69b3b4
--- /dev/null
+++ b/sysdeps/generic/set-freeres-system.h
@@ -0,0 +1,27 @@ 
+/* System specific resource deallocation.  Generic version.
+   Copyright (C) 2020-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* Each system may define weak functions to free any resource allocated with
+   malloc to avoid interfere with mtrace.  */
+
+#include <set-freeres-os.h>
+#include <set-freeres-fp.h>
+
+#define SET_FREERES_SYSTEM_FUNCS	\
+  SET_FREERES_OS_FUNCS			\
+  SET_FREERES_FP_FUNCS
diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-qefgcvt.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-qefgcvt.c
index 9703069b95..67e7afd86a 100644
--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-qefgcvt.c
+++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-qefgcvt.c
@@ -42,6 +42,7 @@  typeof (qfcvt_r) ___qfcvtieee128_r;
 #define __GCVT ___qgcvtieee128
 #define __ECVT_R ___qecvtieee128_r
 #define __FCVT_R ___qfcvtieee128_r
+#define __EFGCVT_FREEMEM __libc_efgcvtieee128_freemem
 #include <efgcvt-ldbl-macros.h>
 #include <efgcvt-template.c>
 
diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/set-freeres-fp.h b/sysdeps/ieee754/ldbl-128ibm-compat/set-freeres-fp.h
new file mode 100644
index 0000000000..b652b11f64
--- /dev/null
+++ b/sysdeps/ieee754/ldbl-128ibm-compat/set-freeres-fp.h
@@ -0,0 +1,22 @@ 
+/* System specific resource deallocation. Long double 128 IBM version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+void __libc_efgcvtieee128_freemem (void) weak_function;
+
+#define SET_FREERES_FP_FUNCS \
+  __libc_efgcvtieee128_freemem
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 5cda9bb072..bdc56d9781 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1761,7 +1761,8 @@  check_gaiconf_mtime (const struct __stat64_t64 *st)
 #endif
 
 
-libc_freeres_fn(fini)
+void
+__libc_getaddrinfo_freemem (void)
 {
   if (labels != default_labels)
     {
@@ -2233,7 +2234,7 @@  no_file:
 
   /* If we previously read the file but it is gone now, free the old data and
      use the builtin one.  Leave the reload flag alone.  */
-  fini ();
+  __libc_getaddrinfo_freemem ();
 }
 
 
diff --git a/sysdeps/posix/ttyname.c b/sysdeps/posix/ttyname.c
index fd41f7fb24..32ada6c1f1 100644
--- a/sysdeps/posix/ttyname.c
+++ b/sysdeps/posix/ttyname.c
@@ -31,7 +31,13 @@  static char *getttyname (int fd, dev_t mydev, ino_t myino,
 			 int save, int *dostat);
 
 
-libc_freeres_ptr (static char *getttyname_name);
+static char *getttyname_name;
+
+void
+__ttyname_freemem (void)
+{
+  free (getttyname_name);
+}
 
 static char *
 getttyname (int fd, dev_t mydev, ino_t myino, int save, int *dostat)
diff --git a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c
index 0b77a2d897..bdda4e890d 100644
--- a/sysdeps/unix/sysv/linux/check_pf.c
+++ b/sysdeps/unix/sysv/linux/check_pf.c
@@ -362,7 +362,8 @@  __check_pf (bool *seen_ipv4, bool *seen_ipv6,
 }
 
 /* Free the cache if it has been allocated.  */
-libc_freeres_fn (freecache)
+void
+__check_pf_freemem (void)
 {
   if (cache)
     __free_in6ai (cache->in6ai);
diff --git a/sysdeps/unix/sysv/linux/set-freeres-os.h b/sysdeps/unix/sysv/linux/set-freeres-os.h
new file mode 100644
index 0000000000..864a31a123
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/set-freeres-os.h
@@ -0,0 +1,24 @@ 
+/* System specific resource deallocation.  Linux version.
+   Copyright (C) 2020-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+void __check_pf_freemem (void) weak_function;
+void __ttyname_freemem (void) weak_function;
+
+#define SET_FREERES_OS_FUNCS	\
+  __check_pf_freemem,		\
+  __ttyname_freemem,
diff --git a/sysdeps/unix/sysv/linux/ttyname.c b/sysdeps/unix/sysv/linux/ttyname.c
index 947c1f72a1..77313f8577 100644
--- a/sysdeps/unix/sysv/linux/ttyname.c
+++ b/sysdeps/unix/sysv/linux/ttyname.c
@@ -24,7 +24,8 @@ 
 
 static char *ttyname_buf = NULL;
 
-libc_freeres_fn (free_mem)
+void
+__ttyname_freemem (void)
 {
   free (ttyname_buf);
 }
diff --git a/time/tzfile.c b/time/tzfile.c
index dd75848ba9..454c70178b 100644
--- a/time/tzfile.c
+++ b/time/tzfile.c
@@ -50,7 +50,7 @@  struct leap
   };
 
 static size_t num_transitions;
-libc_freeres_ptr (static __time64_t *transitions);
+static __time64_t *transitions;
 static unsigned char *type_idxs;
 static size_t num_types;
 static struct ttinfo *types;
@@ -777,3 +777,9 @@  __tzfile_compute (__time64_t timer, int use_localtime,
 	}
     }
 }
+
+void
+__libc_tzfile_freemem (void)
+{
+  free (transitions);
+}
diff --git a/time/tzset.c b/time/tzset.c
index a06142bb58..b8b1ee2662 100644
--- a/time/tzset.c
+++ b/time/tzset.c
@@ -610,7 +610,8 @@  __tz_convert (__time64_t timer, int use_localtime, struct tm *tp)
 }
 
 
-libc_freeres_fn (free_mem)
+void
+__libc_tzset_freemem (void)
 {
   while (tzstring_list != NULL)
     {