523 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			523 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * $Revision: 42951 $
 | |
|  * $Date: 2014-01-21 14:41:41 -0600 (Tue, 21 Jan 2014) $
 | |
|  */
 | |
| 
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is dual licensed under the MIT and the University of Illinois Open
 | |
| // Source Licenses. See LICENSE.txt for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <iostream>
 | |
| #include <strstream>
 | |
| #include <fstream>
 | |
| #include <string>
 | |
| #include <set>
 | |
| #include <map>
 | |
| #include <vector>
 | |
| #include <cstring>
 | |
| 
 | |
| using namespace std;
 | |
| 
 | |
| typedef std::string                      string_t;
 | |
| typedef std::vector< string_t >          strings_t;
 | |
| typedef std::map< string_t, string_t >   str_hash_t;
 | |
| typedef std::pair< string_t, string_t >  str_pair_t;
 | |
| #ifdef _WIN32
 | |
|     typedef long long  int64_t;
 | |
| #endif
 | |
| 
 | |
| string_t
 | |
| shift( strings_t & strs ) {
 | |
|     string_t first = strs.front();
 | |
|     strs.erase( strs.begin() );
 | |
|     return first;
 | |
| } // shift
 | |
| 
 | |
| string_t
 | |
| find(
 | |
|     str_hash_t const & hash,
 | |
|     string_t const &   key
 | |
| ) {
 | |
|     string_t value;
 | |
|     str_hash_t::const_iterator it = hash.find( key );
 | |
|     if ( it != hash.end() ) {
 | |
|         value = it->second;
 | |
|     }; // if
 | |
|     return value;
 | |
| } // find
 | |
| 
 | |
| void die( string_t const & message ) {
 | |
|     std::cerr << message << std::endl;
 | |
|     exit( 1 );
 | |
| } // die
 | |
| 
 | |
| void stop( string_t const & message ) {
 | |
|     printf( "%s\n", message.c_str() );
 | |
|     exit( 1 );
 | |
| }
 | |
| 
 | |
| // An entry in the symbol table of a .obj file.
 | |
| struct symbol_t {
 | |
|     long long        name;
 | |
|     unsigned         value;
 | |
|     unsigned  short  section_num;
 | |
|     unsigned  short  type;
 | |
|     char             storage_class;
 | |
|     char             nAux;
 | |
| }; // struct symbol_t
 | |
| 
 | |
| 
 | |
| class _rstream_t : public std::istrstream {
 | |
| 
 | |
|     private:
 | |
| 
 | |
|         const char * buf;
 | |
| 
 | |
|     protected:
 | |
| 
 | |
|         _rstream_t( pair< const char *, streamsize > p )
 | |
|             : istrstream( p.first, p.second ), buf( p.first )
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         ~_rstream_t() {
 | |
|             delete [] buf;
 | |
|         }
 | |
| 
 | |
| }; // class _rstream_t
 | |
| 
 | |
| /* A stream encapuslating the content of a file or the content of a string, overriding the
 | |
|    >> operator to read various integer types in binary form, as well as a symbol table
 | |
|    entry.
 | |
| */
 | |
| class rstream_t : public _rstream_t {
 | |
| private:
 | |
| 
 | |
|     template< typename type_t >
 | |
|     inline rstream_t & do_read( type_t & x ) {
 | |
| 	read( (char*) & x, sizeof( type_t ) );
 | |
| 	return * this;
 | |
|     }
 | |
| 
 | |
|     static pair<const char*, streamsize> getBuf(const char *fileName) {
 | |
| 	ifstream raw(fileName,ios::binary | ios::in);
 | |
| 	if(!raw.is_open())
 | |
| 	    stop("rstream.getBuf: Error opening file");
 | |
| 	raw.seekg(0,ios::end);
 | |
| 	streampos fileSize = raw.tellg();
 | |
| 	if(fileSize < 0)
 | |
| 	    stop("rstream.getBuf: Error reading file");
 | |
| 	char *buf = new char[fileSize];
 | |
| 	raw.seekg(0,ios::beg);
 | |
| 	raw.read(buf, fileSize);
 | |
| 	return pair<const char*, streamsize>(buf,fileSize);
 | |
|     }
 | |
| public:
 | |
|     // construct from a string
 | |
|     rstream_t( const char * buf, streamsize size ) :
 | |
|         _rstream_t( pair< const char *, streamsize >( buf, size ) )
 | |
|     {}
 | |
|     /* construct from a file whole content is fully read once to initialize the content of
 | |
|        this stream
 | |
|     */
 | |
|     rstream_t( string_t const & fileName )
 | |
|         : _rstream_t( getBuf( fileName.c_str() ) )
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     rstream_t & operator >>( int & x ) {
 | |
| 	return do_read(x);
 | |
|     }
 | |
|     rstream_t & operator >>(unsigned &x) {
 | |
| 	return do_read(x);
 | |
|     }
 | |
|     rstream_t & operator>>(short &x) {
 | |
| 	return do_read(x);
 | |
|     }
 | |
|     rstream_t & operator>>(unsigned short &x) {
 | |
| 	return do_read(x);
 | |
|     }
 | |
|     rstream_t & operator>>( symbol_t & e ) {
 | |
| 	read((char*)&e, 18);
 | |
| 	return *this;
 | |
|     }
 | |
| }; // class rstream_t
 | |
