New command line parsing. This isn't as perfect as I would have liked. The CommandLin...
[oota-llvm.git] / tools / llvm-ar / llvm-ar.cpp
1 //===-- tools/llvm-ar/llvm-ar.cpp - LLVM archive librarian utility --------===//
2 // 
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by the LLVM research group and is distributed under
6 // the University of Illinois Open Source License. See LICENSE.TXT for details.
7 // 
8 //===----------------------------------------------------------------------===//
9 //
10 // Builds up standard unix archive files (.a) containing LLVM bytecode.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "Support/CommandLine.h"
15 #include "llvm/Bytecode/Reader.h"
16 #include "llvm/Module.h"
17 #include <string>
18 #include <iostream>
19 #include <fstream>
20 #include <vector>
21 #include <sys/stat.h>
22 #include <cstdio>
23 #include <sys/types.h> 
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <sys/mman.h>
27
28 using namespace llvm;
29
30 using std::string;
31 using std::vector;
32 using std::cout;
33
34
35 #define  ARFMAG    "\n"      /* header trailer string */ 
36 #define  ARMAG   "!<arch>\n"  /* magic string */ 
37 #define  SARMAG  8            /* length of magic string */ 
38 #define VERSION "llvm-ar is a part of the LLVM compiler infrastructure.\nPlease see http://llvm.cs.uiuc.edu for more information.\n";
39
40
41 // Each file member is preceded by a file member header. Which is
42 // of the following format:
43 //
44 // char ar_name[16]  - '/' terminated file member name. 
45 //                     If the file name does not fit, a dummy name is used.
46 // char ar_date[12]  - file date in decimal
47 // char ar_uid[6]    - User id of file owner in decimal.
48 // char ar_gid[6]    - Group ID file belongs to in decimal.
49 // char ar_mode[8]   - File mode in octal.
50 // char ar_size[10]  - Size of file in decimal.
51 // char ar_fmag[2]   - Trailer of header file, a newline.
52 struct ar_hdr {
53   char name[16];
54   char date[12];
55   char uid[6];
56   char gid[6];
57   char mode[8];
58   char size[10];
59   char fmag[2]; 
60   void init() {
61     memset(name,' ',16);
62     memset(date,' ',12);
63     memset(uid,' ',6);
64     memset(gid,' ',6);
65     memset(mode,' ',8);
66     memset(size,' ',10);
67     memset(fmag,' ',2);
68     }
69 };
70
71
72 //Option for X32_64, not used but must allow it to be present.
73 cl::opt<bool> X32Option ("X32_64", cl::desc("Ignored option spelt -X32_64, for compatibility with AIX"), cl::Optional);
74
75 //llvm-ar options
76 cl::opt<string> Options(cl::Positional, cl::desc("{dmpqrstx}[abcfilNoPsSuvV] "), cl::Required);
77
78 //llvm-ar options
79 cl::list<string> RestofArgs(cl::Positional, cl::desc("[relpos] [count]] <archive-file> [members..]"), cl::Optional);
80
81 //booleans to represent Operation, only one can be preformed at a time
82 bool Print, Delete, Move, QuickAppend, InsertWithReplacement, DisplayTable;
83 bool Extract;
84
85 //Modifiers to follow operation to vary behavior
86 bool AddAfter, AddBefore, Create, TruncateNames, InsertBefore, UseCount;
87 bool OriginalDates,  FullPath, SymTable, OnlyUpdate, Verbose;
88
89 //Realtive Pos Arg
90 string RelPos;
91
92 //Count, use for multiple entries in the archive with the same name
93 int Count;
94
95 //Archive
96 string Archive;
97
98 //Member Files
99 vector<string> Members;
100
101
102 // WriteSymbolTable - Writes symbol table to ArchiveFile, return false
103 // on errors. Also returns by reference size of symbol table.
104 //
105 // Overview of method:
106 // 1) Generate the header for the symbol table. This is a normal
107 //    archive member header, but it has a zero length name.
108 // 2) For each archive member file, stat the file and parse the bytecode
109 //    Store cumulative offset (file size + header size).
110 // 3) Loop over all the symbols for the current member file, 
111 //    add offset entry to offset vector, and add symbol name to its vector.
112 //    Note: The symbol name vector is a vector of chars to speed up calculating
113 //    the total size of the symbol table.
114 // 4) Update offset vector once we know the total size of symbol table. This is
115 //    because the symbol table appears before all archive member file contents.
116 //    We add the size of magic string, and size of symbol table to each offset.
117 // 5) If the new updated offset it not even, we add 1 byte to offset because
118 //    a newline will be inserted when writing member files. This adjustment is
119 //    cummulative (ie. each time we have an odd offset we add 1 to total adjustment).
120 // 6) Lastly, write symbol table to file.
121 //
122 bool WriteSymbolTable(std::ofstream &ArchiveFile) {
123  
124   //Create header for symbol table. This is essentially an empty header with the
125   //name set to a '/' to indicate its a symbol table.
126   ar_hdr Hdr;
127   Hdr.init();
128
129   //Name of symbol table is '/'
130   Hdr.name[0] = '/';
131   Hdr.name[1] = '\0';
132   
133   //Set the header trailer to a newline
134   memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG));
135
136   
137   //Write header to archive file
138   ArchiveFile.write((char*)&Hdr, sizeof(Hdr));
139   
140
141   unsigned memoff = 0;  //Keep Track of total size of files added to archive
142   vector<unsigned> offsets; //Vector of offsets into archive file
143   vector<char> names; //Vector of characters that are the symbol names. 
144
145   //Loop over archive member files, parse bytecode, and generate symbol table.
146   for(unsigned i=0; i<Members.size(); ++i) { 
147     
148     //Open Member file for reading and copy to buffer
149     int FD = open(Members[i].c_str(),O_RDONLY);
150     
151     //Check for errors opening the file.
152     if (FD == -1) {
153       std::cerr << "Error opening file!\n";
154       return false;
155     }
156
157     //Stat the file to get its size.
158     struct stat StatBuf;
159     if (stat(Members[i].c_str(), &StatBuf) == -1 || StatBuf.st_size == 0) {
160       std::cerr << "Error stating file\n";
161       return false;
162     }
163
164     //Size of file
165     unsigned Length = StatBuf.st_size;
166     
167     //Read in file into a buffer.
168     unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ,
169                                               MAP_PRIVATE, FD, 0);
170   
171     //Check if mmap failed.
172     if (buf == (unsigned char*)MAP_FAILED) {
173       std::cerr << "Error mmapping file!\n";
174       return false;
175     }
176     
177     //Parse the bytecode file and get all the symbols.
178     string ErrorStr;
179     Module *M = ParseBytecodeBuffer(buf,Length,Members[i],&ErrorStr);
180     
181     //Check for errors parsing bytecode.
182     //if(ErrorStr) {
183     //std::cerr << "Error Parsing Bytecode\n";
184     //return false;
185     //}
186
187     //Loop over function names and global vars and add to symbol maps
188     for(Module::iterator I = M->begin(), E=M->end(); I != E; ++I) {
189       
190       //get function name
191       string NM = ((Function*)I)->getName();
192             
193       //Loop over the characters in the name and add to symbol name vector
194       for(unsigned i=0; i<NM.size(); ++i)
195         names.push_back(NM[i]);
196
197       //Each symbol is null terminated.
198       names.push_back('\0');
199
200       //Add offset to vector of offsets
201       offsets.push_back(memoff);
202     }
203
204     memoff += Length + sizeof(Hdr);
205   }
206
207   //Determine how large our symbol table is.
208   unsigned symbolTableSize = sizeof(Hdr) + 4 + 4*(offsets.size()) + names.size();
209   cout << "Symbol Table Size: " << symbolTableSize << "\n";
210
211   //Number of symbols should be in network byte order as well
212   char num[4];
213   unsigned temp = offsets.size();
214   num[0] = (temp >> 24) & 255;
215   num[1] = (temp >> 16) & 255;
216   num[2] = (temp >> 8) & 255;
217   num[3] = temp & 255;
218
219   //Write number of symbols to archive file
220   ArchiveFile.write(num,4);
221
222   //Adjustment to offset to start files on even byte boundaries
223   unsigned adjust = 0;
224   
225   //Update offsets write symbol table to archive.
226   for(unsigned i=0; i<offsets.size(); ++i) {
227     char output[4];
228     offsets[i] = offsets[i] + symbolTableSize + SARMAG;
229     offsets[i] += adjust;
230     if((offsets[i] % 2 != 0)) {
231       adjust++;
232       offsets[i] += adjust;
233     }
234     
235     cout << "Offset: " << offsets[i] << "\n";
236     output[0] = (offsets[i] >> 24) & 255;
237     output[1] = (offsets[i] >> 16) & 255;
238     output[2] = (offsets[i] >> 8) & 255;
239     output[3] = offsets[i] & 255;
240     ArchiveFile.write(output,4);
241   }
242
243
244   //Write out symbol name vector.
245   for(unsigned i=0; i<names.size(); ++i)
246     ArchiveFile << names[i];
247
248   return true;
249 }
250
251 // AddMemberToArchive - Writes member file to archive. Returns false on errors.
252 // 
253 // Overview of method: 
254 // 1) Open file, and stat it.  
255 // 2) Fill out header using stat information. If name is longer then 15 
256 //    characters, use "dummy" name.
257 // 3) Write header and file contents to disk.
258 // 4) Keep track of total offset into file, and insert a newline if it is odd.
259 //
260 bool AddMemberToArchive(string Member, std::ofstream &ArchiveFile) {
261
262   cout << "Member File Start: " << ArchiveFile.tellp() << "\n";
263
264   ar_hdr Hdr; //Header for archive member file.
265
266   //stat the file to get info
267   struct stat StatBuf;
268   if (stat(Member.c_str(), &StatBuf) == -1 || StatBuf.st_size == 0)
269     return false;
270
271   //fill in header
272   
273   //set name to white spaces
274   memset(Hdr.name,' ', sizeof(Hdr.name));
275
276   //check the size of the name, if less than 15, we can copy it directly
277   //otherwise we give it a dummy name for now
278   if(Member.length() < 16)
279     memcpy(Hdr.name,Member.c_str(),Member.length());
280   else
281     memcpy(Hdr.name, "Dummy", 5);
282
283   //terminate name with forward slash
284   Hdr.name[15] = '/';
285
286   //file member size in decimal
287   unsigned Length = StatBuf.st_size;
288   sprintf(Hdr.size,"%d", Length);
289   cout << "Size: " << Length << "\n";
290
291   //file member user id in decimal
292   sprintf(Hdr.uid, "%d", StatBuf.st_uid);
293
294   //file member group id in decimal
295   sprintf(Hdr.gid, "%d", StatBuf.st_gid);
296
297   //file member date in decimal
298   sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime);
299   
300   //file member mode in OCTAL
301   sprintf(Hdr.mode,"%d", StatBuf.st_mode);
302  
303   //add our header trailer
304   memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG));
305
306   //write header to archive file
307   ArchiveFile.write((char*)&Hdr, sizeof(Hdr));
308   
309   //open Member file for reading and copy to buffer
310   int FD = open(Member.c_str(),O_RDONLY);
311   if (FD == -1) {
312     std::cerr << "Error opening file!\n";
313     return false;
314   }
315
316   unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ,
317                           MAP_PRIVATE, FD, 0);
318   
319   //check if mmap failed
320   if (buf == (unsigned char*)MAP_FAILED) {
321     std::cerr << "Error mmapping file!\n";
322     return false;
323   }
324
325   //write to archive file
326   ArchiveFile.write((char*)buf,Length);
327   
328   // Unmmap the memberfile
329   munmap((char*)buf, Length);
330   
331   cout << "Member File End: " << ArchiveFile.tellp() << "\n";
332
333   return true;
334 }
335
336
337 // CreateArchive - Generates archive with or without symbol table.
338 //
339 void CreateArchive() {
340   
341   std::cerr << "Archive File: " << Archive << "\n";
342
343   //Create archive file for output.
344   std::ofstream ArchiveFile(Archive.c_str());
345   
346   //Check for errors opening or creating archive file.
347   if(!ArchiveFile.is_open() || ArchiveFile.bad() ) {
348     std::cerr << "Error opening Archive File\n";
349     exit(1);
350   }
351
352   //Write magic string to archive.
353   ArchiveFile << ARMAG;
354
355   //If the '-s' option was specified, generate symbol table.
356   if(SymTable) {
357     cout << "Symbol Table Start: " << ArchiveFile.tellp() << "\n";
358     if(!WriteSymbolTable(ArchiveFile)) {
359       std::cerr << "Error creating symbol table. Exiting program.";
360       exit(1);
361     }
362     cout << "Symbol Table End: " << ArchiveFile.tellp() << "\n";
363   }
364   //Loop over all member files, and add to the archive.
365   for(unsigned i=0; i < Members.size(); ++i) {
366     if(ArchiveFile.tellp() % 2 != 0)
367       ArchiveFile << ARFMAG;
368     if(AddMemberToArchive(Members[i],ArchiveFile) != true) {
369       std::cerr << "Error adding " << Members[i] << "to archive. Exiting program.\n";
370       exit(1);
371     }
372   }
373   
374   //Close archive file.
375   ArchiveFile.close();
376 }
377
378 //Print out usage for errors in command line
379 void printUse() {
380   std::cout << "USAGE: ar [-X32_64] [-]{dmpqrstx}[abcfilNoPsSuvV] [member-name] [count] archive-file [files..]\n\n";
381
382   std::cout << "commands:\n" <<
383     "d            - delete file(s) from the archive\n"
384   << "m[ab]        - move file(s) in the archive\n"
385   << "p            - print file(s) found in the archive\n"
386   << "q[f]         - quick append file(s) to the archive\n"
387   << "r[ab][f][u]  - replace existing or insert new file(s) into the archive\n"
388   << "t            - display contents of archive\n"
389   << "x[o]         - extract file(s) from the archive\n";
390
391   std::cout << "\ncommand specific modifiers:\n"
392             << "[a]          - put file(s) after [member-name]\n"
393             << "[b]          - put file(s) before [member-name] (same as [i])\n"
394             << "[N]          - use instance [count] of name\n"
395             << "[f]          - truncate inserted file names\n"
396             << "[P]          - use full path names when matching\n"
397             << "[o]          - preserve original dates\n"
398             << "[u]          - only replace files that are newer than current archive contents\n";
399
400   std::cout << "generic modifiers:\n"
401             << "[c]          - do not warn if the library had to be created\n"
402             << "[s]          - create an archive index (cf. ranlib)\n"
403             << "[S]          - do not build a symbol table\n"
404             << "[v]          - be verbose\n"
405             << "[V]          - display the version number\n";
406   exit(1);
407 }
408
409
410 //Print version
411 void printVersion() {
412   cout << VERSION;
413   exit(0);
414 }
415
416 //Extract the memberfile name from the command line
417 void getRelPos() {
418   if(RestofArgs.size() > 0) {
419     RelPos = RestofArgs[0];
420     RestofArgs.erase(RestofArgs.begin());
421   }
422   //Throw error if needed and not present
423   else
424     printUse();
425 }
426
427 //Extract count from the command line
428 void getCount() {
429   if(RestofArgs.size() > 0) {
430     Count = atoi(RestofArgs[0].c_str());
431     RestofArgs.erase(RestofArgs.begin());
432   }
433   //Throw error if needed and not present
434   else
435     printUse();
436 }
437
438 //Get the Archive File Name from the command line
439 void getArchive() {
440   std::cerr << RestofArgs.size() << "\n";
441   if(RestofArgs.size() > 0) {
442     Archive = RestofArgs[0];
443     RestofArgs.erase(RestofArgs.begin());
444   }
445   //Throw error if needed and not present
446   else
447     printUse();
448 }
449
450
451 //Copy over remaining items in RestofArgs to our Member File vector.
452 //This is just for clarity.
453 void getMembers() {
454   std::cerr << RestofArgs.size() << "\n";
455   if(RestofArgs.size() > 0)
456     Members = vector<string>(RestofArgs); 
457 }
458
459 // Parse the operations and operation modifiers
460 // FIXME: Not all of these options has been implemented, but we still
461 // do all the command line parsing for them.
462 void parseCL() {
463
464   //Keep track of number of operations. We can only specify one
465   //per execution
466   unsigned NumOperations = 0;
467
468   for(unsigned i=0; i<Options.size(); ++i) {
469     switch(Options[i]) {
470     case 'd':
471       ++NumOperations;
472       Delete = true;
473       break;
474     case 'm':
475       ++NumOperations;
476       Move = true;
477       break;
478     case 'p':
479       ++NumOperations;
480       Print = true;
481       break;
482     case 'r':
483       ++NumOperations;
484       InsertWithReplacement = true;
485       break;
486     case 't':
487       ++NumOperations;
488       DisplayTable = true;
489       break;
490     case 'x':
491       ++NumOperations;
492       Extract = true;
493       break;
494     case 'a':
495       AddAfter = true;
496       getRelPos();
497       break;
498     case 'b':
499       AddBefore = true;
500       getRelPos();
501       break;
502     case 'c':
503       Create = true;
504       break;
505     case 'f':
506       TruncateNames = true;
507       break;
508     case 'i':
509       InsertBefore = true;
510       getRelPos();
511       break;
512     case 'l':
513       break;
514     case 'N':
515       UseCount = true;
516       getCount();
517       break;
518     case 'o':
519       OriginalDates = true;
520       break;
521     case 'P':
522       FullPath = true;
523       break;
524     case 's':
525       SymTable = true;
526       break;
527     case 'S':
528       SymTable = false;
529       break;
530     case 'u':
531       OnlyUpdate = true;
532       break;
533     case 'v':
534       Verbose = true;
535       break;
536     case 'V':
537       printVersion();
538       break;
539     default:
540       printUse();
541     }
542   }
543
544   //Check that only one operation has been specified
545   if(NumOperations > 1)
546     printUse();
547
548   getArchive();
549   getMembers();
550
551 }
552
553 int main(int argc, char **argv) {
554
555   //Parse Command line options
556   cl::ParseCommandLineOptions(argc, argv);
557   parseCL();
558
559   //Create archive!
560   if(Create)
561     CreateArchive();
562
563   return 0;
564 }
565