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. When there is a DT_VERNEED entry with
libc.so on DT_NEEDED, 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.
---
elf/Makefile | 14 ++++++++++--
elf/Versions | 5 +++++
elf/dl-version.c | 51 ++++++++++++++++++++++++++++++++++++++------
include/link.h | 6 ++++++
scripts/abilist.awk | 2 ++
scripts/versions.awk | 7 +++++-
6 files changed, 76 insertions(+), 9 deletions(-)
@@ -1110,8 +1110,12 @@ $(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 \
+# tests-special
update-abi: update-abi-ld
update-all-abi: update-all-abi-ld
@@ -2733,3 +2737,9 @@ $(objpfx)tst-p_align3: $(objpfx)tst-p_alignmod3.so
$(objpfx)tst-p_align3.out: tst-p_align3.sh $(objpfx)tst-p_align3
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
$(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)
@@ -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;
@@ -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;
@@ -243,16 +251,23 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
}
/* We also must store the names of the defined versions. Determine
- the maximum index here as well.
-
- XXX We could avoid the loop by just taking the number of definitions
- as an upper bound of new indices. */
+ the maximum index here as well. */
if (def != NULL)
{
ElfW(Verdef) *ent;
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 +367,30 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
}
}
+ /* When there is a DT_VERNEED entry with libc.so on DT_NEEDED, issue
+ an error if there is a DT_RELR entry without GLIBC_ABI_DT_RELR
+ dependency nor GLIBC_PRIVATE definition. */
+ if (dyn != NULL
+ && map->l_info[DT_NEEDED] != NULL
+ && map->l_info[DT_RELR] != NULL
+ && __glibc_unlikely (map->l_abi_version == lav_none))
+ {
+ const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+ const ElfW(Dyn) *d;
+ for (d = map->l_ld; d->d_tag != DT_NULL; ++d)
+ if (d->d_tag == DT_NEEDED)
+ {
+ const char *name = strtab + d->d_un.d_val;
+ if (strncmp (name, "libc.so.", 8) == 0)
+ {
+ _dl_exception_create
+ (&exception, DSO_FILENAME (map->l_name),
+ N_("DT_RELR without GLIBC_ABI_DT_RELR dependency"));
+ goto call_error;
+ }
+ }
+ }
+
return result;
}
@@ -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. */
@@ -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 = "";
@@ -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) {