blob: 07fc1810e82fdb111d78e1cc2a0105c816cbed01 [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"
Reid Spencer551ccae2004-09-01 22:55:40 +000016#include "llvm/Support/CommandLine.h"
17#include "llvm/Support/FileUtilities.h"
Chris Lattnerbed85ff2004-05-27 05:41:36 +000018#include "llvm/System/Signals.h"
Tanya Lattner14baebf2003-08-28 15:22:38 +000019#include <string>
Tanya Lattner14baebf2003-08-28 15:22:38 +000020#include <fstream>
Reid Spencer86f42bd2004-07-04 12:20:55 +000021#include <iostream>
Brian Gaeke4fa9fd32003-10-10 18:47:08 +000022#include <cstdio>
Chris Lattner97f752f2003-12-30 07:45:46 +000023#include <sys/stat.h>
Tanya Lattner14baebf2003-08-28 15:22:38 +000024#include <sys/types.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <sys/mman.h>
Brian Gaeked0fde302003-11-11 22:41:34 +000028using namespace llvm;
29
Tanya Lattner14baebf2003-08-28 15:22:38 +000030using std::string;
Tanya Lattner14baebf2003-08-28 15:22:38 +000031
32
33#define ARFMAG "\n" /* header trailer string */
34#define ARMAG "!<arch>\n" /* magic string */
35#define SARMAG 8 /* length of magic string */
Tanya Lattner57bd7962003-12-06 23:01:25 +000036#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 +000037
Tanya Lattner14baebf2003-08-28 15:22:38 +000038
Tanya Lattner57bd7962003-12-06 23:01:25 +000039// 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);
Tanya Lattner14baebf2003-08-28 15:22:38 +000066 }
Tanya Lattner57bd7962003-12-06 23:01:25 +000067};
Tanya Lattner14baebf2003-08-28 15:22:38 +000068
69
Tanya Lattner57bd7962003-12-06 23:01:25 +000070//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
Chris Lattner97f752f2003-12-30 07:45:46 +000097std::vector<string> Members;
Tanya Lattner14baebf2003-08-28 15:22:38 +000098
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
Misha Brukmancf00c4a2003-10-10 17:57:28 +0000107// Store cumulative offset (file size + header size).
Tanya Lattner14baebf2003-08-28 15:22:38 +0000108// 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] = '/';
Tanya Lattnere3df92e2003-09-13 03:18:56 +0000129 Hdr.name[1] = '\0';
Tanya Lattner14baebf2003-08-28 15:22:38 +0000130
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
Chris Lattner97f752f2003-12-30 07:45:46 +0000140 std::vector<unsigned> offsets; //Vector of offsets into archive file
141 std::vector<char> names; //Vector of characters that are the symbol names.
Tanya Lattner14baebf2003-08-28 15:22:38 +0000142
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
Chris Lattner97f752f2003-12-30 07:45:46 +0000155 // Size of file
156 unsigned Length = getFileSize(Members[i]);
157 if (Length == (unsigned)-1) {
Tanya Lattner14baebf2003-08-28 15:22:38 +0000158 std::cerr << "Error stating file\n";
159 return false;
160 }
Tanya Lattner14baebf2003-08-28 15:22:38 +0000161
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();
Chris Lattner97f752f2003-12-30 07:45:46 +0000204 std::cout << "Symbol Table Size: " << symbolTableSize << "\n";
Tanya Lattner14baebf2003-08-28 15:22:38 +0000205
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
Misha Brukmancf00c4a2003-10-10 17:57:28 +0000220 //Update offsets write symbol table to archive.
Tanya Lattner14baebf2003-08-28 15:22:38 +0000221 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
Chris Lattner97f752f2003-12-30 07:45:46 +0000230 std::cout << "Offset: " << offsets[i] << "\n";
Tanya Lattner14baebf2003-08-28 15:22:38 +0000231 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) {
Tanya Lattner57bd7962003-12-06 23:01:25 +0000256
Chris Lattner97f752f2003-12-30 07:45:46 +0000257 std::cout << "Member File Start: " << ArchiveFile.tellp() << "\n";
Tanya Lattner57bd7962003-12-06 23:01:25 +0000258
Tanya Lattner14baebf2003-08-28 15:22:38 +0000259 ar_hdr Hdr; //Header for archive member file.
Tanya Lattner57bd7962003-12-06 23:01:25 +0000260
Tanya Lattner14baebf2003-08-28 15:22:38 +0000261 //stat the file to get info
262 struct stat StatBuf;
263 if (stat(Member.c_str(), &StatBuf) == -1 || StatBuf.st_size == 0)
Tanya Lattner57bd7962003-12-06 23:01:25 +0000264 return false;
Tanya Lattner14baebf2003-08-28 15:22:38 +0000265
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);
Chris Lattner97f752f2003-12-30 07:45:46 +0000284 std::cout << "Size: " << Length << "\n";
Tanya Lattner14baebf2003-08-28 15:22:38 +0000285
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
Misha Brukman8d2ff132003-09-23 17:27:02 +0000293 sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime);
Tanya Lattner14baebf2003-08-28 15:22:38 +0000294
295 //file member mode in OCTAL
296 sprintf(Hdr.mode,"%d", StatBuf.st_mode);
Tanya Lattner14baebf2003-08-28 15:22:38 +0000297
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
Tanya Lattner14baebf2003-08-28 15:22:38 +0000304 //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);
Tanya Lattner57bd7962003-12-06 23:01:25 +0000322
Tanya Lattner14baebf2003-08-28 15:22:38 +0000323 // Unmmap the memberfile
324 munmap((char*)buf, Length);
Tanya Lattner57bd7962003-12-06 23:01:25 +0000325
Chris Lattner97f752f2003-12-30 07:45:46 +0000326 std::cout << "Member File End: " << ArchiveFile.tellp() << "\n";
Tanya Lattner14baebf2003-08-28 15:22:38 +0000327
328 return true;
329}
330
331
332// CreateArchive - Generates archive with or without symbol table.
333//
334void CreateArchive() {
335
Tanya Lattner57bd7962003-12-06 23:01:25 +0000336 std::cerr << "Archive File: " << Archive << "\n";
337
Tanya Lattner14baebf2003-08-28 15:22:38 +0000338 //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.
Tanya Lattner57bd7962003-12-06 23:01:25 +0000351 if(SymTable) {
Chris Lattner97f752f2003-12-30 07:45:46 +0000352 std::cout << "Symbol Table Start: " << ArchiveFile.tellp() << "\n";
Tanya Lattner14baebf2003-08-28 15:22:38 +0000353 if(!WriteSymbolTable(ArchiveFile)) {
354 std::cerr << "Error creating symbol table. Exiting program.";
355 exit(1);
356 }
Chris Lattner97f752f2003-12-30 07:45:46 +0000357 std::cout << "Symbol Table End: " << ArchiveFile.tellp() << "\n";
Tanya Lattner14baebf2003-08-28 15:22:38 +0000358 }
359 //Loop over all member files, and add to the archive.
Tanya Lattner57bd7962003-12-06 23:01:25 +0000360 for(unsigned i=0; i < Members.size(); ++i) {
Tanya Lattner14baebf2003-08-28 15:22:38 +0000361 if(ArchiveFile.tellp() % 2 != 0)
362 ArchiveFile << ARFMAG;
Tanya Lattner14baebf2003-08-28 15:22:38 +0000363 if(AddMemberToArchive(Members[i],ArchiveFile) != true) {
Tanya Lattner57bd7962003-12-06 23:01:25 +0000364 std::cerr << "Error adding " << Members[i] << "to archive. Exiting program.\n";
Tanya Lattner14baebf2003-08-28 15:22:38 +0000365 exit(1);
366 }
Tanya Lattner14baebf2003-08-28 15:22:38 +0000367 }
368
369 //Close archive file.
370 ArchiveFile.close();
371}
372
Tanya Lattner57bd7962003-12-06 23:01:25 +0000373//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() {
Chris Lattner97f752f2003-12-30 07:45:46 +0000407 std::cout << VERSION;
Tanya Lattner57bd7962003-12-06 23:01:25 +0000408 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)
Chris Lattner97f752f2003-12-30 07:45:46 +0000451 Members = std::vector<string>(RestofArgs);
Tanya Lattner57bd7962003-12-06 23:01:25 +0000452}
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}
Tanya Lattner14baebf2003-08-28 15:22:38 +0000547
548int main(int argc, char **argv) {
Tanya Lattner57bd7962003-12-06 23:01:25 +0000549 cl::ParseCommandLineOptions(argc, argv);
Reid Spencer9de7b332004-08-29 19:28:55 +0000550 sys::PrintStackTraceOnErrorSignal();
Chris Lattnerf73b4ca2004-02-19 20:32:12 +0000551
Tanya Lattner57bd7962003-12-06 23:01:25 +0000552 parseCL();
Tanya Lattner14baebf2003-08-28 15:22:38 +0000553
554 //Create archive!
Tanya Lattner57bd7962003-12-06 23:01:25 +0000555 if(Create)
556 CreateArchive();
Tanya Lattner14baebf2003-08-28 15:22:38 +0000557
558 return 0;
559}
Tanya Lattner57bd7962003-12-06 23:01:25 +0000560