llvm-ar.cpp revision 551ccae044b0ff658fe629dd67edd5ffe75d10e8
1//===-- 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 "llvm/Module.h" 15#include "llvm/Bytecode/Reader.h" 16#include "llvm/Support/CommandLine.h" 17#include "llvm/Support/FileUtilities.h" 18#include "llvm/System/Signals.h" 19#include <string> 20#include <fstream> 21#include <iostream> 22#include <cstdio> 23#include <sys/stat.h> 24#include <sys/types.h> 25#include <fcntl.h> 26#include <unistd.h> 27#include <sys/mman.h> 28using namespace llvm; 29 30using std::string; 31 32 33#define ARFMAG "\n" /* header trailer string */ 34#define ARMAG "!<arch>\n" /* magic string */ 35#define SARMAG 8 /* length of magic string */ 36#define VERSION "llvm-ar is a part of the LLVM compiler infrastructure.\nPlease see http://llvm.cs.uiuc.edu for more information.\n"; 37 38 39// 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); 66 } 67}; 68 69 70//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 97std::vector<string> Members; 98 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 107// Store cumulative offset (file size + header size). 108// 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] = '/'; 129 Hdr.name[1] = '\0'; 130 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 140 std::vector<unsigned> offsets; //Vector of offsets into archive file 141 std::vector<char> names; //Vector of characters that are the symbol names. 142 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 155 // Size of file 156 unsigned Length = getFileSize(Members[i]); 157 if (Length == (unsigned)-1) { 158 std::cerr << "Error stating file\n"; 159 return false; 160 } 161 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(); 204 std::cout << "Symbol Table Size: " << symbolTableSize << "\n"; 205 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 220 //Update offsets write symbol table to archive. 221 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 230 std::cout << "Offset: " << offsets[i] << "\n"; 231 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) { 256 257 std::cout << "Member File Start: " << ArchiveFile.tellp() << "\n"; 258 259 ar_hdr Hdr; //Header for archive member file. 260 261 //stat the file to get info 262 struct stat StatBuf; 263 if (stat(Member.c_str(), &StatBuf) == -1 || StatBuf.st_size == 0) 264 return false; 265 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); 284 std::cout << "Size: " << Length << "\n"; 285 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 293 sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime); 294 295 //file member mode in OCTAL 296 sprintf(Hdr.mode,"%d", StatBuf.st_mode); 297 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 304 //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); 322 323 // Unmmap the memberfile 324 munmap((char*)buf, Length); 325 326 std::cout << "Member File End: " << ArchiveFile.tellp() << "\n"; 327 328 return true; 329} 330 331 332// CreateArchive - Generates archive with or without symbol table. 333// 334void CreateArchive() { 335 336 std::cerr << "Archive File: " << Archive << "\n"; 337 338 //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. 351 if(SymTable) { 352 std::cout << "Symbol Table Start: " << ArchiveFile.tellp() << "\n"; 353 if(!WriteSymbolTable(ArchiveFile)) { 354 std::cerr << "Error creating symbol table. Exiting program."; 355 exit(1); 356 } 357 std::cout << "Symbol Table End: " << ArchiveFile.tellp() << "\n"; 358 } 359 //Loop over all member files, and add to the archive. 360 for(unsigned i=0; i < Members.size(); ++i) { 361 if(ArchiveFile.tellp() % 2 != 0) 362 ArchiveFile << ARFMAG; 363 if(AddMemberToArchive(Members[i],ArchiveFile) != true) { 364 std::cerr << "Error adding " << Members[i] << "to archive. Exiting program.\n"; 365 exit(1); 366 } 367 } 368 369 //Close archive file. 370 ArchiveFile.close(); 371} 372 373//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() { 407 std::cout << VERSION; 408 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) 451 Members = std::vector<string>(RestofArgs); 452} 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} 547 548int main(int argc, char **argv) { 549 cl::ParseCommandLineOptions(argc, argv); 550 sys::PrintStackTraceOnErrorSignal(); 551 552 parseCL(); 553 554 //Create archive! 555 if(Create) 556 CreateArchive(); 557 558 return 0; 559} 560 561