Message ID | 20220310200329.1935466-4-hjl.tools@gmail.com |
---|---|
State | New |
Headers | show |
Series | Support DT_RELR relative relocation format | expand |
On 10/03/2022 17:03, H.J. Lu via Libc-alpha wrote: > The EI_ABIVERSION field of the ELF header in executables and shared > libraries can be bumped to indicate the minimum ABI requirement on the > dynamic linker. However, EI_ABIVERSION in executables isn't checked by > the Linux kernel ELF loader nor the existing dynamic linker. Executables > will crash mysteriously if the dynamic linker doesn't support the ABI > features required by the EI_ABIVERSION field. The dynamic linker should > be changed to check EI_ABIVERSION in executables. > > Add a glibc version, GLIBC_ABI_DT_RELR, to indicate DT_RELR support so > that the existing dynamic linkers will issue an error on executables with > GLIBC_ABI_DT_RELR dependency. Issue an error if there is a DT_RELR entry > without GLIBC_ABI_DT_RELR dependency nor GLIBC_PRIVATE definition. > > Support __placeholder_only_for_empty_version_map as the placeholder symbol > used only for empty version map to generate GLIBC_ABI_DT_RELR without any > symbols. I think it only make sense to add the GLIBC_ABI_DT_RELR if the ABI actually supports DT_RELR, otherwise if the ABI start to support old glibcs might wrongly assumes DT_RELR and fails with undefined behavior at runtime (which this patch essentially tries to avoid). > --- > elf/Makefile | 12 ++++++++++-- > elf/Versions | 5 +++++ > elf/dl-version.c | 33 +++++++++++++++++++++++++++++++-- > include/link.h | 6 ++++++ > scripts/abilist.awk | 2 ++ > scripts/versions.awk | 7 ++++++- > 6 files changed, 60 insertions(+), 5 deletions(-) > > diff --git a/elf/Makefile b/elf/Makefile > index fd462ba315..d3118b9777 100644 > --- a/elf/Makefile > +++ b/elf/Makefile > @@ -1113,8 +1113,10 @@ $(eval $(call include_dsosort_tests,dso-sort-tests-1.def)) > $(eval $(call include_dsosort_tests,dso-sort-tests-2.def)) > endif > > -check-abi: $(objpfx)check-abi-ld.out > -tests-special += $(objpfx)check-abi-ld.out > +check-abi: $(objpfx)check-abi-ld.out \ > + $(objpfx)check-abi-version-libc.out > +tests-special += $(objpfx)check-abi-ld.out \ > + $(objpfx)check-abi-version-libc.out One line per definition. > update-abi: update-abi-ld > update-all-abi: update-all-abi-ld > > @@ -2739,3 +2741,9 @@ $(objpfx)check-tst-relr-pie.out: $(objpfx)tst-relr-pie > | sed -ne '/required from libc.so/,$$ p' \ > | grep GLIBC_ABI_DT_RELR > $@; \ > $(evaluate-test) > + > +$(objpfx)check-abi-version-libc.out: $(common-objpfx)libc.so > + LC_ALL=C $(READELF) -V -W $< \ > + | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ > + | grep GLIBC_ABI_DT_RELR > $@; \ > + $(evaluate-test) This test should be enable iff for $(have-dt-relr) == yes. > diff --git a/elf/Versions b/elf/Versions > index 8bed855d8c..a9ff278de7 100644 > --- a/elf/Versions > +++ b/elf/Versions > @@ -23,6 +23,11 @@ libc { > GLIBC_2.35 { > _dl_find_object; > } > + GLIBC_ABI_DT_RELR { > + # This symbol is used only for empty version map and will be removed > + # by scripts/versions.awk. > + __placeholder_only_for_empty_version_map; > + } Same as before. Maybe use the same strategy we do for time64: add a config.h define to HAVE_DTRELR (set by configure) and use it to set the symbol if it is defined. This define would be used on dl-version.c to also enable the l_abi_version check. > GLIBC_PRIVATE { > # functions used in other libraries > __libc_early_init; > diff --git a/elf/dl-version.c b/elf/dl-version.c > index b47bd91727..720ec596a5 100644 > --- a/elf/dl-version.c > +++ b/elf/dl-version.c > @@ -214,12 +214,20 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > while (1) > { > /* Match the symbol. */ > + const char *string = strtab + aux->vna_name; > result |= match_symbol (DSO_FILENAME (map->l_name), > map->l_ns, aux->vna_hash, > - strtab + aux->vna_name, > - needed->l_real, verbose, > + string, needed->l_real, verbose, > aux->vna_flags & VER_FLG_WEAK); > > + if (map->l_abi_version == lav_none > + /* 0xfd0e42: _dl_elf_hash ("GLIBC_ABI_DT_RELR"). */ > + && aux->vna_hash == 0xfd0e42 > + && __glibc_likely (strcmp (string, > + "GLIBC_ABI_DT_RELR") > + == 0)) > + map->l_abi_version = lav_dt_relr_ref; > + > /* Compare the version index. */ > if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high) > ndx_high = aux->vna_other & 0x7fff; > @@ -253,6 +261,16 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr); > while (1) > { > + /* 0x0963cf85: _dl_elf_hash ("GLIBC_PRIVATE"). */ > + if (ent->vd_hash == 0x0963cf85) > + { > + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) ent > + + ent->vd_aux); > + if (__glibc_likely (strcmp ("GLIBC_PRIVATE", > + strtab + aux->vda_name) == 0)) > + map->l_abi_version = lav_private_def; > + } > + > if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high) > ndx_high = ent->vd_ndx & 0x7fff; > > @@ -352,6 +370,17 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > } > } > > + /* Issue an error if there is a DT_RELR entry without GLIBC_ABI_DT_RELR > + dependency nor GLIBC_PRIVATE definition. */ > + if (map->l_info[DT_RELR] != NULL > + && __glibc_unlikely (map->l_abi_version == lav_none)) > + { > + _dl_exception_create > + (&exception, DSO_FILENAME (map->l_name), > + N_("DT_RELR without GLIBC_ABI_DT_RELR dependency")); > + goto call_error; > + } > + > return result; > } > > diff --git a/include/link.h b/include/link.h > index 03db14c7b0..8ec5e35cf2 100644 > --- a/include/link.h > +++ b/include/link.h > @@ -177,6 +177,12 @@ struct link_map > lt_library, /* Library needed by main executable. */ > lt_loaded /* Extra run-time loaded shared object. */ > } l_type:2; > + enum /* ABI dependency of this object. */ > + { > + lav_none, /* No ABI dependency. */ > + lav_dt_relr_ref, /* Need GLIBC_ABI_DT_RELR. */ > + lav_private_def /* Define GLIBC_PRIVATE. */ > + } l_abi_version:2; > unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ > unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ > unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ > diff --git a/scripts/abilist.awk b/scripts/abilist.awk > index 24a34ccbed..6cc7af6ac8 100644 > --- a/scripts/abilist.awk > +++ b/scripts/abilist.awk > @@ -55,6 +55,8 @@ $2 == "g" || $2 == "w" && (NF == 7 || NF == 8) { > # caused STV_HIDDEN symbols to appear in .dynsym, though that is useless. > if (NF > 7 && $7 == ".hidden") next; > > + if (version ~ /^GLIBC_ABI_/ && !include_abi_version) next; > + > if (version == "GLIBC_PRIVATE" && !include_private) next; > > desc = ""; > diff --git a/scripts/versions.awk b/scripts/versions.awk > index 357ad1355e..d70b07bd1a 100644 > --- a/scripts/versions.awk > +++ b/scripts/versions.awk > @@ -185,8 +185,13 @@ END { > closeversion(oldver, veryoldver); > veryoldver = oldver; > } > - printf("%s {\n global:\n", $2) > outfile; > oldver = $2; > + # Skip the placeholder symbol used only for empty version map. > + if ($3 == "__placeholder_only_for_empty_version_map;") { > + printf("%s {\n", $2) > outfile; > + continue; > + } > + printf("%s {\n global:\n", $2) > outfile; > } > printf(" ") > outfile; > for (n = 3; n <= NF; ++n) {
On Tue, Mar 29, 2022 at 9:52 AM Adhemerval Zanella <adhemerval.zanella@linaro.org> wrote: > > > > On 10/03/2022 17:03, H.J. Lu via Libc-alpha wrote: > > The EI_ABIVERSION field of the ELF header in executables and shared > > libraries can be bumped to indicate the minimum ABI requirement on the > > dynamic linker. However, EI_ABIVERSION in executables isn't checked by > > the Linux kernel ELF loader nor the existing dynamic linker. Executables > > will crash mysteriously if the dynamic linker doesn't support the ABI > > features required by the EI_ABIVERSION field. The dynamic linker should > > be changed to check EI_ABIVERSION in executables. > > > > Add a glibc version, GLIBC_ABI_DT_RELR, to indicate DT_RELR support so > > that the existing dynamic linkers will issue an error on executables with > > GLIBC_ABI_DT_RELR dependency. Issue an error if there is a DT_RELR entry > > without GLIBC_ABI_DT_RELR dependency nor GLIBC_PRIVATE definition. > > > > Support __placeholder_only_for_empty_version_map as the placeholder symbol > > used only for empty version map to generate GLIBC_ABI_DT_RELR without any > > symbols. > > I think it only make sense to add the GLIBC_ABI_DT_RELR if the ABI actually > supports DT_RELR, otherwise if the ABI start to support old glibcs might > wrongly assumes DT_RELR and fails with undefined behavior at runtime > (which this patch essentially tries to avoid). The DT_RELR set enables DT_RELR in glibc for all targets. > > --- > > elf/Makefile | 12 ++++++++++-- > > elf/Versions | 5 +++++ > > elf/dl-version.c | 33 +++++++++++++++++++++++++++++++-- > > include/link.h | 6 ++++++ > > scripts/abilist.awk | 2 ++ > > scripts/versions.awk | 7 ++++++- > > 6 files changed, 60 insertions(+), 5 deletions(-) > > > > diff --git a/elf/Makefile b/elf/Makefile > > index fd462ba315..d3118b9777 100644 > > --- a/elf/Makefile > > +++ b/elf/Makefile > > @@ -1113,8 +1113,10 @@ $(eval $(call include_dsosort_tests,dso-sort-tests-1.def)) > > $(eval $(call include_dsosort_tests,dso-sort-tests-2.def)) > > endif > > > > -check-abi: $(objpfx)check-abi-ld.out > > -tests-special += $(objpfx)check-abi-ld.out > > +check-abi: $(objpfx)check-abi-ld.out \ > > + $(objpfx)check-abi-version-libc.out > > +tests-special += $(objpfx)check-abi-ld.out \ > > + $(objpfx)check-abi-version-libc.out > > One line per definition. Fixed in v7. > > update-abi: update-abi-ld > > update-all-abi: update-all-abi-ld > > > > @@ -2739,3 +2741,9 @@ $(objpfx)check-tst-relr-pie.out: $(objpfx)tst-relr-pie > > | sed -ne '/required from libc.so/,$$ p' \ > > | grep GLIBC_ABI_DT_RELR > $@; \ > > $(evaluate-test) > > + > > +$(objpfx)check-abi-version-libc.out: $(common-objpfx)libc.so > > + LC_ALL=C $(READELF) -V -W $< \ > > + | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ > > + | grep GLIBC_ABI_DT_RELR > $@; \ > > + $(evaluate-test) > > This test should be enable iff for $(have-dt-relr) == yes. This test checks if libc.so has the GLIBC_ABI_DT_RELR version definition. Since it doesn't require a new linker, there is no need to check $(have-dt-relr). This test passes with an old linker. > > diff --git a/elf/Versions b/elf/Versions > > index 8bed855d8c..a9ff278de7 100644 > > --- a/elf/Versions > > +++ b/elf/Versions > > @@ -23,6 +23,11 @@ libc { > > GLIBC_2.35 { > > _dl_find_object; > > } > > + GLIBC_ABI_DT_RELR { > > + # This symbol is used only for empty version map and will be removed > > + # by scripts/versions.awk. > > + __placeholder_only_for_empty_version_map; > > + } > > Same as before. Maybe use the same strategy we do for time64: add a config.h > define to HAVE_DTRELR (set by configure) and use it to set the symbol if > it is defined. > > This define would be used on dl-version.c to also enable the l_abi_version > check. > > > GLIBC_PRIVATE { > > # functions used in other libraries > > __libc_early_init; > > diff --git a/elf/dl-version.c b/elf/dl-version.c > > index b47bd91727..720ec596a5 100644 > > --- a/elf/dl-version.c > > +++ b/elf/dl-version.c > > @@ -214,12 +214,20 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > > while (1) > > { > > /* Match the symbol. */ > > + const char *string = strtab + aux->vna_name; > > result |= match_symbol (DSO_FILENAME (map->l_name), > > map->l_ns, aux->vna_hash, > > - strtab + aux->vna_name, > > - needed->l_real, verbose, > > + string, needed->l_real, verbose, > > aux->vna_flags & VER_FLG_WEAK); > > > > + if (map->l_abi_version == lav_none > > + /* 0xfd0e42: _dl_elf_hash ("GLIBC_ABI_DT_RELR"). */ > > + && aux->vna_hash == 0xfd0e42 > > + && __glibc_likely (strcmp (string, > > + "GLIBC_ABI_DT_RELR") > > + == 0)) > > + map->l_abi_version = lav_dt_relr_ref; > > + > > /* Compare the version index. */ > > if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high) > > ndx_high = aux->vna_other & 0x7fff; > > @@ -253,6 +261,16 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > > ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr); > > while (1) > > { > > + /* 0x0963cf85: _dl_elf_hash ("GLIBC_PRIVATE"). */ > > + if (ent->vd_hash == 0x0963cf85) > > + { > > + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) ent > > + + ent->vd_aux); > > + if (__glibc_likely (strcmp ("GLIBC_PRIVATE", > > + strtab + aux->vda_name) == 0)) > > + map->l_abi_version = lav_private_def; > > + } > > + > > if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high) > > ndx_high = ent->vd_ndx & 0x7fff; > > > > @@ -352,6 +370,17 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > > } > > } > > > > + /* Issue an error if there is a DT_RELR entry without GLIBC_ABI_DT_RELR > > + dependency nor GLIBC_PRIVATE definition. */ > > + if (map->l_info[DT_RELR] != NULL > > + && __glibc_unlikely (map->l_abi_version == lav_none)) > > + { > > + _dl_exception_create > > + (&exception, DSO_FILENAME (map->l_name), > > + N_("DT_RELR without GLIBC_ABI_DT_RELR dependency")); > > + goto call_error; > > + } > > + > > return result; > > } > > > > diff --git a/include/link.h b/include/link.h > > index 03db14c7b0..8ec5e35cf2 100644 > > --- a/include/link.h > > +++ b/include/link.h > > @@ -177,6 +177,12 @@ struct link_map > > lt_library, /* Library needed by main executable. */ > > lt_loaded /* Extra run-time loaded shared object. */ > > } l_type:2; > > + enum /* ABI dependency of this object. */ > > + { > > + lav_none, /* No ABI dependency. */ > > + lav_dt_relr_ref, /* Need GLIBC_ABI_DT_RELR. */ > > + lav_private_def /* Define GLIBC_PRIVATE. */ > > + } l_abi_version:2; > > unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ > > unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ > > unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ > > diff --git a/scripts/abilist.awk b/scripts/abilist.awk > > index 24a34ccbed..6cc7af6ac8 100644 > > --- a/scripts/abilist.awk > > +++ b/scripts/abilist.awk > > @@ -55,6 +55,8 @@ $2 == "g" || $2 == "w" && (NF == 7 || NF == 8) { > > # caused STV_HIDDEN symbols to appear in .dynsym, though that is useless. > > if (NF > 7 && $7 == ".hidden") next; > > > > + if (version ~ /^GLIBC_ABI_/ && !include_abi_version) next; > > + > > if (version == "GLIBC_PRIVATE" && !include_private) next; > > > > desc = ""; > > diff --git a/scripts/versions.awk b/scripts/versions.awk > > index 357ad1355e..d70b07bd1a 100644 > > --- a/scripts/versions.awk > > +++ b/scripts/versions.awk > > @@ -185,8 +185,13 @@ END { > > closeversion(oldver, veryoldver); > > veryoldver = oldver; > > } > > - printf("%s {\n global:\n", $2) > outfile; > > oldver = $2; > > + # Skip the placeholder symbol used only for empty version map. > > + if ($3 == "__placeholder_only_for_empty_version_map;") { > > + printf("%s {\n", $2) > outfile; > > + continue; > > + } > > + printf("%s {\n global:\n", $2) > outfile; > > } > > printf(" ") > outfile; > > for (n = 3; n <= NF; ++n) {
On 29/03/2022 19:29, H.J. Lu wrote: > On Tue, Mar 29, 2022 at 9:52 AM Adhemerval Zanella > <adhemerval.zanella@linaro.org> wrote: >> >> >> >> On 10/03/2022 17:03, H.J. Lu via Libc-alpha wrote: >>> The EI_ABIVERSION field of the ELF header in executables and shared >>> libraries can be bumped to indicate the minimum ABI requirement on the >>> dynamic linker. However, EI_ABIVERSION in executables isn't checked by >>> the Linux kernel ELF loader nor the existing dynamic linker. Executables >>> will crash mysteriously if the dynamic linker doesn't support the ABI >>> features required by the EI_ABIVERSION field. The dynamic linker should >>> be changed to check EI_ABIVERSION in executables. >>> >>> Add a glibc version, GLIBC_ABI_DT_RELR, to indicate DT_RELR support so >>> that the existing dynamic linkers will issue an error on executables with >>> GLIBC_ABI_DT_RELR dependency. Issue an error if there is a DT_RELR entry >>> without GLIBC_ABI_DT_RELR dependency nor GLIBC_PRIVATE definition. >>> >>> Support __placeholder_only_for_empty_version_map as the placeholder symbol >>> used only for empty version map to generate GLIBC_ABI_DT_RELR without any >>> symbols. >> >> I think it only make sense to add the GLIBC_ABI_DT_RELR if the ABI actually >> supports DT_RELR, otherwise if the ABI start to support old glibcs might >> wrongly assumes DT_RELR and fails with undefined behavior at runtime >> (which this patch essentially tries to avoid). > > The DT_RELR set enables DT_RELR in glibc for all targets. If I understood correctly, if we add GLIBC_ABI_DT_RELR on 2.36 but only enable it on some architecture on 2.37 and try to run a binary with DT_RELR enabled on glibc 2.36, _dl_check_map_versions won't accuse a failure and it will eventually only fail at runtime (since relocation won't be applied). My understanding we are trying to avoid such failures. > >>> --- >>> elf/Makefile | 12 ++++++++++-- >>> elf/Versions | 5 +++++ >>> elf/dl-version.c | 33 +++++++++++++++++++++++++++++++-- >>> include/link.h | 6 ++++++ >>> scripts/abilist.awk | 2 ++ >>> scripts/versions.awk | 7 ++++++- >>> 6 files changed, 60 insertions(+), 5 deletions(-) >>> >>> diff --git a/elf/Makefile b/elf/Makefile >>> index fd462ba315..d3118b9777 100644 >>> --- a/elf/Makefile >>> +++ b/elf/Makefile >>> @@ -1113,8 +1113,10 @@ $(eval $(call include_dsosort_tests,dso-sort-tests-1.def)) >>> $(eval $(call include_dsosort_tests,dso-sort-tests-2.def)) >>> endif >>> >>> -check-abi: $(objpfx)check-abi-ld.out >>> -tests-special += $(objpfx)check-abi-ld.out >>> +check-abi: $(objpfx)check-abi-ld.out \ >>> + $(objpfx)check-abi-version-libc.out >>> +tests-special += $(objpfx)check-abi-ld.out \ >>> + $(objpfx)check-abi-version-libc.out >> >> One line per definition. > > Fixed in v7. > >>> update-abi: update-abi-ld >>> update-all-abi: update-all-abi-ld >>> >>> @@ -2739,3 +2741,9 @@ $(objpfx)check-tst-relr-pie.out: $(objpfx)tst-relr-pie >>> | sed -ne '/required from libc.so/,$$ p' \ >>> | grep GLIBC_ABI_DT_RELR > $@; \ >>> $(evaluate-test) >>> + >>> +$(objpfx)check-abi-version-libc.out: $(common-objpfx)libc.so >>> + LC_ALL=C $(READELF) -V -W $< \ >>> + | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ >>> + | grep GLIBC_ABI_DT_RELR > $@; \ >>> + $(evaluate-test) >> >> This test should be enable iff for $(have-dt-relr) == yes. > > This test checks if libc.so has the GLIBC_ABI_DT_RELR > version definition. Since it doesn't require a new linker, > there is no need to check $(have-dt-relr). This test > passes with an old linker. > >>> diff --git a/elf/Versions b/elf/Versions >>> index 8bed855d8c..a9ff278de7 100644 >>> --- a/elf/Versions >>> +++ b/elf/Versions >>> @@ -23,6 +23,11 @@ libc { >>> GLIBC_2.35 { >>> _dl_find_object; >>> } >>> + GLIBC_ABI_DT_RELR { >>> + # This symbol is used only for empty version map and will be removed >>> + # by scripts/versions.awk. >>> + __placeholder_only_for_empty_version_map; >>> + } >> >> Same as before. Maybe use the same strategy we do for time64: add a config.h >> define to HAVE_DTRELR (set by configure) and use it to set the symbol if >> it is defined. >> >> This define would be used on dl-version.c to also enable the l_abi_version >> check. >> >>> GLIBC_PRIVATE { >>> # functions used in other libraries >>> __libc_early_init; >>> diff --git a/elf/dl-version.c b/elf/dl-version.c >>> index b47bd91727..720ec596a5 100644 >>> --- a/elf/dl-version.c >>> +++ b/elf/dl-version.c >>> @@ -214,12 +214,20 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) >>> while (1) >>> { >>> /* Match the symbol. */ >>> + const char *string = strtab + aux->vna_name; >>> result |= match_symbol (DSO_FILENAME (map->l_name), >>> map->l_ns, aux->vna_hash, >>> - strtab + aux->vna_name, >>> - needed->l_real, verbose, >>> + string, needed->l_real, verbose, >>> aux->vna_flags & VER_FLG_WEAK); >>> >>> + if (map->l_abi_version == lav_none >>> + /* 0xfd0e42: _dl_elf_hash ("GLIBC_ABI_DT_RELR"). */ >>> + && aux->vna_hash == 0xfd0e42 >>> + && __glibc_likely (strcmp (string, >>> + "GLIBC_ABI_DT_RELR") >>> + == 0)) >>> + map->l_abi_version = lav_dt_relr_ref; >>> + >>> /* Compare the version index. */ >>> if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high) >>> ndx_high = aux->vna_other & 0x7fff; >>> @@ -253,6 +261,16 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) >>> ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr); >>> while (1) >>> { >>> + /* 0x0963cf85: _dl_elf_hash ("GLIBC_PRIVATE"). */ >>> + if (ent->vd_hash == 0x0963cf85) >>> + { >>> + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) ent >>> + + ent->vd_aux); >>> + if (__glibc_likely (strcmp ("GLIBC_PRIVATE", >>> + strtab + aux->vda_name) == 0)) >>> + map->l_abi_version = lav_private_def; >>> + } >>> + >>> if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high) >>> ndx_high = ent->vd_ndx & 0x7fff; >>> >>> @@ -352,6 +370,17 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) >>> } >>> } >>> >>> + /* Issue an error if there is a DT_RELR entry without GLIBC_ABI_DT_RELR >>> + dependency nor GLIBC_PRIVATE definition. */ >>> + if (map->l_info[DT_RELR] != NULL >>> + && __glibc_unlikely (map->l_abi_version == lav_none)) >>> + { >>> + _dl_exception_create >>> + (&exception, DSO_FILENAME (map->l_name), >>> + N_("DT_RELR without GLIBC_ABI_DT_RELR dependency")); >>> + goto call_error; >>> + } >>> + >>> return result; >>> } >>> >>> diff --git a/include/link.h b/include/link.h >>> index 03db14c7b0..8ec5e35cf2 100644 >>> --- a/include/link.h >>> +++ b/include/link.h >>> @@ -177,6 +177,12 @@ struct link_map >>> lt_library, /* Library needed by main executable. */ >>> lt_loaded /* Extra run-time loaded shared object. */ >>> } l_type:2; >>> + enum /* ABI dependency of this object. */ >>> + { >>> + lav_none, /* No ABI dependency. */ >>> + lav_dt_relr_ref, /* Need GLIBC_ABI_DT_RELR. */ >>> + lav_private_def /* Define GLIBC_PRIVATE. */ >>> + } l_abi_version:2; >>> unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ >>> unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ >>> unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ >>> diff --git a/scripts/abilist.awk b/scripts/abilist.awk >>> index 24a34ccbed..6cc7af6ac8 100644 >>> --- a/scripts/abilist.awk >>> +++ b/scripts/abilist.awk >>> @@ -55,6 +55,8 @@ $2 == "g" || $2 == "w" && (NF == 7 || NF == 8) { >>> # caused STV_HIDDEN symbols to appear in .dynsym, though that is useless. >>> if (NF > 7 && $7 == ".hidden") next; >>> >>> + if (version ~ /^GLIBC_ABI_/ && !include_abi_version) next; >>> + >>> if (version == "GLIBC_PRIVATE" && !include_private) next; >>> >>> desc = ""; >>> diff --git a/scripts/versions.awk b/scripts/versions.awk >>> index 357ad1355e..d70b07bd1a 100644 >>> --- a/scripts/versions.awk >>> +++ b/scripts/versions.awk >>> @@ -185,8 +185,13 @@ END { >>> closeversion(oldver, veryoldver); >>> veryoldver = oldver; >>> } >>> - printf("%s {\n global:\n", $2) > outfile; >>> oldver = $2; >>> + # Skip the placeholder symbol used only for empty version map. >>> + if ($3 == "__placeholder_only_for_empty_version_map;") { >>> + printf("%s {\n", $2) > outfile; >>> + continue; >>> + } >>> + printf("%s {\n global:\n", $2) > outfile; >>> } >>> printf(" ") > outfile; >>> for (n = 3; n <= NF; ++n) { > > >
On Wed, Mar 30, 2022 at 7:18 AM Adhemerval Zanella <adhemerval.zanella@linaro.org> wrote: > > > > On 29/03/2022 19:29, H.J. Lu wrote: > > On Tue, Mar 29, 2022 at 9:52 AM Adhemerval Zanella > > <adhemerval.zanella@linaro.org> wrote: > >> > >> > >> > >> On 10/03/2022 17:03, H.J. Lu via Libc-alpha wrote: > >>> The EI_ABIVERSION field of the ELF header in executables and shared > >>> libraries can be bumped to indicate the minimum ABI requirement on the > >>> dynamic linker. However, EI_ABIVERSION in executables isn't checked by > >>> the Linux kernel ELF loader nor the existing dynamic linker. Executables > >>> will crash mysteriously if the dynamic linker doesn't support the ABI > >>> features required by the EI_ABIVERSION field. The dynamic linker should > >>> be changed to check EI_ABIVERSION in executables. > >>> > >>> Add a glibc version, GLIBC_ABI_DT_RELR, to indicate DT_RELR support so > >>> that the existing dynamic linkers will issue an error on executables with > >>> GLIBC_ABI_DT_RELR dependency. Issue an error if there is a DT_RELR entry > >>> without GLIBC_ABI_DT_RELR dependency nor GLIBC_PRIVATE definition. > >>> > >>> Support __placeholder_only_for_empty_version_map as the placeholder symbol > >>> used only for empty version map to generate GLIBC_ABI_DT_RELR without any > >>> symbols. > >> > >> I think it only make sense to add the GLIBC_ABI_DT_RELR if the ABI actually > >> supports DT_RELR, otherwise if the ABI start to support old glibcs might > >> wrongly assumes DT_RELR and fails with undefined behavior at runtime > >> (which this patch essentially tries to avoid). > > > > The DT_RELR set enables DT_RELR in glibc for all targets. > If I understood correctly, if we add GLIBC_ABI_DT_RELR on 2.36 but only enable > it on some architecture on 2.37 and try to run a binary with DT_RELR enabled > on glibc 2.36, _dl_check_map_versions won't accuse a failure and it will > eventually only fail at runtime (since relocation won't be applied). My > understanding we are trying to avoid such failures. My patch set enables DT_RELR and GLIBC_ABI_DT_RELR on glibc 2.36 for all, regardless of binutils version. There is no per-arch behavior. Only DT_RELR run-time tests are enabled for linkers with -z pack-relative-relocs. > > > >>> --- > >>> elf/Makefile | 12 ++++++++++-- > >>> elf/Versions | 5 +++++ > >>> elf/dl-version.c | 33 +++++++++++++++++++++++++++++++-- > >>> include/link.h | 6 ++++++ > >>> scripts/abilist.awk | 2 ++ > >>> scripts/versions.awk | 7 ++++++- > >>> 6 files changed, 60 insertions(+), 5 deletions(-) > >>> > >>> diff --git a/elf/Makefile b/elf/Makefile > >>> index fd462ba315..d3118b9777 100644 > >>> --- a/elf/Makefile > >>> +++ b/elf/Makefile > >>> @@ -1113,8 +1113,10 @@ $(eval $(call include_dsosort_tests,dso-sort-tests-1.def)) > >>> $(eval $(call include_dsosort_tests,dso-sort-tests-2.def)) > >>> endif > >>> > >>> -check-abi: $(objpfx)check-abi-ld.out > >>> -tests-special += $(objpfx)check-abi-ld.out > >>> +check-abi: $(objpfx)check-abi-ld.out \ > >>> + $(objpfx)check-abi-version-libc.out > >>> +tests-special += $(objpfx)check-abi-ld.out \ > >>> + $(objpfx)check-abi-version-libc.out > >> > >> One line per definition. > > > > Fixed in v7. > > > >>> update-abi: update-abi-ld > >>> update-all-abi: update-all-abi-ld > >>> > >>> @@ -2739,3 +2741,9 @@ $(objpfx)check-tst-relr-pie.out: $(objpfx)tst-relr-pie > >>> | sed -ne '/required from libc.so/,$$ p' \ > >>> | grep GLIBC_ABI_DT_RELR > $@; \ > >>> $(evaluate-test) > >>> + > >>> +$(objpfx)check-abi-version-libc.out: $(common-objpfx)libc.so > >>> + LC_ALL=C $(READELF) -V -W $< \ > >>> + | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ > >>> + | grep GLIBC_ABI_DT_RELR > $@; \ > >>> + $(evaluate-test) > >> > >> This test should be enable iff for $(have-dt-relr) == yes. > > > > This test checks if libc.so has the GLIBC_ABI_DT_RELR > > version definition. Since it doesn't require a new linker, > > there is no need to check $(have-dt-relr). This test > > passes with an old linker. > > > >>> diff --git a/elf/Versions b/elf/Versions > >>> index 8bed855d8c..a9ff278de7 100644 > >>> --- a/elf/Versions > >>> +++ b/elf/Versions > >>> @@ -23,6 +23,11 @@ libc { > >>> GLIBC_2.35 { > >>> _dl_find_object; > >>> } > >>> + GLIBC_ABI_DT_RELR { > >>> + # This symbol is used only for empty version map and will be removed > >>> + # by scripts/versions.awk. > >>> + __placeholder_only_for_empty_version_map; > >>> + } > >> > >> Same as before. Maybe use the same strategy we do for time64: add a config.h > >> define to HAVE_DTRELR (set by configure) and use it to set the symbol if > >> it is defined. > >> > >> This define would be used on dl-version.c to also enable the l_abi_version > >> check. > >> > >>> GLIBC_PRIVATE { > >>> # functions used in other libraries > >>> __libc_early_init; > >>> diff --git a/elf/dl-version.c b/elf/dl-version.c > >>> index b47bd91727..720ec596a5 100644 > >>> --- a/elf/dl-version.c > >>> +++ b/elf/dl-version.c > >>> @@ -214,12 +214,20 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > >>> while (1) > >>> { > >>> /* Match the symbol. */ > >>> + const char *string = strtab + aux->vna_name; > >>> result |= match_symbol (DSO_FILENAME (map->l_name), > >>> map->l_ns, aux->vna_hash, > >>> - strtab + aux->vna_name, > >>> - needed->l_real, verbose, > >>> + string, needed->l_real, verbose, > >>> aux->vna_flags & VER_FLG_WEAK); > >>> > >>> + if (map->l_abi_version == lav_none > >>> + /* 0xfd0e42: _dl_elf_hash ("GLIBC_ABI_DT_RELR"). */ > >>> + && aux->vna_hash == 0xfd0e42 > >>> + && __glibc_likely (strcmp (string, > >>> + "GLIBC_ABI_DT_RELR") > >>> + == 0)) > >>> + map->l_abi_version = lav_dt_relr_ref; > >>> + > >>> /* Compare the version index. */ > >>> if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high) > >>> ndx_high = aux->vna_other & 0x7fff; > >>> @@ -253,6 +261,16 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > >>> ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr); > >>> while (1) > >>> { > >>> + /* 0x0963cf85: _dl_elf_hash ("GLIBC_PRIVATE"). */ > >>> + if (ent->vd_hash == 0x0963cf85) > >>> + { > >>> + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) ent > >>> + + ent->vd_aux); > >>> + if (__glibc_likely (strcmp ("GLIBC_PRIVATE", > >>> + strtab + aux->vda_name) == 0)) > >>> + map->l_abi_version = lav_private_def; > >>> + } > >>> + > >>> if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high) > >>> ndx_high = ent->vd_ndx & 0x7fff; > >>> > >>> @@ -352,6 +370,17 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) > >>> } > >>> } > >>> > >>> + /* Issue an error if there is a DT_RELR entry without GLIBC_ABI_DT_RELR > >>> + dependency nor GLIBC_PRIVATE definition. */ > >>> + if (map->l_info[DT_RELR] != NULL > >>> + && __glibc_unlikely (map->l_abi_version == lav_none)) > >>> + { > >>> + _dl_exception_create > >>> + (&exception, DSO_FILENAME (map->l_name), > >>> + N_("DT_RELR without GLIBC_ABI_DT_RELR dependency")); > >>> + goto call_error; > >>> + } > >>> + > >>> return result; > >>> } > >>> > >>> diff --git a/include/link.h b/include/link.h > >>> index 03db14c7b0..8ec5e35cf2 100644 > >>> --- a/include/link.h > >>> +++ b/include/link.h > >>> @@ -177,6 +177,12 @@ struct link_map > >>> lt_library, /* Library needed by main executable. */ > >>> lt_loaded /* Extra run-time loaded shared object. */ > >>> } l_type:2; > >>> + enum /* ABI dependency of this object. */ > >>> + { > >>> + lav_none, /* No ABI dependency. */ > >>> + lav_dt_relr_ref, /* Need GLIBC_ABI_DT_RELR. */ > >>> + lav_private_def /* Define GLIBC_PRIVATE. */ > >>> + } l_abi_version:2; > >>> unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ > >>> unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ > >>> unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ > >>> diff --git a/scripts/abilist.awk b/scripts/abilist.awk > >>> index 24a34ccbed..6cc7af6ac8 100644 > >>> --- a/scripts/abilist.awk > >>> +++ b/scripts/abilist.awk > >>> @@ -55,6 +55,8 @@ $2 == "g" || $2 == "w" && (NF == 7 || NF == 8) { > >>> # caused STV_HIDDEN symbols to appear in .dynsym, though that is useless. > >>> if (NF > 7 && $7 == ".hidden") next; > >>> > >>> + if (version ~ /^GLIBC_ABI_/ && !include_abi_version) next; > >>> + > >>> if (version == "GLIBC_PRIVATE" && !include_private) next; > >>> > >>> desc = ""; > >>> diff --git a/scripts/versions.awk b/scripts/versions.awk > >>> index 357ad1355e..d70b07bd1a 100644 > >>> --- a/scripts/versions.awk > >>> +++ b/scripts/versions.awk > >>> @@ -185,8 +185,13 @@ END { > >>> closeversion(oldver, veryoldver); > >>> veryoldver = oldver; > >>> } > >>> - printf("%s {\n global:\n", $2) > outfile; > >>> oldver = $2; > >>> + # Skip the placeholder symbol used only for empty version map. > >>> + if ($3 == "__placeholder_only_for_empty_version_map;") { > >>> + printf("%s {\n", $2) > outfile; > >>> + continue; > >>> + } > >>> + printf("%s {\n global:\n", $2) > outfile; > >>> } > >>> printf(" ") > outfile; > >>> for (n = 3; n <= NF; ++n) { > > > > > >
On 30/03/2022 11:41, H.J. Lu wrote: > On Wed, Mar 30, 2022 at 7:18 AM Adhemerval Zanella > <adhemerval.zanella@linaro.org> wrote: >> >> >> >> On 29/03/2022 19:29, H.J. Lu wrote: >>> On Tue, Mar 29, 2022 at 9:52 AM Adhemerval Zanella >>> <adhemerval.zanella@linaro.org> wrote: >>>> >>>> >>>> >>>> On 10/03/2022 17:03, H.J. Lu via Libc-alpha wrote: >>>>> The EI_ABIVERSION field of the ELF header in executables and shared >>>>> libraries can be bumped to indicate the minimum ABI requirement on the >>>>> dynamic linker. However, EI_ABIVERSION in executables isn't checked by >>>>> the Linux kernel ELF loader nor the existing dynamic linker. Executables >>>>> will crash mysteriously if the dynamic linker doesn't support the ABI >>>>> features required by the EI_ABIVERSION field. The dynamic linker should >>>>> be changed to check EI_ABIVERSION in executables. >>>>> >>>>> Add a glibc version, GLIBC_ABI_DT_RELR, to indicate DT_RELR support so >>>>> that the existing dynamic linkers will issue an error on executables with >>>>> GLIBC_ABI_DT_RELR dependency. Issue an error if there is a DT_RELR entry >>>>> without GLIBC_ABI_DT_RELR dependency nor GLIBC_PRIVATE definition. >>>>> >>>>> Support __placeholder_only_for_empty_version_map as the placeholder symbol >>>>> used only for empty version map to generate GLIBC_ABI_DT_RELR without any >>>>> symbols. >>>> >>>> I think it only make sense to add the GLIBC_ABI_DT_RELR if the ABI actually >>>> supports DT_RELR, otherwise if the ABI start to support old glibcs might >>>> wrongly assumes DT_RELR and fails with undefined behavior at runtime >>>> (which this patch essentially tries to avoid). >>> >>> The DT_RELR set enables DT_RELR in glibc for all targets. >> If I understood correctly, if we add GLIBC_ABI_DT_RELR on 2.36 but only enable >> it on some architecture on 2.37 and try to run a binary with DT_RELR enabled >> on glibc 2.36, _dl_check_map_versions won't accuse a failure and it will >> eventually only fail at runtime (since relocation won't be applied). My >> understanding we are trying to avoid such failures. > > My patch set enables DT_RELR and GLIBC_ABI_DT_RELR on glibc 2.36 > for all, regardless of binutils version. There is no per-arch > behavior. Only > DT_RELR run-time tests are enabled for linkers with -z pack-relative-relocs. But it still does not fail when the architecture does not actually implement RELR even when static linker does: $ clang --target=aarch64-linux-gnu -fuse-ld=lld -Wl,-z,pack-relative-relocs -fpie -o test -pie test.c $ aarch64-glibc-linux-gnu-readelf -S test | grep relr\.dyn [ 9] .relr.dyn RELR 0000000000000528 00000528 $ ./elf/ld.so --library-path . ./test $ I would expect that even when binary adds GLIBC_ABI_DT_RELR, if dynamic linker does not have DT_RELR it would fail early.
Does the linker add the version dependency? Does ld.so support DT_RELR? On Wed, Mar 30, 2022, 10:17 AM Adhemerval Zanella < adhemerval.zanella@linaro.org> wrote: > > > On 30/03/2022 11:41, H.J. Lu wrote: > > On Wed, Mar 30, 2022 at 7:18 AM Adhemerval Zanella > > <adhemerval.zanella@linaro.org> wrote: > >> > >> > >> > >> On 29/03/2022 19:29, H.J. Lu wrote: > >>> On Tue, Mar 29, 2022 at 9:52 AM Adhemerval Zanella > >>> <adhemerval.zanella@linaro.org> wrote: > >>>> > >>>> > >>>> > >>>> On 10/03/2022 17:03, H.J. Lu via Libc-alpha wrote: > >>>>> The EI_ABIVERSION field of the ELF header in executables and shared > >>>>> libraries can be bumped to indicate the minimum ABI requirement on > the > >>>>> dynamic linker. However, EI_ABIVERSION in executables isn't checked > by > >>>>> the Linux kernel ELF loader nor the existing dynamic linker. > Executables > >>>>> will crash mysteriously if the dynamic linker doesn't support the ABI > >>>>> features required by the EI_ABIVERSION field. The dynamic linker > should > >>>>> be changed to check EI_ABIVERSION in executables. > >>>>> > >>>>> Add a glibc version, GLIBC_ABI_DT_RELR, to indicate DT_RELR support > so > >>>>> that the existing dynamic linkers will issue an error on executables > with > >>>>> GLIBC_ABI_DT_RELR dependency. Issue an error if there is a DT_RELR > entry > >>>>> without GLIBC_ABI_DT_RELR dependency nor GLIBC_PRIVATE definition. > >>>>> > >>>>> Support __placeholder_only_for_empty_version_map as the placeholder > symbol > >>>>> used only for empty version map to generate GLIBC_ABI_DT_RELR > without any > >>>>> symbols. > >>>> > >>>> I think it only make sense to add the GLIBC_ABI_DT_RELR if the ABI > actually > >>>> supports DT_RELR, otherwise if the ABI start to support old glibcs > might > >>>> wrongly assumes DT_RELR and fails with undefined behavior at runtime > >>>> (which this patch essentially tries to avoid). > >>> > >>> The DT_RELR set enables DT_RELR in glibc for all targets. > >> If I understood correctly, if we add GLIBC_ABI_DT_RELR on 2.36 but only > enable > >> it on some architecture on 2.37 and try to run a binary with DT_RELR > enabled > >> on glibc 2.36, _dl_check_map_versions won't accuse a failure and it will > >> eventually only fail at runtime (since relocation won't be applied). My > >> understanding we are trying to avoid such failures. > > > > My patch set enables DT_RELR and GLIBC_ABI_DT_RELR on glibc 2.36 > > for all, regardless of binutils version. There is no per-arch > > behavior. Only > > DT_RELR run-time tests are enabled for linkers with -z > pack-relative-relocs. > > But it still does not fail when the architecture does not actually > implement > RELR even when static linker does: > > $ clang --target=aarch64-linux-gnu -fuse-ld=lld > -Wl,-z,pack-relative-relocs -fpie -o test -pie test.c > $ aarch64-glibc-linux-gnu-readelf -S test | grep relr\.dyn > [ 9] .relr.dyn RELR 0000000000000528 00000528 > $ ./elf/ld.so --library-path . ./test > $ > > I would expect that even when binary adds GLIBC_ABI_DT_RELR, if dynamic > linker > does not have DT_RELR it would fail early. > >
On 30/03/2022 14:32, H.J. Lu wrote: > Does the linker add the version dependency? Does ld.so support DT_RELR? > $ readelf -V test | grep GLIBC_ABI_DT_RELR 0x0030: Name: GLIBC_ABI_DT_RELR Flags: none Version: 4 But ld.so does not support DT_RELR $ grep libc_cv_dt_relr config.log libc_cv_dt_relr=no > On Wed, Mar 30, 2022, 10:17 AM Adhemerval Zanella <adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>> wrote: > > > > On 30/03/2022 11:41, H.J. Lu wrote: > > On Wed, Mar 30, 2022 at 7:18 AM Adhemerval Zanella > > <adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>> wrote: > >> > >> > >> > >> On 29/03/2022 19:29, H.J. Lu wrote: > >>> On Tue, Mar 29, 2022 at 9:52 AM Adhemerval Zanella > >>> <adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>> wrote: > >>>> > >>>> > >>>> > >>>> On 10/03/2022 17:03, H.J. Lu via Libc-alpha wrote: > >>>>> The EI_ABIVERSION field of the ELF header in executables and shared > >>>>> libraries can be bumped to indicate the minimum ABI requirement on the > >>>>> dynamic linker. However, EI_ABIVERSION in executables isn't checked by > >>>>> the Linux kernel ELF loader nor the existing dynamic linker. Executables > >>>>> will crash mysteriously if the dynamic linker doesn't support the ABI > >>>>> features required by the EI_ABIVERSION field. The dynamic linker should > >>>>> be changed to check EI_ABIVERSION in executables. > >>>>> > >>>>> Add a glibc version, GLIBC_ABI_DT_RELR, to indicate DT_RELR support so > >>>>> that the existing dynamic linkers will issue an error on executables with > >>>>> GLIBC_ABI_DT_RELR dependency. Issue an error if there is a DT_RELR entry > >>>>> without GLIBC_ABI_DT_RELR dependency nor GLIBC_PRIVATE definition. > >>>>> > >>>>> Support __placeholder_only_for_empty_version_map as the placeholder symbol > >>>>> used only for empty version map to generate GLIBC_ABI_DT_RELR without any > >>>>> symbols. > >>>> > >>>> I think it only make sense to add the GLIBC_ABI_DT_RELR if the ABI actually > >>>> supports DT_RELR, otherwise if the ABI start to support old glibcs might > >>>> wrongly assumes DT_RELR and fails with undefined behavior at runtime > >>>> (which this patch essentially tries to avoid). > >>> > >>> The DT_RELR set enables DT_RELR in glibc for all targets. > >> If I understood correctly, if we add GLIBC_ABI_DT_RELR on 2.36 but only enable > >> it on some architecture on 2.37 and try to run a binary with DT_RELR enabled > >> on glibc 2.36, _dl_check_map_versions won't accuse a failure and it will > >> eventually only fail at runtime (since relocation won't be applied). My > >> understanding we are trying to avoid such failures. > > > > My patch set enables DT_RELR and GLIBC_ABI_DT_RELR on glibc 2.36 > > for all, regardless of binutils version. There is no per-arch > > behavior. Only > > DT_RELR run-time tests are enabled for linkers with -z pack-relative-relocs. > > But it still does not fail when the architecture does not actually implement > RELR even when static linker does: > > $ clang --target=aarch64-linux-gnu -fuse-ld=lld -Wl,-z,pack-relative-relocs -fpie -o test -pie test.c > $ aarch64-glibc-linux-gnu-readelf -S test | grep relr\.dyn > [ 9] .relr.dyn RELR 0000000000000528 00000528 > $ ./elf/ld.so --library-path . ./test > $ > > I would expect that even when binary adds GLIBC_ABI_DT_RELR, if dynamic linker > does not have DT_RELR it would fail early. >
On Wed, 30 Mar 2022, Adhemerval Zanella via Libc-alpha wrote: > But it still does not fail when the architecture does not actually implement > RELR even when static linker does: The dynamic linker is supposed to support executing binaries using RELR relocations on all architectures - there's not meant to be anything architecture-specific about that support. What's architecture-specific is the static linker support (thus, if missing in the static linker used to build and test glibc, then (a) the dynamic linker could still run binaries using RELR if they were built using some other static linker, but (b) none of the glibc binaries, either installed or test, will contain any RELR relocations themselves).
On 30/03/2022 16:22, Joseph Myers wrote: > On Wed, 30 Mar 2022, Adhemerval Zanella via Libc-alpha wrote: > >> But it still does not fail when the architecture does not actually implement >> RELR even when static linker does: > > The dynamic linker is supposed to support executing binaries using RELR > relocations on all architectures - there's not meant to be anything > architecture-specific about that support. What's architecture-specific is > the static linker support (thus, if missing in the static linker used to > build and test glibc, then (a) the dynamic linker could still run binaries > using RELR if they were built using some other static linker, but (b) none > of the glibc binaries, either installed or test, will contain any RELR > relocations themselves). > Indeed, I was confused by the expected support. This part seems ok H.J.
On 2022-03-30, Adhemerval Zanella via Libc-alpha wrote: > > >On 30/03/2022 16:22, Joseph Myers wrote: >> On Wed, 30 Mar 2022, Adhemerval Zanella via Libc-alpha wrote: >> >>> But it still does not fail when the architecture does not actually implement >>> RELR even when static linker does: >> >> The dynamic linker is supposed to support executing binaries using RELR >> relocations on all architectures - there's not meant to be anything >> architecture-specific about that support. What's architecture-specific is >> the static linker support (thus, if missing in the static linker used to >> build and test glibc, then (a) the dynamic linker could still run binaries >> using RELR if they were built using some other static linker, but (b) none >> of the glibc binaries, either installed or test, will contain any RELR >> relocations themselves). >> > >Indeed, I was confused by the expected support. This part seems ok H.J. Agree. Having the generic support helps GNU ld developers to contribute RELR for non-x86. When I first seen "Add --disable-default-dt-relr" I was thinking whether it is necessary to add this so early, but then I realized that this essentially turns the whole glibc testsuite into tests for GNU ld's RELR support. binutils itself has very few run-time tests and bugs tend to be difficult to surface.
diff --git a/elf/Makefile b/elf/Makefile index fd462ba315..d3118b9777 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -1113,8 +1113,10 @@ $(eval $(call include_dsosort_tests,dso-sort-tests-1.def)) $(eval $(call include_dsosort_tests,dso-sort-tests-2.def)) endif -check-abi: $(objpfx)check-abi-ld.out -tests-special += $(objpfx)check-abi-ld.out +check-abi: $(objpfx)check-abi-ld.out \ + $(objpfx)check-abi-version-libc.out +tests-special += $(objpfx)check-abi-ld.out \ + $(objpfx)check-abi-version-libc.out update-abi: update-abi-ld update-all-abi: update-all-abi-ld @@ -2739,3 +2741,9 @@ $(objpfx)check-tst-relr-pie.out: $(objpfx)tst-relr-pie | sed -ne '/required from libc.so/,$$ p' \ | grep GLIBC_ABI_DT_RELR > $@; \ $(evaluate-test) + +$(objpfx)check-abi-version-libc.out: $(common-objpfx)libc.so + LC_ALL=C $(READELF) -V -W $< \ + | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ + | grep GLIBC_ABI_DT_RELR > $@; \ + $(evaluate-test) diff --git a/elf/Versions b/elf/Versions index 8bed855d8c..a9ff278de7 100644 --- a/elf/Versions +++ b/elf/Versions @@ -23,6 +23,11 @@ libc { GLIBC_2.35 { _dl_find_object; } + GLIBC_ABI_DT_RELR { + # This symbol is used only for empty version map and will be removed + # by scripts/versions.awk. + __placeholder_only_for_empty_version_map; + } GLIBC_PRIVATE { # functions used in other libraries __libc_early_init; diff --git a/elf/dl-version.c b/elf/dl-version.c index b47bd91727..720ec596a5 100644 --- a/elf/dl-version.c +++ b/elf/dl-version.c @@ -214,12 +214,20 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) while (1) { /* Match the symbol. */ + const char *string = strtab + aux->vna_name; result |= match_symbol (DSO_FILENAME (map->l_name), map->l_ns, aux->vna_hash, - strtab + aux->vna_name, - needed->l_real, verbose, + string, needed->l_real, verbose, aux->vna_flags & VER_FLG_WEAK); + if (map->l_abi_version == lav_none + /* 0xfd0e42: _dl_elf_hash ("GLIBC_ABI_DT_RELR"). */ + && aux->vna_hash == 0xfd0e42 + && __glibc_likely (strcmp (string, + "GLIBC_ABI_DT_RELR") + == 0)) + map->l_abi_version = lav_dt_relr_ref; + /* Compare the version index. */ if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high) ndx_high = aux->vna_other & 0x7fff; @@ -253,6 +261,16 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr); while (1) { + /* 0x0963cf85: _dl_elf_hash ("GLIBC_PRIVATE"). */ + if (ent->vd_hash == 0x0963cf85) + { + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) ent + + ent->vd_aux); + if (__glibc_likely (strcmp ("GLIBC_PRIVATE", + strtab + aux->vda_name) == 0)) + map->l_abi_version = lav_private_def; + } + if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high) ndx_high = ent->vd_ndx & 0x7fff; @@ -352,6 +370,17 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) } } + /* Issue an error if there is a DT_RELR entry without GLIBC_ABI_DT_RELR + dependency nor GLIBC_PRIVATE definition. */ + if (map->l_info[DT_RELR] != NULL + && __glibc_unlikely (map->l_abi_version == lav_none)) + { + _dl_exception_create + (&exception, DSO_FILENAME (map->l_name), + N_("DT_RELR without GLIBC_ABI_DT_RELR dependency")); + goto call_error; + } + return result; } diff --git a/include/link.h b/include/link.h index 03db14c7b0..8ec5e35cf2 100644 --- a/include/link.h +++ b/include/link.h @@ -177,6 +177,12 @@ struct link_map lt_library, /* Library needed by main executable. */ lt_loaded /* Extra run-time loaded shared object. */ } l_type:2; + enum /* ABI dependency of this object. */ + { + lav_none, /* No ABI dependency. */ + lav_dt_relr_ref, /* Need GLIBC_ABI_DT_RELR. */ + lav_private_def /* Define GLIBC_PRIVATE. */ + } l_abi_version:2; unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ diff --git a/scripts/abilist.awk b/scripts/abilist.awk index 24a34ccbed..6cc7af6ac8 100644 --- a/scripts/abilist.awk +++ b/scripts/abilist.awk @@ -55,6 +55,8 @@ $2 == "g" || $2 == "w" && (NF == 7 || NF == 8) { # caused STV_HIDDEN symbols to appear in .dynsym, though that is useless. if (NF > 7 && $7 == ".hidden") next; + if (version ~ /^GLIBC_ABI_/ && !include_abi_version) next; + if (version == "GLIBC_PRIVATE" && !include_private) next; desc = ""; diff --git a/scripts/versions.awk b/scripts/versions.awk index 357ad1355e..d70b07bd1a 100644 --- a/scripts/versions.awk +++ b/scripts/versions.awk @@ -185,8 +185,13 @@ END { closeversion(oldver, veryoldver); veryoldver = oldver; } - printf("%s {\n global:\n", $2) > outfile; oldver = $2; + # Skip the placeholder symbol used only for empty version map. + if ($3 == "__placeholder_only_for_empty_version_map;") { + printf("%s {\n", $2) > outfile; + continue; + } + printf("%s {\n global:\n", $2) > outfile; } printf(" ") > outfile; for (n = 3; n <= NF; ++n) {