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