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