17
17
*/
18
18
19
19
#include < algorithm>
20
+ #include < fstream>
20
21
#include < limits>
21
22
#include < map>
22
23
#include < memory>
24
+ #include < optional>
23
25
#include < set>
24
26
#include < sstream>
25
27
#include < stdexcept>
26
28
#include < string>
27
29
#include < string_view>
28
30
#include < unordered_map>
31
+ #include < unordered_set>
29
32
#include < vector>
30
- #include < optional>
31
33
32
34
#include < cassert>
33
35
#include < cerrno>
@@ -599,6 +601,27 @@ std::optional<std::reference_wrapper<const Elf_Shdr>> ElfFile<ElfFileParamNames>
599
601
return {};
600
602
}
601
603
604
+ template <ElfFileParams>
605
+ template <class T >
606
+ span<T> ElfFile<ElfFileParamNames>::getSectionSpan(const Elf_Shdr & shdr) const
607
+ {
608
+ return span ((T*)(fileContents->data () + rdi (shdr.sh_offset )), rdi (shdr.sh_size )/sizeof (T));
609
+ }
610
+
611
+ template <ElfFileParams>
612
+ template <class T >
613
+ span<T> ElfFile<ElfFileParamNames>::getSectionSpan(const SectionName & sectionName)
614
+ {
615
+ return getSectionSpan<T>(findSectionHeader (sectionName));
616
+ }
617
+
618
+ template <ElfFileParams>
619
+ template <class T >
620
+ span<T> ElfFile<ElfFileParamNames>::tryGetSectionSpan(const SectionName & sectionName)
621
+ {
622
+ auto shdrOpt = tryFindSectionHeader (sectionName);
623
+ return shdrOpt ? getSectionSpan<T>(*shdrOpt) : span<T>();
624
+ }
602
625
603
626
template <ElfFileParams>
604
627
unsigned int ElfFile<ElfFileParamNames>::getSectionIndex(const SectionName & sectionName) const
@@ -1910,6 +1933,220 @@ void ElfFile<ElfFileParamNames>::addDebugTag()
1910
1933
changed = true ;
1911
1934
}
1912
1935
1936
+ static uint32_t gnuHash (std::string_view name) {
1937
+ uint32_t h = 5381 ;
1938
+ for (uint8_t c : name)
1939
+ h = ((h << 5 ) + h) + c;
1940
+ return h;
1941
+ }
1942
+
1943
+ template <ElfFileParams>
1944
+ auto ElfFile<ElfFileParamNames>::parseGnuHashTable(span<char > sectionData) -> GnuHashTable
1945
+ {
1946
+ auto hdr = (typename GnuHashTable::Header*)sectionData.begin ();
1947
+ auto bloomFilters = span ((typename GnuHashTable::BloomWord*)(hdr+1 ), rdi (hdr->maskwords ));
1948
+ auto buckets = span ((uint32_t *)bloomFilters.end (), rdi (hdr->numBuckets ));
1949
+ auto table = span (buckets.end (), ((uint32_t *)sectionData.end ()) - buckets.end ());
1950
+ return GnuHashTable{*hdr, bloomFilters, buckets, table};
1951
+ }
1952
+
1953
+ template <ElfFileParams>
1954
+ void ElfFile<ElfFileParamNames>::rebuildGnuHashTable(span<char > strTab, span<Elf_Sym> dynsyms)
1955
+ {
1956
+ auto sectionData = tryGetSectionSpan<char >(" .gnu.hash" );
1957
+ if (!sectionData)
1958
+ return ;
1959
+
1960
+ auto ght = parseGnuHashTable (sectionData);
1961
+
1962
+ // We can't trust the value of symndx when the hash table is empty
1963
+ if (ght.m_table .size () == 0 )
1964
+ return ;
1965
+
1966
+ // The hash table includes only a subset of dynsyms
1967
+ auto firstSymIdx = rdi (ght.m_hdr .symndx );
1968
+ dynsyms = span (&dynsyms[firstSymIdx], dynsyms.end ());
1969
+
1970
+ // Only use the range of symbol versions that will be changed
1971
+ auto versyms = tryGetSectionSpan<Elf_Versym>(" .gnu.version" );
1972
+ if (versyms)
1973
+ versyms = span (&versyms[firstSymIdx], versyms.end ());
1974
+
1975
+ struct Entry
1976
+ {
1977
+ uint32_t hash, bucketIdx, originalPos;
1978
+ };
1979
+
1980
+ std::vector<Entry> entries;
1981
+ entries.reserve (dynsyms.size ());
1982
+
1983
+ uint32_t pos = 0 ; // Track the original position of the symbol in the table
1984
+ for (auto & sym : dynsyms)
1985
+ {
1986
+ Entry e;
1987
+ e.hash = gnuHash (&strTab[rdi (sym.st_name )]);
1988
+ e.bucketIdx = e.hash % ght.m_buckets .size ();
1989
+ e.originalPos = pos++;
1990
+ entries.push_back (e);
1991
+ }
1992
+
1993
+ // Sort the entries based on the buckets. This is a requirement for gnu hash table to work
1994
+ std::sort (entries.begin (), entries.end (), [&] (auto & l, auto & r) {
1995
+ return l.bucketIdx < r.bucketIdx ;
1996
+ });
1997
+
1998
+ // Create a map of old positions to new positions after sorting
1999
+ std::vector<uint32_t > old2new (entries.size ());
2000
+ for (size_t i = 0 ; i < entries.size (); ++i)
2001
+ old2new[entries[i].originalPos ] = i;
2002
+
2003
+ // Update the symbol table with the new order and
2004
+ // all tables that refer to symbols through indexes in the symbol table
2005
+ auto reorderSpan = [] (auto dst, auto & old2new)
2006
+ {
2007
+ std::vector tmp (dst.begin (), dst.end ());
2008
+ for (size_t i = 0 ; i < tmp.size (); ++i)
2009
+ dst[old2new[i]] = tmp[i];
2010
+ };
2011
+
2012
+ reorderSpan (dynsyms, old2new);
2013
+ if (versyms)
2014
+ reorderSpan (versyms, old2new);
2015
+
2016
+ auto remapSymbolId = [&old2new, firstSymIdx] (auto & oldSymIdx)
2017
+ {
2018
+ return oldSymIdx >= firstSymIdx ? old2new[oldSymIdx - firstSymIdx] + firstSymIdx
2019
+ : oldSymIdx;
2020
+ };
2021
+
2022
+ for (unsigned int i = 1 ; i < rdi (hdr ()->e_shnum ); ++i)
2023
+ {
2024
+ auto & shdr = shdrs.at (i);
2025
+ auto shtype = rdi (shdr.sh_type );
2026
+ if (shtype == SHT_REL)
2027
+ changeRelocTableSymIds<Elf_Rel>(shdr, remapSymbolId);
2028
+ else if (shtype == SHT_RELA)
2029
+ changeRelocTableSymIds<Elf_Rela>(shdr, remapSymbolId);
2030
+ }
2031
+
2032
+ // Update bloom filters
2033
+ std::fill (ght.m_bloomFilters .begin (), ght.m_bloomFilters .end (), 0 );
2034
+ for (size_t i = 0 ; i < entries.size (); ++i)
2035
+ {
2036
+ auto h = entries[i].hash ;
2037
+ size_t idx = (h / ElfClass) % ght.m_bloomFilters .size ();
2038
+ auto val = rdi (ght.m_bloomFilters [idx]);
2039
+ val |= uint64_t (1 ) << (h % ElfClass);
2040
+ val |= uint64_t (1 ) << ((h >> rdi (ght.m_hdr .shift2 )) % ElfClass);
2041
+ wri (ght.m_bloomFilters [idx], val);
2042
+ }
2043
+
2044
+ // Fill buckets
2045
+ std::fill (ght.m_buckets .begin (), ght.m_buckets .end (), 0 );
2046
+ for (size_t i = 0 ; i < entries.size (); ++i)
2047
+ {
2048
+ auto symBucketIdx = entries[i].bucketIdx ;
2049
+ if (!ght.m_buckets [symBucketIdx])
2050
+ wri (ght.m_buckets [symBucketIdx], i + firstSymIdx);
2051
+ }
2052
+
2053
+ // Fill hash table
2054
+ for (size_t i = 0 ; i < entries.size (); ++i)
2055
+ {
2056
+ auto & n = entries[i];
2057
+ bool isLast = (i == entries.size () - 1 ) || (n.bucketIdx != entries[i+1 ].bucketIdx );
2058
+ // Add hash with first bit indicating end of chain
2059
+ wri (ght.m_table [i], isLast ? (n.hash | 1 ) : (n.hash & ~1 ));
2060
+ }
2061
+ }
2062
+
2063
+ static uint32_t sysvHash (std::string_view name) {
2064
+ uint32_t h = 0 ;
2065
+ for (uint8_t c : name)
2066
+ {
2067
+ h = (h << 4 ) + c;
2068
+ uint32_t g = h & 0xf0000000 ;
2069
+ if (g != 0 )
2070
+ h ^= g >> 24 ;
2071
+ h &= ~g;
2072
+ }
2073
+ return h;
2074
+ }
2075
+
2076
+ template <ElfFileParams>
2077
+ auto ElfFile<ElfFileParamNames>::parseHashTable(span<char > sectionData) -> HashTable
2078
+ {
2079
+ auto hdr = (typename HashTable::Header*)sectionData.begin ();
2080
+ auto buckets = span ((uint32_t *)(hdr+1 ), rdi (hdr->numBuckets ));
2081
+ auto table = span (buckets.end (), ((uint32_t *)sectionData.end ()) - buckets.end ());
2082
+ return HashTable{*hdr, buckets, table};
2083
+ }
2084
+
2085
+ template <ElfFileParams>
2086
+ void ElfFile<ElfFileParamNames>::rebuildHashTable(span<char > strTab, span<Elf_Sym> dynsyms)
2087
+ {
2088
+ auto sectionData = tryGetSectionSpan<char >(" .hash" );
2089
+ if (!sectionData)
2090
+ return ;
2091
+
2092
+ auto ht = parseHashTable (sectionData);
2093
+
2094
+ std::fill (ht.m_buckets .begin (), ht.m_buckets .end (), 0 );
2095
+ std::fill (ht.m_chain .begin (), ht.m_chain .end (), 0 );
2096
+
2097
+ // The hash table includes only a subset of dynsyms
2098
+ auto firstSymIdx = dynsyms.size () - ht.m_chain .size ();
2099
+ dynsyms = span (&dynsyms[firstSymIdx], dynsyms.end ());
2100
+
2101
+ for (auto & sym : dynsyms)
2102
+ {
2103
+ auto name = &strTab[rdi (sym.st_name )];
2104
+ uint32_t i = &sym - dynsyms.begin ();
2105
+ uint32_t hash = sysvHash (name) % ht.m_buckets .size ();
2106
+ wri (ht.m_chain [i], rdi (ht.m_buckets [hash]));
2107
+ wri (ht.m_buckets [hash], i);
2108
+ }
2109
+ }
2110
+
2111
+ template <ElfFileParams>
2112
+ void ElfFile<ElfFileParamNames>::renameDynamicSymbols(const std::unordered_map<std::string_view, std::string>& remap)
2113
+ {
2114
+ auto dynsyms = getSectionSpan<Elf_Sym>(" .dynsym" );
2115
+ auto strTab = getSectionSpan<char >(" .dynstr" );
2116
+
2117
+ std::vector<char > extraStrings;
2118
+ extraStrings.reserve (remap.size () * 30 ); // Just an estimate
2119
+ for (auto & dynsym : dynsyms)
2120
+ {
2121
+ std::string_view name = &strTab[rdi (dynsym.st_name )];
2122
+ auto it = remap.find (name);
2123
+ if (it != remap.end ())
2124
+ {
2125
+ wri (dynsym.st_name , strTab.size () + extraStrings.size ());
2126
+ auto & newName = it->second ;
2127
+ debug (" renaming dynamic symbol %s to %s\n " , name.data (), it->second .c_str ());
2128
+ extraStrings.insert (extraStrings.end (), newName.begin (), newName.end () + 1 );
2129
+ changed = true ;
2130
+ } else {
2131
+ debug (" skip renaming dynamic symbol %sn" , name.data ());
2132
+ }
2133
+ }
2134
+
2135
+ if (changed)
2136
+ {
2137
+ auto newStrTabSize = strTab.size () + extraStrings.size ();
2138
+ auto & newSec = replaceSection (" .dynstr" , newStrTabSize);
2139
+ auto newStrTabSpan = span (newSec.data (), newStrTabSize);
2140
+
2141
+ std::copy (extraStrings.begin (), extraStrings.end (), &newStrTabSpan[strTab.size ()]);
2142
+
2143
+ rebuildGnuHashTable (newStrTabSpan, dynsyms);
2144
+ rebuildHashTable (newStrTabSpan, dynsyms);
2145
+ }
2146
+
2147
+ this ->rewriteSections ();
2148
+ }
2149
+
1913
2150
template <ElfFileParams>
1914
2151
void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
1915
2152
{
@@ -2032,12 +2269,15 @@ static bool removeRPath = false;
2032
2269
static bool setRPath = false ;
2033
2270
static bool addRPath = false ;
2034
2271
static bool addDebugTag = false ;
2272
+ static bool renameDynamicSymbols = false ;
2035
2273
static bool printRPath = false ;
2036
2274
static std::string newRPath;
2037
2275
static std::set<std::string> neededLibsToRemove;
2038
2276
static std::map<std::string, std::string> neededLibsToReplace;
2039
2277
static std::set<std::string> neededLibsToAdd;
2040
2278
static std::set<std::string> symbolsToClearVersion;
2279
+ static std::unordered_map<std::string_view, std::string> symbolsToRename;
2280
+ static std::unordered_set<std::string> symbolsToRenameKeys;
2041
2281
static bool printNeeded = false ;
2042
2282
static bool noDefaultLib = false ;
2043
2283
static bool printExecstack = false ;
@@ -2097,6 +2337,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
2097
2337
if (addDebugTag)
2098
2338
elfFile.addDebugTag ();
2099
2339
2340
+ if (renameDynamicSymbols)
2341
+ elfFile.renameDynamicSymbols (symbolsToRename);
2342
+
2100
2343
if (elfFile.isChanged ()){
2101
2344
writeFile (fileName, elfFile.fileContents );
2102
2345
} else if (alwaysWrite) {
@@ -2116,9 +2359,9 @@ static void patchElf()
2116
2359
const std::string & outputFileName2 = outputFileName.empty () ? fileName : outputFileName;
2117
2360
2118
2361
if (getElfType (fileContents).is32Bit )
2119
- patchElf2 (ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
2362
+ 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);
2120
2363
else
2121
- patchElf2 (ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
2364
+ 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);
2122
2365
}
2123
2366
}
2124
2367
@@ -2160,6 +2403,7 @@ static void showHelp(const std::string & progName)
2160
2403
[--print-execstack]\t\t Prints whether the object requests an executable stack\n \
2161
2404
[--clear-execstack]\n \
2162
2405
[--set-execstack]\n \
2406
+ [--rename-dynamic-symbols NAME_MAP_FILE]\t Renames dynamic symbols. The map file should contain two symbols (old_name new_name) per line\n \
2163
2407
[--output FILE]\n \
2164
2408
[--debug]\n \
2165
2409
[--version]\n \
@@ -2291,6 +2535,31 @@ static int mainWrapped(int argc, char * * argv)
2291
2535
else if (arg == " --add-debug-tag" ) {
2292
2536
addDebugTag = true ;
2293
2537
}
2538
+ else if (arg == " --rename-dynamic-symbols" ) {
2539
+ renameDynamicSymbols = true ;
2540
+ if (++i == argc) error (" missing argument" );
2541
+
2542
+ const char * fname = argv[i];
2543
+ std::ifstream infile (fname);
2544
+ if (!infile) error (fmt (" Cannot open map file " , fname));
2545
+
2546
+ std::string line, from, to;
2547
+ size_t lineCount = 1 ;
2548
+ while (std::getline (infile, line))
2549
+ {
2550
+ std::istringstream iss (line);
2551
+ if (!(iss >> from))
2552
+ break ;
2553
+ if (!(iss >> to))
2554
+ error (fmt (fname, " :" , lineCount, " : Map file line is missing the second element" ));
2555
+ if (symbolsToRenameKeys.count (from))
2556
+ error (fmt (fname, " :" , lineCount, " : Name '" , from, " ' appears twice in the map file" ));
2557
+ if (from.find (' @' ) != std::string_view::npos || to.find (' @' ) != std::string_view::npos)
2558
+ error (fmt (fname, " :" , lineCount, " : Name pair contains version tag: " , from, " " , to));
2559
+ lineCount++;
2560
+ symbolsToRename[*symbolsToRenameKeys.insert (from).first ] = to;
2561
+ }
2562
+ }
2294
2563
else if (arg == " --help" || arg == " -h" ) {
2295
2564
showHelp (argv[0 ]);
2296
2565
return 0 ;
0 commit comments