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)
@@ -1861,6 +1884,222 @@ 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>::GnuHashTable::parse(span<char > sectionData) -> GnuHashTable
1896+ {
1897+ auto hdr = (Header*)sectionData.begin ();
1898+ auto bloomFilters = span ((BloomWord*)(hdr+1 ), hdr->maskwords );
1899+ auto buckets = span ((uint32_t *)bloomFilters.end (), 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 = GnuHashTable::parse (sectionData);
1912+
1913+ // Only work with the last "m_table.size()" symbols from dynsyms which are the
1914+ // symbols that belong to the hash table
1915+ auto firstSymIdx = dynsyms.size () - ght.m_table .size ();
1916+ auto symsToInsert = span (dynsyms.begin () + firstSymIdx, dynsyms.end ());
1917+
1918+ // Only use the range of symbol versions that will be changed
1919+ auto versyms = tryGetSectionSpan<Elf_Versym>(" .gnu.version" );
1920+ if (versyms)
1921+ versyms = span (versyms.begin () + firstSymIdx, versyms.end ());
1922+
1923+ struct Entry
1924+ {
1925+ Elf_Sym sym;
1926+ uint32_t hash, bucketIdx;
1927+ Elf_Versym vsym {};
1928+ uint32_t originalPos;
1929+ };
1930+
1931+ std::vector<Entry> entries;
1932+ entries.reserve (symsToInsert.size ());
1933+
1934+ uint32_t pos = 0 ;
1935+ for (auto & sym : symsToInsert)
1936+ {
1937+ Entry e;
1938+ e.sym = sym;
1939+ e.hash = gnuHash (strTab + rdi (e.sym .st_name ));
1940+ e.originalPos = pos++;
1941+ e.bucketIdx = e.hash % ght.m_hdr .numBuckets ;
1942+ if (versyms)
1943+ e.vsym = versyms[e.originalPos ];
1944+
1945+ entries.push_back (e);
1946+ }
1947+
1948+ // Sort the entries based on the buckets. This is a requirement for gnu hash table to work
1949+ std::sort (entries.begin (), entries.end (), [&] (auto & l, auto & r) {
1950+ return l.bucketIdx < r.bucketIdx ;
1951+ });
1952+
1953+ // Update the symbol table with the new order and
1954+ // all tables that refer to symbols through indexes in the symbol table
1955+ std::vector<uint32_t > old2new (entries.size ());
1956+ for (size_t i = 0 ; i < entries.size (); ++i)
1957+ {
1958+ auto & entry = entries[i];
1959+ old2new[entry.originalPos ] = i + firstSymIdx;
1960+ symsToInsert[i] = entry.sym ;
1961+ if (versyms)
1962+ versyms[i] = entry.vsym ;
1963+ }
1964+
1965+ auto fixRelocationTable = [&old2new, firstSymIdx, this ] <class ER > (auto & hdr)
1966+ {
1967+ auto rela = getSectionSpan<ER>(hdr);
1968+ for (auto & r : rela)
1969+ {
1970+ auto info = rdi (r.r_info );
1971+ auto oldSymIdx = rel_getSymId (info);
1972+ if (oldSymIdx >= firstSymIdx)
1973+ {
1974+ auto newSymIdx = old2new[oldSymIdx - firstSymIdx];
1975+ if (newSymIdx != oldSymIdx) {
1976+ wri (r.r_info , rel_setSymId (info, newSymIdx));
1977+ }
1978+ }
1979+ }
1980+ };
1981+
1982+ for (unsigned int i = 1 ; i < rdi (hdr ()->e_shnum ); ++i)
1983+ {
1984+ auto & shdr = shdrs.at (i);
1985+ auto shtype = rdi (shdr.sh_type );
1986+ if (shtype == SHT_REL)
1987+ fixRelocationTable.template operator ()<Elf_Rel>(shdr);
1988+ else if (shtype == SHT_RELA)
1989+ fixRelocationTable.template operator ()<Elf_Rela>(shdr);
1990+ }
1991+
1992+ // Update bloom filters
1993+ std::fill (ght.m_bloomFilters .begin (), ght.m_bloomFilters .end (), 0 );
1994+ for (size_t i = 0 ; i < entries.size (); ++i)
1995+ {
1996+ auto h = entries[i].hash ;
1997+ size_t idx = (h / ElfWordSize) % ght.m_bloomFilters .size ();
1998+ auto val = rdi (ght.m_bloomFilters [idx]);
1999+ val |= uint64_t (1 ) << (h % ElfWordSize);
2000+ val |= uint64_t (1 ) << ((h >> ght.m_hdr .shift2 ) % ElfWordSize);
2001+ wri (ght.m_bloomFilters [idx], val);
2002+ }
2003+
2004+ // Fill buckets
2005+ std::fill (ght.m_buckets .begin (), ght.m_buckets .end (), 0 );
2006+ for (size_t i = 0 ; i < entries.size (); ++i)
2007+ {
2008+ auto symBucketIdx = entries[i].bucketIdx ;
2009+ if (!ght.m_buckets [symBucketIdx])
2010+ ght.m_buckets [symBucketIdx] = i + ght.m_hdr .symndx ;
2011+ }
2012+
2013+ // Fill hash table
2014+ for (size_t i = 0 ; i < entries.size (); ++i)
2015+ {
2016+ auto & n = entries[i];
2017+ bool isLast = (i == entries.size () - 1 ) || (n.bucketIdx != entries[i+1 ].bucketIdx );
2018+ // Add hash with first bit indicating end of chain
2019+ ght.m_table [i] = isLast ? (n.hash | 1 ) : (n.hash & ~1 );
2020+ }
2021+ }
2022+
2023+ static uint32_t sysvHash (std::string_view name) {
2024+ uint32_t h = 0 ;
2025+ for (uint8_t c : name)
2026+ {
2027+ h = (h << 4 ) + c;
2028+ uint32_t g = h & 0xf0000000 ;
2029+ if (g != 0 )
2030+ h ^= g >> 24 ;
2031+ h &= ~g;
2032+ }
2033+ return h;
2034+ }
2035+
2036+ template <ElfFileParams>
2037+ auto ElfFile<ElfFileParamNames>::HashTable::parse(span<char > sectionData) -> HashTable
2038+ {
2039+ auto hdr = (Header*)sectionData.begin ();
2040+ auto buckets = span ((uint32_t *)(hdr+1 ), hdr->numBuckets );
2041+ auto table = span (buckets.end (), ((uint32_t *)sectionData.end ()) - buckets.end ());
2042+ return HashTable{*hdr, buckets, table};
2043+ }
2044+
2045+ template <ElfFileParams>
2046+ void ElfFile<ElfFileParamNames>::rebuildHashTable(const char * strTab, span<Elf_Sym> dynsyms)
2047+ {
2048+ auto sectionData = tryGetSectionSpan<char >(" .hash" );
2049+ if (!sectionData)
2050+ return ;
2051+
2052+ auto ht = HashTable::parse (sectionData);
2053+
2054+ std::fill (ht.m_buckets .begin (), ht.m_buckets .end (), 0 );
2055+ std::fill (ht.m_chain .begin (), ht.m_chain .end (), 0 );
2056+
2057+ auto symsToInsert = span (dynsyms.end () - ht.m_chain .size (), dynsyms.end ());
2058+
2059+ for (auto & sym : symsToInsert)
2060+ {
2061+ auto name = strTab + rdi (sym.st_name );
2062+ uint32_t i = &sym - dynsyms.begin ();
2063+ uint32_t hash = sysvHash (name) % ht.m_buckets .size ();
2064+ ht.m_chain [i] = ht.m_buckets [hash];
2065+ ht.m_buckets [hash] = i;
2066+ }
2067+ }
2068+
2069+ template <ElfFileParams>
2070+ void ElfFile<ElfFileParamNames>::renameDynamicSymbols(const std::unordered_map<std::string_view, std::string>& remap)
2071+ {
2072+ auto dynsyms = getSectionSpan<Elf_Sym>(" .dynsym" );
2073+ auto strTab = getSectionSpan<char >(" .dynstr" );
2074+
2075+ std::vector<char > extraStrings;
2076+ extraStrings.reserve (remap.size () * 30 ); // Just an estimate
2077+ for (size_t i = 0 ; i < dynsyms.size (); i++)
2078+ {
2079+ auto & dynsym = dynsyms[i];
2080+ std::string_view name = &strTab[rdi (dynsym.st_name )];
2081+ auto it = remap.find (name);
2082+ if (it != remap.end ())
2083+ {
2084+ wri (dynsym.st_name , strTab.size () + extraStrings.size ());
2085+ auto & newName = it->second ;
2086+ extraStrings.insert (extraStrings.end (), newName.data (), newName.data () + newName.size () + 1 );
2087+ changed = true ;
2088+ }
2089+ }
2090+
2091+ if (changed)
2092+ {
2093+ auto & newSec = replaceSection (" .dynstr" , strTab.size () + extraStrings.size ());
2094+ std::copy (extraStrings.begin (), extraStrings.end (), newSec.begin () + strTab.size ());
2095+
2096+ rebuildGnuHashTable (newSec.data (), dynsyms);
2097+ rebuildHashTable (newSec.data (), dynsyms);
2098+ }
2099+
2100+ this ->rewriteSections ();
2101+ }
2102+
18642103template <ElfFileParams>
18652104void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
18662105{
@@ -1904,12 +2143,15 @@ static bool removeRPath = false;
19042143static bool setRPath = false ;
19052144static bool addRPath = false ;
19062145static bool addDebugTag = false ;
2146+ static bool renameDynamicSymbols = false ;
19072147static bool printRPath = false ;
19082148static std::string newRPath;
19092149static std::set<std::string> neededLibsToRemove;
19102150static std::map<std::string, std::string> neededLibsToReplace;
19112151static std::set<std::string> neededLibsToAdd;
19122152static std::set<std::string> symbolsToClearVersion;
2153+ static std::unordered_map<std::string_view, std::string> symbolsToRename;
2154+ static std::unordered_set<std::string> symbolsToRenameKeys;
19132155static bool printNeeded = false ;
19142156static bool noDefaultLib = false ;
19152157
@@ -1959,6 +2201,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
19592201 if (addDebugTag)
19602202 elfFile.addDebugTag ();
19612203
2204+ if (renameDynamicSymbols)
2205+ elfFile.renameDynamicSymbols (symbolsToRename);
2206+
19622207 if (elfFile.isChanged ()){
19632208 writeFile (fileName, elfFile.fileContents );
19642209 } else if (alwaysWrite) {
@@ -1978,9 +2223,9 @@ static void patchElf()
19782223 const std::string & outputFileName2 = outputFileName.empty () ? fileName : outputFileName;
19792224
19802225 if (getElfType (fileContents).is32Bit )
1981- patchElf2 (ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
2226+ patchElf2 (ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym, Elf32_Rel, Elf32_Rela >(fileContents), fileContents, outputFileName2);
19822227 else
1983- patchElf2 (ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
2228+ patchElf2 (ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym, Elf64_Rel, Elf64_Rela >(fileContents), fileContents, outputFileName2);
19842229 }
19852230}
19862231
@@ -2019,6 +2264,7 @@ void showHelp(const std::string & progName)
20192264 [--no-sort]\t\t Do not sort program+section headers; useful for debugging patchelf.\n \
20202265 [--clear-symbol-version SYMBOL]\n \
20212266 [--add-debug-tag]\n \
2267+ [--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 \
20222268 [--output FILE]\n \
20232269 [--debug]\n \
20242270 [--version]\n \
@@ -2141,6 +2387,25 @@ int mainWrapped(int argc, char * * argv)
21412387 else if (arg == " --add-debug-tag" ) {
21422388 addDebugTag = true ;
21432389 }
2390+ else if (arg == " --rename-dynamic-symbols" ) {
2391+ renameDynamicSymbols = true ;
2392+ if (++i == argc) error (" missing argument" );
2393+
2394+ std::ifstream infile (argv[i]);
2395+ if (!infile) error (fmt (" Cannot open map file " , argv[i]));
2396+
2397+ std::string from, to;
2398+ while (true )
2399+ {
2400+ if (!(infile >> from))
2401+ break ;
2402+ if (!(infile >> to))
2403+ error (" Odd number of symbols in map file" );
2404+ if (symbolsToRenameKeys.count (from))
2405+ error (fmt (" Symbol appears twice in the map file: " , from.c_str ()));
2406+ symbolsToRename[*symbolsToRenameKeys.insert (from).first ] = to;
2407+ }
2408+ }
21442409 else if (arg == " --help" || arg == " -h" ) {
21452410 showHelp (argv[0 ]);
21462411 return 0 ;
0 commit comments