Skip to content

Commit b02a149

Browse files
DerDakonMic92
authored andcommitted
avoid adding the same .dynstr entries multiple times
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.
1 parent b4cb6ca commit b02a149

File tree

1 file changed

+33
-9
lines changed

1 file changed

+33
-9
lines changed

src/patchelf.cc

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <sstream>
2525
#include <stdexcept>
2626
#include <string>
27+
#include <unordered_map>
2728
#include <vector>
2829

2930
#include <cassert>
@@ -1584,6 +1585,7 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
15841585
unsigned int verNeedNum = 0;
15851586

15861587
unsigned int dynStrAddedBytes = 0;
1588+
std::unordered_map<std::string, Elf_Off> addedStrings;
15871589

15881590
for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) {
15891591
if (rdi(dyn->d_tag) == DT_NEEDED) {
@@ -1594,15 +1596,25 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
15941596

15951597
debug("replacing DT_NEEDED entry '%s' with '%s'\n", name, replacement.c_str());
15961598

1599+
auto a = addedStrings.find(replacement);
1600+
// the same replacement string has already been added, reuse it
1601+
if (a != addedStrings.end()) {
1602+
wri(dyn->d_un.d_val, a->second);
1603+
continue;
1604+
}
1605+
15971606
// technically, the string referred by d_val could be used otherwise, too (although unlikely)
15981607
// we'll therefore add a new string
15991608
debug("resizing .dynstr ...\n");
16001609

1610+
// relative location of the new string
1611+
Elf_Off strOffset = rdi(shdrDynStr.sh_size) + dynStrAddedBytes;
16011612
std::string & newDynStr = replaceSection(".dynstr",
1602-
rdi(shdrDynStr.sh_size) + replacement.size() + 1 + dynStrAddedBytes);
1603-
setSubstr(newDynStr, rdi(shdrDynStr.sh_size) + dynStrAddedBytes, replacement + '\0');
1613+
strOffset + replacement.size() + 1);
1614+
setSubstr(newDynStr, strOffset, replacement + '\0');
16041615

1605-
wri(dyn->d_un.d_val, rdi(shdrDynStr.sh_size) + dynStrAddedBytes);
1616+
wri(dyn->d_un.d_val, strOffset);
1617+
addedStrings[replacement] = strOffset;
16061618

16071619
dynStrAddedBytes += replacement.size() + 1;
16081620

@@ -1640,6 +1652,9 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
16401652
// added bytes into account.
16411653
if (versionRStringsSName == ".dynstr")
16421654
verStrAddedBytes += dynStrAddedBytes;
1655+
else
1656+
// otherwise the already added strings can't be reused
1657+
addedStrings.clear();
16431658

16441659
auto need = (Elf_Verneed *)(fileContents->data() + rdi(shdrVersionR.sh_offset));
16451660
while (verNeedNum > 0) {
@@ -1649,15 +1664,24 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
16491664
auto replacement = i->second;
16501665

16511666
debug("replacing .gnu.version_r entry '%s' with '%s'\n", file, replacement.c_str());
1652-
debug("resizing string section %s ...\n", versionRStringsSName.c_str());
16531667

1654-
std::string & newVerDynStr = replaceSection(versionRStringsSName,
1655-
rdi(shdrVersionRStrings.sh_size) + replacement.size() + 1 + verStrAddedBytes);
1656-
setSubstr(newVerDynStr, rdi(shdrVersionRStrings.sh_size) + verStrAddedBytes, replacement + '\0');
1668+
auto a = addedStrings.find(replacement);
1669+
// the same replacement string has already been added, reuse it
1670+
if (a != addedStrings.end()) {
1671+
wri(need->vn_file, a->second);
1672+
} else {
1673+
debug("resizing string section %s ...\n", versionRStringsSName.c_str());
1674+
1675+
Elf_Off strOffset = rdi(shdrVersionRStrings.sh_size) + verStrAddedBytes;
1676+
std::string & newVerDynStr = replaceSection(versionRStringsSName,
1677+
strOffset + replacement.size() + 1);
1678+
setSubstr(newVerDynStr, strOffset, replacement + '\0');
16571679

1658-
wri(need->vn_file, rdi(shdrVersionRStrings.sh_size) + verStrAddedBytes);
1680+
wri(need->vn_file, strOffset);
1681+
addedStrings[replacement] = strOffset;
16591682

1660-
verStrAddedBytes += replacement.size() + 1;
1683+
verStrAddedBytes += replacement.size() + 1;
1684+
}
16611685

16621686
changed = true;
16631687
} else {

0 commit comments

Comments
 (0)