llvm-ar.cpp revision 57bd7966049f77d93ed937780bee8a6c4e260cef
1//===-- tools/llvm-ar/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 "Support/CommandLine.h" 15#include "llvm/Bytecode/Reader.h" 16#include "llvm/Module.h" 17#include <string> 18#include <iostream> 19#include <fstream> 20#include <vector> 21#include <sys/stat.h> 22#include <cstdio> 23#include <sys/types.h> 24#include <fcntl.h> 25#include <unistd.h> 26#include <sys/mman.h> 27 28using namespace llvm; 29 30using 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 */ 38#define VERSION "llvm-ar is a part of the LLVM compiler infrastructure.\nPlease see http://llvm.cs.uiuc.edu for more information.\n"; 39 40 41// 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); 68 } 69}; 70 71 72//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; 100 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 109// Store cumulative offset (file size + header size). 110// 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] = '/'; 131 Hdr.name[1] = '\0'; 132 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 225 //Update offsets write symbol table to archive. 226 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) { 261 262 cout << "Member File Start: " << ArchiveFile.tellp() << "\n"; 263 264 ar_hdr Hdr; //Header for archive member file. 265 266 //stat the file to get info 267 struct stat StatBuf; 268 if (stat(Member.c_str(), &StatBuf) == -1 || StatBuf.st_size == 0) 269 return false; 270 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 298 sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime); 299 300 //file member mode in OCTAL 301 sprintf(Hdr.mode,"%d", StatBuf.st_mode); 302 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 309 //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); 327 328 // Unmmap the memberfile 329 munmap((char*)buf, Length); 330 331 cout << "Member File End: " << ArchiveFile.tellp() << "\n"; 332 333 return true; 334} 335 336 337// CreateArchive - Generates archive with or without symbol table. 338// 339void CreateArchive() { 340 341 std::cerr << "Archive File: " << Archive << "\n"; 342 343 //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. 356 if(SymTable) { 357 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. 365 for(unsigned i=0; i < Members.size(); ++i) { 366 if(ArchiveFile.tellp() % 2 != 0) 367 ArchiveFile << ARFMAG; 368 if(AddMemberToArchive(Members[i],ArchiveFile) != true) { 369 std::cerr << "Error adding " << Members[i] << "to archive. Exiting program.\n"; 370 exit(1); 371 } 372 } 373 374 //Close archive file. 375 ArchiveFile.close(); 376} 377 378//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} 552 553int main(int argc, char **argv) { 554 555 //Parse Command line options 556 cl::ParseCommandLineOptions(argc, argv); 557 parseCL(); 558 559 //Create archive! 560 if(Create) 561 CreateArchive(); 562 563 return 0; 564} 565 566