summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRolf Eike Beer <eb@emlix.com>2020-09-17 10:26:49 +0200
committerJörg Thalheim <joerg@thalheim.io>2021-11-27 11:06:49 +0100
commitb02a14949f66175517a330ffdd5d803ba81e937d (patch)
treeff3f3c371f434493f4778510c440a8fe6ea2c842
parentb4cb6cac7844a112f3fbb1f64ffefd2624d0537e (diff)
downloadpatchelf-replace-lib.tar.gz
avoid adding the same .dynstr entries multiple timesreplace-lib
This can happen especially if .gnu.version_r stores the strings in .dynstr, so replacing the library names would add them twice to the same section. Keep a map of what was already added and where, and simply reuse the old entries if they are needed again.
-rw-r--r--src/patchelf.cc42
1 files changed, 33 insertions, 9 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 6148abd..41fdde0 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -24,6 +24,7 @@
#include <sstream>
#include <stdexcept>
#include <string>
+#include <unordered_map>
#include <vector>
#include <cassert>
@@ -1584,6 +1585,7 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
unsigned int verNeedNum = 0;
unsigned int dynStrAddedBytes = 0;
+ std::unordered_map<std::string, Elf_Off> addedStrings;
for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) {
if (rdi(dyn->d_tag) == DT_NEEDED) {
@@ -1594,15 +1596,25 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
debug("replacing DT_NEEDED entry '%s' with '%s'\n", name, replacement.c_str());
+ auto a = addedStrings.find(replacement);
+ // the same replacement string has already been added, reuse it
+ if (a != addedStrings.end()) {
+ wri(dyn->d_un.d_val, a->second);
+ continue;
+ }
+
// technically, the string referred by d_val could be used otherwise, too (although unlikely)
// we'll therefore add a new string
debug("resizing .dynstr ...\n");
+ // relative location of the new string
+ Elf_Off strOffset = rdi(shdrDynStr.sh_size) + dynStrAddedBytes;
std::string & newDynStr = replaceSection(".dynstr",
- rdi(shdrDynStr.sh_size) + replacement.size() + 1 + dynStrAddedBytes);
- setSubstr(newDynStr, rdi(shdrDynStr.sh_size) + dynStrAddedBytes, replacement + '\0');
+ strOffset + replacement.size() + 1);
+ setSubstr(newDynStr, strOffset, replacement + '\0');
- wri(dyn->d_un.d_val, rdi(shdrDynStr.sh_size) + dynStrAddedBytes);
+ wri(dyn->d_un.d_val, strOffset);
+ addedStrings[replacement] = strOffset;
dynStrAddedBytes += replacement.size() + 1;
@@ -1640,6 +1652,9 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
// added bytes into account.
if (versionRStringsSName == ".dynstr")
verStrAddedBytes += dynStrAddedBytes;
+ else
+ // otherwise the already added strings can't be reused
+ addedStrings.clear();
auto need = (Elf_Verneed *)(fileContents->data() + rdi(shdrVersionR.sh_offset));
while (verNeedNum > 0) {
@@ -1649,15 +1664,24 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
auto replacement = i->second;
debug("replacing .gnu.version_r entry '%s' with '%s'\n", file, replacement.c_str());
- debug("resizing string section %s ...\n", versionRStringsSName.c_str());
- std::string & newVerDynStr = replaceSection(versionRStringsSName,
- rdi(shdrVersionRStrings.sh_size) + replacement.size() + 1 + verStrAddedBytes);
- setSubstr(newVerDynStr, rdi(shdrVersionRStrings.sh_size) + verStrAddedBytes, replacement + '\0');
+ auto a = addedStrings.find(replacement);
+ // the same replacement string has already been added, reuse it
+ if (a != addedStrings.end()) {
+ wri(need->vn_file, a->second);
+ } else {
+ debug("resizing string section %s ...\n", versionRStringsSName.c_str());
+
+ Elf_Off strOffset = rdi(shdrVersionRStrings.sh_size) + verStrAddedBytes;
+ std::string & newVerDynStr = replaceSection(versionRStringsSName,
+ strOffset + replacement.size() + 1);
+ setSubstr(newVerDynStr, strOffset, replacement + '\0');
- wri(need->vn_file, rdi(shdrVersionRStrings.sh_size) + verStrAddedBytes);
+ wri(need->vn_file, strOffset);
+ addedStrings[replacement] = strOffset;
- verStrAddedBytes += replacement.size() + 1;
+ verStrAddedBytes += replacement.size() + 1;
+ }
changed = true;
} else {