| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_soltools.hxx" |
| |
| /* |
| * adjustvisibilty -- a tool to adjust the visibility of the so called |
| * 'fix and continue' globalized symbols generated by |
| * the Sun Studio 8 compiler from 'DEFAULT' to 'HIDDEN' |
| * |
| * References: "Linker and Libraries Guide", Solaris 9 documentation |
| * "Stabs Interface", SunStudio 8 documentation |
| */ |
| |
| #include <string> |
| #include <iostream> |
| #include <exception> |
| #include <stdexcept> |
| #include <cerrno> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <libelf.h> |
| #include <gelf.h> |
| #include <utime.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <limits> |
| #include <stdio.h> |
| |
| // Note: There is no GELF_ST_VISIBILITY macro in gelf.h, we roll our own. |
| #define GELF_ST_VISIBILITY(o) ((o)&0x3) // See "Linker and Libraries Guide". |
| |
| // See "Linker and Libraries Guide", ELF object format description. |
| static const char* SymbolType[STT_NUM] = { |
| "NOTYPE", |
| "OBJECT", |
| "FUNC ", |
| "SECT ", |
| "FILE ", |
| "COMM ", |
| "TLS " |
| }; |
| |
| static const char* SymbolBinding[STB_NUM] = { |
| "LOCAL ", |
| "GLOBAL", |
| "WEAK " |
| }; |
| |
| static const char* SymbolVisibility[4] = { // Note: There is no STV_NUM macro |
| "DEFAULT ", |
| "INTERNAL ", |
| "HIDDEN ", |
| "PROTECTED" |
| }; |
| |
| class ElfError : public std::exception |
| { |
| public: |
| ElfError(const std::string& rFile, const std::string& rMessage); |
| ~ElfError() throw() {}; |
| virtual const char* what() const throw() { return m_sMessage.c_str(); } |
| |
| private: |
| std::string m_sMessage; |
| }; |
| |
| ElfError::ElfError(const std::string& rFile, const std::string& rMessage) |
| { |
| if ( rFile != "" ) { |
| m_sMessage = rFile; |
| m_sMessage += ": "; |
| } |
| m_sMessage += rMessage; |
| const char *pElfMsg = elf_errmsg(0); |
| if ( pElfMsg ) { |
| m_sMessage += ": "; |
| m_sMessage += pElfMsg; |
| } |
| } |
| |
| void initElfLib() |
| { |
| if ( elf_version(EV_CURRENT) == EV_NONE) { |
| throw ElfError("", "elf_version() failed"); |
| } |
| return; |
| } |
| |
| bool isFixAndContinueSymbol(const std::string& rSymbol) |
| { |
| // The globalized 'fix and continue' symbols have the following |
| // form, see "Stabs interface", page 164: |
| // {.$}X{ABC}uniquepattern[.function_name][EQUIVn][.variable_name] |
| char c0 = rSymbol[0]; |
| char c1 = rSymbol[1]; |
| char c2 = rSymbol[2]; |
| if ( c0 == '.' || c0 == '$' ) { |
| if ( c1 == 'X' ) { |
| if ( c2 == 'A' || c2 == 'B' || c2 == 'C' || c2 == 'D' ) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| void adjustVisibility( const std::string& rFile, int fd, bool bVerbose) |
| { |
| if ( bVerbose ) { |
| std::cout << "File: " << rFile << ": adjusting 'fix and continue' symbol visibility\n"; |
| } |
| |
| try { |
| Elf* pElf; |
| if ((pElf = elf_begin(fd, ELF_C_RDWR, 0)) == NULL) { |
| throw ElfError(rFile, "elf_begin() failed"); |
| } |
| // Check if file is ELF file. |
| if ( elf_kind(pElf) != ELF_K_ELF ) { |
| throw ElfError(rFile, "elf_kind() failed, file is not an ELF object file"); |
| } |
| |
| // Iterate over sections. |
| Elf_Scn* pScn = 0; |
| while ( (pScn = elf_nextscn(pElf, pScn)) != 0 ) { |
| GElf_Shdr aShdr; |
| if ( gelf_getshdr(pScn, &aShdr) == 0 ) { |
| throw ElfError(rFile, "gelf_getshdr() failed"); |
| } |
| if ( aShdr.sh_type != SHT_SYMTAB ) { |
| continue; |
| } |
| // Section is a symbol section. Get the assiociated data. |
| Elf_Data* pSymbolData; |
| if ( (pSymbolData = elf_getdata(pScn, 0)) == NULL ) { |
| throw ElfError(rFile, "elf_getdata() failed"); |
| } |
| // Iterate over symbol table. |
| GElf_Xword nSymbols = aShdr.sh_size / aShdr.sh_entsize; |
| if ( nSymbols > std::numeric_limits< int >::max() ) |
| { |
| throw ElfError(rFile, "too many symbols"); |
| } |
| for ( int nIndex = 0; nIndex < nSymbols; ++nIndex) { |
| // Get symbol. |
| GElf_Sym aSymbol; |
| if ( gelf_getsym(pSymbolData, nIndex, &aSymbol) == NULL ) |
| { |
| throw ElfError(rFile, "gelf_getsym() failed"); |
| } |
| std::string sSymbolName(elf_strptr(pElf, aShdr.sh_link, aSymbol.st_name)); |
| if ( isFixAndContinueSymbol(sSymbolName) ) { |
| // Get the symbol visibility. |
| unsigned int nSymbolVisibility = GELF_ST_VISIBILITY(aSymbol.st_other); |
| if ( bVerbose ) { |
| // Get the symbol type and binding. |
| unsigned int nSymbolType = GELF_ST_TYPE(aSymbol.st_info); |
| unsigned int nSymbolBind = GELF_ST_BIND(aSymbol.st_info); |
| std::cout << "Symbol: " << sSymbolName << ", " |
| << "Type: "; |
| if ( SymbolType[nSymbolType] ) { |
| std::cout << SymbolType[nSymbolType]; |
| } else { |
| std::cout << nSymbolType; |
| } |
| std::cout << ", Binding: "; |
| if ( SymbolBinding[nSymbolBind] ) { |
| std::cout << SymbolBinding[nSymbolBind]; |
| } else { |
| std::cout << nSymbolBind; |
| } |
| std::cout << ", Visibility: "; |
| if ( SymbolVisibility[nSymbolVisibility] ) { |
| std::cout << SymbolVisibility[nSymbolVisibility]; |
| } else { |
| std::cout << nSymbolVisibility; |
| } |
| std::cout << "-> " << SymbolVisibility[STV_HIDDEN] << "\n"; |
| } |
| // Toggle visibility to "hidden". |
| aSymbol.st_other = GELF_ST_VISIBILITY(STV_HIDDEN); |
| // Write back symbol data to underlying structure. |
| if ( gelf_update_sym(pSymbolData, nIndex, &aSymbol) == NULL ) |
| { |
| throw ElfError(rFile, "gelf_update_sym() failed"); |
| } |
| } |
| } |
| } |
| // Write changed object file to disk. |
| if ( elf_update(pElf, ELF_C_WRITE) == -1 ) { |
| throw ElfError(rFile, "elf_update() failed"); |
| } |
| elf_end(pElf); |
| |
| } catch (ElfError& e) { |
| close(fd); |
| throw; |
| } |
| return; |
| } |
| |
| void processObject(const std::string& rFile, bool bPreserve, bool bVerbose) |
| { |
| int fd; |
| struct stat aStatBuf; |
| |
| if ((fd = open(rFile.c_str(), O_RDWR)) == -1) { |
| std::string sMessage("adjustVisibilty() failed: can't open file "); |
| sMessage += rFile; |
| sMessage += ": "; |
| sMessage += std::strerror(errno); |
| throw std::runtime_error(sMessage); |
| } |
| |
| if ( bPreserve ) { |
| if ( fstat(fd, &aStatBuf) == -1) { |
| std::string sMessage("adjustVisibilty() failed: can't stat file "); |
| sMessage += rFile; |
| sMessage += ": "; |
| sMessage += std::strerror(errno); |
| throw std::runtime_error(sMessage); |
| } |
| } |
| |
| adjustVisibility(rFile, fd, bVerbose); |
| |
| close(fd); |
| |
| if ( bPreserve ) { |
| struct utimbuf aUtimBuf = {aStatBuf.st_atime, aStatBuf.st_mtime}; |
| if ( utime(rFile.c_str(), &aUtimBuf) == -1 ) { |
| std::string sMessage("adjustVisibilty() failed: can't reset timestamp "); |
| sMessage += rFile; |
| sMessage += ": "; |
| sMessage += std::strerror(errno); |
| throw std::runtime_error(sMessage); |
| } |
| } |
| return; |
| } |
| |
| int main(int argc, char* argv[]) |
| { |
| int c; |
| bool bPreserve = false; |
| bool bVerbose = false; |
| |
| while ( (c = getopt(argc, argv, "pv")) != -1 ) { |
| switch(c) { |
| case 'p': |
| bPreserve = true; |
| break; |
| case 'v': |
| bVerbose = true; |
| break; |
| case '?': |
| std::cerr << "Unrecognized option: -" << optopt << "\n"; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if ( optind == argc ) { |
| std::cout << "usage: " << argv[0] << " [-pv] <elf-object> ...\n"; |
| std::cout << " -p preserve time stamps\n"; |
| std::cout << " -v verbose\n"; |
| return 1; |
| } |
| |
| try { |
| initElfLib(); |
| |
| for ( ; optind < argc; optind++ ) { |
| processObject(std::string(argv[optind]), bPreserve, bVerbose); |
| } |
| |
| } catch (std::exception& e) { |
| std::cerr << argv[0] << ": " << e.what() << "\n"; |
| return 1; |
| } |
| |
| return 0; |
| } |