This is an archive of the discontinued LLVM Phabricator instance.

[ELF] Support copy relocation on non-default version symbols
ClosedPublic

Authored by MaskRay on Aug 4 2021, 10:06 PM.

Details

Summary

Copy relocation on a non-default version symbol is unsupported and can crash at
runtime. Fortunately there is a one-line fix which works for most cases:
ensure getSymbolsAt unconditionally returns ss.

If two non-default version symbols are defined at the same place and both
are copy relocated, our implementation will copy relocated them into different
addresses. The pointer inequality is very unlikely an issue. In GNU ld, copy
relocating version aliases seems to create more pointer inequality problems than
us.

(
In glibc, sys_errlist@GLIBC_2.2.5 sys_errlist@GLIBC_2.3 sys_errlist@GLIBC_2.4
are defined at the same place, but it is unlikely they are all copy relocated in
one executable. Even if so, the variables are read-only and pointer inequality
should not be a problem.
)

Diff Detail

Event Timeline

MaskRay created this revision.Aug 4 2021, 10:06 PM
MaskRay requested review of this revision.Aug 4 2021, 10:06 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 4 2021, 10:06 PM
MaskRay edited the summary of this revision. (Show Details)Aug 4 2021, 10:08 PM
peter.smith accepted this revision.Aug 5 2021, 10:03 AM

I think this is worth doing as an improvement over where we are today. A quick empirical test with GNU ld by extending copy-rel-version.s with an extra non-default version and 2 separate copy-relocs to each non-default version also results in two separate copy relocations to two separate locations. So I don't think that there is likely to be a program today that relies on the addresses being identical, although there may be in the future.

IIUC if we were to fix the alias problem then we'd need to load the versym section and get the version info for each alias candidate so that we'd do find("sym@ver") rather than ("sym").

This revision is now accepted and ready to land.Aug 5 2021, 10:03 AM

Thanks!

IIUC if we were to fix the alias problem then we'd need to load the versym section and get the version info for each alias candidate so that we'd do find("sym@ver") rather than ("sym").

Yes. I have actually implemented it but realized that it is an overkill.

diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index d5b9efbe18fc..e3cc3821ffad 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -1608,6 +1608,9 @@ template <class ELFT> void SharedFile::parse() {
                                    sym.st_other, sym.getType(), sym.st_value,
                                    sym.st_size, alignment, idx});
   }
+
+  if (versymSec)
+    this->versyms = std::move(versyms);
 }
 
 static ELFKind getBitcodeELFKind(const Triple &t) {
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index bd72cfcdd050..bc01ad31e1cd 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -367,6 +367,8 @@ public:
   // This is actually a vector of Elf_Verdef pointers.
   std::vector<const void *> verdefs;
 
+  std::vector<uint16_t> versyms;
+
   // If the output file needs Elf_Verneed data structures for this file, this is
   // a vector of Elf_Vernaux version identifiers that map onto the entries in
   // Verdefs, otherwise it is empty.
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index e3cc210972b2..778b2086581a 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -516,13 +516,27 @@ static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &ss) {
   using Elf_Sym = typename ELFT::Sym;
 
   SharedFile &file = ss.getFile();
-
+  SmallString<128> buf;
   SmallSet<SharedSymbol *, 4> ret;
-  for (const Elf_Sym &s : file.template getGlobalELFSyms<ELFT>()) {
+  for (auto it : enumerate(file.template getGlobalELFSyms<ELFT>())) {
+    const Elf_Sym &s = it.value();
     if (s.st_shndx == SHN_UNDEF || s.st_shndx == SHN_ABS ||
         s.getType() == STT_TLS || s.st_value != ss.value)
       continue;
     StringRef name = check(s.getName(file.getStringTable()));
+    // Append @ suffix if the symbol has a non-default version.
+    const uint16_t idx =
+        file.versyms.empty() ? VER_NDX_GLOBAL : file.versyms[it.index()];
+    if ((idx & VERSYM_HIDDEN) && (idx & ~VERSYM_HIDDEN) > VER_NDX_GLOBAL) {
+      StringRef verName = file.getStringTable().data() +
+                          reinterpret_cast<const typename ELFT::Verdef *>(
+                              file.verdefs[idx & ~VERSYM_HIDDEN])
+                              ->getAux()
+                              ->vda_name;
+      buf.clear();
+      name = (name + "@" + verName).toStringRef(buf);
+    }
+
     Symbol *sym = symtab->find(name);
     if (auto *alias = dyn_cast_or_null<SharedSymbol>(sym))
       ret.insert(alias);
MaskRay updated this revision to Diff 364539.Aug 5 2021, 10:30 AM
MaskRay edited the summary of this revision. (Show Details)

update comments

This revision was landed with ongoing or failed builds.Aug 5 2021, 10:32 AM
This revision was automatically updated to reflect the committed changes.