Commit 54620ebb95 for frr
commit 54620ebb950f0fdc29854ad2dfc9db19641c2f4e
Author: Deepak Singhal <deepsinghal@microsoft.com>
Date: Mon Sep 14 18:13:19 2026 +0000
build: keep tcmalloc in DT_NEEDED
Both tcmalloc options append the library to the global LIBS, so it reaches
every daemon's link line:
--enable-tcmalloc LIBS="$LIBS -ltcmalloc_minimal"
--enable-gperf-tcmalloc LIBS="$LIBS -ltcmalloc"
gcc on Debian and Ubuntu links with --as-needed by default. It keeps a
DT_NEEDED entry for a library when the binary's own objects reference a symbol
that library defines. Daemons allocate through lib/'s XMALLOC/XFREE macros, so
the malloc-family references sit in libfrr, and the linker drops libtcmalloc
from the daemons:
zebra : libfrr.so.0 libmlag_pb.so.0 libyang.so.3 libjson-c.so.5 libc.so.6
bgpd : libfrr.so.0 libm.so.6 libpcre2-posix.so.3 libtcmalloc.so.4 ...
bgpd keeps it because one of its objects calls free() directly. Membership
therefore depends on where a malloc-family symbol happens to be referenced.
libfrr.so.0 still lists libtcmalloc, so it is loaded, at depth 2. The global
symbol scope is searched breadth-first, so libc.so.6 sits at depth 1 and wins
malloc. The dynamic linker resolves the same libfrr.so.0 according to the
executable that loaded it:
zebra : binding file libfrr.so.0 to libc.so.6 : normal symbol `malloc'
bgpd : binding file libfrr.so.0 to libtcmalloc.so.4 : normal symbol `malloc'
Those daemons run on glibc malloc while reporting tcmalloc enabled. zebra holds
the RIB and is the largest memory consumer of the set.
Wrap both libraries in a --no-as-needed region so the linker keeps them.
--push-state/--pop-state confines --no-as-needed to this one library, so every
other dependency keeps its as-needed treatment.
The flags and the library travel as a single -Wl group on purpose. libtool
splits a -Wl, list into individual -Wl, arguments and keeps them adjacent. A
bare -ltcmalloc moves to the end of the link line, past any trailing
--as-needed, which restores the original behaviour:
in : -Wl,--no-as-needed -ltcmalloc -Wl,--as-needed
out : -Wl,--no-as-needed -Wl,--as-needed ./.libs/libfrr.so -ltcmalloc
Measured on master with --enable-tcmalloc: zebra gains libtcmalloc_minimal.so.4
in DT_NEEDED, and LD_DEBUG=bindings shows the allocator symbols resolving to
tcmalloc. Daemons that already carried tcmalloc stay as they are.
Signed-off-by: Deepak Singhal <deepsinghal@microsoft.com>
diff --git a/configure.ac b/configure.ac
index 55419c6d05..d05eae1d7a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -235,7 +235,7 @@ AC_ARG_ENABLE(tcmalloc,
AS_HELP_STRING([--enable-tcmalloc], [Turn on tcmalloc]),
[case "${enableval}" in
yes) tcmalloc_enabled=true
-LIBS="$LIBS -ltcmalloc_minimal"
+LIBS="$LIBS -Wl,--push-state,--no-as-needed,-ltcmalloc_minimal,--pop-state"
;;
no) tcmalloc_enabled=false ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-tcmalloc]) ;;
@@ -872,7 +872,7 @@ AC_ARG_ENABLE(gperf-tcmalloc,
AC_MSG_CHECKING([for gperftools tcmalloc])
AC_CHECK_LIB([tcmalloc], [MallocExtension_ReleaseToSystem], [
gperf_tcmalloc_enabled=true
-LIBS="$LIBS -ltcmalloc"
+LIBS="$LIBS -Wl,--push-state,--no-as-needed,-ltcmalloc,--pop-state"
AC_DEFINE([HAVE_TCMALLOC], [1], [Enable tcmalloc])
], [AC_MSG_ERROR([tcmalloc specified but libtcmalloc not found, or missing apis])])
;;