summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBreno Rodrigues Guimaraes <brenorg@gmail.com>2023-02-16 13:58:36 -0300
committerBreno Rodrigues Guimaraes <brenorg@gmail.com>2023-02-20 18:43:01 -0300
commit0b6b66687370f2ae97310edbdbd61e7a81ac3019 (patch)
tree53f07e7aa028f30ad995df5a57ee50f0f02ebf1c
parent365e1e01868f453ee01c5aaafd78631818ddf7f0 (diff)
downloadpatchelf-0b6b66687370f2ae97310edbdbd61e7a81ac3019.tar.gz
Add support for symbol name remapping
-rw-r--r--src/patchelf.cc272
-rw-r--r--src/patchelf.h72
-rw-r--r--tests/Makefile.am23
-rwxr-xr-xtests/rename-dynamic-symbols.sh84
4 files changed, 444 insertions, 7 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc
index e91e2ab..847d4dc 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -17,16 +17,18 @@
*/
#include <algorithm>
+#include <fstream>
#include <limits>
#include <map>
#include <memory>
+#include <optional>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
-#include <optional>
#include <cassert>
#include <cerrno>
@@ -553,6 +555,27 @@ std::optional<std::reference_wrapper<Elf_Shdr>> ElfFile<ElfFileParamNames>::tryF
return {};
}
+template<ElfFileParams>
+template<class T>
+span<T> ElfFile<ElfFileParamNames>::getSectionSpan(const Elf_Shdr & shdr) const
+{
+ return span((T*)(fileContents->data() + rdi(shdr.sh_offset)), rdi(shdr.sh_size)/sizeof(T));
+}
+
+template<ElfFileParams>
+template<class T>
+span<T> ElfFile<ElfFileParamNames>::getSectionSpan(const SectionName & sectionName)
+{
+ return getSectionSpan<T>(findSectionHeader(sectionName));
+}
+
+template<ElfFileParams>
+template<class T>
+span<T> ElfFile<ElfFileParamNames>::tryGetSectionSpan(const SectionName & sectionName)
+{
+ auto shdrOpt = tryFindSectionHeader(sectionName);
+ return shdrOpt ? getSectionSpan<T>(*shdrOpt) : span<T>();
+}
template<ElfFileParams>
unsigned int ElfFile<ElfFileParamNames>::getSectionIndex(const SectionName & sectionName)
@@ -1861,6 +1884,223 @@ void ElfFile<ElfFileParamNames>::addDebugTag()
changed = true;
}
+static uint32_t gnuHash(std::string_view name) {
+ uint32_t h = 5381;
+ for (uint8_t c : name)
+ h = ((h << 5) + h) + c;
+ return h;
+}
+
+template<ElfFileParams>
+auto ElfFile<ElfFileParamNames>::parseGnuHashTable(span<char> sectionData) -> GnuHashTable
+{
+ auto hdr = (typename GnuHashTable::Header*)sectionData.begin();
+ auto bloomFilters = span((typename GnuHashTable::BloomWord*)(hdr+1), rdi(hdr->maskwords));
+ auto buckets = span((uint32_t*)bloomFilters.end(), rdi(hdr->numBuckets));
+ auto table = span(buckets.end(), ((uint32_t*)sectionData.end()) - buckets.end());
+ return GnuHashTable{*hdr, bloomFilters, buckets, table};
+}
+
+template<ElfFileParams>
+void ElfFile<ElfFileParamNames>::rebuildGnuHashTable(const char* strTab, span<Elf_Sym> dynsyms)
+{
+ auto sectionData = tryGetSectionSpan<char>(".gnu.hash");
+ if (!sectionData)
+ return;
+
+ auto ght = parseGnuHashTable(sectionData);
+
+ // We can't trust the value of symndx when the hash table is empty
+ if (ght.m_table.size() == 0)
+ return;
+
+ // The hash table includes only a subset of dynsyms
+ auto firstSymIdx = rdi(ght.m_hdr.symndx);
+ dynsyms = span(dynsyms.begin() + firstSymIdx, dynsyms.end());
+
+ // Only use the range of symbol versions that will be changed
+ auto versyms = tryGetSectionSpan<Elf_Versym>(".gnu.version");
+ if (versyms)
+ versyms = span(versyms.begin() + firstSymIdx, versyms.end());
+
+ struct Entry
+ {
+ uint32_t hash, bucketIdx, originalPos;
+ };
+
+ std::vector<Entry> entries;
+ entries.reserve(dynsyms.size());
+
+ uint32_t pos = 0; // Track the original position of the symbol in the table
+ for (auto& sym : dynsyms)
+ {
+ Entry e;
+ e.hash = gnuHash(strTab + rdi(sym.st_name));
+ e.bucketIdx = e.hash % ght.m_buckets.size();
+ e.originalPos = pos++;
+ entries.push_back(e);
+ }
+
+ // Sort the entries based on the buckets. This is a requirement for gnu hash table to work
+ std::sort(entries.begin(), entries.end(), [&] (auto& l, auto& r) {
+ return l.bucketIdx < r.bucketIdx;
+ });
+
+ // Create a map of old positions to new positions after sorting
+ std::vector<uint32_t> old2new(entries.size());
+ for (size_t i = 0; i < entries.size(); ++i)
+ old2new[entries[i].originalPos] = i;
+
+ // Update the symbol table with the new order and
+ // all tables that refer to symbols through indexes in the symbol table
+ auto reorderSpan = [] (auto dst, auto& old2new)
+ {
+ std::vector tmp(dst.begin(), dst.end());
+ for (size_t i = 0; i < tmp.size(); ++i)
+ dst[old2new[i]] = tmp[i];
+ };
+
+ reorderSpan(dynsyms, old2new);
+ if (versyms)
+ reorderSpan(versyms, old2new);
+
+ auto fixRelocationTable = [&old2new, firstSymIdx, this] <class ER> (auto& hdr)
+ {
+ auto rela = getSectionSpan<ER>(hdr);
+ for (auto& r : rela)
+ {
+ auto info = rdi(r.r_info);
+ auto oldSymIdx = rel_getSymId(info);
+ if (oldSymIdx >= firstSymIdx)
+ {
+ auto newSymIdx = old2new[oldSymIdx - firstSymIdx] + firstSymIdx;
+ if (newSymIdx != oldSymIdx)
+ wri(r.r_info, rel_setSymId(info, newSymIdx));
+ }
+ }
+ };
+
+ for (unsigned int i = 1; i < rdi(hdr()->e_shnum); ++i)
+ {
+ auto& shdr = shdrs.at(i);
+ auto shtype = rdi(shdr.sh_type);
+ if (shtype == SHT_REL)
+ fixRelocationTable.template operator()<Elf_Rel>(shdr);
+ else if (shtype == SHT_RELA)
+ fixRelocationTable.template operator()<Elf_Rela>(shdr);
+ }
+
+ // Update bloom filters
+ std::fill(ght.m_bloomFilters.begin(), ght.m_bloomFilters.end(), 0);
+ for (size_t i = 0; i < entries.size(); ++i)
+ {
+ auto h = entries[i].hash;
+ size_t idx = (h / ElfClass) % ght.m_bloomFilters.size();
+ auto val = rdi(ght.m_bloomFilters[idx]);
+ val |= uint64_t(1) << (h % ElfClass);
+ val |= uint64_t(1) << ((h >> rdi(ght.m_hdr.shift2)) % ElfClass);
+ wri(ght.m_bloomFilters[idx], val);
+ }
+
+ // Fill buckets
+ std::fill(ght.m_buckets.begin(), ght.m_buckets.end(), 0);
+ for (size_t i = 0; i < entries.size(); ++i)
+ {
+ auto symBucketIdx = entries[i].bucketIdx;
+ if (!ght.m_buckets[symBucketIdx])
+ wri(ght.m_buckets[symBucketIdx], i + firstSymIdx);
+ }
+
+ // Fill hash table
+ for (size_t i = 0; i < entries.size(); ++i)
+ {
+ auto& n = entries[i];
+ bool isLast = (i == entries.size() - 1) || (n.bucketIdx != entries[i+1].bucketIdx);
+ // Add hash with first bit indicating end of chain
+ wri(ght.m_table[i], isLast ? (n.hash | 1) : (n.hash & ~1));
+ }
+}
+
+static uint32_t sysvHash(std::string_view name) {
+ uint32_t h = 0;
+ for (uint8_t c : name)
+ {
+ h = (h << 4) + c;
+ uint32_t g = h & 0xf0000000;
+ if (g != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+template<ElfFileParams>
+auto ElfFile<ElfFileParamNames>::parseHashTable(span<char> sectionData) -> HashTable
+{
+ auto hdr = (typename HashTable::Header*)sectionData.begin();
+ auto buckets = span((uint32_t*)(hdr+1), rdi(hdr->numBuckets));
+ auto table = span(buckets.end(), ((uint32_t*)sectionData.end()) - buckets.end());
+ return HashTable{*hdr, buckets, table};
+}
+
+template<ElfFileParams>
+void ElfFile<ElfFileParamNames>::rebuildHashTable(const char* strTab, span<Elf_Sym> dynsyms)
+{
+ auto sectionData = tryGetSectionSpan<char>(".hash");
+ if (!sectionData)
+ return;
+
+ auto ht = parseHashTable(sectionData);
+
+ std::fill(ht.m_buckets.begin(), ht.m_buckets.end(), 0);
+ std::fill(ht.m_chain.begin(), ht.m_chain.end(), 0);
+
+ auto symsToInsert = span(dynsyms.end() - ht.m_chain.size(), dynsyms.end());
+
+ for (auto& sym : symsToInsert)
+ {
+ auto name = strTab + rdi(sym.st_name);
+ uint32_t i = &sym - dynsyms.begin();
+ uint32_t hash = sysvHash(name) % ht.m_buckets.size();
+ wri(ht.m_chain[i], rdi(ht.m_buckets[hash]));
+ wri(ht.m_buckets[hash], i);
+ }
+}
+
+template<ElfFileParams>
+void ElfFile<ElfFileParamNames>::renameDynamicSymbols(const std::unordered_map<std::string_view, std::string>& remap)
+{
+ auto dynsyms = getSectionSpan<Elf_Sym>(".dynsym");
+ auto strTab = getSectionSpan<char>(".dynstr");
+
+ std::vector<char> extraStrings;
+ extraStrings.reserve(remap.size() * 30); // Just an estimate
+ for (size_t i = 0; i < dynsyms.size(); i++)
+ {
+ auto& dynsym = dynsyms[i];
+ std::string_view name = &strTab[rdi(dynsym.st_name)];
+ auto it = remap.find(name);
+ if (it != remap.end())
+ {
+ wri(dynsym.st_name, strTab.size() + extraStrings.size());
+ auto& newName = it->second;
+ extraStrings.insert(extraStrings.end(), newName.data(), newName.data() + newName.size() + 1);
+ changed = true;
+ }
+ }
+
+ if (changed)
+ {
+ auto& newSec = replaceSection(".dynstr", strTab.size() + extraStrings.size());
+ std::copy(extraStrings.begin(), extraStrings.end(), newSec.begin() + strTab.size());
+
+ rebuildGnuHashTable(newSec.data(), dynsyms);
+ rebuildHashTable(newSec.data(), dynsyms);
+ }
+
+ this->rewriteSections();
+}
+
template<ElfFileParams>
void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
{
@@ -1983,12 +2223,15 @@ static bool removeRPath = false;
static bool setRPath = false;
static bool addRPath = false;
static bool addDebugTag = false;
+static bool renameDynamicSymbols = false;
static bool printRPath = false;
static std::string newRPath;
static std::set<std::string> neededLibsToRemove;
static std::map<std::string, std::string> neededLibsToReplace;
static std::set<std::string> neededLibsToAdd;
static std::set<std::string> symbolsToClearVersion;
+static std::unordered_map<std::string_view, std::string> symbolsToRename;
+static std::unordered_set<std::string> symbolsToRenameKeys;
static bool printNeeded = false;
static bool noDefaultLib = false;
static bool printExecstack = false;
@@ -2048,6 +2291,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
if (addDebugTag)
elfFile.addDebugTag();
+ if (renameDynamicSymbols)
+ elfFile.renameDynamicSymbols(symbolsToRename);
+
if (elfFile.isChanged()){
writeFile(fileName, elfFile.fileContents);
} else if (alwaysWrite) {
@@ -2067,9 +2313,9 @@ static void patchElf()
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
if (getElfType(fileContents).is32Bit)
- patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
+ patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym, Elf32_Rel, Elf32_Rela, 32>(fileContents), fileContents, outputFileName2);
else
- patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
+ patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym, Elf64_Rel, Elf64_Rela, 64>(fileContents), fileContents, outputFileName2);
}
}
@@ -2111,6 +2357,7 @@ void showHelp(const std::string & progName)
[--print-execstack]\t\tPrints whether the object requests an executable stack\n\
[--clear-execstack]\n\
[--set-execstack]\n\
+ [--rename-dynamic-symbols NAME_MAP_FILE]\tRenames dynamic symbols. The name map file should contain two symbols (old_name new_name) per line\n\
[--output FILE]\n\
[--debug]\n\
[--version]\n\
@@ -2242,6 +2489,25 @@ int mainWrapped(int argc, char * * argv)
else if (arg == "--add-debug-tag") {
addDebugTag = true;
}
+ else if (arg == "--rename-dynamic-symbols") {
+ renameDynamicSymbols = true;
+ if (++i == argc) error("missing argument");
+
+ std::ifstream infile(argv[i]);
+ if (!infile) error(fmt("Cannot open map file ", argv[i]));
+
+ std::string from, to;
+ while (true)
+ {
+ if (!(infile >> from))
+ break;
+ if (!(infile >> to))
+ error("Odd number of symbols in map file");
+ if (symbolsToRenameKeys.count(from))
+ error(fmt("Symbol appears twice in the map file: ", from.c_str()));
+ symbolsToRename[*symbolsToRenameKeys.insert(from).first] = to;
+ }
+ }
else if (arg == "--help" || arg == "-h" ) {
showHelp(argv[0]);
return 0;
diff --git a/src/patchelf.h b/src/patchelf.h
index c3096ff..1914d96 100644
--- a/src/patchelf.h
+++ b/src/patchelf.h
@@ -1,7 +1,22 @@
using FileContents = std::shared_ptr<std::vector<unsigned char>>;
-#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed, class Elf_Versym
-#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym
+#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed, class Elf_Versym, class Elf_Rel, class Elf_Rela, unsigned ElfClass
+#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym, Elf_Rel, Elf_Rela, ElfClass
+
+template<class T>
+struct span
+{
+ span(T* d = {}, size_t l = {}) : data(d), len(l) {}
+ span(T* from, T* to) : data(from), len(to-from) {}
+ T& operator[](std::size_t i) { return data[i]; }
+ T* begin() { return data; }
+ T* end() { return data + len; }
+ auto size() { return len; }
+ explicit operator bool() { return size() > 0; }
+
+ T* data;
+ size_t len;
+};
template<ElfFileParams>
class ElfFile
@@ -85,6 +100,10 @@ private:
std::optional<std::reference_wrapper<Elf_Shdr>> tryFindSectionHeader(const SectionName & sectionName);
+ template<class T> span<T> getSectionSpan(const Elf_Shdr & shdr) const;
+ template<class T> span<T> getSectionSpan(const SectionName & sectionName);
+ template<class T> span<T> tryGetSectionSpan(const SectionName & sectionName);
+
unsigned int getSectionIndex(const SectionName & sectionName);
std::string & replaceSection(const SectionName & sectionName,
@@ -137,6 +156,55 @@ public:
void addDebugTag();
+ void renameDynamicSymbols(const std::unordered_map<std::string_view, std::string>&);
+
+ struct GnuHashTable {
+ using BloomWord = Elf_Addr;
+ struct Header {
+ uint32_t numBuckets, symndx, maskwords, shift2;
+ } m_hdr;
+ span<BloomWord> m_bloomFilters;
+ span<uint32_t> m_buckets, m_table;
+ };
+ GnuHashTable parseGnuHashTable(span<char> gh);
+
+ struct HashTable {
+ struct Header {
+ uint32_t numBuckets, nchain;
+ } m_hdr;
+ span<uint32_t> m_buckets, m_chain;
+ };
+ HashTable parseHashTable(span<char> gh);
+
+ void rebuildGnuHashTable(const char* strTab, span<Elf_Sym> dynsyms);
+ void rebuildHashTable(const char* strTab, span<Elf_Sym> dynsyms);
+
+ using Elf_Rel_Info = decltype(Elf_Rel::r_info);
+
+ uint32_t rel_getSymId(const Elf_Rel_Info& info) const
+ {
+ if constexpr (std::is_same_v<Elf_Rel, Elf64_Rel>)
+ return ELF64_R_SYM(info);
+ else
+ return ELF32_R_SYM(info);
+ }
+
+ Elf_Rel_Info rel_setSymId(Elf_Rel_Info info, uint32_t id) const
+ {
+ if constexpr (std::is_same_v<Elf_Rel, Elf64_Rel>)
+ {
+ constexpr Elf_Rel_Info idmask = (~Elf_Rel_Info()) << 32;
+ info = (info & ~idmask) | (Elf_Rel_Info(id) << 32);
+ }
+ else
+ {
+ constexpr Elf_Rel_Info idmask = (~Elf_Rel_Info()) << 8;
+ info = (info & ~idmask) | (Elf_Rel_Info(id) << 8);
+ }
+ return info;
+ }
+
+
void clearSymbolVersions(const std::set<std::string> & syms);
enum class ExecstackMode { print, set, clear };
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9d36645..4a08c14 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -45,7 +45,9 @@ src_TESTS = \
add-debug-tag.sh \
empty-note.sh \
print-execstack.sh \
- modify-execstack.sh
+ modify-execstack.sh \
+ rename-dynamic-symbols.sh \
+ empty-note.sh
build_TESTS = \
$(no_rpath_arch_TESTS)
@@ -116,7 +118,7 @@ check_DATA = libbig-dynstr.debug
# - with libtool, it is difficult to control options
# - with libtool, it is not possible to compile convenience *dynamic* libraries :-(
check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so libsimple.so libsimple-execstack.so libbuildid.so libtoomanystrtab.so \
- phdr-corruption.so
+ phdr-corruption.so many-syms-main libmany-syms.so
libbuildid_so_SOURCES = simple.c
libbuildid_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,--build-id
@@ -147,6 +149,14 @@ too_many_strtab_SOURCES = too-many-strtab.c too-many-strtab2.s
libtoomanystrtab_so_SOURCES = too-many-strtab.c too-many-strtab2.s
libtoomanystrtab_so_LDFLAGS = $(LDFLAGS_sharedlib)
+many_syms_main_SOURCES = many-syms-main.c
+many_syms_main_LDFLAGS = $(LDFLAGS_local)
+many_syms_main_LDADD = -lmany-syms $(AM_LDADD)
+many_syms_main_DEPENDENCIES = libmany-syms.so
+many_syms_main_CFLAGS = -pie
+libmany_syms_so_SOURCES = many-syms.c
+libmany_syms_so_LDFLAGS = $(LDFLAGS_sharedlib)
+
no_rpath_SOURCES = no-rpath.c
# no -fpic for no-rpath.o
no_rpath_CFLAGS =
@@ -158,3 +168,12 @@ contiguous_note_sections_CFLAGS = -pie
phdr_corruption_so_SOURCES = void.c phdr-corruption.ld
phdr_corruption_so_LDFLAGS = -nostdlib -shared -Wl,-T$(srcdir)/phdr-corruption.ld
phdr_corruption_so_CFLAGS =
+
+many-syms.c:
+ i=1; while [ $$i -le 2000 ]; do echo "void f$$i() {};"; i=$$(($$i + 1)); done > $@
+
+many-syms-main.c:
+ echo "int main() {" > $@
+ i=1; while [ $$i -le 2000 ]; do echo "void f$$i(); f$$i();"; i=$$(($$i + 1)); done >> $@
+ echo "}" >> $@
+
diff --git a/tests/rename-dynamic-symbols.sh b/tests/rename-dynamic-symbols.sh
new file mode 100755
index 0000000..6fabb33
--- /dev/null
+++ b/tests/rename-dynamic-symbols.sh
@@ -0,0 +1,84 @@
+#!/bin/sh -e
+SCRATCH=scratch/$(basename $0 .sh)
+PATCHELF=$(readlink -f "../src/patchelf")
+
+rm -rf ${SCRATCH}
+mkdir -p ${SCRATCH}
+
+full_main_name="${PWD}/many-syms-main"
+full_lib_name="${PWD}/libmany-syms.so"
+chmod -w $full_lib_name $full_main_name
+
+suffix="_special_suffix"
+
+cd ${SCRATCH}
+
+###############################################################################
+# Test that all symbols in the dynamic symbol table will have the expected
+# names after renaming.
+# Also test that if we rename all symbols back, the symbols are as expected
+###############################################################################
+
+list_symbols() {
+ nm -D $@ | awk '{ print $NF }' | sed '/^ *$/d'
+}
+
+list_symbols $full_lib_name | cut -d@ -f1 | sort -u | awk "{printf \"%s %s${suffix}\n\",\$1,\$1}" > map
+list_symbols $full_lib_name | cut -d@ -f1 | sort -u | awk "{printf \"%s${suffix} %s\n\",\$1,\$1}" > rmap
+
+${PATCHELF} --rename-dynamic-symbols map --output libmapped.so $full_lib_name
+${PATCHELF} --rename-dynamic-symbols rmap --output libreversed.so libmapped.so
+
+list_symbols $full_lib_name | sort > orig_syms
+list_symbols libmapped.so | sort > map_syms
+list_symbols libreversed.so | sort > rev_syms
+
+diff orig_syms rev_syms > diff_orig_syms_rev_syms || exit 1
+
+# Renamed symbols that match version numbers will be printed with version instead of them being ommited
+# CXXABI10 is printed as CXXABI10
+# but CXXABI10_renamed is printed as CXXABI10_renamed@@CXXABI10
+# awk is used to remove these cases so that we can match the "mapped" symbols to original symbols
+sed "s/${suffix}//" map_syms | awk -F @ '{ if ($1 == $2 || $1 == $3) { print $1; } else { print $0; }}' | sort > map_syms_r
+diff orig_syms map_syms_r > diff_orig_syms_map_syms_r || exit 1
+
+###############################################################################
+# Check the relocation tables after renaming
+###############################################################################
+
+print_relocation_table() {
+ readelf -W -r $1 | awk '{ printf "%s\n",$5 }' | cut -f1 -d@
+}
+
+print_relocation_table $full_lib_name > orig_rel
+print_relocation_table libmapped.so > map_rel
+print_relocation_table libreversed.so > rev_rel
+
+diff orig_rel rev_rel > diff_orig_rel_rev_rel || exit 1
+sed "s/${suffix}//" map_rel > map_rel_r
+diff orig_rel map_rel_r > diff_orig_rel_map_rel_r || exit 1
+
+###############################################################################
+# Test that the hash table is correctly updated.
+# For this to work, we need to rename symbols and actually use the library
+# Here we:
+# 1. Create a map from all symbols in libstdc++.so as "sym sym_special_suffix"
+# 2. Copy Patchelf and all of its transitive library dependencies into a new directory
+# 3. Rename symbols in Patchelf and all dependencies according to the map
+# 4. Run patchelf with the modified dependencies
+###############################################################################
+
+echo "# Create the map"
+list_symbols --defined-only $full_lib_name | cut -d@ -f1 | sort -u | awk "{printf \"%s %s${suffix}\n\",\$1,\$1}" > map
+
+echo "# Copy all dependencies"
+mkdir env
+cd env
+cp $full_lib_name $full_main_name .
+
+echo "# Apply renaming"
+chmod +w *
+${PATCHELF} --rename-dynamic-symbols ../map *
+
+echo "# Run the patched tool and libraries"
+env LD_BIND_NOW=1 LD_LIBRARY_PATH=${PWD} ./many-syms-main