Skip to content

Commit 0b6b666

Browse files
committed
Add support for symbol name remapping
1 parent 365e1e0 commit 0b6b666

File tree

4 files changed

+444
-7
lines changed

4 files changed

+444
-7
lines changed

src/patchelf.cc

Lines changed: 269 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@
1717
*/
1818

1919
#include <algorithm>
20+
#include <fstream>
2021
#include <limits>
2122
#include <map>
2223
#include <memory>
24+
#include <optional>
2325
#include <set>
2426
#include <sstream>
2527
#include <stdexcept>
2628
#include <string>
2729
#include <unordered_map>
30+
#include <unordered_set>
2831
#include <vector>
29-
#include <optional>
3032

3133
#include <cassert>
3234
#include <cerrno>
@@ -553,6 +555,27 @@ std::optional<std::reference_wrapper<Elf_Shdr>> ElfFile<ElfFileParamNames>::tryF
553555
return {};
554556
}
555557

558+
template<ElfFileParams>
559+
template<class T>
560+
span<T> ElfFile<ElfFileParamNames>::getSectionSpan(const Elf_Shdr & shdr) const
561+
{
562+
return span((T*)(fileContents->data() + rdi(shdr.sh_offset)), rdi(shdr.sh_size)/sizeof(T));
563+
}
564+
565+
template<ElfFileParams>
566+
template<class T>
567+
span<T> ElfFile<ElfFileParamNames>::getSectionSpan(const SectionName & sectionName)
568+
{
569+
return getSectionSpan<T>(findSectionHeader(sectionName));
570+
}
571+
572+
template<ElfFileParams>
573+
template<class T>
574+
span<T> ElfFile<ElfFileParamNames>::tryGetSectionSpan(const SectionName & sectionName)
575+
{
576+
auto shdrOpt = tryFindSectionHeader(sectionName);
577+
return shdrOpt ? getSectionSpan<T>(*shdrOpt) : span<T>();
578+
}
556579

557580
template<ElfFileParams>
558581
unsigned int ElfFile<ElfFileParamNames>::getSectionIndex(const SectionName & sectionName)
@@ -1861,6 +1884,223 @@ void ElfFile<ElfFileParamNames>::addDebugTag()
18611884
changed = true;
18621885
}
18631886

