[PATCH] ld: Disable separate code by default for Linux/x86

H.J. Lu hjl.tools@gmail.com
Mon Mar 16 15:58:14 GMT 2026


On Mon, Mar 16, 2026 at 7:20 AM WANG Rui <r@hev.cc> wrote:
>
> Years ago, commit f6aec96dce1d ("ld: Add --enable-separate-code") introduced
> -z separate-code, mainly to avoid mixing code and data in the same segment
> for better cache performance, and enabled it by default for Linux/x86.

-z separate-code reduces cache pollution and improves security by avoiding
putting read-only data in the same page with code.

> Today, Linux already has working support for mapping executable code with THP,
> which can noticeably reduce iTLB pressure and bring measurable performance
> benefits.
>
> In practice, I observed that on AArch64 and LoongArch64 distros, ELF executables
> typically place the code PT_LOAD segment first, so both the virtual address and
> file offset are 0. That turns out to be an important prerequisite for THP-aware

Should ld.so map read-only data segment plus code segment into the same huge
page?  This should have the similar effect as -z noseparate-code.

> PT_LOAD alignment optimizations [1][2][3], and the data shows clear performance
> wins on both AArch64 and LoongArch64. [4][1]
>
> Linux/x86 is a bit of an outlier here: the default -z separate-code setting
> happens to break this layout. So this patch changes the Linux/x86 default to
> -z noseparate-code.
>
> Benchmark
>
> Machine: AMD Ryzen 9 7950X (x86_64)
> Binutils: 2.46
> GCC: 15.2.1 (built with -z,noseparate-code + --enable-host-pie)
> Linux kernel: 6.19.6 with patch [1]
>
> Workload: building Linux v7.0-rc1 vmlinux with x86_64_defconfig.
>
>                 Without patch        With patch
> instructions    8,246,133,611,932    8,246,025,137,750
> cpu-cycles      8,001,028,142,928    7,565,925,107,502
> itlb-misses     3,672,158,331        26,821,242
> time elapsed    64.66 s              61.97 s
>
> Instructions are basically unchanged. iTLB misses drop from ~3.67B to
> ~26M (~99.27% reduction), which results in about a ~5.44% reduction in
> cycles and ~4.18% shorter wall time for this workload.

Hongtao, haochecn, can you work with Rui to measure -z noseparate-code
performance impact on SPEC CPU?

> [1] https://lore.kernel.org/linux-fsdevel/20260313005211.882831-1-r@hev.cc
> [2] https://sourceware.org/pipermail/libc-alpha/2026-March/175776.html
> [3] https://lore.kernel.org/linux-fsdevel/20260310145406.3073394-1-usama.arif@linux.dev
> [4] https://lore.kernel.org/linux-fsdevel/20260314095022.217231-1-r@hev.cc
>
> bfd/
>
>         * configure.ac: Remove Linux/x86 default enabling of
>         -z separate-code.
>         * configure: Regenerated.
>
> ld/
>
>         * configure.tgt: Do not enable -z separate-code by default
>         for Linux/x86 targets.
>
> Signed-off-by: WANG Rui <r@hev.cc>
> ---
>  bfd/configure    |  8 --------
>  bfd/configure.ac | 10 ----------
>  ld/configure.tgt |  5 +----
>  3 files changed, 1 insertion(+), 22 deletions(-)
>
> diff --git a/bfd/configure b/bfd/configure
> index f474c6af9cd..830c06a6c34 100755
> --- a/bfd/configure
> +++ b/bfd/configure
> @@ -12356,14 +12356,6 @@ if test "${enable_separate_code+set}" = set; then :
>  esac
>  fi
>
> -# Enable -z separate-code by default for Linux/x86.
> -case "${target}" in
> -i[3-7]86-*-linux-* | x86_64-*-linux-*)
> -  if test ${ac_default_ld_z_separate_code} = unset; then
> -    ac_default_ld_z_separate_code=1
> -  fi
> -  ;;
> -esac
>  if test "${ac_default_ld_z_separate_code}" = unset; then
>    ac_default_ld_z_separate_code=0
>  fi
> diff --git a/bfd/configure.ac b/bfd/configure.ac
> index c30a75ba8b7..e152cf0cf94 100644
> --- a/bfd/configure.ac
> +++ b/bfd/configure.ac
> @@ -131,16 +131,6 @@ AC_ARG_ENABLE(separate-code,
>    yes) ac_default_ld_z_separate_code=1 ;;
>    no) ac_default_ld_z_separate_code=0 ;;
>  esac])
> -# Enable -z separate-code by default for Linux/x86.
> -changequote(,)dnl
> -case "${target}" in
> -i[3-7]86-*-linux-* | x86_64-*-linux-*)
> -changequote([,])dnl
> -  if test ${ac_default_ld_z_separate_code} = unset; then
> -    ac_default_ld_z_separate_code=1
> -  fi
> -  ;;
> -esac
>  if test "${ac_default_ld_z_separate_code}" = unset; then
>    ac_default_ld_z_separate_code=0
>  fi
> diff --git a/ld/configure.tgt b/ld/configure.tgt
> index 3e158913b89..c96f55ab7ce 100644
> --- a/ld/configure.tgt
> +++ b/ld/configure.tgt
> @@ -1119,12 +1119,9 @@ frv-*-* | hppa*-*-* | ia64-*-* | mips*-*-*)
>    ;;
>  esac
>
> -# Enable -z separate-code and --warn-textrel by default for Linux/x86.
> +# Enable --warn-textrel by default for Linux/x86.
>  case "${target}" in
>  i[3-7]86-*-linux-* | x86_64-*-linux-*)
> -  if test ${ac_default_ld_z_separate_code} = unset; then
> -    ac_default_ld_z_separate_code=1
> -  fi
>    if test ${ac_default_ld_textrel_check} = unset; then
>      ac_default_ld_textrel_check=yes
>    fi
> --
> 2.53.0
>


-- 
H.J.


More information about the Libc-alpha mailing list