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
557580template <ElfFileParams>
558581unsigned int ElfFile<ElfFileParamNames>::getSectionIndex(const SectionName & sectionName)
@@ -1855,6 +1878,219 @@ void ElfFile<ElfFileParamNames>::addDebugTag()
18551878 changed = true ;
18561879}
18571880
1881+ static uint32_t gnuHash (std::string_view name) {
1882+ uint32_t h = 5381 ;
1883+ for (uint8_t c : name)
1884+ h = ((h << 5 ) + h) + c;
1885+ return h;
1886+ }
1887+
1888+ template <ElfFileParams>
1889+ auto ElfFile<ElfFileParamNames>::parseGnuHashTable(span<char > sectionData) -> GnuHashTable
1890+ {
1891+ auto hdr = (typename GnuHashTable::Header*)sectionData.begin ();
1892+ auto bloomFilters = span ((typename GnuHashTable::BloomWord*)(hdr+1 ), rdi (hdr->maskwords ));
1893+ auto buckets = span ((uint32_t *)bloomFilters.end (), rdi (hdr->numBuckets ));
1894+ auto table = span (buckets.end (), ((uint32_t *)sectionData.end ()) - buckets.end ());
1895+ return GnuHashTable{*hdr, bloomFilters, buckets, table};
1896+ }
1897+
1898+ template <ElfFileParams>
1899+ void ElfFile<ElfFileParamNames>::rebuildGnuHashTable(const char * strTab, span<Elf_Sym> dynsyms)
1900+ {
1901+ auto sectionData = tryGetSectionSpan<char >(" .gnu.hash" );
1902+ if (!sectionData)
1903+ return ;
1904+
1905+ auto ght = parseGnuHashTable (sectionData);
1906+
1907+ // The hash table includes only a subset of dynsyms
1908+ auto firstSymIdx = rdi (ght.m_hdr .symndx );
1909+ dynsyms = span (dynsyms.begin () + firstSymIdx, dynsyms.end ());
1910+
1911+ // Only use the range of symbol versions that will be changed
1912+ auto versyms = tryGetSectionSpan<Elf_Versym>(" .gnu.version" );
1913+ if (versyms)
1914+ versyms = span (versyms.begin () + firstSymIdx, versyms.end ());
1915+
1916+ struct Entry
1917+ {
1918+ uint32_t hash, bucketIdx, originalPos;
1919+ };
1920+
1921+ std::vector<Entry> entries;
1922+ entries.reserve (dynsyms.size ());
1923+
1924+ uint32_t pos = 0 ; // Track the original position of the symbol in the table
1925+ for (auto & sym : dynsyms)
1926+ {
1927+ Entry e;
1928+ e.hash = gnuHash (strTab + rdi (sym.st_name ));
1929+ e.bucketIdx = e.hash % ght.m_buckets .size ();
1930+ e.originalPos = pos++;
1931+ entries.push_back (e);
1932+ }
1933+
1934+ // Sort the entries based on the buckets. This is a requirement for gnu hash table to work
1935+ std::sort (entries.begin (), entries.end (), [&] (auto & l, auto & r) {
1936+ return l.bucketIdx < r.bucketIdx ;
1937+ });
1938+
1939+ // Create a map of old positions to new positions after sorting
1940+ std::vector<uint32_t > old2new (entries.size ());
1941+ for (size_t i = 0 ; i < entries.size (); ++i)
1942+ old2new[entries[i].originalPos ] = i;
1943+
1944+ // Update the symbol table with the new order and
1945+ // all tables that refer to symbols through indexes in the symbol table
1946+ auto reorderSpan = [] (auto dst, auto & old2new)
1947+ {
1948+ std::vector tmp (dst.begin (), dst.end ());
1949+ for (size_t i = 0 ; i < tmp.size (); ++i)
1950+ dst[old2new[i]] = tmp[i];
1951+ };
1952+
1953+ reorderSpan (dynsyms, old2new);
1954+ if (versyms)
1955+ reorderSpan (versyms, old2new);
1956+
1957+ auto fixRelocationTable = [&old2new, firstSymIdx, this ] <class ER > (auto & hdr)
1958+ {
1959+ auto rela = getSectionSpan<ER>(hdr);
1960+ for (auto & r : rela)
1961+ {
1962+ auto info = rdi (r.r_info );
1963+ auto oldSymIdx = rel_getSymId (info);
1964+ if (oldSymIdx >= firstSymIdx)
1965+ {
1966+ auto newSymIdx = old2new[oldSymIdx - firstSymIdx] + firstSymIdx;
1967+ if (newSymIdx != oldSymIdx)
1968+ wri (r.r_info , rel_setSymId (info, newSymIdx));
1969+ }
1970+ }
1971+ };
1972+
1973+ for (unsigned int i = 1 ; i < rdi (hdr ()->e_shnum ); ++i)
1974+ {
1975+ auto & shdr = shdrs.at (i);
1976+ auto shtype = rdi (shdr.sh_type );
1977+ if (shtype == SHT_REL)
1978+ fixRelocationTable.template operator ()<Elf_Rel>(shdr);
1979+ else if (shtype == SHT_RELA)
1980+ fixRelocationTable.template operator ()<Elf_Rela>(shdr);
1981+ }
1982+
1983+ // Update bloom filters
1984+ std::fill (ght.m_bloomFilters .begin (), ght.m_bloomFilters .end (), 0 );
1985+ for (size_t i = 0 ; i < entries.size (); ++i)
1986+ {
1987+ auto h = entries[i].hash ;
1988+ size_t idx = (h / ElfClass) % ght.m_bloomFilters .size ();
1989+ auto val = rdi (ght.m_bloomFilters [idx]);
1990+ val |= uint64_t (1 ) << (h % ElfClass);
1991+ val |= uint64_t (1 ) << ((h >> rdi (ght.m_hdr .shift2 )) % ElfClass);
1992+ wri (ght.m_bloomFilters [idx], val);
1993+ }
1994+
1995+ // Fill buckets
1996+ std::fill (ght.m_buckets .begin (), ght.m_buckets .end (), 0 );
1997+ for (size_t i = 0 ; i < entries.size (); ++i)
1998+ {
1999+ auto symBucketIdx = entries[i].bucketIdx ;
2000+ if (!ght.m_buckets [symBucketIdx])
2001+ wri (ght.m_buckets [symBucketIdx], i + firstSymIdx);
2002+ }
2003+
2004+ // Fill hash table
2005+ for (size_t i = 0 ; i < entries.size (); ++i)
2006+ {
2007+ auto & n = entries[i];
2008+ bool isLast = (i == entries.size () - 1 ) || (n.bucketIdx != entries[i+1 ].bucketIdx );
2009+ // Add hash with first bit indicating end of chain
2010+ wri (ght.m_table [i], isLast ? (n.hash | 1 ) : (n.hash & ~1 ));
2011+ }
2012+ }
2013+
2014+ static uint32_t sysvHash (std::string_view name) {
2015+ uint32_t h = 0 ;
2016+ for (uint8_t c : name)
2017+ {
2018+ h = (h << 4 ) + c;
2019+ uint32_t g = h & 0xf0000000 ;
2020+ if (g != 0 )
2021+ h ^= g >> 24 ;
2022+ h &= ~g;
2023+ }
2024+ return h;
2025+ }
2026+
2027+ template <ElfFileParams>
2028+ auto ElfFile<ElfFileParamNames>::parseHashTable(span<char > sectionData) -> HashTable
2029+ {
2030+ auto hdr = (typename HashTable::Header*)sectionData.begin ();
2031+ auto buckets = span ((uint32_t *)(hdr+1 ), rdi (hdr->numBuckets ));
2032+ auto table = span (buckets.end (), ((uint32_t *)sectionData.end ()) - buckets.end ());
2033+ return HashTable{*hdr, buckets, table};
2034+ }
2035+
2036+ template <ElfFileParams>
2037+ void ElfFile<ElfFileParamNames>::rebuildHashTable(const char * strTab, span<Elf_Sym> dynsyms)
2038+ {
2039+ auto sectionData = tryGetSectionSpan<char >(" .hash" );
2040+ if (!sectionData)
2041+ return ;
2042+
2043+ auto ht = parseHashTable (sectionData);
2044+
2045+ std::fill (ht.m_buckets .begin (), ht.m_buckets .end (), 0 );
2046+ std::fill (ht.m_chain .begin (), ht.m_chain .end (), 0 );
2047+
2048+ auto symsToInsert = span (dynsyms.end () - ht.m_chain .size (), dynsyms.end ());
2049+
2050+ for (auto & sym : symsToInsert)
2051+ {
2052+ auto name = strTab + rdi (sym.st_name );
2053+ uint32_t i = &sym - dynsyms.begin ();
2054+ uint32_t hash = sysvHash (name) % ht.m_buckets .size ();
2055+ wri (ht.m_chain [i], rdi (ht.m_buckets [hash]));
2056+ wri (ht.m_buckets [hash], i);
2057+ }
2058+ }
2059+
2060+ template <ElfFileParams>
2061+ void ElfFile<ElfFileParamNames>::renameDynamicSymbols(const std::unordered_map<std::string_view, std::string>& remap)
2062+ {
2063+ auto dynsyms = getSectionSpan<Elf_Sym>(" .dynsym" );
2064+ auto strTab = getSectionSpan<char >(" .dynstr" );
2065+
2066+ std::vector<char > extraStrings;
2067+ extraStrings.reserve (remap.size () * 30 ); // Just an estimate
2068+ for (size_t i = 0 ; i < dynsyms.size (); i++)
2069+ {
2070+ auto & dynsym = dynsyms[i];
2071+ std::string_view name = &strTab[rdi (dynsym.st_name )];
2072+ auto it = remap.find (name);
2073+ if (it != remap.end ())
2074+ {
2075+ wri (dynsym.st_name , strTab.size () + extraStrings.size ());
2076+ auto & newName = it->second ;
2077+ extraStrings.insert (extraStrings.end (), newName.data (), newName.data () + newName.size () + 1 );
2078+ changed = true ;
2079+ }
2080+ }
2081+
2082+ if (changed)
2083+ {
2084+ auto & newSec = replaceSection (" .dynstr" , strTab.size () + extraStrings.size ());
2085+ std::copy (extraStrings.begin (), extraStrings.end (), newSec.begin () + strTab.size ());
2086+
2087+ rebuildGnuHashTable (newSec.data (), dynsyms);
2088+ rebuildHashTable (newSec.data (), dynsyms);
2089+ }
2090+
2091+ this ->rewriteSections ();
2092+ }
2093+
18582094template <ElfFileParams>
18592095void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
18602096{
@@ -1898,12 +2134,15 @@ static bool removeRPath = false;
18982134static bool setRPath = false ;
18992135static bool addRPath = false ;
19002136static bool addDebugTag = false ;
2137+ static bool renameDynamicSymbols = false ;
19012138static bool printRPath = false ;
19022139static std::string newRPath;
19032140static std::set<std::string> neededLibsToRemove;
19042141static std::map<std::string, std::string> neededLibsToReplace;
19052142static std::set<std::string> neededLibsToAdd;
19062143static std::set<std::string> symbolsToClearVersion;
2144+ static std::unordered_map<std::string_view, std::string> symbolsToRename;
2145+ static std::unordered_set<std::string> symbolsToRenameKeys;
19072146static bool printNeeded = false ;
19082147static bool noDefaultLib = false ;
19092148
@@ -1953,6 +2192,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
19532192 if (addDebugTag)
19542193 elfFile.addDebugTag ();
19552194
2195+ if (renameDynamicSymbols)
2196+ elfFile.renameDynamicSymbols (symbolsToRename);
2197+
19562198 if (elfFile.isChanged ()){
19572199 writeFile (fileName, elfFile.fileContents );
19582200 } else if (alwaysWrite) {
@@ -1972,9 +2214,9 @@ static void patchElf()
19722214 const std::string & outputFileName2 = outputFileName.empty () ? fileName : outputFileName;
19732215
19742216 if (getElfType (fileContents).is32Bit )
1975- patchElf2 (ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
2217+ 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);
19762218 else
1977- patchElf2 (ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
2219+ 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);
19782220 }
19792221}
19802222
@@ -2013,6 +2255,7 @@ void showHelp(const std::string & progName)
20132255 [--no-sort]\t\t Do not sort program+section headers; useful for debugging patchelf.\n \
20142256 [--clear-symbol-version SYMBOL]\n \
20152257 [--add-debug-tag]\n \
2258+ [--rename-dynamic-symbols NAME_MAP_FILE]\t Renames dynamic symbols. The name map file should contain two symbols (old_name new_name) per line\n \
20162259 [--output FILE]\n \
20172260 [--debug]\n \
20182261 [--version]\n \
@@ -2135,6 +2378,25 @@ int mainWrapped(int argc, char * * argv)
21352378 else if (arg == " --add-debug-tag" ) {
21362379 addDebugTag = true ;
21372380 }
2381+ else if (arg == " --rename-dynamic-symbols" ) {
2382+ renameDynamicSymbols = true ;
2383+ if (++i == argc) error (" missing argument" );
2384+
2385+ std::ifstream infile (argv[i]);
2386+ if (!infile) error (fmt (" Cannot open map file " , argv[i]));
2387+
2388+ std::string from, to;
2389+ while (true )
2390+ {
2391+ if (!(infile >> from))
2392+ break ;
2393+ if (!(infile >> to))
2394+ error (" Odd number of symbols in map file" );
2395+ if (symbolsToRenameKeys.count (from))
2396+ error (fmt (" Symbol appears twice in the map file: " , from.c_str ()));
2397+ symbolsToRename[*symbolsToRenameKeys.insert (from).first ] = to;
2398+ }
2399+ }
21382400 else if (arg == " --help" || arg == " -h" ) {
21392401 showHelp (argv[0 ]);
21402402 return 0 ;
0 commit comments