Skip to content

Commit 85b83d0

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 c2b419d commit 85b83d0

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

src/patchelf.cc

+73-2
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,73 @@ void ElfFile<ElfFileParamNames>::addDebugTag()
17091709
changed = true;
17101710
}
17111711

1712+
/* Remove any unused dependency symbol versions from .gnu.version_r */
1713+
template<ElfFileParams>
1714+
void ElfFile<ElfFileParamNames>::cleanDependencySymbolVersions()
1715+
{
1716+
auto shdrVersym = findSectionHeader(".gnu.version");
1717+
auto shdrVersymR = findSectionHeader(".gnu.version_r");
1718+
1719+
auto versyms = (Elf_Versym *)(fileContents->data() + rdi(shdrVersym.sh_offset));
1720+
size_t count = rdi(shdrVersym.sh_size) / sizeof(Elf_Versym);
1721+
1722+
/* Set of versions actually used. */
1723+
std::set<Elf_Versym> allVersions;
1724+
1725+
for (size_t i = 0; i < count; i++) {
1726+
allVersions.insert(versyms[i]);
1727+
}
1728+
1729+
/* Strings associated with .gnu_version_r section: used for debug only. */
1730+
Elf_Shdr & shdrVersionRStrings = shdrs.at(rdi(shdrVersymR.sh_link));
1731+
char * verStrTab = (char *) fileContents->data() + rdi(shdrVersionRStrings.sh_offset);
1732+
1733+
1734+
auto ver_r = (Elf_Verneed *)(fileContents->data() + rdi(shdrVersymR.sh_offset));
1735+
while (true) {
1736+
auto prev = (Elf_Vernaux *)nullptr;
1737+
auto vern_aux = (Elf_Vernaux *)((char *)ver_r + rdi(ver_r->vn_aux));
1738+
char * file = verStrTab + rdi(ver_r->vn_file);
1739+
for (size_t j = 0; j < ver_r->vn_cnt ; j++) {
1740+
char * ver_name = verStrTab + rdi(vern_aux->vna_name);
1741+
auto next = (Elf_Vernaux *)((char *)vern_aux + rdi(vern_aux->vna_next));
1742+
1743+
if (!allVersions.count(rdi(vern_aux->vna_other) & ~0x8000)) {
1744+
debug("Removing version identifier %d %s@%s\n", rdi(vern_aux->vna_other), file, ver_name);
1745+
/* Symbol version is no longer used, unlink it. */
1746+
if (!prev) {
1747+
auto next_off = (intptr_t)(vern_aux) + rdi(vern_aux->vna_next) - (intptr_t)(ver_r);
1748+
wri(ver_r->vn_aux, next_off);
1749+
} else {
1750+
auto next_off = (intptr_t)(vern_aux) + rdi(vern_aux->vna_next) - (intptr_t)(prev);
1751+
wri(prev->vna_next, next_off);
1752+
}
1753+
wri(ver_r->vn_cnt, rdi(ver_r->vn_cnt) - 1);
1754+
} else {
1755+
prev = vern_aux;
1756+
}
1757+
1758+
if (vern_aux == next) {
1759+
if (j != rdi(ver_r->vn_cnt)) {
1760+
debug("Section missing elements! Ended on element %d, expected %d\n", j, rdi(ver_r->vn_cnt));
1761+
}
1762+
break;
1763+
}
1764+
vern_aux = next;
1765+
}
1766+
1767+
/* If this was the last entry, we're done. */
1768+
if (!rdi(ver_r->vn_next)) {
1769+
break;
1770+
}
1771+
1772+
ver_r = (Elf_Verneed *) (((char *) ver_r) + rdi(ver_r->vn_next));
1773+
}
1774+
1775+
changed = true;
1776+
this->rewriteSections();
1777+
}
1778+
17121779
template<ElfFileParams>
17131780
void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
17141781
{
@@ -1734,6 +1801,10 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
17341801
wri(versyms[i], 1);
17351802
}
17361803
}
1804+
1805+
/* Remove entries in the .gnu.versions_r table which are no;-longer required. */
1806+
cleanDependencySymbolVersions();
1807+
17371808
changed = true;
17381809
this->rewriteSections();
17391810
}
@@ -1817,9 +1888,9 @@ static void patchElf()
18171888
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
18181889

18191890
if (getElfType(fileContents).is32Bit)
1820-
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
1891+
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);
18211892
else
1822-
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
1893+
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);
18231894
}
18241895
}
18251896

src/patchelf.h

+4-2
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)