Message ID | 20220107190631.309790-11-hjl.tools@gmail.com |
---|---|
State | New |
Headers | show |
Series | ld: Implement DT_RELR for x86 | expand |
On 2022-01-07, H.J. Lu via Binutils wrote: >When DT_RELR is enabled, to avoid random run-time crash with older glibc >binaries without DT_RELR support, add a GLIBC_ABI_DT_RELR symbol version, >which is provided by glibc with DT_RELR support, dependency on the shared >C library if it provides a GLIBC_2.XX symbol version. > >bfd/ > > * elflink.c (elf_link_add_dt_relr_dependency): New function. > (bfd_elf_size_dynamic_sections): Call > elf_link_add_dt_relr_dependency if DT_RELR is enabled. > >ld/ > > * ld.texi: Mention GLIBC_ABI_DT_RELR in -z pack-relative-relocs > entry. > * testsuite/ld-elf/dt-relr-glibc-1.c: New file. > * testsuite/ld-elf/dt-relr-glibc-1a.rd: Likewise. > * testsuite/ld-elf/dt-relr-glibc-1b.rd: Likewise. > * testsuite/ld-elf/dt-relr.exp: Likewise. >--- > bfd/elflink.c | 86 +++++++++++++++++++++++++ > ld/ld.texi | 4 +- > ld/testsuite/ld-elf/dt-relr-glibc-1.c | 11 ++++ > ld/testsuite/ld-elf/dt-relr-glibc-1a.rd | 4 ++ > ld/testsuite/ld-elf/dt-relr-glibc-1b.rd | 7 ++ > ld/testsuite/ld-elf/dt-relr.exp | 44 +++++++++++++ > 6 files changed, 155 insertions(+), 1 deletion(-) > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1.c > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1a.rd > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1b.rd > create mode 100644 ld/testsuite/ld-elf/dt-relr.exp > >diff --git a/bfd/elflink.c b/bfd/elflink.c >index 31b13f5df7a..05ac1cb7a63 100644 >--- a/bfd/elflink.c >+++ b/bfd/elflink.c >@@ -2213,6 +2213,85 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data) > return true; > } > >+/* Return true if GLIBC_ABI_DT_RELR is added to the list of version >+ dependencies successfully. GLIBC_ABI_DT_RELR will be put into the >+ .gnu.version_r section. */ >+ >+static bool >+elf_link_add_dt_relr_dependency (struct elf_find_verdep_info *rinfo) >+{ >+ bfd *glibc_bfd = NULL; >+ Elf_Internal_Verneed *t; >+ Elf_Internal_Vernaux *a; >+ size_t amt; >+ const char *relr = "GLIBC_ABI_DT_RELR"; >+ >+ /* See if we already know about GLIBC_PRIVATE_DT_RELR. */ >+ for (t = elf_tdata (rinfo->info->output_bfd)->verref; >+ t != NULL; >+ t = t->vn_nextref) >+ { >+ const char *soname = bfd_elf_get_dt_soname (t->vn_bfd); >+ /* Skip the shared library if it isn't libc.so. */ >+ if (!soname || !startswith (soname, "libc.so.")) >+ continue; Having the dependency on ld.so is better. (The linker knows the --dynamic-linker value.) libc.so does not need to know whether DT_RELR is used. It's also easier on glibc side. Defining __glibc_abi_dt_relr@@GLIBC_ABI_DT_RELR in elf/Versions is simpler than defining it in libc.so and updating every libc.abilist. >+ for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) >+ { >+ /* Return if GLIBC_PRIVATE_DT_RELR dependency has been >+ added. */ >+ if (a->vna_nodename == relr >+ || strcmp (a->vna_nodename, relr) == 0) >+ return true; >+ >+ /* Check if libc.so provides GLIBC_2.XX version. */ >+ if (!glibc_bfd && startswith (a->vna_nodename, "GLIBC_2.")) >+ glibc_bfd = t->vn_bfd; >+ } >+ >+ break; >+ } >+ >+ /* Skip if it isn't linked against glibc. */ >+ if (glibc_bfd == NULL) >+ return true; >+ >+ /* This is a new version. Add it to tree we are building. */ >+ if (t == NULL) >+ { >+ amt = sizeof *t; >+ t = (Elf_Internal_Verneed *) bfd_zalloc (rinfo->info->output_bfd, >+ amt); >+ if (t == NULL) >+ { >+ rinfo->failed = true; >+ return false; >+ } >+ >+ t->vn_bfd = glibc_bfd; >+ t->vn_nextref = elf_tdata (rinfo->info->output_bfd)->verref; >+ elf_tdata (rinfo->info->output_bfd)->verref = t; >+ } >+ >+ amt = sizeof *a; >+ a = (Elf_Internal_Vernaux *) bfd_zalloc (rinfo->info->output_bfd, amt); >+ if (a == NULL) >+ { >+ rinfo->failed = true; >+ return false; >+ } >+ >+ a->vna_nodename = relr; >+ a->vna_flags = 0; >+ a->vna_nextptr = t->vn_auxptr; >+ a->vna_other = rinfo->vers + 1; >+ ++rinfo->vers; >+ >+ t->vn_auxptr = a; >+ >+ return true; >+} >+ > /* Look through the symbols which are defined in other shared > libraries and referenced here. Update the list of version > dependencies. This will be put into the .gnu.version_r section. >@@ -6940,6 +7019,13 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd, > if (sinfo.failed) > return false; > >+ if (info->enable_dt_relr) >+ { >+ elf_link_add_dt_relr_dependency (&sinfo); >+ if (sinfo.failed) >+ return false; >+ } >+ > if (elf_tdata (output_bfd)->verref == NULL) > s->flags |= SEC_EXCLUDE; > else >diff --git a/ld/ld.texi b/ld/ld.texi >index 457089ec06a..a16657ddb45 100644 >--- a/ld/ld.texi >+++ b/ld/ld.texi >@@ -1439,7 +1439,9 @@ and shared library. It adds @code{DT_RELR}, @code{DT_RELRSZ} and > building position-dependent executable and relocatable output. This > option also implies @option{combreloc} and @option{--relax}. > @option{nopack-relative-relocs} is the default, which disables >-compact relative relocation. Supported for i386 and x86-64. >+compact relative relocation. When linked against the GNU C Library, >+a GLIBC_ABI_DT_RELR symbol version dependency on the shared C Library >+is added to the output. Supported for i386 and x86-64. > @item relro > @itemx norelro >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1.c b/ld/testsuite/ld-elf/dt-relr-glibc-1.c >new file mode 100644 >index 00000000000..beacffe29e7 >--- /dev/null >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1.c >@@ -0,0 +1,11 @@ >+#define REL(n) \ >+ static int data##n; \ >+ void *p##n = &data##n; >+ >+REL(1) >+REL(2) >+REL(3) >+REL(4) >+REL(5) >+REL(6) >+REL(7) >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd b/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd >new file mode 100644 >index 00000000000..51bda5d70a1 >--- /dev/null >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd >@@ -0,0 +1,4 @@ >+#failif >+#... >+ 0x[a-f0-9]+: Name: GLIBC_ABI_DT_RELR Flags: none Version: [0-9]+ >+#... >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd b/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd >new file mode 100644 >index 00000000000..6556a6d939e >--- /dev/null >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd >@@ -0,0 +1,7 @@ >+#... >+Version needs section '.gnu.version_r' contains 1 entry: >+ Addr: 0x[0-9a-f]+ +Offset: 0x[0-9a-f]+ +Link: +[0-9]+ +\(.dynstr\) >+ +0+: Version: 1 +File: libc\.so\.6(|\.1) +Cnt: +[0-9]+ >+#... >+ 0x[a-f0-9]+: Name: GLIBC_ABI_DT_RELR Flags: none Version: [0-9]+ >+#pass >diff --git a/ld/testsuite/ld-elf/dt-relr.exp b/ld/testsuite/ld-elf/dt-relr.exp >new file mode 100644 >index 00000000000..51d21e400ab >--- /dev/null >+++ b/ld/testsuite/ld-elf/dt-relr.exp >@@ -0,0 +1,44 @@ >+# Expect script for DT_RELR. >+# Copyright (C) 2022 Free Software Foundation, Inc. >+# >+# This file is part of the GNU Binutils. >+# >+# This program is free software; you can redistribute it and/or modify >+# it under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# This program 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 General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with this program; if not, write to the Free Software >+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, >+# MA 02110-1301, USA. >+# >+ >+# Linux tests. >+if { ![istarget "*-*-linux*"] } { >+ return >+} >+ >+run_cc_link_tests [list \ >+ [list \ >+ "Build dt-relr-glibc-1a.so" \ >+ "-shared $NO_DT_RELR_CC_LDFLAGS" \ >+ "-fPIC" \ >+ { dt-relr-glibc-1.c } \ >+ {{readelf {--version-info} dt-relr-glibc-1a.rd}} \ >+ "glibc-relr-1a.so" \ >+ ] \ >+ [list \ >+ "Build dt-relr-glibc-1b.so" \ >+ "-shared $DT_RELR_CC_LDFLAGS" \ >+ "-fPIC" \ >+ { dt-relr-glibc-1.c } \ >+ {{readelf {-W --version-info} dt-relr-glibc-1b.rd}} \ >+ "dt-relr-glibc-1b.so" \ >+ ] \ >+] >-- >2.33.1 >
On Fri, Jan 7, 2022 at 8:43 PM Fangrui Song <i@maskray.me> wrote: > > On 2022-01-07, H.J. Lu via Binutils wrote: > >When DT_RELR is enabled, to avoid random run-time crash with older glibc > >binaries without DT_RELR support, add a GLIBC_ABI_DT_RELR symbol version, > >which is provided by glibc with DT_RELR support, dependency on the shared > >C library if it provides a GLIBC_2.XX symbol version. > > > >bfd/ > > > > * elflink.c (elf_link_add_dt_relr_dependency): New function. > > (bfd_elf_size_dynamic_sections): Call > > elf_link_add_dt_relr_dependency if DT_RELR is enabled. > > > >ld/ > > > > * ld.texi: Mention GLIBC_ABI_DT_RELR in -z pack-relative-relocs > > entry. > > * testsuite/ld-elf/dt-relr-glibc-1.c: New file. > > * testsuite/ld-elf/dt-relr-glibc-1a.rd: Likewise. > > * testsuite/ld-elf/dt-relr-glibc-1b.rd: Likewise. > > * testsuite/ld-elf/dt-relr.exp: Likewise. > >--- > > bfd/elflink.c | 86 +++++++++++++++++++++++++ > > ld/ld.texi | 4 +- > > ld/testsuite/ld-elf/dt-relr-glibc-1.c | 11 ++++ > > ld/testsuite/ld-elf/dt-relr-glibc-1a.rd | 4 ++ > > ld/testsuite/ld-elf/dt-relr-glibc-1b.rd | 7 ++ > > ld/testsuite/ld-elf/dt-relr.exp | 44 +++++++++++++ > > 6 files changed, 155 insertions(+), 1 deletion(-) > > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1.c > > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1a.rd > > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1b.rd > > create mode 100644 ld/testsuite/ld-elf/dt-relr.exp > > > >diff --git a/bfd/elflink.c b/bfd/elflink.c > >index 31b13f5df7a..05ac1cb7a63 100644 > >--- a/bfd/elflink.c > >+++ b/bfd/elflink.c > >@@ -2213,6 +2213,85 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data) > > return true; > > } > > > >+/* Return true if GLIBC_ABI_DT_RELR is added to the list of version > >+ dependencies successfully. GLIBC_ABI_DT_RELR will be put into the > >+ .gnu.version_r section. */ > >+ > >+static bool > >+elf_link_add_dt_relr_dependency (struct elf_find_verdep_info *rinfo) > >+{ > >+ bfd *glibc_bfd = NULL; > >+ Elf_Internal_Verneed *t; > >+ Elf_Internal_Vernaux *a; > >+ size_t amt; > >+ const char *relr = "GLIBC_ABI_DT_RELR"; > >+ > >+ /* See if we already know about GLIBC_PRIVATE_DT_RELR. */ > >+ for (t = elf_tdata (rinfo->info->output_bfd)->verref; > >+ t != NULL; > >+ t = t->vn_nextref) > >+ { > >+ const char *soname = bfd_elf_get_dt_soname (t->vn_bfd); > >+ /* Skip the shared library if it isn't libc.so. */ > >+ if (!soname || !startswith (soname, "libc.so.")) > >+ continue; > > Having the dependency on ld.so is better. > (The linker knows the --dynamic-linker value.) > libc.so does not need to know whether DT_RELR is used. > > It's also easier on glibc side. > Defining __glibc_abi_dt_relr@@GLIBC_ABI_DT_RELR in elf/Versions is > simpler than defining it in libc.so and updating every libc.abilist. I chose libc.so instead of ld.so since shared libraries may not be linked against ld.so. The glibc change can be simple: https://patchwork.sourceware.org/project/glibc/patch/20211123223053.3334224-1-hjl.tools@gmail.com/ Please replace GLIBC_ABI_VERSION_GEN2 with GLIBC_ABI_DT_RELR. or like https://gitlab.com/x86-glibc/glibc/-/commit/3bc53baa5304855b5105dabd7726b23d454605fb I can go either way. > >+ for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) > >+ { > >+ /* Return if GLIBC_PRIVATE_DT_RELR dependency has been > >+ added. */ > >+ if (a->vna_nodename == relr > >+ || strcmp (a->vna_nodename, relr) == 0) > >+ return true; > >+ > >+ /* Check if libc.so provides GLIBC_2.XX version. */ > >+ if (!glibc_bfd && startswith (a->vna_nodename, "GLIBC_2.")) > >+ glibc_bfd = t->vn_bfd; > >+ } > >+ > >+ break; > >+ } > >+ > >+ /* Skip if it isn't linked against glibc. */ > >+ if (glibc_bfd == NULL) > >+ return true; > >+ > >+ /* This is a new version. Add it to tree we are building. */ > >+ if (t == NULL) > >+ { > >+ amt = sizeof *t; > >+ t = (Elf_Internal_Verneed *) bfd_zalloc (rinfo->info->output_bfd, > >+ amt); > >+ if (t == NULL) > >+ { > >+ rinfo->failed = true; > >+ return false; > >+ } > >+ > >+ t->vn_bfd = glibc_bfd; > >+ t->vn_nextref = elf_tdata (rinfo->info->output_bfd)->verref; > >+ elf_tdata (rinfo->info->output_bfd)->verref = t; > >+ } > >+ > >+ amt = sizeof *a; > >+ a = (Elf_Internal_Vernaux *) bfd_zalloc (rinfo->info->output_bfd, amt); > >+ if (a == NULL) > >+ { > >+ rinfo->failed = true; > >+ return false; > >+ } > >+ > >+ a->vna_nodename = relr; > >+ a->vna_flags = 0; > >+ a->vna_nextptr = t->vn_auxptr; > >+ a->vna_other = rinfo->vers + 1; > >+ ++rinfo->vers; > >+ > >+ t->vn_auxptr = a; > >+ > >+ return true; > >+} > >+ > > /* Look through the symbols which are defined in other shared > > libraries and referenced here. Update the list of version > > dependencies. This will be put into the .gnu.version_r section. > >@@ -6940,6 +7019,13 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd, > > if (sinfo.failed) > > return false; > > > >+ if (info->enable_dt_relr) > >+ { > >+ elf_link_add_dt_relr_dependency (&sinfo); > >+ if (sinfo.failed) > >+ return false; > >+ } > >+ > > if (elf_tdata (output_bfd)->verref == NULL) > > s->flags |= SEC_EXCLUDE; > > else > >diff --git a/ld/ld.texi b/ld/ld.texi > >index 457089ec06a..a16657ddb45 100644 > >--- a/ld/ld.texi > >+++ b/ld/ld.texi > >@@ -1439,7 +1439,9 @@ and shared library. It adds @code{DT_RELR}, @code{DT_RELRSZ} and > > building position-dependent executable and relocatable output. This > > option also implies @option{combreloc} and @option{--relax}. > > @option{nopack-relative-relocs} is the default, which disables > >-compact relative relocation. Supported for i386 and x86-64. > >+compact relative relocation. When linked against the GNU C Library, > >+a GLIBC_ABI_DT_RELR symbol version dependency on the shared C Library > >+is added to the output. Supported for i386 and x86-64. > > > @item relro > > @itemx norelro > >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1.c b/ld/testsuite/ld-elf/dt-relr-glibc-1.c > >new file mode 100644 > >index 00000000000..beacffe29e7 > >--- /dev/null > >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1.c > >@@ -0,0 +1,11 @@ > >+#define REL(n) \ > >+ static int data##n; \ > >+ void *p##n = &data##n; > >+ > >+REL(1) > >+REL(2) > >+REL(3) > >+REL(4) > >+REL(5) > >+REL(6) > >+REL(7) > >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd b/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd > >new file mode 100644 > >index 00000000000..51bda5d70a1 > >--- /dev/null > >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd > >@@ -0,0 +1,4 @@ > >+#failif > >+#... > >+ 0x[a-f0-9]+: Name: GLIBC_ABI_DT_RELR Flags: none Version: [0-9]+ > >+#... > >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd b/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd > >new file mode 100644 > >index 00000000000..6556a6d939e > >--- /dev/null > >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd > >@@ -0,0 +1,7 @@ > >+#... > >+Version needs section '.gnu.version_r' contains 1 entry: > >+ Addr: 0x[0-9a-f]+ +Offset: 0x[0-9a-f]+ +Link: +[0-9]+ +\(.dynstr\) > >+ +0+: Version: 1 +File: libc\.so\.6(|\.1) +Cnt: +[0-9]+ > >+#... > >+ 0x[a-f0-9]+: Name: GLIBC_ABI_DT_RELR Flags: none Version: [0-9]+ > >+#pass > >diff --git a/ld/testsuite/ld-elf/dt-relr.exp b/ld/testsuite/ld-elf/dt-relr.exp > >new file mode 100644 > >index 00000000000..51d21e400ab > >--- /dev/null > >+++ b/ld/testsuite/ld-elf/dt-relr.exp > >@@ -0,0 +1,44 @@ > >+# Expect script for DT_RELR. > >+# Copyright (C) 2022 Free Software Foundation, Inc. > >+# > >+# This file is part of the GNU Binutils. > >+# > >+# This program is free software; you can redistribute it and/or modify > >+# it under the terms of the GNU General Public License as published by > >+# the Free Software Foundation; either version 3 of the License, or > >+# (at your option) any later version. > >+# > >+# This program 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 General Public License for more details. > >+# > >+# You should have received a copy of the GNU General Public License > >+# along with this program; if not, write to the Free Software > >+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, > >+# MA 02110-1301, USA. > >+# > >+ > >+# Linux tests. > >+if { ![istarget "*-*-linux*"] } { > >+ return > >+} > >+ > >+run_cc_link_tests [list \ > >+ [list \ > >+ "Build dt-relr-glibc-1a.so" \ > >+ "-shared $NO_DT_RELR_CC_LDFLAGS" \ > >+ "-fPIC" \ > >+ { dt-relr-glibc-1.c } \ > >+ {{readelf {--version-info} dt-relr-glibc-1a.rd}} \ > >+ "glibc-relr-1a.so" \ > >+ ] \ > >+ [list \ > >+ "Build dt-relr-glibc-1b.so" \ > >+ "-shared $DT_RELR_CC_LDFLAGS" \ > >+ "-fPIC" \ > >+ { dt-relr-glibc-1.c } \ > >+ {{readelf {-W --version-info} dt-relr-glibc-1b.rd}} \ > >+ "dt-relr-glibc-1b.so" \ > >+ ] \ > >+] > >-- > >2.33.1 > > -- H.J.
On 2022-01-07, H.J. Lu wrote: >On Fri, Jan 7, 2022 at 8:43 PM Fangrui Song <i@maskray.me> wrote: >> >> On 2022-01-07, H.J. Lu via Binutils wrote: >> >When DT_RELR is enabled, to avoid random run-time crash with older glibc >> >binaries without DT_RELR support, add a GLIBC_ABI_DT_RELR symbol version, >> >which is provided by glibc with DT_RELR support, dependency on the shared >> >C library if it provides a GLIBC_2.XX symbol version. >> > >> >bfd/ >> > >> > * elflink.c (elf_link_add_dt_relr_dependency): New function. >> > (bfd_elf_size_dynamic_sections): Call >> > elf_link_add_dt_relr_dependency if DT_RELR is enabled. >> > >> >ld/ >> > >> > * ld.texi: Mention GLIBC_ABI_DT_RELR in -z pack-relative-relocs >> > entry. >> > * testsuite/ld-elf/dt-relr-glibc-1.c: New file. >> > * testsuite/ld-elf/dt-relr-glibc-1a.rd: Likewise. >> > * testsuite/ld-elf/dt-relr-glibc-1b.rd: Likewise. >> > * testsuite/ld-elf/dt-relr.exp: Likewise. >> >--- >> > bfd/elflink.c | 86 +++++++++++++++++++++++++ >> > ld/ld.texi | 4 +- >> > ld/testsuite/ld-elf/dt-relr-glibc-1.c | 11 ++++ >> > ld/testsuite/ld-elf/dt-relr-glibc-1a.rd | 4 ++ >> > ld/testsuite/ld-elf/dt-relr-glibc-1b.rd | 7 ++ >> > ld/testsuite/ld-elf/dt-relr.exp | 44 +++++++++++++ >> > 6 files changed, 155 insertions(+), 1 deletion(-) >> > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1.c >> > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1a.rd >> > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1b.rd >> > create mode 100644 ld/testsuite/ld-elf/dt-relr.exp >> > >> >diff --git a/bfd/elflink.c b/bfd/elflink.c >> >index 31b13f5df7a..05ac1cb7a63 100644 >> >--- a/bfd/elflink.c >> >+++ b/bfd/elflink.c >> >@@ -2213,6 +2213,85 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data) >> > return true; >> > } >> > >> >+/* Return true if GLIBC_ABI_DT_RELR is added to the list of version >> >+ dependencies successfully. GLIBC_ABI_DT_RELR will be put into the >> >+ .gnu.version_r section. */ >> >+ >> >+static bool >> >+elf_link_add_dt_relr_dependency (struct elf_find_verdep_info *rinfo) >> >+{ >> >+ bfd *glibc_bfd = NULL; >> >+ Elf_Internal_Verneed *t; >> >+ Elf_Internal_Vernaux *a; >> >+ size_t amt; >> >+ const char *relr = "GLIBC_ABI_DT_RELR"; >> >+ >> >+ /* See if we already know about GLIBC_PRIVATE_DT_RELR. */ >> >+ for (t = elf_tdata (rinfo->info->output_bfd)->verref; >> >+ t != NULL; >> >+ t = t->vn_nextref) >> >+ { >> >+ const char *soname = bfd_elf_get_dt_soname (t->vn_bfd); >> >+ /* Skip the shared library if it isn't libc.so. */ >> >+ if (!soname || !startswith (soname, "libc.so.")) >> >+ continue; >> >> Having the dependency on ld.so is better. >> (The linker knows the --dynamic-linker value.) >> libc.so does not need to know whether DT_RELR is used. >> >> It's also easier on glibc side. >> Defining __glibc_abi_dt_relr@@GLIBC_ABI_DT_RELR in elf/Versions is >> simpler than defining it in libc.so and updating every libc.abilist. > >I chose libc.so instead of ld.so since shared libraries may >not be linked against ld.so. The glibc change can be simple: > >https://patchwork.sourceware.org/project/glibc/patch/20211123223053.3334224-1-hjl.tools@gmail.com/ > >Please replace GLIBC_ABI_VERSION_GEN2 with >GLIBC_ABI_DT_RELR. > >or like > >https://gitlab.com/x86-glibc/glibc/-/commit/3bc53baa5304855b5105dabd7726b23d454605fb > >I can go either way. OK... I picked the first way: https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/maskray/relr > > > >> >+ for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) >> >+ { >> >+ /* Return if GLIBC_PRIVATE_DT_RELR dependency has been >> >+ added. */ >> >+ if (a->vna_nodename == relr >> >+ || strcmp (a->vna_nodename, relr) == 0) >> >+ return true; >> >+ >> >+ /* Check if libc.so provides GLIBC_2.XX version. */ >> >+ if (!glibc_bfd && startswith (a->vna_nodename, "GLIBC_2.")) >> >+ glibc_bfd = t->vn_bfd; >> >+ } >> >+ >> >+ break; >> >+ } >> >+ >> >+ /* Skip if it isn't linked against glibc. */ >> >+ if (glibc_bfd == NULL) >> >+ return true; >> >+ >> >+ /* This is a new version. Add it to tree we are building. */ >> >+ if (t == NULL) >> >+ { >> >+ amt = sizeof *t; >> >+ t = (Elf_Internal_Verneed *) bfd_zalloc (rinfo->info->output_bfd, >> >+ amt); >> >+ if (t == NULL) >> >+ { >> >+ rinfo->failed = true; >> >+ return false; >> >+ } >> >+ >> >+ t->vn_bfd = glibc_bfd; >> >+ t->vn_nextref = elf_tdata (rinfo->info->output_bfd)->verref; >> >+ elf_tdata (rinfo->info->output_bfd)->verref = t; >> >+ } >> >+ >> >+ amt = sizeof *a; >> >+ a = (Elf_Internal_Vernaux *) bfd_zalloc (rinfo->info->output_bfd, amt); >> >+ if (a == NULL) >> >+ { >> >+ rinfo->failed = true; >> >+ return false; >> >+ } >> >+ >> >+ a->vna_nodename = relr; >> >+ a->vna_flags = 0; >> >+ a->vna_nextptr = t->vn_auxptr; >> >+ a->vna_other = rinfo->vers + 1; >> >+ ++rinfo->vers; >> >+ >> >+ t->vn_auxptr = a; >> >+ >> >+ return true; >> >+} >> >+ >> > /* Look through the symbols which are defined in other shared >> > libraries and referenced here. Update the list of version >> > dependencies. This will be put into the .gnu.version_r section. >> >@@ -6940,6 +7019,13 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd, >> > if (sinfo.failed) >> > return false; >> > >> >+ if (info->enable_dt_relr) >> >+ { >> >+ elf_link_add_dt_relr_dependency (&sinfo); >> >+ if (sinfo.failed) >> >+ return false; >> >+ } >> >+ >> > if (elf_tdata (output_bfd)->verref == NULL) >> > s->flags |= SEC_EXCLUDE; >> > else >> >diff --git a/ld/ld.texi b/ld/ld.texi >> >index 457089ec06a..a16657ddb45 100644 >> >--- a/ld/ld.texi >> >+++ b/ld/ld.texi >> >@@ -1439,7 +1439,9 @@ and shared library. It adds @code{DT_RELR}, @code{DT_RELRSZ} and >> > building position-dependent executable and relocatable output. This >> > option also implies @option{combreloc} and @option{--relax}. >> > @option{nopack-relative-relocs} is the default, which disables >> >-compact relative relocation. Supported for i386 and x86-64. >> >+compact relative relocation. When linked against the GNU C Library, >> >+a GLIBC_ABI_DT_RELR symbol version dependency on the shared C Library >> >+is added to the output. Supported for i386 and x86-64. >> >> > @item relro >> > @itemx norelro >> >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1.c b/ld/testsuite/ld-elf/dt-relr-glibc-1.c >> >new file mode 100644 >> >index 00000000000..beacffe29e7 >> >--- /dev/null >> >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1.c >> >@@ -0,0 +1,11 @@ >> >+#define REL(n) \ >> >+ static int data##n; \ >> >+ void *p##n = &data##n; >> >+ >> >+REL(1) >> >+REL(2) >> >+REL(3) >> >+REL(4) >> >+REL(5) >> >+REL(6) >> >+REL(7) >> >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd b/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd >> >new file mode 100644 >> >index 00000000000..51bda5d70a1 >> >--- /dev/null >> >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd >> >@@ -0,0 +1,4 @@ >> >+#failif >> >+#... >> >+ 0x[a-f0-9]+: Name: GLIBC_ABI_DT_RELR Flags: none Version: [0-9]+ >> >+#... >> >diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd b/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd >> >new file mode 100644 >> >index 00000000000..6556a6d939e >> >--- /dev/null >> >+++ b/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd >> >@@ -0,0 +1,7 @@ >> >+#... >> >+Version needs section '.gnu.version_r' contains 1 entry: >> >+ Addr: 0x[0-9a-f]+ +Offset: 0x[0-9a-f]+ +Link: +[0-9]+ +\(.dynstr\) >> >+ +0+: Version: 1 +File: libc\.so\.6(|\.1) +Cnt: +[0-9]+ >> >+#... >> >+ 0x[a-f0-9]+: Name: GLIBC_ABI_DT_RELR Flags: none Version: [0-9]+ >> >+#pass >> >diff --git a/ld/testsuite/ld-elf/dt-relr.exp b/ld/testsuite/ld-elf/dt-relr.exp >> >new file mode 100644 >> >index 00000000000..51d21e400ab >> >--- /dev/null >> >+++ b/ld/testsuite/ld-elf/dt-relr.exp >> >@@ -0,0 +1,44 @@ >> >+# Expect script for DT_RELR. >> >+# Copyright (C) 2022 Free Software Foundation, Inc. >> >+# >> >+# This file is part of the GNU Binutils. >> >+# >> >+# This program is free software; you can redistribute it and/or modify >> >+# it under the terms of the GNU General Public License as published by >> >+# the Free Software Foundation; either version 3 of the License, or >> >+# (at your option) any later version. >> >+# >> >+# This program 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 General Public License for more details. >> >+# >> >+# You should have received a copy of the GNU General Public License >> >+# along with this program; if not, write to the Free Software >> >+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, >> >+# MA 02110-1301, USA. >> >+# >> >+ >> >+# Linux tests. >> >+if { ![istarget "*-*-linux*"] } { >> >+ return >> >+} >> >+ >> >+run_cc_link_tests [list \ >> >+ [list \ >> >+ "Build dt-relr-glibc-1a.so" \ >> >+ "-shared $NO_DT_RELR_CC_LDFLAGS" \ >> >+ "-fPIC" \ >> >+ { dt-relr-glibc-1.c } \ >> >+ {{readelf {--version-info} dt-relr-glibc-1a.rd}} \ >> >+ "glibc-relr-1a.so" \ >> >+ ] \ >> >+ [list \ >> >+ "Build dt-relr-glibc-1b.so" \ >> >+ "-shared $DT_RELR_CC_LDFLAGS" \ >> >+ "-fPIC" \ >> >+ { dt-relr-glibc-1.c } \ >> >+ {{readelf {-W --version-info} dt-relr-glibc-1b.rd}} \ >> >+ "dt-relr-glibc-1b.so" \ >> >+ ] \ >> >+] >> >-- >> >2.33.1 >> > > > > >-- >H.J.
On Sat, Jan 8, 2022 at 12:19 AM Fangrui Song <i@maskray.me> wrote: > > On 2022-01-07, H.J. Lu wrote: > >On Fri, Jan 7, 2022 at 8:43 PM Fangrui Song <i@maskray.me> wrote: > >> > >> On 2022-01-07, H.J. Lu via Binutils wrote: > >> >When DT_RELR is enabled, to avoid random run-time crash with older glibc > >> >binaries without DT_RELR support, add a GLIBC_ABI_DT_RELR symbol version, > >> >which is provided by glibc with DT_RELR support, dependency on the shared > >> >C library if it provides a GLIBC_2.XX symbol version. > >> > > >> >bfd/ > >> > > >> > * elflink.c (elf_link_add_dt_relr_dependency): New function. > >> > (bfd_elf_size_dynamic_sections): Call > >> > elf_link_add_dt_relr_dependency if DT_RELR is enabled. > >> > > >> >ld/ > >> > > >> > * ld.texi: Mention GLIBC_ABI_DT_RELR in -z pack-relative-relocs > >> > entry. > >> > * testsuite/ld-elf/dt-relr-glibc-1.c: New file. > >> > * testsuite/ld-elf/dt-relr-glibc-1a.rd: Likewise. > >> > * testsuite/ld-elf/dt-relr-glibc-1b.rd: Likewise. > >> > * testsuite/ld-elf/dt-relr.exp: Likewise. > >> >--- > >> > bfd/elflink.c | 86 +++++++++++++++++++++++++ > >> > ld/ld.texi | 4 +- > >> > ld/testsuite/ld-elf/dt-relr-glibc-1.c | 11 ++++ > >> > ld/testsuite/ld-elf/dt-relr-glibc-1a.rd | 4 ++ > >> > ld/testsuite/ld-elf/dt-relr-glibc-1b.rd | 7 ++ > >> > ld/testsuite/ld-elf/dt-relr.exp | 44 +++++++++++++ > >> > 6 files changed, 155 insertions(+), 1 deletion(-) > >> > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1.c > >> > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1a.rd > >> > create mode 100644 ld/testsuite/ld-elf/dt-relr-glibc-1b.rd > >> > create mode 100644 ld/testsuite/ld-elf/dt-relr.exp > >> > > >> >diff --git a/bfd/elflink.c b/bfd/elflink.c > >> >index 31b13f5df7a..05ac1cb7a63 100644 > >> >--- a/bfd/elflink.c > >> >+++ b/bfd/elflink.c > >> >@@ -2213,6 +2213,85 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data) > >> > return true; > >> > } > >> > > >> >+/* Return true if GLIBC_ABI_DT_RELR is added to the list of version > >> >+ dependencies successfully. GLIBC_ABI_DT_RELR will be put into the > >> >+ .gnu.version_r section. */ > >> >+ > >> >+static bool > >> >+elf_link_add_dt_relr_dependency (struct elf_find_verdep_info *rinfo) > >> >+{ > >> >+ bfd *glibc_bfd = NULL; > >> >+ Elf_Internal_Verneed *t; > >> >+ Elf_Internal_Vernaux *a; > >> >+ size_t amt; > >> >+ const char *relr = "GLIBC_ABI_DT_RELR"; > >> >+ > >> >+ /* See if we already know about GLIBC_PRIVATE_DT_RELR. */ > >> >+ for (t = elf_tdata (rinfo->info->output_bfd)->verref; > >> >+ t != NULL; > >> >+ t = t->vn_nextref) > >> >+ { > >> >+ const char *soname = bfd_elf_get_dt_soname (t->vn_bfd); > >> >+ /* Skip the shared library if it isn't libc.so. */ > >> >+ if (!soname || !startswith (soname, "libc.so.")) > >> >+ continue; > >> > >> Having the dependency on ld.so is better. > >> (The linker knows the --dynamic-linker value.) > >> libc.so does not need to know whether DT_RELR is used. > >> > >> It's also easier on glibc side. > >> Defining __glibc_abi_dt_relr@@GLIBC_ABI_DT_RELR in elf/Versions is > >> simpler than defining it in libc.so and updating every libc.abilist. > > > >I chose libc.so instead of ld.so since shared libraries may > >not be linked against ld.so. The glibc change can be simple: > > > >https://patchwork.sourceware.org/project/glibc/patch/20211123223053.3334224-1-hjl.tools@gmail.com/ > > > >Please replace GLIBC_ABI_VERSION_GEN2 with > >GLIBC_ABI_DT_RELR. > > > >or like > > > >https://gitlab.com/x86-glibc/glibc/-/commit/3bc53baa5304855b5105dabd7726b23d454605fb > > > >I can go either way. > > OK... I picked the first way: > https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/maskray/relr Here it is: https://gitlab.com/x86-glibc/glibc/-/commit/0cef25a4c61f39355224c13ae4f5c15f036659f7 -- H.J.
diff --git a/bfd/elflink.c b/bfd/elflink.c index 31b13f5df7a..05ac1cb7a63 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -2213,6 +2213,85 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data) return true; } +/* Return true if GLIBC_ABI_DT_RELR is added to the list of version + dependencies successfully. GLIBC_ABI_DT_RELR will be put into the + .gnu.version_r section. */ + +static bool +elf_link_add_dt_relr_dependency (struct elf_find_verdep_info *rinfo) +{ + bfd *glibc_bfd = NULL; + Elf_Internal_Verneed *t; + Elf_Internal_Vernaux *a; + size_t amt; + const char *relr = "GLIBC_ABI_DT_RELR"; + + /* See if we already know about GLIBC_PRIVATE_DT_RELR. */ + for (t = elf_tdata (rinfo->info->output_bfd)->verref; + t != NULL; + t = t->vn_nextref) + { + const char *soname = bfd_elf_get_dt_soname (t->vn_bfd); + /* Skip the shared library if it isn't libc.so. */ + if (!soname || !startswith (soname, "libc.so.")) + continue; + + for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) + { + /* Return if GLIBC_PRIVATE_DT_RELR dependency has been + added. */ + if (a->vna_nodename == relr + || strcmp (a->vna_nodename, relr) == 0) + return true; + + /* Check if libc.so provides GLIBC_2.XX version. */ + if (!glibc_bfd && startswith (a->vna_nodename, "GLIBC_2.")) + glibc_bfd = t->vn_bfd; + } + + break; + } + + /* Skip if it isn't linked against glibc. */ + if (glibc_bfd == NULL) + return true; + + /* This is a new version. Add it to tree we are building. */ + if (t == NULL) + { + amt = sizeof *t; + t = (Elf_Internal_Verneed *) bfd_zalloc (rinfo->info->output_bfd, + amt); + if (t == NULL) + { + rinfo->failed = true; + return false; + } + + t->vn_bfd = glibc_bfd; + t->vn_nextref = elf_tdata (rinfo->info->output_bfd)->verref; + elf_tdata (rinfo->info->output_bfd)->verref = t; + } + + amt = sizeof *a; + a = (Elf_Internal_Vernaux *) bfd_zalloc (rinfo->info->output_bfd, amt); + if (a == NULL) + { + rinfo->failed = true; + return false; + } + + a->vna_nodename = relr; + a->vna_flags = 0; + a->vna_nextptr = t->vn_auxptr; + a->vna_other = rinfo->vers + 1; + ++rinfo->vers; + + t->vn_auxptr = a; + + return true; +} + /* Look through the symbols which are defined in other shared libraries and referenced here. Update the list of version dependencies. This will be put into the .gnu.version_r section. @@ -6940,6 +7019,13 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd, if (sinfo.failed) return false; + if (info->enable_dt_relr) + { + elf_link_add_dt_relr_dependency (&sinfo); + if (sinfo.failed) + return false; + } + if (elf_tdata (output_bfd)->verref == NULL) s->flags |= SEC_EXCLUDE; else diff --git a/ld/ld.texi b/ld/ld.texi index 457089ec06a..a16657ddb45 100644 --- a/ld/ld.texi +++ b/ld/ld.texi @@ -1439,7 +1439,9 @@ and shared library. It adds @code{DT_RELR}, @code{DT_RELRSZ} and building position-dependent executable and relocatable output. This option also implies @option{combreloc} and @option{--relax}. @option{nopack-relative-relocs} is the default, which disables -compact relative relocation. Supported for i386 and x86-64. +compact relative relocation. When linked against the GNU C Library, +a GLIBC_ABI_DT_RELR symbol version dependency on the shared C Library +is added to the output. Supported for i386 and x86-64. @item relro @itemx norelro diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1.c b/ld/testsuite/ld-elf/dt-relr-glibc-1.c new file mode 100644 index 00000000000..beacffe29e7 --- /dev/null +++ b/ld/testsuite/ld-elf/dt-relr-glibc-1.c @@ -0,0 +1,11 @@ +#define REL(n) \ + static int data##n; \ + void *p##n = &data##n; + +REL(1) +REL(2) +REL(3) +REL(4) +REL(5) +REL(6) +REL(7) diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd b/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd new file mode 100644 index 00000000000..51bda5d70a1 --- /dev/null +++ b/ld/testsuite/ld-elf/dt-relr-glibc-1a.rd @@ -0,0 +1,4 @@ +#failif +#... + 0x[a-f0-9]+: Name: GLIBC_ABI_DT_RELR Flags: none Version: [0-9]+ +#... diff --git a/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd b/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd new file mode 100644 index 00000000000..6556a6d939e --- /dev/null +++ b/ld/testsuite/ld-elf/dt-relr-glibc-1b.rd @@ -0,0 +1,7 @@ +#... +Version needs section '.gnu.version_r' contains 1 entry: + Addr: 0x[0-9a-f]+ +Offset: 0x[0-9a-f]+ +Link: +[0-9]+ +\(.dynstr\) + +0+: Version: 1 +File: libc\.so\.6(|\.1) +Cnt: +[0-9]+ +#... + 0x[a-f0-9]+: Name: GLIBC_ABI_DT_RELR Flags: none Version: [0-9]+ +#pass diff --git a/ld/testsuite/ld-elf/dt-relr.exp b/ld/testsuite/ld-elf/dt-relr.exp new file mode 100644 index 00000000000..51d21e400ab --- /dev/null +++ b/ld/testsuite/ld-elf/dt-relr.exp @@ -0,0 +1,44 @@ +# Expect script for DT_RELR. +# Copyright (C) 2022 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# Linux tests. +if { ![istarget "*-*-linux*"] } { + return +} + +run_cc_link_tests [list \ + [list \ + "Build dt-relr-glibc-1a.so" \ + "-shared $NO_DT_RELR_CC_LDFLAGS" \ + "-fPIC" \ + { dt-relr-glibc-1.c } \ + {{readelf {--version-info} dt-relr-glibc-1a.rd}} \ + "glibc-relr-1a.so" \ + ] \ + [list \ + "Build dt-relr-glibc-1b.so" \ + "-shared $DT_RELR_CC_LDFLAGS" \ + "-fPIC" \ + { dt-relr-glibc-1.c } \ + {{readelf {-W --version-info} dt-relr-glibc-1b.rd}} \ + "dt-relr-glibc-1b.so" \ + ] \ +]