Skip to content

Commit

Permalink
Implement --clean-strtab
Browse files Browse the repository at this point in the history
  • Loading branch information
brenoguim committed Feb 20, 2023
1 parent 6ba561f commit 6bfd015
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 4 deletions.
76 changes: 74 additions & 2 deletions src/patchelf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <variant>
#include <vector>

#include <cassert>
Expand Down Expand Up @@ -1855,7 +1856,78 @@ void ElfFile<ElfFileParamNames>::noDefaultLib()
template<ElfFileParams>
void ElfFile<ElfFileParamNames>::cleanStrTab()
{
std::unordered_map<std::string, unsigned> requiredStrs2Idx {{"",0}};

// A collection of pointers to the fields that refer to str indices
// and a pointer to the new index value to be calculated
using StrIndexPtr = std::variant<Elf32_Word*, Elf64_Xword*>;
std::vector<std::pair<StrIndexPtr, unsigned*>> strRefs;

auto& strTabHdr = findSectionHeader(".dynstr");
auto strTab = getSectionSpan<char>(strTabHdr);

// Utility to collect a string index field from any table
auto collect = [&] (auto& idx) {
auto [it, _] = requiredStrs2Idx.emplace(&strTab[rdi(idx)], 0);
strRefs.emplace_back(&idx, &it->second);
};

// Iterate on tables known to store references to .dynstr
for (auto& sym : tryGetSectionSpan<Elf_Sym>(".dynsym"))
collect(sym.st_name);

for (auto& dyn : tryGetSectionSpan<Elf_Dyn>(".dynamic"))
switch (rdi(dyn.d_tag))
{
case DT_NEEDED:
case DT_SONAME:
case DT_RPATH:
case DT_RUNPATH: collect(dyn.d_un.d_val);
default:;
}

if (auto verdHdr = tryFindSectionHeader(".gnu.version_d"))
{
// Only collect fields if they use the strtab we are cleaning
if (&shdrs.at(rdi(verdHdr->get().sh_link)) == &strTabHdr)
forAll_ElfVer(getSectionSpan<Elf_Verdef>(*verdHdr),
[] (auto& vd) {},
[&] (auto& vda) { collect(vda.vda_name); }
);
}

if (auto vernHdr = tryFindSectionHeader(".gnu.version_r"))
{
// Only collect fields if they use the strtab we are cleaning
if (&shdrs.at(rdi(vernHdr->get().sh_link)) == &strTabHdr)
forAll_ElfVer(getSectionSpan<Elf_Verneed>(*vernHdr),
[&] (auto& vn) { collect(vn.vn_file); },
[&] (auto& vna) { collect(vna.vna_name); }
);
}

// Iterate on all required strings calculating the new position
size_t curIdx = 1;
for (auto& [str,idx] : requiredStrs2Idx)
{
idx = curIdx;
curIdx += str.size()+1;
}

// Add required strings to the new dynstr section
auto& newStrSec = replaceSection(".dynstr", curIdx);
for (auto& [str,idx] : requiredStrs2Idx)
std::copy(str.begin(), str.end()+1, newStrSec.begin()+idx);

// Iterate on all fields on all tables setting the new index value
for (auto& [oldIndexPtr, newIdxPtr] : strRefs)
std::visit(
[&] (auto* ptr) { wri(*ptr, *newIdxPtr); },
oldIndexPtr
);

changed = true;
this->rewriteSections();
}

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

if (getElfType(fileContents).is32Bit)
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);
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);
else
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);
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);
}
}

Expand Down
42 changes: 40 additions & 2 deletions src/patchelf.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using FileContents = std::shared_ptr<std::vector<unsigned char>>;

#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
#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
#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
#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

template<class T>
struct span
Expand Down Expand Up @@ -206,6 +206,44 @@ class ElfFile
return info;
}

template<class T, class U>
auto follow(U* ptr, size_t offset) -> T* {
return offset ? (T*)(((char*)ptr)+offset) : nullptr;
};

template<class Fn1, class Fn2>
void forAll_ElfVer(span<Elf_Verdef> vdspan, Fn1&& fn1, Fn2&& fn2)
{
auto* vd = vdspan.begin();
while (vd)
{
fn1(*vd);
auto va = follow<Elf_Verdaux>(vd, rdi(vd->vd_aux));
while (va)
{
fn2(*va);
va = follow<Elf_Verdaux>(va, rdi(va->vda_next));
}
vd = follow<Elf_Verdef>(vd, rdi(vd->vd_next));
}
}

template<class Fn1, class Fn2>
void forAll_ElfVer(span<Elf_Verneed> vnspan, Fn1&& fn1, Fn2&& fn2)
{
auto* vn = vnspan.begin();
while (vn)
{
fn1(*vn);
auto va = follow<Elf_Vernaux>(vn, rdi(vn->vn_aux));
while (va)
{
fn2(*va);
va = follow<Elf_Vernaux>(va, rdi(va->vna_next));
}
vn = follow<Elf_Verneed>(vn, rdi(vn->vn_next));
}
}

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

Expand Down
1 change: 1 addition & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ src_TESTS = \
replace-add-needed.sh \
add-debug-tag.sh \
rename-dynamic-symbols.sh \
clean-strtab.sh \
empty-note.sh

build_TESTS = \
Expand Down
32 changes: 32 additions & 0 deletions tests/clean-strtab.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#! /bin/sh -e
SCRATCH=scratch/$(basename $0 .sh)
PATCHELF=$(readlink -f "../src/patchelf")

rm -rf ${SCRATCH}
mkdir -p ${SCRATCH}

cp libfoo.so ${SCRATCH}/

cd ${SCRATCH}

the_string=VERY_SPECIFIC_STRING
function check_count {
count="$(strings libfoo.so | grep -c $the_string || true)"
expected=$1
echo "####### Checking count. Expected: $expected"
((count == expected)) || exit 1
}

check_count 0

${PATCHELF} --clean-strtab libfoo.so
check_count 0

${PATCHELF} --add-needed $the_string libfoo.so
check_count 1

${PATCHELF} --remove-needed $the_string libfoo.so
check_count 1

${PATCHELF} --clean-strtab libfoo.so
check_count 0

0 comments on commit 6bfd015

Please sign in to comment.