36
36
#include < cstring>
37
37
38
38
#include < fcntl.h>
39
+ #include < dlfcn.h>
39
40
#include < sys/stat.h>
40
41
#include < sys/types.h>
41
42
#include < unistd.h>
43
+ #include < regex>
44
+ #include < array>
42
45
43
46
#include " elf.h"
44
47
@@ -93,6 +96,8 @@ class ElfFile
93
96
94
97
const FileContents fileContents;
95
98
99
+ const std::string fileName;
100
+
96
101
private:
97
102
98
103
std::vector<Elf_Phdr> phdrs;
@@ -118,7 +123,7 @@ class ElfFile
118
123
std::vector<SectionName> sectionsByOldIndex;
119
124
120
125
public:
121
- explicit ElfFile (FileContents fileContents);
126
+ explicit ElfFile (FileContents fileContents, std::string fileName );
122
127
123
128
bool isChanged ()
124
129
{
@@ -210,6 +215,10 @@ class ElfFile
210
215
211
216
void replaceNeeded (const std::map<std::string, std::string> & libs);
212
217
218
+ void shrinkWrap (std::map<std::string, std::string> & neededLibsToReplace, std::set<std::string> & neededLibsToAdd);
219
+
220
+ std::vector<std::string> getNeededLibs ();
221
+
213
222
void printNeededLibs () /* should be const */ ;
214
223
215
224
void noDefaultLib ();
@@ -382,8 +391,8 @@ static void checkPointer(const FileContents & contents, void * p, unsigned int s
382
391
383
392
384
393
template <ElfFileParams>
385
- ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents )
386
- : fileContents(fContents )
394
+ ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents , std::string fileName )
395
+ : fileContents(fContents ), fileName(fileName)
387
396
{
388
397
/* Check the ELF header for basic validity. */
389
398
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)
1745
1754
changed = true ;
1746
1755
}
1747
1756
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
+
1748
1776
template <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
1750
1810
{
1751
1811
const auto shdrDynamic = findSection (" .dynamic" );
1752
1812
const auto shdrDynStr = findSection (" .dynstr" );
1753
1813
const char *strTab = (char *)fileContents->data () + rdi (shdrDynStr.sh_offset );
1754
1814
1755
1815
const Elf_Dyn *dyn = (Elf_Dyn *) (fileContents->data () + rdi (shdrDynamic.sh_offset ));
1756
1816
1817
+ std::vector<std::string> results;
1818
+
1757
1819
for (; rdi (dyn->d_tag ) != DT_NULL; dyn++) {
1758
1820
if (rdi (dyn->d_tag ) == DT_NEEDED) {
1759
1821
const char *name = strTab + rdi (dyn->d_un .d_val );
1760
- printf ( " %s \n " , name);
1822
+ results. push_back ( std::string ( name) );
1761
1823
}
1762
1824
}
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
+ }
1763
1837
}
1764
1838
1765
1839
@@ -1832,6 +1906,7 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
1832
1906
1833
1907
static bool printInterpreter = false ;
1834
1908
static bool printSoname = false ;
1909
+ static bool shrinkWrap = false ;
1835
1910
static bool setSoname = false ;
1836
1911
static std::string newSoname;
1837
1912
static std::string newInterpreter;
@@ -1855,6 +1930,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
1855
1930
if (printInterpreter)
1856
1931
printf (" %s\n " , elfFile.getInterpreter ().c_str ());
1857
1932
1933
+ if (shrinkWrap)
1934
+ elfFile.shrinkWrap (neededLibsToReplace, neededLibsToAdd);
1935
+
1858
1936
if (printSoname)
1859
1937
elfFile.modifySoname (elfFile.printSoname , " " );
1860
1938
@@ -1906,9 +1984,9 @@ static void patchElf()
1906
1984
const std::string & outputFileName2 = outputFileName.empty () ? fileName : outputFileName;
1907
1985
1908
1986
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);
1910
1988
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);
1912
1990
}
1913
1991
}
1914
1992
@@ -1927,6 +2005,7 @@ void showHelp(const std::string & progName)
1927
2005
fprintf (stderr, " syntax: %s\n \
1928
2006
[--set-interpreter FILENAME]\n \
1929
2007
[--page-size SIZE]\n \
2008
+ [--shrink-wrap]\n \
1930
2009
[--print-interpreter]\n \
1931
2010
[--print-soname]\t\t Prints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n \
1932
2011
[--set-soname SONAME]\t\t Sets 'DT_SONAME' entry to SONAME.\n \
@@ -1978,6 +2057,9 @@ int mainWrapped(int argc, char * * argv)
1978
2057
else if (arg == " --print-soname" ) {
1979
2058
printSoname = true ;
1980
2059
}
2060
+ else if (arg == " --shrink-wrap" ) {
2061
+ shrinkWrap = true ;
2062
+ }
1981
2063
else if (arg == " --set-soname" ) {
1982
2064
if (++i == argc) error (" missing argument" );
1983
2065
setSoname = true ;
0 commit comments