office-gobmx/soltools/adjustvisibility/adjustvisibility.cxx
2012-06-27 19:30:33 +01:00

305 lines
9.7 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
/*
* 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 <cstring>
#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 (const 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) {
close(fd);
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 (const std::exception& e) {
std::cerr << argv[0] << ": " << e.what() << "\n";
return 1;
}
return 0;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */