Skip to content

Commit 6bfd015

Browse files
committed
Implement --clean-strtab
1 parent 6ba561f commit 6bfd015

File tree

4 files changed

+147
-4
lines changed

4 files changed

+147
-4
lines changed

src/patchelf.cc

+74-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <string>
2929
#include <unordered_map>
3030
#include <unordered_set>
31+
#include <variant>
3132
#include <vector>
3233

3334
#include <cassert>
@@ -1855,7 +1856,78 @@ void ElfFile<ElfFileParamNames>::noDefaultLib()
18551856
template<ElfFileParams>
18561857
void ElfFile<ElfFileParamNames>::cleanStrTab()
18571858
{
1859+
std::unordered_map<std::string, unsigned> requiredStrs2Idx {{"",0}};
18581860

1861+
// A collection of pointers to the fields that refer to str indices
1862+
// and a pointer to the new index value to be calculated
1863+
using StrIndexPtr = std::variant<Elf32_Word*, Elf64_Xword*>;
1864+
std::vector<std::pair<StrIndexPtr, unsigned*>> strRefs;
1865+
1866+
auto& strTabHdr = findSectionHeader(".dynstr");
1867+
auto strTab = getSectionSpan<char>(strTabHdr);
1868+
1869+
// Utility to collect a string index field from any table
1870+
auto collect = [&] (auto& idx) {
1871+
auto [it, _] = requiredStrs2Idx.emplace(&strTab[rdi(idx)], 0);
1872+
strRefs.emplace_back(&idx, &it->second);
1873+
};
1874+
1875+
// Iterate on tables known to store references to .dynstr
1876+
for (auto& sym : tryGetSectionSpan<Elf_Sym>(".dynsym"))
1877+
collect(sym.st_name);
1878+
1879+
for (auto& dyn : tryGetSectionSpan<Elf_Dyn>(".dynamic"))
1880+
switch (rdi(dyn.d_tag))
1881+
{
1882+
case DT_NEEDED:
1883+
case DT_SONAME:
1884+
case DT_RPATH:
1885+
case DT_RUNPATH: collect(dyn.d_un.d_val);
1886+
default:;
1887+
}
1888+
1889+
if (auto verdHdr = tryFindSectionHeader(".gnu.version_d"))
1890+
{
1891+
// Only collect fields if they use the strtab we are cleaning
1892+
if (&shdrs.at(rdi(verdHdr->get().sh_link)) == &strTabHdr)
1893+
forAll_ElfVer(getSectionSpan<Elf_Verdef>(*verdHdr),
1894+
[] (auto& vd) {},
1895+
[&] (auto& vda) { collect(vda.vda_name); }
1896+
);
1897+
}
1898+
1899+
if (auto vernHdr = tryFindSectionHeader(".gnu.version_r"))
1900+
{
1901+
// Only collect fields if they use the strtab we are cleaning
1902+
if (&shdrs.at(rdi(vernHdr->get().sh_link)) == &strTabHdr)
1903+
forAll_ElfVer(getSectionSpan<Elf_Verneed>(*vernHdr),
1904+
[&] (auto& vn) { collect(vn.vn_file); },
1905+
[&] (auto& vna) { collect(vna.vna_name); }
1906+
);
1907+
}
1908+
1909+
// Iterate on all required strings calculating the new position
1910+
size_t curIdx = 1;
1911+
for (auto& [str,idx] : requiredStrs2Idx)
1912+
{
1913+
idx = curIdx;
1914+
curIdx += str.size()+1;
1915+
}
1916+
1917+
// Add required strings to the new dynstr section
1918+
auto& newStrSec = replaceSection(".dynstr", curIdx);
1919+
for (auto& [str,idx] : requiredStrs2Idx)
1920+
std::copy(str.begin(), str.end()+1, newStrSec.begin()+idx);
1921+
1922+
// Iterate on all fields on all tables setting the new index value
1923+
for (auto& [oldIndexPtr, newIdxPtr] : strRefs)
1924+
std::visit(
1925+
[&] (auto* ptr) { wri(*ptr, *newIdxPtr); },
1926+
oldIndexPtr
1927+
);
1928+
1929+
changed = true;
1930+
this->rewriteSections();
18591931
}
18601932

18611933
template<ElfFileParams>
@@ -2230,9 +2302,9 @@ static void patchElf()
22302302
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
22312303

