blob: a2811c6261d7b54f3a55a291cb01ce0383f354f5 [file] [log] [blame]
Jim Cownie5e8470a2013-09-27 10:38:44 +00001/*
Jim Cownie5e8470a2013-09-27 10:38:44 +00002 */
3
4
5//===----------------------------------------------------------------------===//
6//
7// The LLVM Compiler Infrastructure
8//
9// This file is dual licensed under the MIT and the University of Illinois Open
10// Source Licenses. See LICENSE.txt for details.
11//
12//===----------------------------------------------------------------------===//
13
14
15#include <stdlib.h>
16#include <iostream>
17#include <strstream>
18#include <fstream>
19#include <string>
20#include <set>
21#include <map>
22#include <vector>
23#include <cstring>
24
25using namespace std;
26
27typedef std::string string_t;
28typedef std::vector< string_t > strings_t;
29typedef std::map< string_t, string_t > str_hash_t;
30typedef std::pair< string_t, string_t > str_pair_t;
31#ifdef _WIN32
32 typedef long long int64_t;
33#endif
34
35string_t
36shift( strings_t & strs ) {
37 string_t first = strs.front();
38 strs.erase( strs.begin() );
39 return first;
40} // shift
41
42string_t
43find(
44 str_hash_t const & hash,
45 string_t const & key
46) {
47 string_t value;
48 str_hash_t::const_iterator it = hash.find( key );
49 if ( it != hash.end() ) {
50 value = it->second;
51 }; // if
52 return value;
53} // find
54
55void die( string_t const & message ) {
56 std::cerr << message << std::endl;
57 exit( 1 );
58} // die
59
60void stop( string_t const & message ) {
61 printf( "%s\n", message.c_str() );
62 exit( 1 );
63}
64
65// An entry in the symbol table of a .obj file.
66struct symbol_t {
67 long long name;
68 unsigned value;
69 unsigned short section_num;
70 unsigned short type;
71 char storage_class;
72 char nAux;
73}; // struct symbol_t
74
75
76class _rstream_t : public std::istrstream {
77
78 private:
79
80 const char * buf;
81
82 protected:
83
84 _rstream_t( pair< const char *, streamsize > p )
85 : istrstream( p.first, p.second ), buf( p.first )
86 {
87 }
88
89 ~_rstream_t() {
90 delete [] buf;
91 }
92
93}; // class _rstream_t
94
95/* A stream encapuslating the content of a file or the content of a string, overriding the
96 >> operator to read various integer types in binary form, as well as a symbol table
97 entry.
98*/
99class rstream_t : public _rstream_t {
100private:
101
102 template< typename type_t >
103 inline rstream_t & do_read( type_t & x ) {
104 read( (char*) & x, sizeof( type_t ) );
105 return * this;
106 }
107
108 static pair<const char*, streamsize> getBuf(const char *fileName) {
109 ifstream raw(fileName,ios::binary | ios::in);
110 if(!raw.is_open())
111 stop("rstream.getBuf: Error opening file");
112 raw.seekg(0,ios::end);
113 streampos fileSize = raw.tellg();
114 if(fileSize < 0)
115 stop("rstream.getBuf: Error reading file");
116 char *buf = new char[fileSize];
117 raw.seekg(0,ios::beg);
118 raw.read(buf, fileSize);
119 return pair<const char*, streamsize>(buf,fileSize);
120 }
121public:
122 // construct from a string
123 rstream_t( const char * buf, streamsize size ) :
124 _rstream_t( pair< const char *, streamsize >( buf, size ) )
125 {}
126 /* construct from a file whole content is fully read once to initialize the content of
127 this stream
128 */
129 rstream_t( string_t const & fileName )
130 : _rstream_t( getBuf( fileName.c_str() ) )
131 {
132 }
133
134 rstream_t & operator >>( int & x ) {
135 return do_read(x);
136 }
137 rstream_t & operator >>(unsigned &x) {
138 return do_read(x);
139 }
140 rstream_t & operator>>(short &x) {
141 return do_read(x);
142 }
143 rstream_t & operator>>(unsigned short &x) {
144 return do_read(x);
145 }
146 rstream_t & operator>>( symbol_t & e ) {
147 read((char*)&e, 18);
148 return *this;
149 }
150}; // class rstream_t
151
152// string table in a .OBJ file
153class StringTable {
154private:
155 map<string, unsigned> directory;
156 size_t length;
157 char *data;
158
159 // make <directory> from <length> bytes in <data>
160 void makeDirectory(void) {
161 unsigned i = 4;
162 while(i < length) {
163 string s = string(data + i);
164 directory.insert(make_pair(s, i));
165 i += s.size() + 1;
166 }
167 }
168 // initialize <length> and <data> with contents specified by the arguments
169 void init(const char *_data) {
170 unsigned _length = *(unsigned*)_data;
171
172 if(_length < sizeof(unsigned) || _length != *(unsigned*)_data)
173 stop("StringTable.init: Invalid symbol table");
174 if(_data[_length - 1]) {
175 // to prevent runaway strings, make sure the data ends with a zero
176 data = new char[length = _length + 1];
177 data[_length] = 0;
178 } else {
179 data = new char[length = _length];
180 }
181 *(unsigned*)data = length;
182 memcpy( data + sizeof(unsigned), _data + sizeof(unsigned), length - sizeof(unsigned) );
183 makeDirectory();
184 }
185public:
186 StringTable( rstream_t & f ) {
187 /* Construct string table by reading from f.
188 */
189 streampos s;
190 unsigned strSize;
191 char *strData;
192
193 s = f.tellg();
194 f>>strSize;
195 if(strSize < sizeof(unsigned))
196 stop("StringTable: Invalid string table");
197 strData = new char[strSize];
198 *(unsigned*)strData = strSize;
199 // read the raw data into <strData>
200 f.read(strData + sizeof(unsigned), strSize - sizeof(unsigned));
201 s = f.tellg() - s;
202 if(s < strSize)
203 stop("StringTable: Unexpected EOF");
204 init(strData);
205 delete[]strData;
206 }
207 StringTable(const set<string> &strings) {
208 /* Construct string table from given strings.
209 */
210 char *p;
211 set<string>::const_iterator it;
212 size_t s;
213
214 // count required size for data
215 for(length = sizeof(unsigned), it = strings.begin(); it != strings.end(); ++it) {
216 size_t l = (*it).size();
217
218 if(l > (unsigned) 0xFFFFFFFF)
219 stop("StringTable: String too long");
220 if(l > 8) {
221 length += l + 1;
222 if(length > (unsigned) 0xFFFFFFFF)
223 stop("StringTable: Symbol table too long");
224 }
225 }
226 data = new char[length];
227 *(unsigned*)data = length;
228 // populate data and directory
229 for(p = data + sizeof(unsigned), it = strings.begin(); it != strings.end(); ++it) {
230 const string &str = *it;
231 size_t l = str.size();
232 if(l > 8) {
233 directory.insert(make_pair(str, p - data));
234 memcpy(p, str.c_str(), l);
235 p[l] = 0;
236 p += l + 1;
237 }
238 }
239 }
240 ~StringTable() {
241 delete[] data;
242 }
243 /* Returns encoding for given string based on this string table.
244 Error if string length is greater than 8 but string is not in
245 the string table--returns 0.
246 */
247 int64_t encode(const string &str) {
248 int64_t r;
249
250 if(str.size() <= 8) {
251 // encoded directly
252 ((char*)&r)[7] = 0;
253 strncpy((char*)&r, str.c_str(), 8);
254 return r;
255 } else {
256 // represented as index into table
257 map<string,unsigned>::const_iterator it = directory.find(str);
258 if(it == directory.end())
259 stop("StringTable::encode: String now found in string table");
260 ((unsigned*)&r)[0] = 0;
261 ((unsigned*)&r)[1] = (*it).second;
262 return r;
263 }
264 }
265 /* Returns string represented by x based on this string table.
266 Error if x references an invalid position in the table--returns
267 the empty string.
268 */
269 string decode(int64_t x) const {
270 if(*(unsigned*)&x == 0) {
271 // represented as index into table
272 unsigned &p = ((unsigned*)&x)[1];
273 if(p >= length)
274 stop("StringTable::decode: Invalid string table lookup");
275 return string(data + p);
276 } else {
277 // encoded directly
278 char *p = (char*)&x;
279 int i;
280
281 for(i = 0; i < 8 && p[i]; ++i);
282 return string(p, i);
283 }
284 }
285 void write(ostream &os) {
286 os.write(data, length);
287 }
288};
289
290
291void
292obj_copy(
293 string_t const & src, // Name of source file.
294 string_t const & dst, // Name of destination file.
295 str_hash_t const & redefs // List of redefinititions.
296) {
297
298 set< string > strings; // set of all occurring symbols, appropriately prefixed
299 streampos fileSize;
300 size_t strTabStart;
301 unsigned symTabStart;
302 unsigned symNEntries;
303 int i;
304
305
306 string const error_reading = "Error reading \"" + src + "\" file: ";
307
308 rstream_t in( src );
309
310 in.seekg( 0, ios::end );
311 fileSize = in.tellg();
312
313 in.seekg( 8 );
314 in >> symTabStart >> symNEntries;
315 strTabStart = symTabStart + 18 * size_t( symNEntries );
316 in.seekg( strTabStart );
317 if ( in.eof() ) {
318 stop( error_reading + "Unexpected end of file" );
319 }
320 StringTable stringTableOld( in ); // Read original string table.
321
322 if ( in.tellg() != fileSize ) {
323 stop( error_reading + "Unexpected data after string table" );
324 }
325
326 // compute set of occurring strings with prefix added
327 for ( i = 0; i < symNEntries; ++ i ) {
328
329 symbol_t e;
330
331 in.seekg( symTabStart + i * 18 );
332 if ( in.eof() ) {
333 stop("hideSymbols: Unexpected EOF");
334 }
335 in >> e;
336 if ( in.fail() ) {
337 stop("hideSymbols: File read error");
338 }
339 if ( e.nAux ) {
340 i += e.nAux;
341 }
342 const string & s = stringTableOld.decode( e.name );
343 // if symbol is extern and found in <hide>, prefix and insert into strings,
344 // otherwise, just insert into strings without prefix
345 string_t name = find( redefs, s );
346 strings.insert( name != "" && e.storage_class == 2 ? name : s );
347 }
348
349 ofstream out( dst.c_str(), ios::trunc | ios::out | ios::binary );
350 if ( ! out.is_open() ) {
351 stop("hideSymbols: Error opening output file");
352 }
353
354 // make new string table from string set
355 StringTable stringTableNew = StringTable( strings );
356
357 // copy input file to output file up to just before the symbol table
358 in.seekg( 0 );
359 char * buf = new char[ symTabStart ];
360 in.read( buf, symTabStart );
361 out.write( buf, symTabStart );
362 delete [] buf;
363
364 // copy input symbol table to output symbol table with name translation
365 for ( i = 0; i < symNEntries; ++ i ) {
366 symbol_t e;
367
368 in.seekg( symTabStart + i * 18 );
369 if ( in.eof() ) {
370 stop("hideSymbols: Unexpected EOF");
371 }
372 in >> e;
373 if ( in.fail() ) {
374 stop("hideSymbols: File read error");
375 }
376 const string & s = stringTableOld.decode( e.name );
377 out.seekp( symTabStart + i * 18 );
378 string_t name = find( redefs, s );
379 e.name = stringTableNew.encode( ( e.storage_class == 2 && name != "" ) ? name : s );
380 out.write( (char*) & e, 18 );
381 if ( out.fail() ) {
382 stop( "hideSymbols: File write error" );
383 }
384 if ( e.nAux ) {
385 // copy auxiliary symbol table entries
386 int nAux = e.nAux;
387 for (int j = 1; j <= nAux; ++j ) {
388 in >> e;
389 out.seekp( symTabStart + ( i + j ) * 18 );
390 out.write( (char*) & e, 18 );
391 }
392 i += nAux;
393 }
394 }
395 // output string table
396 stringTableNew.write( out );
397}
398
399
400void
401split( string_t const & str, char ch, string_t & head, string_t & tail ) {
402 string_t::size_type pos = str.find( ch );
403 if ( pos == string_t::npos ) {
404 head = str;
405 tail = "";
406 } else {
407 head = str.substr( 0, pos );
408 tail = str.substr( pos + 1 );
409 }; // if
410} // split
411
412
413void help() {
414 std::cout
415 << "NAME\n"
416 << " objcopy -- copy and translate object files\n"
417 << "\n"
418 << "SYNOPSIS\n"
419 << " objcopy options... source destination\n"
420 << "\n"
421 << "OPTIONS\n"
422 << " --help Print this help and exit.\n"
423 << " --redefine-sym old=new\n"
424 << " Rename \"old\" symbol in source object file to \"new\" symbol in\n"
425 << " destination object file.\n"
426 << " --redefine-syms sym_file\n"
427 << " For each pair \"old new\" in sym_file rename \"old\" symbol in \n"
428 << " source object file to \"new\" symbol in destination object file.\n"
429 << "\n"
430 << "ARGUMENTS\n"
431 << " source The name of source object file.\n"
432 << " destination\n"
433 << " The name of destination object file.\n"
434 << "\n"
435 << "DESCRIPTION\n"
436 << " This program implements a minor bit of Linux* OS's objcopy utility on Windows* OS.\n"
437 << " It can copy object files and edit its symbol table.\n"
438 << "\n"
439 << "EXAMPLES\n"
440 << " \n"
441 << " > objcopy --redefine-sym fastcpy=__xxx_fastcpy a.obj b.obj\n"
442 << "\n";
443} // help
444
445
446int
447main( int argc, char const * argv[] ) {
448
449 strings_t args( argc - 1 );
450 str_hash_t redefs;
451 strings_t files;
452
453 std::copy( argv + 1, argv + argc, args.begin() );
454
455 while ( args.size() > 0 ) {
456 string_t arg = shift( args );
457 if ( arg.substr( 0, 2 ) == "--" ) {
458 // An option.
459 if ( 0 ) {
460 } else if ( arg == "--help" ) {
461 help();
462 return 0;
463 } else if ( arg == "--redefine-sym" ) {
464 if ( args.size() == 0 ) {
465 die( "\"" + arg + "\" option requires an argument" );
466 }; // if
467 // read list of symbol pairs "old new" from command line.
468 string_t redef = shift( args );
469 string_t old_sym;
470 string_t new_sym;
471 split( redef, '=', old_sym, new_sym );
472 if ( old_sym.length() == 0 || new_sym.length() == 0 ) {
473 die( "Illegal redefinition: \"" + redef + "\"; neither old symbol nor new symbol may be empty" );
474 }; // if
475 redefs.insert( str_pair_t( old_sym, new_sym ) );
476 } else if ( arg == "--redefine-syms" ) {
477 if ( args.size() == 0 ) {
478 die( "\"" + arg + "\" option requires an argument" );
479 }; // if
480 // read list of symbol pairs "old new" from file.
481 string_t fname = shift( args );
482 string_t redef;
483 ifstream ifs( fname.c_str() );
484 while ( ifs.good() ) {
485 getline( ifs, redef );// get pair of old/new symbols separated by space
486 string_t old_sym;
487 string_t new_sym;
488 // AC: gcount() does not work here (always return 0), so comment it
489 //if ( ifs.gcount() ) { // skip empty lines
490 split( redef, ' ', old_sym, new_sym );
491 if ( old_sym.length() == 0 || new_sym.length() == 0 ) {
492 break; // end of file reached (last empty line)
493 //die( "Illegal redefinition: \"" + redef + "\"; neither old symbol nor new symbol may be empty" );
494 }; // if
495 redefs.insert( str_pair_t( old_sym, new_sym ) );
496 //}
497 }
498 } else {
499 die( "Illegal option: \"" + arg + "\"" );
500 }; // if
501 } else {
502 // Not an option, a file name.
503 if ( files.size() >= 2 ) {
504 die( "Too many files specified; two files required (use --help option for help)" );
505 }; // if
506 files.push_back( arg );
507 }; // if
508 }; // while
509 if ( files.size() < 2 ) {
510 die( "Not enough files specified; two files required (use --help option for help)" );
511 }; // if
512
513 obj_copy( files[ 0 ], files[ 1 ], redefs );
514
515 return 0;
516
517} // main
518
519
520// end of file //