1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_soltools.hxx"
30 
31 /*
32  * adjustvisibilty -- a tool to adjust the visibility of the so called
33  *                    'fix and continue' globalized symbols generated by
34  *                    the Sun Studio 8 compiler from 'DEFAULT' to 'HIDDEN'
35  *
36  * References: "Linker and Libraries Guide", Solaris 9 documentation
37  *             "Stabs Interface", SunStudio 8 documentation
38  */
39 
40 #include <string>
41 #include <iostream>
42 #include <exception>
43 #include <stdexcept>
44 #include <cerrno>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <libelf.h>
48 #include <gelf.h>
49 #include <utime.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <limits>
53 #include <stdio.h>
54 
55 // Note: There is no GELF_ST_VISIBILITY macro in gelf.h, we roll our own.
56 #define GELF_ST_VISIBILITY(o)   ((o)&0x3) // See "Linker and Libraries Guide".
57 
58 // See "Linker and Libraries Guide", ELF object format description.
59 static const char* SymbolType[STT_NUM] = {
60     "NOTYPE",
61     "OBJECT",
62     "FUNC  ",
63     "SECT  ",
64     "FILE  ",
65     "COMM  ",
66     "TLS   "
67 };
68 
69 static const char* SymbolBinding[STB_NUM] = {
70     "LOCAL ",
71     "GLOBAL",
72     "WEAK  "
73 };
74 
75 static const char* SymbolVisibility[4] = {   // Note: There is no STV_NUM macro
76     "DEFAULT  ",
77     "INTERNAL ",
78     "HIDDEN   ",
79     "PROTECTED"
80 };
81 
82 class ElfError : public std::exception
83 {
84     public:
85         ElfError(const std::string& rFile, const std::string& rMessage);
86         ~ElfError() throw() {};
87         virtual const char* what() const throw() { return m_sMessage.c_str(); }
88 
89     private:
90         std::string m_sMessage;
91 };
92 
93 ElfError::ElfError(const std::string& rFile, const std::string& rMessage)
94 {
95     if ( rFile != "" ) {
96         m_sMessage = rFile;
97         m_sMessage += ": ";
98     }
99     m_sMessage += rMessage;
100     const char *pElfMsg = elf_errmsg(0);
101     if ( pElfMsg ) {
102         m_sMessage += ": ";
103         m_sMessage += pElfMsg;
104     }
105 }
106 
107 void initElfLib()
108 {
109     if ( elf_version(EV_CURRENT) == EV_NONE) {
110         throw ElfError("", "elf_version() failed");
111     }
112     return;
113 }
114 
115 bool isFixAndContinueSymbol(const std::string& rSymbol)
116 {
117     // The globalized 'fix and continue' symbols have the following
118     // form, see "Stabs interface", page 164:
119     // {.$}X{ABC}uniquepattern[.function_name][EQUIVn][.variable_name]
120     char c0 = rSymbol[0];
121     char c1 = rSymbol[1];
122     char c2 = rSymbol[2];
123     if ( c0 == '.' || c0 == '$' ) {
124         if ( c1 == 'X' ) {
125             if ( c2 == 'A' || c2 == 'B' || c2 == 'C' || c2 == 'D' ) {
126                 return true;
127             }
128         }
129     }
130     return false;
131 }
132 
133 void adjustVisibility( const std::string& rFile, int fd, bool bVerbose)
134 {
135     if ( bVerbose ) {
136         std::cout << "File: " << rFile << ": adjusting 'fix and continue' symbol visibility\n";
137     }
138 
139     try {
140         Elf* pElf;
141 		if ((pElf = elf_begin(fd, ELF_C_RDWR, 0)) == NULL) {
142             throw ElfError(rFile, "elf_begin() failed");
143         }
144         // Check if file is ELF file.
145 		if ( elf_kind(pElf) != ELF_K_ELF ) {
146             throw ElfError(rFile, "elf_kind() failed, file is not an ELF object file");
147         }
148 
149         // Iterate over sections.
150 	    Elf_Scn* pScn = 0;
151 	    while ( (pScn = elf_nextscn(pElf, pScn)) != 0 ) {
152             GElf_Shdr aShdr;
153     		if ( gelf_getshdr(pScn, &aShdr) == 0 ) {
154                 throw ElfError(rFile, "gelf_getshdr() failed");
155 	    	}
156 		    if ( aShdr.sh_type != SHT_SYMTAB ) {
157     			continue;
158             }
159             // Section is a symbol section. Get the assiociated data.
160             Elf_Data* pSymbolData;
161 		    if ( (pSymbolData = elf_getdata(pScn, 0)) == NULL ) {
162                 throw ElfError(rFile, "elf_getdata() failed");
163             }
164             // Iterate over symbol table.
165             GElf_Xword nSymbols = aShdr.sh_size / aShdr.sh_entsize;
166             if ( nSymbols > std::numeric_limits< int >::max() )
167             {
168                 throw ElfError(rFile, "too many symbols");
169             }
170             for ( int nIndex = 0; nIndex < nSymbols; ++nIndex) {
171                 // Get symbol.
172                 GElf_Sym aSymbol;
173     			if ( gelf_getsym(pSymbolData, nIndex, &aSymbol) == NULL )
174                 {
175                     throw ElfError(rFile, "gelf_getsym() failed");
176                 }
177                 std::string  sSymbolName(elf_strptr(pElf, aShdr.sh_link, aSymbol.st_name));
178                 if ( isFixAndContinueSymbol(sSymbolName) ) {
179                     // Get the symbol visibility.
180     	    		unsigned int nSymbolVisibility = GELF_ST_VISIBILITY(aSymbol.st_other);
181                     if ( bVerbose ) {
182         	    		// Get the symbol type and binding.
183 	        		    unsigned int nSymbolType       = GELF_ST_TYPE(aSymbol.st_info);
184     	        		unsigned int nSymbolBind       = GELF_ST_BIND(aSymbol.st_info);
185                         std::cout << "Symbol: " << sSymbolName << ", "
186                             << "Type: ";
187                             if ( SymbolType[nSymbolType] ) {
188                                 std::cout << SymbolType[nSymbolType];
189                             } else {
190                                 std::cout << nSymbolType;
191                             }
192                             std::cout << ", Binding: ";
193                             if ( SymbolBinding[nSymbolBind] ) {
194                                 std::cout << SymbolBinding[nSymbolBind];
195                             } else {
196                                 std::cout << nSymbolBind;
197                             }
198                             std::cout << ", Visibility: ";
199                             if ( SymbolVisibility[nSymbolVisibility] ) {
200                                 std::cout << SymbolVisibility[nSymbolVisibility];
201                             } else {
202                                 std::cout << nSymbolVisibility;
203                             }
204                             std::cout << "-> " << SymbolVisibility[STV_HIDDEN] << "\n";
205                     }
206                     // Toggle visibility to "hidden".
207                     aSymbol.st_other = GELF_ST_VISIBILITY(STV_HIDDEN);
208                     // Write back symbol data to underlying structure.
209     			    if ( gelf_update_sym(pSymbolData, nIndex, &aSymbol) == NULL )
210                     {
211                         throw ElfError(rFile, "gelf_update_sym() failed");
212                     }
213                 }
214             }
215         }
216         // Write changed object file to disk.
217         if ( elf_update(pElf, ELF_C_WRITE) == -1 ) {
218             throw ElfError(rFile, "elf_update() failed");
219         }
220         elf_end(pElf);
221 
222     } catch (ElfError& e) {
223         close(fd);
224         throw;
225     }
226     return;
227 }
228 
229 void processObject(const std::string& rFile, bool bPreserve, bool bVerbose)
230 {
231     int fd;
232     struct stat aStatBuf;
233 
234     if ((fd = open(rFile.c_str(), O_RDWR)) == -1) {
235          std::string sMessage("adjustVisibilty() failed: can't open file ");
236          sMessage += rFile;
237          sMessage += ": ";
238          sMessage += std::strerror(errno);
239          throw std::runtime_error(sMessage);
240     }
241 
242     if ( bPreserve ) {
243         if ( fstat(fd, &aStatBuf) == -1) {
244              std::string sMessage("adjustVisibilty() failed: can't stat file ");
245              sMessage += rFile;
246              sMessage += ": ";
247              sMessage += std::strerror(errno);
248              throw std::runtime_error(sMessage);
249         }
250     }
251 
252     adjustVisibility(rFile, fd, bVerbose);
253 
254     close(fd);
255 
256     if ( bPreserve ) {
257         struct utimbuf aUtimBuf = {aStatBuf.st_atime, aStatBuf.st_mtime};
258         if ( utime(rFile.c_str(), &aUtimBuf) == -1 ) {
259              std::string sMessage("adjustVisibilty() failed: can't reset timestamp ");
260              sMessage += rFile;
261              sMessage += ": ";
262              sMessage += std::strerror(errno);
263              throw std::runtime_error(sMessage);
264         }
265     }
266     return;
267 }
268 
269 int main(int argc, char* argv[])
270 {
271     int  c;
272     bool bPreserve = false;
273     bool bVerbose  = false;
274 
275     while ( (c = getopt(argc, argv, "pv")) != -1 ) {
276         switch(c) {
277             case 'p':
278                 bPreserve = true;
279                 break;
280             case 'v':
281                 bVerbose = true;
282                 break;
283             case '?':
284                 std::cerr << "Unrecognized option: -" << optopt << "\n";
285                 break;
286             default:
287                 break;
288         }
289     }
290 
291     if ( optind == argc ) {
292         std::cout << "usage: " << argv[0] << " [-pv] <elf-object> ...\n";
293         std::cout << "       -p     preserve time stamps\n";
294         std::cout << "       -v     verbose\n";
295         return 1;
296     }
297 
298     try {
299         initElfLib();
300 
301         for ( ; optind < argc; optind++ )  {
302             processObject(std::string(argv[optind]), bPreserve, bVerbose);
303         }
304 
305     } catch (std::exception& e) {
306         std::cerr << argv[0] << ": " << e.what() << "\n";
307         return 1;
308     }
309 
310     return 0;
311 }
312