Skip to content

Commit 7e39024

Browse files
committed
Implement --clean-strtab
1 parent 7c7572b commit 7e39024

File tree

3 files changed

+157
-5
lines changed

3 files changed

+157
-5
lines changed

src/patchelf.cc

Lines changed: 91 additions & 2 deletions
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>
@@ -1852,6 +1853,87 @@ void ElfFile<ElfFileParamNames>::noDefaultLib()
18521853
changed = true;
18531854
}
18541855

1856+
template<ElfFileParams>
1857+
void ElfFile<ElfFileParamNames>::cleanStrTab()
1858+
{
1859+
std::unordered_map<std::string, unsigned> requiredStrs2Idx {{"",0}};
1860+
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+
{
1925+
auto newIdxPtr = newIdxPtr_; // Some compilers complain about
1926+
// capturing structured bindings
1927+
std::visit(
1928+
[&] (auto* ptr) { wri(*ptr, *newIdxPtr); },
1929+
oldIndexPtr
1930+
);
1931+
}
1932+
1933+
changed = true;
1934+
this->rewriteSections();
1935+
}
1936+
18551937
template<ElfFileParams>
18561938
void ElfFile<ElfFileParamNames>::addDebugTag()
18571939
{
@@ -2223,6 +2305,7 @@ static bool removeRPath = false;
22232305
static bool setRPath = false;
22242306
static bool addRPath = false;
22252307
static bool addDebugTag = false;
2308+
static bool cleanStrTab = false;
22262309
static bool renameDynamicSymbols = false;
22272310
static bool printRPath = false;
22282311
static std::string newRPath;
@@ -2294,6 +2377,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
22942377
if (renameDynamicSymbols)
22952378
elfFile.renameDynamicSymbols(symbolsToRename);
22962379

2380+
if (cleanStrTab)
2381+
elfFile.cleanStrTab();
2382+
22972383
if (elfFile.isChanged()){
22982384
writeFile(fileName, elfFile.fileContents);
22992385
} else if (alwaysWrite) {
@@ -2313,9 +2399,9 @@ static void patchElf()
23132399
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
23142400

23152401
if (getElfType(fileContents).is32Bit)
2316-
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);
2402+
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);
23172403
else
2318-
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);
2404+
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);
23192405
}
23202406
}
23212407

@@ -2489,6 +2575,9 @@ int mainWrapped(int argc, char * * argv)
24892575
else if (arg == "--add-debug-tag") {
24902576
addDebugTag = true;
24912577
}
2578+
else if (arg == "--clean-strtab") {
2579+
cleanStrTab = true;
2580+
}
24922581
else if (arg == "--rename-dynamic-symbols") {
24932582
renameDynamicSymbols = true;
24942583
if (++i == argc) error("missing argument");

src/patchelf.h

Lines changed: 34 additions & 3 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, 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
@@ -158,6 +158,8 @@ class ElfFile
158158

159159
void renameDynamicSymbols(const std::unordered_map<std::string_view, std::string>&);
160160

161+
void cleanStrTab();
162+
161163
struct GnuHashTable {
162164
using BloomWord = Elf_Addr;
163165
struct Header {
@@ -204,14 +206,43 @@ class ElfFile
204206
return info;
205207
}
206208

207-
208209
void clearSymbolVersions(const std::set<std::string> & syms);
209210

210211
enum class ExecstackMode { print, set, clear };
211212

212213
void modifyExecstack(ExecstackMode op);
213214

214215
private:
216+
template<class T, class U>
217+
auto follow(U* ptr, size_t offset) -> T* {
218+
return offset ? (T*)(((char*)ptr)+offset) : nullptr;
219+
};
220+
221+
template<class VdFn, class VaFn>
222+
void forAll_ElfVer(span<Elf_Verdef> vdspan, VdFn&& vdfn, VaFn&& vafn)
223+
{
224+
auto* vd = vdspan.begin();
225+
for (; vd; vd = follow<Elf_Verdef>(vd, rdi(vd->vd_next)))
226+
{
227+
vdfn(*vd);
228+
auto va = follow<Elf_Verdaux>(vd, rdi(vd->vd_aux));
229+
for (; va; va = follow<Elf_Verdaux>(va, rdi(va->vda_next)))
230+
vafn(*va);
231+
}
232+
}
233+
234+
template<class VnFn, class VaFn>
235+
void forAll_ElfVer(span<Elf_Verneed> vnspan, VnFn&& vnfn, VaFn&& vafn)
236+
{
237+
auto* vn = vnspan.begin();
238+
for (; vn; vn = follow<Elf_Verneed>(vn, rdi(vn->vn_next)))
239+
{
240+
vnfn(*vn);
241+
auto va = follow<Elf_Vernaux>(vn, rdi(vn->vn_aux));
242+
for (; va; va = follow<Elf_Vernaux>(va, rdi(va->vna_next)))
243+
vafn(*va);
244+
}
245+
}
215246

216247
/* Convert an integer in big or little endian representation (as
217248
specified by the ELF header) to this platform's integer

tests/clean-strtab.sh

Lines changed: 32 additions & 0 deletions
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+
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)