Skip to content

Commit c73fed9

Browse files
committed
Cleanup .gnu.version_r after removing symbol version
If --clear-symbol-version is used to remove all occurrances of a symbol version, it nevertheless remains in the .gnu.version_r section as being required by a particular dependency. Instead, after removing symbol versions, loop over the .gnu.version and gnu.version_r tables to find any unused version, and unlink them from the linked-lists in the .gnu.version_r section.
1 parent 31d052c commit c73fed9

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

src/patchelf.cc

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,73 @@ void ElfFile<ElfFileParamNames>::addDebugTag()
16891689
changed = true;
16901690
}
16911691

1692+
/* Remove any unused dependency symbol versions from .gnu.version_r */
1693+
template<ElfFileParams>
1694+
void ElfFile<ElfFileParamNames>::cleanDependencySymbolVersions()
1695+
{
1696+
auto shdrVersym = findSectionHeader(".gnu.version");
1697+
auto shdrVersymR = findSectionHeader(".gnu.version_r");
1698+
1699+
auto versyms = (Elf_Versym *)(fileContents->data() + rdi(shdrVersym.sh_offset));
1700+
size_t count = rdi(shdrVersym.sh_size) / sizeof(Elf_Versym);
1701+
1702+
/* Set of versions actually used. */
1703+
std::set<Elf_Versym> allVersions;
1704+
1705+
for (size_t i = 0; i < count; i++) {
1706+
allVersions.insert(versyms[i]);
1707+
}
1708+
1709+
/* Strings associated with .gnu_version_r section: used for debug only. */
1710+
Elf_Shdr & shdrVersionRStrings = shdrs.at(rdi(shdrVersymR.sh_link));
1711+
char * verStrTab = (char *) fileContents->data() + rdi(shdrVersionRStrings.sh_offset);
1712+
1713+
1714+
auto ver_r = (Elf_Verneed *)(fileContents->data() + rdi(shdrVersymR.sh_offset));
1715+
while (true) {
1716+
auto prev = (Elf_Vernaux *)nullptr;
1717+
auto vern_aux = (Elf_Vernaux *)((char *)ver_r + rdi(ver_r->vn_aux));
1718+
char * file = verStrTab + rdi(ver_r->vn_file);
1719+
for (size_t j = 0; j < ver_r->vn_cnt ; j++) {
1720+
char * ver_name = verStrTab + rdi(vern_aux->vna_name);
1721+
auto next = (Elf_Vernaux *)((char *)vern_aux + rdi(vern_aux->vna_next));
1722+
1723+
if (!allVersions.count(rdi(vern_aux->vna_other) & ~0x8000)) {
1724+
debug("Removing version identifier %d %s@%s\n", rdi(vern_aux->vna_other), file, ver_name);
1725+
/* Symbol version is no longer used, unlink it. */
1726+
if (!prev) {
1727+
auto next_off = (intptr_t)(vern_aux) + rdi(vern_aux->vna_next) - (intptr_t)(ver_r);
1728+
wri(ver_r->vn_aux, next_off);
1729+
} else {
1730+
auto next_off = (intptr_t)(vern_aux) + rdi(vern_aux->vna_next) - (intptr_t)(prev);
1731+
wri(prev->vna_next, next_off);
1732+
}
1733+
wri(ver_r->vn_cnt, rdi(ver_r->vn_cnt) - 1);
1734+
} else {
1735+
prev = vern_aux;
1736+
}
1737+
1738+
if (vern_aux == next) {
1739+
if (j != rdi(ver_r->vn_cnt)) {
1740+
debug("Section missing elements! Ended on element %d, expected %d\n", j, rdi(ver_r->vn_cnt));
1741+
}
1742+
break;
1743+
}
1744+
vern_aux = next;
1745+
}
1746+
1747+
/* If this was the last entry, we're done. */
1748+
if (!rdi(ver_r->vn_next)) {
1749+
break;
1750+
}
1751+
1752+
ver_r = (Elf_Verneed *) (((char *) ver_r) + rdi(ver_r->vn_next));
1753+
}
1754+
1755+
changed = true;
1756+
this->rewriteSections();
1757+
}
1758+
16921759
template<ElfFileParams>
16931760
void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
16941761
{
@@ -1714,6 +1781,10 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
17141781
wri(versyms[i], 1);
17151782
}
17161783
}
1784+
1785+
/* Remove entries in the .gnu.versions_r table which are no;-longer required. */
1786+
cleanDependencySymbolVersions();
1787+
17171788
changed = true;
17181789
this->rewriteSections();
17191790
}
@@ -1797,9 +1868,9 @@ static void patchElf()
17971868
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
17981869

17991870
if (getElfType(fileContents).is32Bit)
1800-
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
1871+
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Vernaux, Elf32_Versym>(fileContents), fileContents, outputFileName2);
18011872
else
1802-
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
1873+
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Vernaux, Elf64_Versym>(fileContents), fileContents, outputFileName2);
18031874
}
18041875
}
18051876

src/patchelf.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using FileContents = std::shared_ptr<std::vector<unsigned char>>;
22

3-
#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed, class Elf_Versym
4-
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym
3+
#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed, class Elf_Vernaux, class Elf_Versym
4+
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Vernaux, Elf_Versym
55

66
template<ElfFileParams>
77
class ElfFile
@@ -133,6 +133,8 @@ class ElfFile
133133

134134
void addDebugTag();
135135

136+
void cleanDependencySymbolVersions();
137+
136138
void clearSymbolVersions(const std::set<std::string> & syms);
137139

138140
private:

0 commit comments

Comments
 (0)