Skip to content

Commit 188af43

Browse files
committed
shrinkwrap: introduce new shrinkwrap option
Add a new command to patchelf `--shrink-wrap` which shrink-wraps the binary file. patchelf at the moment works by modifying the RUNPATH to help locate files to the store however it only does this generally for it's immediate DT_NEEDED. There can be cases where binaries correctly run but are doing so just by chance that the linker has found the library previously during it's walk. To avoid this, lets pull up all DT_NEEDED to the top-level executable and immortalize them by having their entries point to very specific location.
1 parent fbf108f commit 188af43

File tree

2 files changed

+93
-8
lines changed

2 files changed

+93
-8
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,8 @@ Makefile
3232
/tests/libbig-dynstr.debug
3333
/tests/contiguous-note-sections
3434
/tests/simple-pie
35+
36+
.direnv/
37+
result
38+
.idea/
39+
.vscode/

src/patchelf.cc

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@
3636
#include <cstring>
3737

3838
#include <fcntl.h>
39+
#include <dlfcn.h>
3940
#include <sys/stat.h>
4041
#include <sys/types.h>
4142
#include <unistd.h>
43+
#include <regex>
4244

4345
#include "elf.h"
4446

@@ -93,6 +95,8 @@ class ElfFile
9395

9496
const FileContents fileContents;
9597

98+
const std::string fileName;
99+
96100
private:
97101

98102
std::vector<Elf_Phdr> phdrs;
@@ -118,7 +122,7 @@ class ElfFile
118122
std::vector<SectionName> sectionsByOldIndex;
119123

120124
public:
121-
explicit ElfFile(FileContents fileContents);
125+
explicit ElfFile(FileContents fileContents, std::string fileName);
122126

123127
bool isChanged()
124128
{
@@ -210,6 +214,10 @@ class ElfFile
210214

211215
void replaceNeeded(const std::map<std::string, std::string> & libs);
212216

217+
void shrinkWrap();
218+
219+
std::vector<std::string> getNeededLibs();
220+
213221
void printNeededLibs() /* should be const */;
214222

215223
void noDefaultLib();
@@ -382,8 +390,8 @@ static void checkPointer(const FileContents & contents, void * p, unsigned int s
382390

383391

384392
template<ElfFileParams>
385-
ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents)
386-
: fileContents(fContents)
393+
ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents, std::string fileName)
394+
: fileContents(fContents), fileName(fileName)
387395
{
388396
/* Check the ELF header for basic validity. */
389397
if (fileContents->size() < (off_t) sizeof(Elf_Ehdr)) error("missing ELF header");
@@ -1237,7 +1245,7 @@ template<ElfFileParams>
12371245
std::string ElfFile<ElfFileParamNames>::getInterpreter()
12381246
{
12391247
auto shdr = findSection(".interp");
1240-
return std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size));
1248+
return std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size) - 1);
12411249
}
12421250

12431251
template<ElfFileParams>
@@ -1745,21 +1753,85 @@ void ElfFile<ElfFileParamNames>::addNeeded(const std::set<std::string> & libs)
17451753
changed = true;
17461754
}
17471755

