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+
96100private:
97101
98102 std::vector<Elf_Phdr> phdrs;
@@ -118,7 +122,7 @@ class ElfFile
118122 std::vector<SectionName> sectionsByOldIndex;
119123
120124public:
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
384392template <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>
12371245std::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
12431251template <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+
17481770template <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
18331905static bool printInterpreter = false ;
18341906static bool printSoname = false ;
1907+ static bool shrinkWrap = false ;
18351908static bool setSoname = false ;
18361909static std::string newSoname;
18371910static 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\t Prints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n \
19322009 [--set-soname SONAME]\t\t Sets '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