Skip to content

Commit

Permalink
avoid adding the same .dynstr entries multiple times
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DerDakon committed Sep 17, 2020
1 parent aa2f73a commit 0856c88
Showing 1 changed file with 33 additions and 9 deletions.
42 changes: 33 additions & 9 deletions src/patchelf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <sstream>
#include <limits>
#include <stdexcept>
#include <unordered_map>

#include <cstdlib>
#include <cstdio>
Expand Down Expand Up @@ -1492,6 +1493,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) {
Expand All @@ -1502,15 +1504,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;

Expand Down Expand Up @@ -1548,6 +1560,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();

Elf_Verneed * need = (Elf_Verneed *) (contents + rdi(shdrVersionR.sh_offset));
while (verNeedNum > 0) {
Expand All @@ -1557,15 +1572,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 {
Expand Down

0 comments on commit 0856c88

Please sign in to comment.