1887+
static uint32_t gnuHash(std::string_view name) {
1888+
uint32_t h = 5381;
1889+
for (uint8_t c : name)
1890+
h = ((h << 5) + h) + c;
1891+
return h;
1892+
}
1893+
1894+
template<ElfFileParams>
1895+
auto ElfFile<ElfFileParamNames>::parseGnuHashTable(span<char> sectionData) -> GnuHashTable
1896+
{
1897+
auto hdr = (typename GnuHashTable::Header*)sectionData.begin();
1898+
auto bloomFilters = span((typename GnuHashTable::BloomWord*)(hdr+1), rdi(hdr->maskwords));
1899+
auto buckets = span((uint32_t*)bloomFilters.end(), rdi(hdr->numBuckets));
1900+
auto table = span(buckets.end(), ((uint32_t*)sectionData.end()) - buckets.end());
1901+
return GnuHashTable{*hdr, bloomFilters, buckets, table};
1902+
}
1903+
1904+
template<ElfFileParams>
1905+
void ElfFile<ElfFileParamNames>::rebuildGnuHashTable(const char* strTab, span<Elf_Sym> dynsyms)
1906+
{
1907+
auto sectionData = tryGetSectionSpan<char>(".gnu.hash");
1908+
if (!sectionData)
1909+
return;
1910+
1911+
auto ght = parseGnuHashTable(sectionData);
1912+
1913+
// We can't trust the value of symndx when the hash table is empty
1914+
if (ght.m_table.size() == 0)
1915+
return;
1916+
1917+
// The hash table includes only a subset of dynsyms
1918+
auto firstSymIdx = rdi(ght.m_hdr.symndx);
1919+
dynsyms = span(dynsyms.begin() + firstSymIdx, dynsyms.end());
1920+
1921+
// Only use the range of symbol versions that will be changed
1922+
auto versyms = tryGetSectionSpan<Elf_Versym>(".gnu.version");
1923+
if (versyms)
1924+
versyms = span(versyms.begin() + firstSymIdx, versyms.end());
1925+
1926+
struct Entry
1927+
{
1928+
uint32_t hash, bucketIdx, originalPos;
1929+
};
1930+
1931+
std::vector<Entry> entries;
1932+
entries.reserve(dynsyms.size());
1933+
1934+
uint32_t pos = 0; // Track the original position of the symbol in the table
1935+
for (auto& sym : dynsyms)
1936+
{
1937+
Entry e;
1938+
e.hash = gnuHash(strTab + rdi(sym.st_name));
1939+
e.bucketIdx = e.hash % ght.m_buckets.size();
1940+
e.originalPos = pos++;
1941+
entries.push_back(e);
1942+
}
1943+
1944+
// Sort the entries based on the buckets. This is a requirement for gnu hash table to work
1945+
std::sort(entries.begin(), entries.end(), [&] (auto& l, auto& r) {
1946+
return l.bucketIdx < r.bucketIdx;
1947+
});
1948+
1949+
// Create a map of old positions to new positions after sorting
1950+
std::vector<uint32_t> old2new(entries.size());
1951+
for (size_t i = 0; i < entries.size(); ++i)
1952+
old2new[entries[i].originalPos] = i;
1953+
1954+
// Update the symbol table with the new order and
1955+
// all tables that refer to symbols through indexes in the symbol table
1956+
auto reorderSpan = [] (auto dst, auto& old2new)
1957+
{
1958+
std::vector tmp(dst.begin(), dst.end());
1959+
for (size_t i = 0; i < tmp.size(); ++i)
1960+
dst[old2new[i]] = tmp[i];
1961+
};
1962+
1963+
reorderSpan(dynsyms, old2new);
1964+
if (versyms)
1965+
reorderSpan(versyms, old2new);
1966+
1967+
auto fixRelocationTable = [&old2new, firstSymIdx, this] <class ER> (auto& hdr)
1968+
{
1969+
auto rela = getSectionSpan<ER>(hdr);
1970+
for (auto& r : rela)
1971+
{
1972+
auto info = rdi(r.r_info);
1973+
auto oldSymIdx = rel_getSymId(info);
1974+
if (oldSymIdx >= firstSymIdx)
1975+
{
1976+
auto newSymIdx = old2new[oldSymIdx - firstSymIdx] + firstSymIdx;
1977+
if (newSymIdx != oldSymIdx)
1978+
wri(r.r_info, rel_setSymId(info, newSymIdx));
1979+
}
1980+
}
1981+
};
1982+
1983+
for (unsigned int i = 1; i < rdi(hdr()->e_shnum); ++i)
1984+
{
1985+
auto& shdr = shdrs.at(i);
1986+
auto shtype = rdi(shdr.sh_type);
1987+
if (shtype == SHT_REL)
1988+
fixRelocationTable.template operator()<Elf_Rel>(shdr);
1989+
else if (shtype == SHT_RELA)
1990+
fixRelocationTable.template operator()<Elf_Rela>(shdr);
1991+
}
1992+
1993+
// Update bloom filters
1994+
std::fill(ght.m_bloomFilters.begin(), ght.m_bloomFilters.end(), 0);
1995+
for (size_t i = 0; i < entries.size(); ++i)
1996+
{
1997+
auto h = entries[i].hash;
1998+
size_t idx = (h / ElfClass) % ght.m_bloomFilters.size();
1999+
auto val = rdi(ght.m_bloomFilters[idx]);
2000+
val |= uint64_t(1) << (h % ElfClass);
2001+
val |= uint64_t(1) << ((h >> rdi(ght.m_hdr.shift2)) % ElfClass);
2002+
wri(ght.m_bloomFilters[idx], val);
2003+
}
2004+
2005+
// Fill buckets
2006+
std::fill(ght.m_buckets.begin(), ght.m_buckets.end(), 0);
2007+
for (size_t i = 0; i < entries.size(); ++i)
2008+
{
2009+
auto symBucketIdx = entries[i].bucketIdx;
2010+
if (!ght.m_buckets[symBucketIdx])
2011+
wri(ght.m_buckets[symBucketIdx], i + firstSymIdx);
2012+
}
2013+
2014+
// Fill hash table
2015+
for (size_t i = 0; i < entries.size(); ++i)
2016+
{
2017+
auto& n = entries[i];
2018+
bool isLast = (i == entries.size() - 1) || (n.bucketIdx != entries[i+1].bucketIdx);
2019+
// Add hash with first bit indicating end of chain
2020+
wri(ght.m_table[i], isLast ? (n.hash | 1) : (n.hash & ~1));
2021+
}
2022+
}
2023+
2024+
static uint32_t sysvHash(std::string_view name) {
2025+
uint32_t h = 0;
2026+
for (uint8_t c : name)
2027+
{
2028+
h = (h << 4) + c;
2029+
uint32_t g = h & 0xf0000000;
2030+
if (g != 0)
2031+
h ^= g >> 24;
2032+
h &= ~g;
2033+
}
2034+
return h;
2035+
}
2036+
2037+
template<ElfFileParams>
2038+
auto ElfFile<ElfFileParamNames>::parseHashTable(span<char> sectionData) -> HashTable
2039+
{
2040+
auto hdr = (typename HashTable::Header*)sectionData.begin();
2041+
auto buckets = span((uint32_t*)(hdr+1), rdi(hdr->numBuckets));
2042+
auto table = span(buckets.end(), ((uint32_t*)sectionData.end()) - buckets.end());
2043+
return HashTable{*hdr, buckets, table};
2044+
}
2045+
2046+
template<ElfFileParams>
2047+
void ElfFile<ElfFileParamNames>::rebuildHashTable(const char* strTab, span<Elf_Sym> dynsyms)
2048+
{
2049+
auto sectionData = tryGetSectionSpan<char>(".hash");
2050+
if (!sectionData)
2051+
return;
2052+
2053+
auto ht = parseHashTable(sectionData);
2054+
2055+
std::fill(ht.m_buckets.begin(), ht.m_buckets.end(), 0);
2056+
std::fill(ht.m_chain.begin(), ht.m_chain.end(), 0);
2057+
2058+
auto symsToInsert = span(dynsyms.end() - ht.m_chain.size(), dynsyms.end());
2059+
2060+
for (auto& sym : symsToInsert)
2061+
{
2062+
auto name = strTab + rdi(sym.st_name);
2063+
uint32_t i = &sym - dynsyms.begin();
2064+
uint32_t hash = sysvHash(name) % ht.m_buckets.size();
2065+
wri(ht.m_chain[i], rdi(ht.m_buckets[hash]));
2066+
wri(ht.m_buckets[hash], i);
2067+
}
2068+
}
2069+
2070+
template<ElfFileParams>
2071+
void ElfFile<ElfFileParamNames>::renameDynamicSymbols(const std::unordered_map<std::string_view, std::string>& remap)
2072+
{
2073+
auto dynsyms = getSectionSpan<Elf_Sym>(".dynsym");
2074+
auto strTab = getSectionSpan<char>(".dynstr");
2075+
2076+
std::vector<char> extraStrings;
2077+
extraStrings.reserve(remap.size() * 30); // Just an estimate
2078+
for (size_t i = 0; i < dynsyms.size(); i++)
2079+
{
2080+
auto& dynsym = dynsyms[i];
2081+
std::string_view name = &strTab[rdi(dynsym.st_name)];
2082+
auto it = remap.find(name);
2083+
if (it != remap.end())
2084+
{
2085+
wri(dynsym.st_name, strTab.size() + extraStrings.size());
2086+
auto& newName = it->second;
2087+
extraStrings.insert(extraStrings.end(), newName.data(), newName.data() + newName.size() + 1);
2088+
changed = true;
2089+
}
2090+
}
2091+
2092+
if (changed)
2093+
{
2094+
auto& newSec = replaceSection(".dynstr", strTab.size() + extraStrings.size());
2095+
std::copy(extraStrings.begin(), extraStrings.end(), newSec.begin() + strTab.size());
2096+
2097+
rebuildGnuHashTable(newSec.data(), dynsyms);
2098+
rebuildHashTable(newSec.data(), dynsyms);
2099+
}
2100+
2101+
this->rewriteSections();
2102+
}
2103+
18642104
template<ElfFileParams>
18652105
void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
18662106
{
@@ -1983,12 +2223,15 @@ static bool removeRPath = false;
19832223
static bool setRPath = false;
19842224
static bool addRPath = false;
19852225
static bool addDebugTag = false;
2226+
static bool renameDynamicSymbols = false;
19862227
static bool printRPath = false;
19872228
static std::string newRPath;
19882229
static std::set<std::string> neededLibsToRemove;
19892230
static std::map<std::string, std::string> neededLibsToReplace;
19902231
static std::set<std::string> neededLibsToAdd;
19912232
static std::set<std::string> symbolsToClearVersion;
2233+
static std::unordered_map<std::string_view, std::string> symbolsToRename;
2234+
static std::unordered_set<std::string> symbolsToRenameKeys;
19922235
static bool printNeeded = false;
19932236
static bool noDefaultLib = false;
19942237
static bool printExecstack = false;
@@ -2048,6 +2291,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
20482291
if (addDebugTag)
20492292
elfFile.addDebugTag();
20502293

2294+
if (renameDynamicSymbols)
2295+
elfFile.renameDynamicSymbols(symbolsToRename);
2296+
20512297
if (elfFile.isChanged()){
20522298
writeFile(fileName, elfFile.fileContents);
20532299
} else if (alwaysWrite) {
@@ -2067,9 +2313,9 @@ static void patchElf()
20672313
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
20682314

20692315
if (getElfType(fileContents).is32Bit)
2070-
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
2316+
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);
20712317
else
2072-
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
2318+
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);
20732319
}
20742320
}
20752321

