| /* |
| */ |
| |
| |
| //===----------------------------------------------------------------------===// |
| // |
| // 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 // |