22322304
if (getElfType(fileContents).is32Bit)
2233-
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym, Elf32_Rel, Elf32_Rela, 32>(fileContents), fileContents, outputFileName2);
2305+
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Versym, Elf32_Verdef, Elf32_Verdaux, Elf32_Verneed, Elf32_Vernaux, Elf32_Rel, Elf32_Rela, 32>(fileContents), fileContents, outputFileName2);
22342306
else
2235-
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym, Elf64_Rel, Elf64_Rela, 64>(fileContents), fileContents, outputFileName2);
2307+
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Versym, Elf64_Verdef, Elf64_Verdaux, Elf64_Verneed, Elf64_Vernaux, Elf64_Rel, Elf64_Rela, 64>(fileContents), fileContents, outputFileName2);
22362308
}
22372309
}
22382310

src/patchelf.h

+40-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, class Elf_Rel, class Elf_Rela, unsigned ElfClass
4-
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym, Elf_Rel, Elf_Rela, ElfClass
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_Versym, class Elf_Verdef, class Elf_Verdaux, class Elf_Verneed, class Elf_Vernaux, class Elf_Rel, class Elf_Rela, unsigned ElfClass
4+
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Versym, Elf_Verdef, Elf_Verdaux, Elf_Verneed, Elf_Vernaux, Elf_Rel, Elf_Rela, ElfClass
55

66
template<class T>
77
struct span
@@ -206,6 +206,44 @@ class ElfFile
206206
return info;
207207
}
208208

209+
template<class T, class U>
210+
auto follow(U* ptr, size_t offset) -> T* {
211+
return offset ? (T*)(((char*)ptr)+offset) : nullptr;
212+
};
213+
214+
template<class Fn1, class Fn2>
215+
void forAll_ElfVer(span<Elf_Verdef> vdspan, Fn1&& fn1, Fn2&& fn2)
216+
{
217+
auto* vd = vdspan.begin();
218+
while (vd)
219+
{
220+
fn1(*vd);
221+
auto va = follow<Elf_Verdaux>(vd, rdi(vd->vd_aux));
222+
while (va)
223+
{
224+
fn2(*va);
225+
va = follow<Elf_Verdaux>(va, rdi(va->vda_next));
226+
}
227+
vd = follow<Elf_Verdef>(vd, rdi(vd->vd_next));
228+
}
229+
}
230+
231+
template<class Fn1, class Fn2>
232+
void forAll_ElfVer(span<Elf_Verneed> vnspan, Fn1&& fn1, Fn2&& fn2)
233+
{
234+
auto* vn = vnspan.begin();
235+
while (vn)
236+
{
237+
fn1(*vn);
238+
auto va = follow<Elf_Vernaux>(vn, rdi(vn->vn_aux));
239+
while (va)
240+
{
241+
fn2(*va);
242+
va = follow<Elf_Vernaux>(va, rdi(va->vna_next));
243+
}
244+
vn = follow<Elf_Verneed>(vn, rdi(vn->vn_next));
245+
}
246+
}
209247

210248
void clearSymbolVersions(const std::set<std::string> & syms);
211249

tests/Makefile.am

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ src_TESTS = \
4444
replace-add-needed.sh \
4545
add-debug-tag.sh \
4646
rename-dynamic-symbols.sh \
47+
clean-strtab.sh \
4748
empty-note.sh
4849

4950
build_TESTS = \

tests/clean-strtab.sh

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#! /bin/sh -e
2+
SCRATCH=scratch/$(basename $0 .sh)
3+
PATCHELF=$(readlink -f "../src/patchelf")
4+
5+
rm -rf ${SCRATCH}
6+
mkdir -p ${SCRATCH}
7+
8+
cp libfoo.so ${SCRATCH}/
9+
10+
cd ${SCRATCH}
11+
12+
the_string=VERY_SPECIFIC_STRING
13+
function check_count {
14+
count="$(strings libfoo.so | grep -c $the_string || true)"
15+
expected=$1
16+
echo "####### Checking count. Expected: $expected"
17+
((count == expected)) || exit 1
18+
}
19+
20+
check_count 0
21+
22+
${PATCHELF} --clean-strtab libfoo.so
23+
check_count 0
24+
25+
${PATCHELF} --add-needed $the_string libfoo.so
26+
check_count 1
27+
28+
${PATCHELF} --remove-needed $the_string libfoo.so
29+
check_count 1
30+
31+
${PATCHELF} --clean-strtab libfoo.so
32+
check_count 0

0 commit comments

Comments
 (0)