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