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>
44+ #include < array>
4245
4346#include " elf.h"
4447
@@ -93,6 +96,8 @@ class ElfFile
9396
9497 const FileContents fileContents;
9598
99+ const std::string fileName;
100+
96101private:
97102
98103 std::vector<Elf_Phdr> phdrs;
@@ -118,7 +123,7 @@ class ElfFile
118123 std::vector<SectionName> sectionsByOldIndex;
119124
120125public:
121- explicit ElfFile (FileContents fileContents);
126+ explicit ElfFile (FileContents fileContents, std::string fileName );
122127
123128 bool isChanged ()
124129 {
@@ -210,6 +215,10 @@ class ElfFile
210215
211216 void replaceNeeded (const std::map<std::string, std::string> & libs);
212217
218+ void shrinkWrap (std::map<std::string, std::string> & neededLibsToReplace, std::set<std::string> & neededLibsToAdd);
219+
220+ std::vector<std::string> getNeededLibs ();
221+
213222 void printNeededLibs () /* should be const */ ;
214223
215224 void noDefaultLib ();
@@ -382,8 +391,8 @@ static void checkPointer(const FileContents & contents, void * p, unsigned int s
382391
383392
384393template <ElfFileParams>
385- ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents )
386- : fileContents(fContents )
394+ ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents , std::string fileName )
395+ : fileContents(fContents ), fileName(fileName)
387396{
388397 /* Check the ELF header for basic validity. */
389398 if (fileContents->size () < (off_t ) sizeof (Elf_Ehdr)) error (" missing ELF header" );
@@ -1237,7 +1246,7 @@ template<ElfFileParams>
12371246std::string ElfFile<ElfFileParamNames>::getInterpreter()
12381247{
12391248 auto shdr = findSection (" .interp" );
1240- return std::string ((char *) fileContents->data () + rdi (shdr.sh_offset ), rdi (shdr.sh_size ));
1249+ return std::string ((char *) fileContents->data () + rdi (shdr.sh_offset ), rdi (shdr.sh_size ) - 1 );
12411250}
12421251
12431252template <ElfFileParams>
@@ -1745,21 +1754,81 @@ void ElfFile<ElfFileParamNames>::addNeeded(const std::set<std::string> & libs)
17451754 changed = true ;
17461755}
17471756
1757+ // https://stackoverflow.com/a/478960/143733
1758+ std::string exec (const char * cmd) {
1759+ std::array<char , 128 > buffer;
1760+ std::string result;
1761+ std::unique_ptr<FILE, decltype (&pclose)> pipe (popen (cmd, " r" ), pclose);
1762+ if (!pipe) {
1763+ throw std::runtime_error (" popen() failed!" );
1764+ }
1765+ while (fgets (buffer.data (), buffer.size (), pipe.get ()) != nullptr ) {
1766+ result += buffer.data ();
1767+ }
1768+ return result;
1769+ }
1770+
17481771template <ElfFileParams>
1749- void ElfFile<ElfFileParamNames>::printNeededLibs() // const
1772+ void ElfFile<ElfFileParamNames>::shrinkWrap(std::map<std::string, std::string> & neededLibsToReplace, std::set<std::string> & neededLibsToAdd)
1773+ {
1774+ const std::string interpreter = getInterpreter ();
1775+ const std::vector<std::string> needed = getNeededLibs ();
1776+ std::stringstream ss;
1777+ ss << interpreter << " --list " << this ->fileName ;
1778+ const std::string cmd = ss.str ();
1779+ const std::string lddOut = exec (cmd.c_str ());
1780+
1781+ std::istringstream iss (lddOut);
1782+ std::string line;
1783+ while (std::getline (iss, line)) {
1784+ std::regex r (" \\ s*([^ ]+) => ([^ ]+)" );
1785+ std::smatch matches;
1786+ if (!std::regex_search (line, matches, r)) {
1787+ continue ;
1788+ }
1789+
1790+ std::string soname = matches.str (1 );
1791+ std::string location = matches.str (2 );
1792+ debug (" Found %s => %s\n " , soname.c_str (), location.c_str ());
1793+
1794+ // if the ELF file has this soname, then merely replace it
1795+ if (std::find (needed.begin (), needed.end (), soname) != needed.end ()) {
1796+ neededLibsToReplace.insert ({soname, location});
1797+ } else {
1798+ neededLibsToAdd.insert (location);
1799+ }
1800+ }
1801+ }
1802+
1803+ template <ElfFileParams>
1804+ std::vector<std::string> ElfFile<ElfFileParamNames>::getNeededLibs() // const
17501805{
17511806 const auto shdrDynamic = findSection (" .dynamic" );
17521807 const auto shdrDynStr = findSection (" .dynstr" );
17531808 const char *strTab = (char *)fileContents->data () + rdi (shdrDynStr.sh_offset );
17541809
17551810 const Elf_Dyn *dyn = (Elf_Dyn *) (fileContents->data () + rdi (shdrDynamic.sh_offset ));
17561811
1812+ std::vector<std::string> results;
1813+
17571814 for (; rdi (dyn->d_tag ) != DT_NULL; dyn++) {
17581815 if (rdi (dyn->d_tag ) == DT_NEEDED) {
17591816 const char *name = strTab + rdi (dyn->d_un .d_val );
1760- printf ( " %s \n " , name);
1817+ results. push_back ( std::string ( name) );
17611818 }
17621819 }
1820+
1821+ return results;
1822+ }
1823+
1824+
1825+ template <ElfFileParams>
1826+ void ElfFile<ElfFileParamNames>::printNeededLibs() // const
1827+ {
1828+ const std::vector<std::string> needed = getNeededLibs ();
1829+ for (std::string soname : needed) {
1830+ printf (" %s\n " , soname.c_str ());
1831+ }
17631832}
17641833
17651834
@@ -1832,6 +1901,7 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
18321901
18331902static bool printInterpreter = false ;
18341903static bool printSoname = false ;
1904+ static bool shrinkWrap = false ;
18351905static bool setSoname = false ;
18361906static std::string newSoname;
18371907static std::string newInterpreter;
@@ -1855,6 +1925,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
18551925 if (printInterpreter)
18561926 printf (" %s\n " , elfFile.getInterpreter ().c_str ());
18571927
1928+ if (shrinkWrap)
1929+ elfFile.shrinkWrap (neededLibsToReplace, neededLibsToAdd);
1930+
18581931 if (printSoname)
18591932 elfFile.modifySoname (elfFile.printSoname , " " );
18601933
@@ -1906,9 +1979,9 @@ static void patchElf()
19061979 const std::string & outputFileName2 = outputFileName.empty () ? fileName : outputFileName;
19071980
19081981 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);
1982+ patchElf2 (ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents, fileName ), fileContents, outputFileName2);
19101983 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);
1984+ patchElf2 (ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents, fileName ), fileContents, outputFileName2);
19121985 }
19131986}
19141987
@@ -1927,6 +2000,7 @@ void showHelp(const std::string & progName)
19272000 fprintf (stderr, " syntax: %s\n \
19282001 [--set-interpreter FILENAME]\n \
19292002 [--page-size SIZE]\n \
2003+ [--shrink-wrap]\n \
19302004 [--print-interpreter]\n \
19312005 [--print-soname]\t\t Prints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n \
19322006 [--set-soname SONAME]\t\t Sets 'DT_SONAME' entry to SONAME.\n \
@@ -1978,6 +2052,9 @@ int mainWrapped(int argc, char * * argv)
19782052 else if (arg == " --print-soname" ) {
19792053 printSoname = true ;
19802054 }
2055+ else if (arg == " --shrink-wrap" ) {
2056+ shrinkWrap = true ;
2057+ }
19812058 else if (arg == " --set-soname" ) {
19822059 if (++i == argc) error (" missing argument" );
19832060 setSoname = true ;
0 commit comments