blob: 33c14b56a314d2830055ef1215b3a22954d2ab59 [file] [log] [blame]
Brian Gaeke81d153e2003-10-10 18:47:08 +00001//===-- tools/llvm-ar/llvm-ar.cpp - LLVM archive librarian utility --------===//
John Criswell09344dc2003-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 Lattner57c70a62003-08-28 15:22:38 +00009//
Brian Gaeke81d153e2003-10-10 18:47:08 +000010// Builds up standard unix archive files (.a) containing LLVM bytecode.
Tanya Lattner57c70a62003-08-28 15:22:38 +000011//
12//===----------------------------------------------------------------------===//
Brian Gaeke81d153e2003-10-10 18:47:08 +000013
Tanya Lattner57c70a62003-08-28 15:22:38 +000014#include "Support/CommandLine.h"
15#include "llvm/Bytecode/Reader.h"
16#include "llvm/Module.h"
Tanya Lattner57c70a62003-08-28 15:22:38 +000017#include <string>
18#include <iostream>
19#include <fstream>
20#include <vector>
21#include <sys/stat.h>
Brian Gaeke81d153e2003-10-10 18:47:08 +000022#include <cstdio>
Tanya Lattner57c70a62003-08-28 15:22:38 +000023#include <sys/types.h>
24#include <fcntl.h>
25#include <unistd.h>
26#include <sys/mman.h>
27
Brian Gaeke960707c2003-11-11 22:41:34 +000028using namespace llvm;
29
Tanya Lattner57c70a62003-08-28 15:22:38 +000030using 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 */
Tanya Lattnerc970a382003-12-06 23:01:25 +000038#define VERSION "llvm-ar is a part of the LLVM compiler infrastructure.\nPlease see http://llvm.cs.uiuc.edu for more information.\n";
Tanya Lattner57c70a62003-08-28 15:22:38 +000039
Tanya Lattner57c70a62003-08-28 15:22:38 +000040
Tanya Lattnerc970a382003-12-06 23:01:25 +000041// 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);
Tanya Lattner57c70a62003-08-28 15:22:38 +000068 }
Tanya Lattnerc970a382003-12-06 23:01:25 +000069};
Tanya Lattner57c70a62003-08-28 15:22:38 +000070
71
Tanya Lattnerc970a382003-12-06 23:01:25 +000072//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;
Tanya Lattner57c70a62003-08-28 15:22:38 +0000100
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
Misha Brukman8b2bd4e2003-10-10 17:57:28 +0000109// Store cumulative offset (file size + header size).
Tanya Lattner57c70a62003-08-28 15:22:38 +0000110// 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] = '/';
Tanya Lattner124ddef2003-09-13 03:18:56 +0000131 Hdr.name[1] = '\0';
Tanya Lattner57c70a62003-08-28 15:22:38 +0000132
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
Misha Brukman8b2bd4e2003-10-10 17:57:28 +0000225 //Update offsets write symbol table to archive.
Tanya Lattner57c70a62003-08-28 15:22:38 +0000226 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) {
Tanya Lattnerc970a382003-12-06 23:01:25 +0000261
262 cout << "Member File Start: " << ArchiveFile.tellp() << "\n";
263
Tanya Lattner57c70a62003-08-28 15:22:38 +0000264 ar_hdr Hdr; //Header for archive member file.
Tanya Lattnerc970a382003-12-06 23:01:25 +0000265
Tanya Lattner57c70a62003-08-28 15:22:38 +0000266 //stat the file to get info
267 struct stat StatBuf;
268 if (stat(Member.c_str(), &StatBuf) == -1 || StatBuf.st_size == 0)
Tanya Lattnerc970a382003-12-06 23:01:25 +0000269 return false;
Tanya Lattner57c70a62003-08-28 15:22:38 +0000270
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
Misha Brukman08993c82003-09-23 17:27:02 +0000298 sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime);
Tanya Lattner57c70a62003-08-28 15:22:38 +0000299
300 //file member mode in OCTAL
301 sprintf(Hdr.mode,"%d", StatBuf.st_mode);
Tanya Lattner57c70a62003-08-28 15:22:38 +0000302
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
Tanya Lattner57c70a62003-08-28 15:22:38 +0000309 //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);
Tanya Lattnerc970a382003-12-06 23:01:25 +0000327
Tanya Lattner57c70a62003-08-28 15:22:38 +0000328 // Unmmap the memberfile
329 munmap((char*)buf, Length);
Tanya Lattnerc970a382003-12-06 23:01:25 +0000330
331 cout << "Member File End: " << ArchiveFile.tellp() << "\n";
Tanya Lattner57c70a62003-08-28 15:22:38 +0000332
333 return true;
334}
335
336
337// CreateArchive - Generates archive with or without symbol table.
338//
339void CreateArchive() {
340
Tanya Lattnerc970a382003-12-06 23:01:25 +0000341 std::cerr << "Archive File: " << Archive << "\n";
342
Tanya Lattner57c70a62003-08-28 15:22:38 +0000343 //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.
Tanya Lattnerc970a382003-12-06 23:01:25 +0000356 if(SymTable) {
Tanya Lattner57c70a62003-08-28 15:22:38 +0000357 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.
Tanya Lattnerc970a382003-12-06 23:01:25 +0000365 for(unsigned i=0; i < Members.size(); ++i) {
Tanya Lattner57c70a62003-08-28 15:22:38 +0000366 if(ArchiveFile.tellp() % 2 != 0)
367 ArchiveFile << ARFMAG;
Tanya Lattner57c70a62003-08-28 15:22:38 +0000368 if(AddMemberToArchive(Members[i],ArchiveFile) != true) {
Tanya Lattnerc970a382003-12-06 23:01:25 +0000369 std::cerr << "Error adding " << Members[i] << "to archive. Exiting program.\n";
Tanya Lattner57c70a62003-08-28 15:22:38 +0000370 exit(1);
371 }
Tanya Lattner57c70a62003-08-28 15:22:38 +0000372 }
373
374 //Close archive file.
375 ArchiveFile.close();
376}
377
Tanya Lattnerc970a382003-12-06 23:01:25 +0000378//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}
Tanya Lattner57c70a62003-08-28 15:22:38 +0000552
553int main(int argc, char **argv) {
554
555 //Parse Command line options
Tanya Lattnerc970a382003-12-06 23:01:25 +0000556 cl::ParseCommandLineOptions(argc, argv);
557 parseCL();
Tanya Lattner57c70a62003-08-28 15:22:38 +0000558
559 //Create archive!
Tanya Lattnerc970a382003-12-06 23:01:25 +0000560 if(Create)
561 CreateArchive();
Tanya Lattner57c70a62003-08-28 15:22:38 +0000562
563 return 0;
564}
Tanya Lattnerc970a382003-12-06 23:01:25 +0000565