@@ -2111,6 +2357,7 @@ void showHelp(const std::string & progName)
21112357
[--print-execstack]\t\tPrints whether the object requests an executable stack\n\
21122358
[--clear-execstack]\n\
21132359
[--set-execstack]\n\
2360+
[--rename-dynamic-symbols NAME_MAP_FILE]\tRenames dynamic symbols. The name map file should contain two symbols (old_name new_name) per line\n\
21142361
[--output FILE]\n\
21152362
[--debug]\n\
21162363
[--version]\n\
@@ -2242,6 +2489,25 @@ int mainWrapped(int argc, char * * argv)
22422489
else if (arg == "--add-debug-tag") {
22432490
addDebugTag = true;
22442491
}
2492+
else if (arg == "--rename-dynamic-symbols") {
2493+
renameDynamicSymbols = true;
2494+
if (++i == argc) error("missing argument");
2495+
2496+
std::ifstream infile(argv[i]);
2497+
if (!infile) error(fmt("Cannot open map file ", argv[i]));
2498+
2499+
std::string from, to;
2500+
while (true)
2501+
{
2502+
if (!(infile >> from))
2503+
break;
2504+
if (!(infile >> to))
2505+
error("Odd number of symbols in map file");
2506+
if (symbolsToRenameKeys.count(from))
2507+
error(fmt("Symbol appears twice in the map file: ", from.c_str()));
2508+
symbolsToRename[*symbolsToRenameKeys.insert(from).first] = to;
2509+
}
2510+
}
22452511
else if (arg == "--help" || arg == "-h" ) {
22462512
showHelp(argv[0]);
22472513
return 0;

0 commit comments

Comments
 (0)