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