summaryrefslogtreecommitdiff
path: root/src/patchelf.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/patchelf.cc')
-rw-r--r--src/patchelf.cc416
1 files changed, 368 insertions, 48 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 0c1fbe8..5dd320d 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -17,16 +17,19 @@
*/
#include <algorithm>
+#include <fstream>
#include <limits>
#include <map>
#include <memory>
+#include <optional>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
+#include <string_view>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
-#include <optional>
#include <cassert>
#include <cerrno>
@@ -69,14 +72,18 @@ static int forcedPageSize = -1;
#define EM_LOONGARCH 258
#endif
-
-static std::vector<std::string> splitColonDelimitedString(const char * s)
+[[nodiscard]] static std::vector<std::string> splitColonDelimitedString(std::string_view s)
{
- std::string item;
std::vector<std::string> parts;
- std::stringstream ss(s);
- while (std::getline(ss, item, ':'))
- parts.push_back(item);
+
+ size_t pos;
+ while ((pos = s.find(':')) != std::string_view::npos) {
+ parts.emplace_back(s.substr(0, pos));
+ s = s.substr(pos + 1);
+ }
+
+ if (!s.empty())
+ parts.emplace_back(s);
return parts;
}
@@ -104,7 +111,7 @@ static std::string downcase(std::string s)
why... */
template<ElfFileParams>
template<class I>
-I ElfFile<ElfFileParamNames>::rdi(I i) const
+constexpr I ElfFile<ElfFileParamNames>::rdi(I i) const noexcept
{
I r = 0;
if (littleEndian) {
@@ -131,7 +138,7 @@ static void debug(const char * format, ...)
}
-void fmt2(std::ostringstream & out)
+static void fmt2([[maybe_unused]] std::ostringstream & out)
{
}
@@ -162,7 +169,7 @@ struct SysError : std::runtime_error
{ }
};
-__attribute__((noreturn)) static void error(const std::string & msg)
+[[noreturn]] static void error(const std::string & msg)
{
if (errno)
throw SysError(msg);
@@ -191,11 +198,11 @@ static FileContents readFile(const std::string & fileName,
while ((portion = read(fd, contents->data() + bytesRead, size - bytesRead)) > 0)
bytesRead += portion;
+ close(fd);
+
if (bytesRead != size)
throw SysError(fmt("reading '", fileName, "'"));
- close(fd);
-
return contents;
}
@@ -207,10 +214,10 @@ struct ElfType
};
-ElfType getElfType(const FileContents & fileContents)
+[[nodiscard]] static ElfType getElfType(const FileContents & fileContents)
{
/* Check the ELF header for basic validity. */
- if (fileContents->size() < static_cast<off_t>(sizeof(Elf32_Ehdr)))
+ if (fileContents->size() < sizeof(Elf32_Ehdr))
error("missing ELF header");
auto contents = fileContents->data();
@@ -231,14 +238,32 @@ ElfType getElfType(const FileContents & fileContents)
}
-static void checkPointer(const FileContents & contents, void * p, unsigned int size)
+static void checkPointer(const FileContents & contents, const void * p, size_t size)
{
- auto q = static_cast<unsigned char *>(p);
- if (!(q >= contents->data() && q + size <= contents->data() + contents->size()))
+ if (p < contents->data() || size > contents->size() || p > contents->data() + contents->size() - size)
error("data region extends past file end");
}
+static void checkOffset(const FileContents & contents, size_t offset, size_t size)
+{
+ size_t end;
+
+ if (offset > contents->size()
+ || size > contents->size()
+ || __builtin_add_overflow(offset, size, &end)
+ || end > contents->size())
+ error("data offset extends past file end");
+}
+
+
+static std::string extractString(const FileContents & contents, size_t offset, size_t size)
+{
+ checkOffset(contents, offset, size);
+ return { reinterpret_cast<const char *>(contents->data()) + offset, size };
+}
+
+
template<ElfFileParams>
ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents)
: fileContents(fContents)
@@ -255,14 +280,34 @@ ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents)
if (rdi(hdr()->e_type) != ET_EXEC && rdi(hdr()->e_type) != ET_DYN)
error("wrong ELF type");
- if (rdi(hdr()->e_phoff) + rdi(hdr()->e_phnum) * rdi(hdr()->e_phentsize) > fileContents->size())
- error("program header table out of bounds");
+ {
+ auto ph_offset = rdi(hdr()->e_phoff);
+ auto ph_num = rdi(hdr()->e_phnum);
+ auto ph_entsize = rdi(hdr()->e_phentsize);
+ size_t ph_size, ph_end;
+
+ if (__builtin_mul_overflow(ph_num, ph_entsize, &ph_size)
+ || __builtin_add_overflow(ph_offset, ph_size, &ph_end)
+ || ph_end > fileContents->size()) {
+ error("program header table out of bounds");
+ }
+ }
if (rdi(hdr()->e_shnum) == 0)
error("no section headers. The input file is probably a statically linked, self-decompressing binary");
- if (rdi(hdr()->e_shoff) + rdi(hdr()->e_shnum) * rdi(hdr()->e_shentsize) > fileContents->size())
- error("section header table out of bounds");
+ {
+ auto sh_offset = rdi(hdr()->e_shoff);
+ auto sh_num = rdi(hdr()->e_shnum);
+ auto sh_entsize = rdi(hdr()->e_shentsize);
+ size_t sh_size, sh_end;
+
+ if (__builtin_mul_overflow(sh_num, sh_entsize, &sh_size)
+ || __builtin_add_overflow(sh_offset, sh_size, &sh_end)
+ || sh_end > fileContents->size()) {
+ error("section header table out of bounds");
+ }
+ }
if (rdi(hdr()->e_phentsize) != sizeof(Elf_Phdr))
error("program headers have wrong size");
@@ -286,12 +331,15 @@ ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents)
/* Get the section header string table section (".shstrtab"). Its
index in the section header table is given by e_shstrndx field
of the ELF header. */
- unsigned int shstrtabIndex = rdi(hdr()->e_shstrndx);
+ auto shstrtabIndex = rdi(hdr()->e_shstrndx);
if (shstrtabIndex >= shdrs.size())
error("string table index out of bounds");
- unsigned int shstrtabSize = rdi(shdrs[shstrtabIndex].sh_size);
- char * shstrtab = (char * ) fileContents->data() + rdi(shdrs[shstrtabIndex].sh_offset);
+ auto shstrtabSize = rdi(shdrs[shstrtabIndex].sh_size);
+ size_t shstrtabptr;
+ if (__builtin_add_overflow(reinterpret_cast<size_t>(fileContents->data()), rdi(shdrs[shstrtabIndex].sh_offset), &shstrtabptr))
+ error("string table overflow");
+ const char *shstrtab = reinterpret_cast<const char *>(shstrtabptr);
checkPointer(fileContents, shstrtab, shstrtabSize);
if (shstrtabSize == 0)
@@ -309,7 +357,7 @@ ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents)
template<ElfFileParams>
-unsigned int ElfFile<ElfFileParamNames>::getPageSize() const
+unsigned int ElfFile<ElfFileParamNames>::getPageSize() const noexcept
{
if (forcedPageSize > 0)
return forcedPageSize;
@@ -431,6 +479,8 @@ static void writeFile(const std::string & fileName, const FileContents & content
static uint64_t roundUp(uint64_t n, uint64_t m)
{
+ if (n == 0)
+ return m;
return ((n - 1) / m + 1) * m;
}
@@ -531,7 +581,7 @@ std::string ElfFile<ElfFileParamNames>::getSectionName(const Elf_Shdr & shdr) co
template<ElfFileParams>
-Elf_Shdr & ElfFile<ElfFileParamNames>::findSectionHeader(const SectionName & sectionName)
+const Elf_Shdr & ElfFile<ElfFileParamNames>::findSectionHeader(const SectionName & sectionName) const
{
auto shdr = tryFindSectionHeader(sectionName);
if (!shdr) {
@@ -545,7 +595,7 @@ Elf_Shdr & ElfFile<ElfFileParamNames>::findSectionHeader(const SectionName & sec
template<ElfFileParams>
-std::optional<std::reference_wrapper<Elf_Shdr>> ElfFile<ElfFileParamNames>::tryFindSectionHeader(const SectionName & sectionName)
+std::optional<std::reference_wrapper<const Elf_Shdr>> ElfFile<ElfFileParamNames>::tryFindSectionHeader(const SectionName & sectionName) const
{
auto i = getSectionIndex(sectionName);
if (i)
@@ -553,9 +603,30 @@ 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>
-unsigned int ElfFile<ElfFileParamNames>::getSectionIndex(const SectionName & sectionName)
+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) const
{
for (unsigned int i = 1; i < rdi(hdr()->e_shnum); ++i)
if (getSectionName(shdrs.at(i)) == sectionName) return i;
@@ -579,7 +650,7 @@ std::string & ElfFile<ElfFileParamNames>::replaceSection(const SectionName & sec
s = std::string(i->second);
} else {
auto shdr = findSectionHeader(sectionName);
- s = std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size));
+ s = extractString(fileContents, rdi(shdr.sh_offset), rdi(shdr.sh_size));
}
s.resize(size);
@@ -598,7 +669,7 @@ void ElfFile<ElfFileParamNames>::writeReplacedSections(Elf_Off & curOff,
clobbering previously written new section contents. */
for (auto & i : replacedSections) {
const std::string & sectionName = i.first;
- Elf_Shdr & shdr = findSectionHeader(sectionName);
+ const Elf_Shdr & shdr = findSectionHeader(sectionName);
if (rdi(shdr.sh_type) != SHT_NOBITS)
memset(fileContents->data() + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size));
}
@@ -617,7 +688,7 @@ void ElfFile<ElfFileParamNames>::writeReplacedSections(Elf_Off & curOff,
debug("rewriting section '%s' from offset 0x%x (size %d) to offset 0x%x (size %d)\n",
sectionName.c_str(), rdi(shdr.sh_offset), rdi(shdr.sh_size), curOff, i->second.size());
- memcpy(fileContents->data() + curOff, (unsigned char *) i->second.c_str(),
+ memcpy(fileContents->data() + curOff, i->second.c_str(),
i->second.size());
/* Update the section header for this section. */
@@ -1215,10 +1286,13 @@ static void setSubstr(std::string & s, unsigned int pos, const std::string & t)
template<ElfFileParams>
-std::string ElfFile<ElfFileParamNames>::getInterpreter()
+std::string ElfFile<ElfFileParamNames>::getInterpreter() const
{
auto shdr = findSectionHeader(".interp");
- return std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size) - 1);
+ auto size = rdi(shdr.sh_size);
+ if (size > 0)
+ size--;
+ return extractString(fileContents, rdi(shdr.sh_offset), size);
}
template<ElfFileParams>
@@ -1341,7 +1415,7 @@ void ElfFile<ElfFileParamNames>::modifySoname(sonameMode op, const std::string &
std::string & newDynamic = replaceSection(".dynamic", rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn));
unsigned int idx = 0;
- for (; rdi(((Elf_Dyn *) newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++);
+ for (; rdi(reinterpret_cast<const Elf_Dyn *>(newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++);
debug("DT_NULL index is %d\n", idx);
/* Shift all entries down by one. */
@@ -1497,7 +1571,7 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
case rpPrint: {
printf("%s\n", rpath ? rpath : "");
return;
- };
+ }
case rpRemove: {
if (!rpath) {
debug("no RPATH to delete\n");
@@ -1510,7 +1584,7 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
if (!rpath) {
debug("no RPATH to shrink\n");
return;
- ;}
+ }
newRPath = shrinkRPath(rpath, neededLibs, allowedRpathPrefixes);
break;
}
@@ -1576,7 +1650,7 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn));
unsigned int idx = 0;
- for ( ; rdi(((Elf_Dyn *) newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ;
+ for ( ; rdi(reinterpret_cast<const Elf_Dyn *>(newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ;
debug("DT_NULL index is %d\n", idx);
/* Shift all entries down by one. */
@@ -1778,7 +1852,7 @@ void ElfFile<ElfFileParamNames>::addNeeded(const std::set<std::string> & libs)
rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn) * libs.size());
unsigned int idx = 0;
- for ( ; rdi(((Elf_Dyn *) newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ;
+ for ( ; rdi(reinterpret_cast<const Elf_Dyn *>(newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ;
debug("DT_NULL index is %d\n", idx);
/* Shift all entries down by the number of new entries. */
@@ -1801,13 +1875,13 @@ void ElfFile<ElfFileParamNames>::addNeeded(const std::set<std::string> & libs)
}
template<ElfFileParams>
-void ElfFile<ElfFileParamNames>::printNeededLibs() // const
+void ElfFile<ElfFileParamNames>::printNeededLibs() const
{
const auto shdrDynamic = findSectionHeader(".dynamic");
const auto shdrDynStr = findSectionHeader(".dynstr");
- const char *strTab = (char *)fileContents->data() + rdi(shdrDynStr.sh_offset);
+ const char *strTab = (const char *)fileContents->data() + rdi(shdrDynStr.sh_offset);
- const Elf_Dyn *dyn = (Elf_Dyn *) (fileContents->data() + rdi(shdrDynamic.sh_offset));
+ const Elf_Dyn *dyn = (const Elf_Dyn *) (fileContents->data() + rdi(shdrDynamic.sh_offset));
for (; rdi(dyn->d_tag) != DT_NULL; dyn++) {
if (rdi(dyn->d_tag) == DT_NEEDED) {
@@ -1840,7 +1914,7 @@ void ElfFile<ElfFileParamNames>::noDefaultLib()
rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn));
unsigned int idx = 0;
- for ( ; rdi(((Elf_Dyn *) newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ;
+ for ( ; rdi(reinterpret_cast<const Elf_Dyn *>(newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ;
debug("DT_NULL index is %d\n", idx);
/* Shift all entries down by one. */
@@ -1873,7 +1947,7 @@ void ElfFile<ElfFileParamNames>::addDebugTag()
rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn));
unsigned int idx = 0;
- for ( ; rdi(((Elf_Dyn *) newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ;
+ for ( ; rdi(reinterpret_cast<const Elf_Dyn *>(newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ;
debug("DT_NULL index is %d\n", idx);
/* Shift all entries down by one. */
@@ -1890,6 +1964,220 @@ 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(span<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[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[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 remapSymbolId = [&old2new, firstSymIdx] (auto& oldSymIdx)
+ {
+ return oldSymIdx >= firstSymIdx ? old2new[oldSymIdx - firstSymIdx] + firstSymIdx
+ : oldSymIdx;
+ };
+
+ 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)
+ changeRelocTableSymIds<Elf_Rel>(shdr, remapSymbolId);
+ else if (shtype == SHT_RELA)
+ changeRelocTableSymIds<Elf_Rela>(shdr, remapSymbolId);
+ }
+
+ // 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(span<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);
+
+ // The hash table includes only a subset of dynsyms
+ auto firstSymIdx = dynsyms.size() - ht.m_chain.size();
+ dynsyms = span(&dynsyms[firstSymIdx], dynsyms.end());
+
+ for (auto& sym : dynsyms)
+ {
+ 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 (auto& dynsym : dynsyms)
+ {
+ 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;
+ debug("renaming dynamic symbol %s to %s\n", name.data(), it->second.c_str());
+ extraStrings.insert(extraStrings.end(), newName.begin(), newName.end() + 1);
+ changed = true;
+ } else {
+ debug("skip renaming dynamic symbol %sn", name.data());
+ }
+ }
+
+ if (changed)
+ {
+ auto newStrTabSize = strTab.size() + extraStrings.size();
+ auto& newSec = replaceSection(".dynstr", newStrTabSize);
+ auto newStrTabSpan = span(newSec.data(), newStrTabSize);
+
+ std::copy(extraStrings.begin(), extraStrings.end(), &newStrTabSpan[strTab.size()]);
+
+ rebuildGnuHashTable(newStrTabSpan, dynsyms);
+ rebuildHashTable(newStrTabSpan, dynsyms);
+ }
+
+ this->rewriteSections();
+}
+
template<ElfFileParams>
void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
{
@@ -2012,12 +2300,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;
@@ -2077,6 +2368,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) {
@@ -2096,13 +2390,13 @@ 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);
}
}
-std::string resolveArgument(const char *arg) {
+[[nodiscard]] static std::string resolveArgument(const char *arg) {
if (strlen(arg) > 0 && arg[0] == '@') {
FileContents cnts = readFile(arg + 1);
return std::string((char *)cnts->data(), cnts->size());
@@ -2112,7 +2406,7 @@ std::string resolveArgument(const char *arg) {
}
-void showHelp(const std::string & progName)
+static void showHelp(const std::string & progName)
{
fprintf(stderr, "syntax: %s\n\
[--set-interpreter FILENAME]\n\
@@ -2140,6 +2434,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 map file should contain two symbols (old_name new_name) per line\n\
[--output FILE]\n\
[--debug]\n\
[--version]\n\
@@ -2147,7 +2442,7 @@ void showHelp(const std::string & progName)
}
-int mainWrapped(int argc, char * * argv)
+static int mainWrapped(int argc, char * * argv)
{
if (argc <= 1) {
showHelp(argv[0]);
@@ -2271,6 +2566,31 @@ 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");
+
+ const char* fname = argv[i];
+ std::ifstream infile(fname);
+ if (!infile) error(fmt("Cannot open map file ", fname));
+
+ std::string line, from, to;
+ size_t lineCount = 1;
+ while (std::getline(infile, line))
+ {
+ std::istringstream iss(line);
+ if (!(iss >> from))
+ break;
+ if (!(iss >> to))
+ error(fmt(fname, ":", lineCount, ": Map file line is missing the second element"));
+ if (symbolsToRenameKeys.count(from))
+ error(fmt(fname, ":", lineCount, ": Name '", from, "' appears twice in the map file"));
+ if (from.find('@') != std::string_view::npos || to.find('@') != std::string_view::npos)
+ error(fmt(fname, ":", lineCount, ": Name pair contains version tag: ", from, " ", to));
+ lineCount++;
+ symbolsToRename[*symbolsToRenameKeys.insert(from).first] = to;
+ }
+ }
else if (arg == "--help" || arg == "-h" ) {
showHelp(argv[0]);
return 0;