llvm-ar.cpp revision 57bd7966049f77d93ed937780bee8a6c4e260cef
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
28using namespace llvm;
29
30using std::string;
31using std::vector;
32using 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.
52struct 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.
73cl::opt<bool> X32Option ("X32_64", cl::desc("Ignored option spelt -X32_64, for compatibility with AIX"), cl::Optional);
74
75//llvm-ar options
76cl::opt<string> Options(cl::Positional, cl::desc("{dmpqrstx}[abcfilNoPsSuvV] "), cl::Required);
77
78//llvm-ar options
79cl::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
82bool Print, Delete, Move, QuickAppend, InsertWithReplacement, DisplayTable;
83bool Extract;
84
85//Modifiers to follow operation to vary behavior
86bool AddAfter, AddBefore, Create, TruncateNames, InsertBefore, UseCount;
87bool OriginalDates,  FullPath, SymTable, OnlyUpdate, Verbose;
88
89//Realtive Pos Arg
90string RelPos;
91
92//Count, use for multiple entries in the archive with the same name
93int Count;
94
95//Archive
96string Archive;
97
98//Member Files
99vector<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//
122bool 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//
260bool 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//
339void 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
379void 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
411void printVersion() {
412  cout << VERSION;
413  exit(0);
414}
415
416//Extract the memberfile name from the command line
417void 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
428void 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
439void 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.
453void 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.
462void 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
553int 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
566