1756+
// https://stackoverflow.com/a/478960/143733
1757+
std::string exec(const char* cmd) {
1758+
std::array<char, 128> buffer;
1759+
std::string result;
1760+
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
1761+
if (!pipe) {
1762+
throw std::runtime_error("popen() failed!");
1763+
}
1764+
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
1765+
result += buffer.data();
1766+
}
1767+
return result;
1768+
}
1769+
17481770
template<ElfFileParams>
1749-
void ElfFile<ElfFileParamNames>::printNeededLibs() // const
1771+
void ElfFile<ElfFileParamNames>::shrinkWrap()
1772+
{
1773+
const std::string interpreter = getInterpreter();
1774+
const std::vector<std::string> needed = getNeededLibs();
1775+
std::stringstream ss;
1776+
ss << interpreter << " --list " << this->fileName;
1777+
const std::string cmd = ss.str();
1778+
const std::string lddOut = exec(cmd.c_str());
1779+
1780+
std::map<std::string, std::string> replaceNeededMap;
1781+
std::set<std::string> addNeededSet;
1782+
1783+
std::istringstream iss(lddOut);
1784+
std::string line;
1785+
while (std::getline(iss, line)) {
1786+
std::regex r("\\s*([^ ]+) => ([^ ]+)");
1787+
std::smatch matches;
1788+
if (!std::regex_search(line, matches, r)) {
1789+
continue;
1790+
}
1791+
1792+
std::string soname = matches.str(1);
1793+
std::string location = matches.str(2);
1794+
1795+
// if the ELF file has this soname, then merely replace it
1796+
if (std::find(needed.begin(), needed.end(), soname) != needed.end()) {
1797+
replaceNeededMap.insert({soname, location});
1798+
} else {
1799+
addNeededSet.insert(location);
1800+
}
1801+
}
1802+
addNeeded(addNeededSet);
1803+
replaceNeeded(replaceNeededMap);
1804+
}
1805+
1806+
template<ElfFileParams>
1807+
std::vector<std::string> ElfFile<ElfFileParamNames>::getNeededLibs() // const
17501808
{
17511809
const auto shdrDynamic = findSection(".dynamic");
17521810
const auto shdrDynStr = findSection(".dynstr");
17531811
const char *strTab = (char *)fileContents->data() + rdi(shdrDynStr.sh_offset);
17541812

17551813
const Elf_Dyn *dyn = (Elf_Dyn *) (fileContents->data() + rdi(shdrDynamic.sh_offset));
17561814

1815+
std::vector<std::string> results;
1816+
17571817
for (; rdi(dyn->d_tag) != DT_NULL; dyn++) {
17581818
if (rdi(dyn->d_tag) == DT_NEEDED) {
17591819
const char *name = strTab + rdi(dyn->d_un.d_val);
1760-
printf("%s\n", name);
1820+
results.push_back(std::string(name));
17611821
}
17621822
}
1823+
1824+
return results;
1825+
}
1826+
1827+
1828+
template<ElfFileParams>
1829+
void ElfFile<ElfFileParamNames>::printNeededLibs() // const
1830+
{
1831+
const std::vector<std::string> needed = getNeededLibs();
1832+
for (std::string soname : needed) {
1833+
printf("%s\n", soname.c_str());
1834+
}
17631835
}
17641836

17651837

@@ -1832,6 +1904,7 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
18321904

18331905
static bool printInterpreter = false;
18341906
static bool printSoname = false;
1907+
static bool shrinkWrap = false;
18351908
static bool setSoname = false;
18361909
static std::string newSoname;
18371910
static std::string newInterpreter;
@@ -1855,6 +1928,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
18551928
if (printInterpreter)
18561929
printf("%s\n", elfFile.getInterpreter().c_str());
18571930

1931+
if (shrinkWrap)
1932+
elfFile.shrinkWrap();
1933+
18581934
if (printSoname)
18591935
elfFile.modifySoname(elfFile.printSoname, "");
18601936

@@ -1906,9 +1982,9 @@ static void patchElf()
19061982
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
19071983

19081984
if (getElfType(fileContents).is32Bit)
1909-
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
1985+
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents, fileName), fileContents, outputFileName2);
19101986
else
1911-
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
1987+
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents, fileName), fileContents, outputFileName2);
19121988
}
19131989
}
19141990

@@ -1927,6 +2003,7 @@ void showHelp(const std::string & progName)
19272003
fprintf(stderr, "syntax: %s\n\
19282004
[--set-interpreter FILENAME]\n\
19292005
[--page-size SIZE]\n\
2006+
[--shrink-wrap]\n\
19302007
[--print-interpreter]\n\
19312008
[--print-soname]\t\tPrints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n\
19322009
[--set-soname SONAME]\t\tSets 'DT_SONAME' entry to SONAME.\n\
@@ -1978,6 +2055,9 @@ int mainWrapped(int argc, char * * argv)
19782055
else if (arg == "--print-soname") {
19792056
printSoname = true;
19802057
}
2058+
else if (arg == "--shrink-wrap") {
2059+
shrinkWrap = true;
2060+
}
19812061
else if (arg == "--set-soname") {
19822062
if (++i == argc) error("missing argument");
19832063
setSoname = true;

0 commit comments

Comments
 (0)