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" );
@@ -1745,21 +1754,86 @@ void ElfFile<ElfFileParamNames>::addNeeded(const std::set<std::string> & libs)
17451754 changed = true ;
17461755}
17471756
1757+ // https://stackoverflow.com/a/54738358/143733
1758+ // https://stackoverflow.com/a/478960/143733
1759+ std::pair<std::string, int > exec (const std::string & cmd) {
1760+ std::array<char , 128 > buffer;
1761+ std::string result;
1762+ // overwrite the destructor to capture also the return code
1763+ int return_code = -1 ;
1764+ auto pclose_wrapper = [&return_code](FILE* cmd){ return_code = pclose (cmd); };
1765+ {
1766+ std::unique_ptr<FILE, decltype (pclose_wrapper)> pipe (popen (cmd.c_str (), " r" ), pclose_wrapper);
1767+ if (pipe) {
1768+ while (fgets (buffer.data (), buffer.size (), pipe.get ()) != nullptr ) {
1769+ result += buffer.data ();
1770+ }
1771+ }
1772+ }
1773+ return make_pair (result, return_code);
1774+ }
1775+
17481776template <ElfFileParams>
1749- void ElfFile<ElfFileParamNames>::printNeededLibs() // const
1777+ void ElfFile<ElfFileParamNames>::shrinkWrap(std::map<std::string, std::string> & neededLibsToReplace, std::set<std::string> & neededLibsToAdd)
1778+ {
1779+ const std::string interpreter = getInterpreter ();
1780+ const std::vector<std::string> needed = getNeededLibs ();
1781+ const std::string cmd = fmt (interpreter, " --list " , this ->fileName );
1782+ const std::pair<std::string, int > result = exec (cmd);
1783+ if (result.second ) {
1784+ error (fmt (" ldd failed. " , result.second , " -" , result.first ));
1785+ }
1786+ std::istringstream iss (result.first );
1787+ std::string line;
1788+ std::regex r (" \\ s*([^ ]+) => ([^ ]+)" );
1789+ while (std::getline (iss, line)) {
1790+ std::smatch matches;
1791+ if (!std::regex_search (line, matches, r)) {
1792+ continue ;
1793+ }
1794+
1795+ std::string soname = matches.str (1 );
1796+ std::string location = matches.str (2 );
1797+ debug (" Found %s => %s\n " , soname.c_str (), location.c_str ());
1798+
1799+ // if the ELF file has this soname, then merely replace it
1800+ if (std::find (needed.begin (), needed.end (), soname) != needed.end ()) {
1801+ neededLibsToReplace.insert ({soname, location});
1802+ } else {
1803+ neededLibsToAdd.insert (location);
1804+ }
1805+ }
1806+ }
1807+
1808+ template <ElfFileParams>
1809+ std::vector<std::string> ElfFile<ElfFileParamNames>::getNeededLibs() // const
17501810{
17511811 const auto shdrDynamic = findSection (" .dynamic" );
17521812 const auto shdrDynStr = findSection (" .dynstr" );
17531813 const char *strTab = (char *)fileContents->data () + rdi (shdrDynStr.sh_offset );
17541814
17551815 const Elf_Dyn *dyn = (Elf_Dyn *) (fileContents->data () + rdi (shdrDynamic.sh_offset ));
17561816
1817+ std::vector<std::string> results;
1818+
17571819 for (; rdi (dyn->d_tag ) != DT_NULL; dyn++) {
17581820 if (rdi (dyn->d_tag ) == DT_NEEDED) {
17591821 const char *name = strTab + rdi (dyn->d_un .d_val );
1760- printf ( " %s \n " , name);
1822+ results. push_back ( std::string ( name) );
17611823 }
17621824 }
1825+
1826+ return results;
1827+ }
1828+
1829+
1830+ template <ElfFileParams>
1831+ void ElfFile<ElfFileParamNames>::printNeededLibs() // const
1832+ {
1833+ const std::vector<std::string> needed = getNeededLibs ();
1834+ for (std::string soname : needed) {
1835+ printf (" %s\n " , soname.c_str ());
1836+ }
17631837}
17641838
17651839
@@ -1832,6 +1906,7 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
18321906
18331907static bool printInterpreter = false ;
18341908static bool printSoname = false ;
1909+ static bool shrinkWrap = false ;
18351910static bool setSoname = false ;
18361911static std::string newSoname;
18371912static std::string newInterpreter;
@@ -1855,6 +1930,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
18551930 if (printInterpreter)
18561931 printf (" %s\n " , elfFile.getInterpreter ().c_str ());
18571932
1933+ if (shrinkWrap)
1934+ elfFile.shrinkWrap (neededLibsToReplace, neededLibsToAdd);
1935+
18581936 if (printSoname)
18591937 elfFile.modifySoname (elfFile.printSoname , " " );
18601938
@@ -1906,9 +1984,9 @@ static void patchElf()
19061984 const std::string & outputFileName2 = outputFileName.empty () ? fileName : outputFileName;
19071985
19081986 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);
1987+ patchElf2 (ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents, fileName ), fileContents, outputFileName2);
19101988 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);
1989+ patchElf2 (ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents, fileName ), fileContents, outputFileName2);
19121990 }
19131991}
19141992
@@ -1927,6 +2005,7 @@ void showHelp(const std::string & progName)
19272005 fprintf (stderr, " syntax: %s\n \
19282006 [--set-interpreter FILENAME]\n \
19292007 [--page-size SIZE]\n \
2008+ [--shrink-wrap]\n \
19302009 [--print-interpreter]\n \
19312010 [--print-soname]\t\t Prints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n \
19322011 [--set-soname SONAME]\t\t Sets 'DT_SONAME' entry to SONAME.\n \
@@ -1978,6 +2057,9 @@ int mainWrapped(int argc, char * * argv)
19782057 else if (arg == " --print-soname" ) {
19792058 printSoname = true ;
19802059 }
2060+ else if (arg == " --shrink-wrap" ) {
2061+ shrinkWrap = true ;
2062+ }
19812063 else if (arg == " --set-soname" ) {
19822064 if (++i == argc) error (" missing argument" );
19832065 setSoname = true ;
0 commit comments