| 
 | |
| // string table in a .OBJ file
 | |
| class StringTable {
 | |
| private:
 | |
|     map<string, unsigned> directory;
 | |
|     size_t length;
 | |
|     char *data;
 | |
| 
 | |
|     // make <directory> from <length> bytes in <data>
 | |
|     void makeDirectory(void) {
 | |
| 	unsigned i = 4;
 | |
| 	while(i < length) {
 | |
| 	    string s = string(data + i);
 | |
| 	    directory.insert(make_pair(s, i));
 | |
| 	    i += s.size() + 1;
 | |
| 	}
 | |
|     }
 | |
|     // initialize <length> and <data> with contents specified by the arguments
 | |
|     void init(const char *_data) {
 | |
| 	unsigned _length = *(unsigned*)_data;
 | |
| 
 | |
| 	if(_length < sizeof(unsigned) || _length != *(unsigned*)_data)
 | |
| 	    stop("StringTable.init: Invalid symbol table");
 | |
| 	if(_data[_length - 1]) {
 | |
| 	    // to prevent runaway strings, make sure the data ends with a zero
 | |
| 	    data = new char[length = _length + 1];
 | |
| 	    data[_length] = 0;
 | |
| 	} else {
 | |
| 	    data = new char[length = _length];
 | |
| 	}
 | |
| 	*(unsigned*)data = length;
 | |
| 	memcpy( data + sizeof(unsigned), _data + sizeof(unsigned), length - sizeof(unsigned) );
 | |
| 	makeDirectory();
 | |
|     }
 | |
| public:
 | |
|     StringTable( rstream_t & f ) {
 | |
| 	/* Construct string table by reading from f.
 | |
| 	 */
 | |
| 	streampos s;
 | |
| 	unsigned strSize;
 | |
| 	char *strData;
 | |
| 
 | |
| 	s = f.tellg();
 | |
| 	f>>strSize;
 | |
| 	if(strSize < sizeof(unsigned))
 | |
| 	    stop("StringTable: Invalid string table");
 | |
| 	strData = new char[strSize];
 | |
| 	*(unsigned*)strData = strSize;
 | |
| 	// read the raw data into <strData>
 | |
| 	f.read(strData + sizeof(unsigned), strSize - sizeof(unsigned));
 | |
| 	s = f.tellg() - s;
 | |
| 	if(s < strSize)
 | |
| 	    stop("StringTable: Unexpected EOF");
 | |
| 	init(strData);
 | |
| 	delete[]strData;
 | |
|     }
 | |
|     StringTable(const set<string> &strings) {
 | |
| 	/* Construct string table from given strings.
 | |
| 	 */
 | |
| 	char *p;
 | |
| 	set<string>::const_iterator it;
 | |
| 	size_t s;
 | |
| 
 | |
| 	// count required size for data
 | |
| 	for(length = sizeof(unsigned), it = strings.begin(); it != strings.end(); ++it) {
 | |
| 	    size_t l = (*it).size();
 | |
| 
 | |
| 	    if(l > (unsigned) 0xFFFFFFFF)
 | |
| 		stop("StringTable: String too long");
 | |
| 	    if(l > 8) {
 | |
| 		length += l + 1;
 | |
| 		if(length > (unsigned) 0xFFFFFFFF)
 | |
| 		    stop("StringTable: Symbol table too long");
 | |
| 	    }
 | |
| 	}
 | |
| 	data = new char[length];
 | |
| 	*(unsigned*)data = length;
 | |
| 	// populate data and directory
 | |
| 	for(p = data + sizeof(unsigned), it = strings.begin(); it != strings.end(); ++it) {
 | |
| 	    const string &str = *it;
 | |
| 	    size_t l = str.size();
 | |
| 	    if(l > 8) {
 | |
| 		directory.insert(make_pair(str, p - data));
 | |
| 		memcpy(p, str.c_str(), l);
 | |
| 		p[l] = 0;
 | |
| 		p += l + 1;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     ~StringTable() {
 | |
| 	delete[] data;
 | |
|     }
 | |
|     /* Returns encoding for given string based on this string table.
 | |
|        Error if string length is greater than 8 but string is not in
 | |
|        the string table--returns 0.
 | |
|     */
 | |
|     int64_t encode(const string &str) {
 | |
| 	int64_t r;
 | |
| 
 | |
| 	if(str.size() <= 8) {
 | |
| 	    // encoded directly
 | |
| 	    ((char*)&r)[7] = 0;
 | |
| 	    strncpy((char*)&r, str.c_str(), 8);
 | |
| 	    return r;
 | |
| 	} else {
 | |
| 	    // represented as index into table
 | |
| 	    map<string,unsigned>::const_iterator it = directory.find(str);
 | |
| 	    if(it == directory.end())
 | |
| 		stop("StringTable::encode: String now found in string table");
 | |
| 	    ((unsigned*)&r)[0] = 0;
 | |
| 	    ((unsigned*)&r)[1] = (*it).second;
 | |
| 	    return r;
 | |
| 	}
 | |
|     }
 | |
|     /* Returns string represented by x based on this string table.
 | |
|        Error if x references an invalid position in the table--returns
 | |
|        the empty string.
 | |
|     */
 | |
|     string decode(int64_t x) const {
 | |
| 	if(*(unsigned*)&x == 0) {
 | |
| 	    // represented as index into table
 | |
| 	    unsigned &p = ((unsigned*)&x)[1];
 | |
| 	    if(p >= length)
 | |
| 		stop("StringTable::decode: Invalid string table lookup");
 | |
| 	    return string(data + p);
 | |
| 	} else {
 | |
| 	    // encoded directly
 | |
| 	    char *p = (char*)&x;
 | |
| 	    int i;
 | |
| 
 | |
| 	    for(i = 0; i < 8 && p[i]; ++i);
 | |
| 	    return string(p, i);
 | |
| 	}
 | |
|     }
 | |
|     void write(ostream &os) {
 | |
| 	os.write(data, length);
 | |
|     }
 | |
| };
 | |
| 
 | |
| 
 | |
| void
 | |
| obj_copy(
 | |
|     string_t const &    src,    // Name of source file.
 | |
|     string_t const &    dst,    // Name of destination file.
 | |
|     str_hash_t const &  redefs  // List of redefinititions.
 | |
| ) {
 | |
| 
 | |
|     set< string > strings; // set of all occurring symbols, appropriately prefixed
 | |
|     streampos fileSize;
 | |
|     size_t strTabStart;
 | |
|     unsigned symTabStart;
 | |
|     unsigned symNEntries;
 | |
|     int i;
 | |
| 
 | |
| 
 | |
|     string const error_reading = "Error reading \"" + src + "\" file: ";
 | |
| 
 | |
|     rstream_t in( src );
 | |
| 
 | |
|     in.seekg( 0, ios::end );
 | |
|     fileSize = in.tellg();
 | |
| 
 | |
|     in.seekg( 8 );
 | |
|     in >> symTabStart >> symNEntries;
 | |
|     strTabStart = symTabStart + 18 * size_t( symNEntries );
 | |
|     in.seekg( strTabStart );
 | |
|     if ( in.eof() ) {
 | |
|         stop( error_reading + "Unexpected end of file" );
 | |
|     }
 | |
|     StringTable stringTableOld( in ); // Read original string table.
 | |
| 
 | |
|     if ( in.tellg() != fileSize ) {
 | |
|         stop( error_reading + "Unexpected data after string table" );
 | |
|     }
 | |
| 
 | |
|     // compute set of occurring strings with prefix added
 | |
|     for ( i = 0; i < symNEntries; ++ i ) {
 | |
| 
 | |
| 	symbol_t e;
 | |
| 
 | |
| 	in.seekg( symTabStart + i * 18 );
 | |
| 	if ( in.eof() ) {
 | |
|             stop("hideSymbols: Unexpected EOF");
 | |
|         }
 | |
| 	in >> e;
 | |
| 	if ( in.fail() ) {
 | |
|             stop("hideSymbols: File read error");
 | |
|         }
 | |
| 	if ( e.nAux ) {
 | |
|             i += e.nAux;
 | |
|         }
 | |
| 	const string & s = stringTableOld.decode( e.name );
 | |
| 	// if symbol is extern and found in <hide>, prefix and insert into strings,
 | |
| 	// otherwise, just insert into strings without prefix
 | |
|         string_t name = find( redefs, s );
 | |
| 	strings.insert( name != "" && e.storage_class == 2 ? name : s );
 | |
|     }
 | |
| 
 | |
|     ofstream out( dst.c_str(), ios::trunc | ios::out | ios::binary );
 | |
|     if ( ! out.is_open() ) {
 | |
|         stop("hideSymbols: Error opening output file");
 | |
|     }
 | |
| 
 | |
|     // make new string table from string set
 | |
|     StringTable stringTableNew = StringTable( strings );
 | |
| 
 | |
|     // copy input file to output file up to just before the symbol table
 | |
|     in.seekg( 0 );
 | |
|     char * buf = new char[ symTabStart ];
 | |
|     in.read( buf, symTabStart );
 | |
|     out.write( buf, symTabStart );
 | |
|     delete [] buf;
 | |
| 
 | |
|     // copy input symbol table to output symbol table with name translation
 | |
|     for ( i = 0; i < symNEntries; ++ i ) {
 | |
| 	symbol_t e;
 | |
| 
 | |
| 	in.seekg( symTabStart + i * 18 );
 | |
| 	if ( in.eof() ) {
 | |
|             stop("hideSymbols: Unexpected EOF");
 | |
|         }
 | |
| 	in >> e;
 | |
| 	if ( in.fail() ) {
 | |
|             stop("hideSymbols: File read error");
 | |
|         }
 | |
| 	const string & s = stringTableOld.decode( e.name );
 | |
| 	out.seekp( symTabStart + i * 18 );
 | |
|         string_t name = find( redefs, s );
 | |
| 	e.name = stringTableNew.encode( ( e.storage_class == 2 && name != "" ) ? name : s );
 | |
| 	out.write( (char*) & e, 18 );
 | |
| 	if ( out.fail() ) {
 | |
|             stop( "hideSymbols: File write error" );
 | |
|         }
 | |
| 	if ( e.nAux ) {
 | |
| 	    // copy auxiliary symbol table entries
 | |
| 	    int nAux = e.nAux;
 | |
| 	    for (int j = 1; j <= nAux; ++j ) {
 | |
| 		in >> e;
 | |
| 		out.seekp( symTabStart + ( i + j ) * 18 );
 | |
| 		out.write( (char*) & e, 18 );
 | |
| 	    }
 | |
| 	    i += nAux;
 | |
| 	}
 | |
|     }
 | |
|     // output string table
 | |
|     stringTableNew.write( out );
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| split( string_t const & str, char ch, string_t & head, string_t & tail ) {
 | |
|     string_t::size_type pos = str.find( ch );
 | |
|     if ( pos == string_t::npos ) {
 | |
|         head = str;
 | |
|         tail = "";
 | |
|     } else {
 | |
|         head = str.substr( 0, pos );
 | |
|         tail = str.substr( pos + 1 );
 | |
|     }; // if
 | |
| } // split
 | |
| 
 | |
| 
 | |
| void help() {
 | |
|     std::cout
 | |
|         << "NAME\n"
 | |
|         << "    objcopy -- copy and translate object files\n"
 | |
|         << "\n"
 | |
|         << "SYNOPSIS\n"
 | |
|         << "    objcopy options... source destination\n"
 | |
|         << "\n"
 | |
|         << "OPTIONS\n"
 | |
|         << "    --help  Print this help and exit.\n"
 | |
|         << "    --redefine-sym old=new\n"
 | |
|         << "            Rename \"old\" symbol in source object file to \"new\" symbol in\n"
 | |
|         << "            destination object file.\n"
 | |
|         << "    --redefine-syms sym_file\n"
 | |
|         << "            For each pair \"old new\" in sym_file rename \"old\" symbol in \n"
 | |
|         << "            source object file to \"new\" symbol in destination object file.\n"
 | |
|         << "\n"
 | |
|         << "ARGUMENTS\n"
 | |
|         << "    source  The name of source object file.\n"
 | |
|         << "    destination\n"
 | |
|         << "            The name of destination object file.\n"
 | |
|         << "\n"
 | |
|         << "DESCRIPTION\n"
 | |
|         << "    This program implements a minor bit of Linux* OS's objcopy utility on Windows* OS.\n"
 | |
|         << "    It can copy object files and edit its symbol table.\n"
 | |
|         << "\n"
 | |
|         << "EXAMPLES\n"
 | |
|         << "    \n"
 | |
|         << "        > objcopy --redefine-sym fastcpy=__xxx_fastcpy a.obj b.obj\n"
 | |
|         << "\n";
 | |
| } // help
 | |
| 
 | |
| 
 | |
| int
 | |
| main( int argc, char const * argv[] ) {
 | |
| 
 | |
|     strings_t   args( argc - 1 );
 | |
|     str_hash_t  redefs;
 | |
|     strings_t   files;
 | |
| 
 | |
|     std::copy( argv + 1, argv + argc, args.begin() );
 | |
| 
 | |
|     while ( args.size() > 0 ) {
 | |
|         string_t arg = shift( args );
 | |
|         if ( arg.substr( 0, 2 ) == "--" ) {
 | |
|             // An option.
 | |
|             if ( 0  ) {
 | |
|             } else if ( arg == "--help" ) {
 | |
|                 help();
 | |
|                 return 0;
 | |
|             } else if ( arg == "--redefine-sym" ) {
 | |
|                 if ( args.size() == 0 ) {
 | |
|                     die( "\"" + arg + "\" option requires an argument" );
 | |
|                 }; // if
 | |
|                 // read list of symbol pairs "old new" from command line.
 | |
|                 string_t redef = shift( args );
 | |
|                 string_t old_sym;
 | |
|                 string_t new_sym;
 | |
|                 split( redef, '=', old_sym, new_sym );
 | |
|                 if ( old_sym.length() == 0 || new_sym.length() == 0 ) {
 | |
|                     die( "Illegal redefinition: \"" + redef + "\"; neither old symbol nor new symbol may be empty" );
 | |
|                 }; // if
 | |
|                 redefs.insert( str_pair_t( old_sym, new_sym ) );
 | |
|             } else if ( arg == "--redefine-syms" ) {
 | |
|                 if ( args.size() == 0 ) {
 | |
|                     die( "\"" + arg + "\" option requires an argument" );
 | |
|                 }; // if
 | |
|                 // read list of symbol pairs "old new" from file.
 | |
|                 string_t fname = shift( args );
 | |
|                 string_t redef;
 | |
| 		ifstream ifs( fname.c_str() );
 | |
| 		while ( ifs.good() ) {
 | |
|                     getline( ifs, redef );// get pair of old/new symbols separated by space
 | |
|                     string_t old_sym;
 | |
|                     string_t new_sym;
 | |
|                     // AC: gcount() does not work here (always return 0), so comment it
 | |
|                     //if ( ifs.gcount() ) { // skip empty lines
 | |
|                     split( redef, ' ', old_sym, new_sym );
 | |
|                     if ( old_sym.length() == 0 || new_sym.length() == 0 ) {
 | |
|                         break;  // end of file reached (last empty line)
 | |
|                         //die( "Illegal redefinition: \"" + redef + "\"; neither old symbol nor new symbol may be empty" );
 | |
|                     }; // if
 | |
|                     redefs.insert( str_pair_t( old_sym, new_sym ) );
 | |
|                     //}
 | |
| 		}
 | |
|             } else {
 | |
|                 die( "Illegal option: \"" + arg + "\"" );
 | |
|             }; // if
 | |
|         } else {
 | |
|             // Not an option, a file name.
 | |
|             if ( files.size() >= 2 ) {
 | |
|                 die( "Too many files specified; two files required (use --help option for help)" );
 | |
|             }; // if
 | |
|             files.push_back( arg );
 | |
|         }; // if
 | |
|     }; // while
 | |
|     if ( files.size() < 2 ) {
 | |
|         die( "Not enough files specified; two files required (use --help option for help)" );
 | |
|     }; // if
 | |
| 
 | |
|     obj_copy( files[ 0 ], files[ 1 ], redefs );
 | |
| 
 | |
|     return 0;
 | |
| 
 | |
| } // main
 | |
| 
 | |
| 
 | |
| // end of file //
 |