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